Commit 4bf43ab9 authored by longpanda's avatar longpanda
Browse files

VentoyPlugson ---- A GUI ventoy.json configurator

parent 9eeb94e8
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
#include "fat_misc.h"
//-----------------------------------------------------------------------------
// fatfs_lfn_cache_init: Clear long file name cache
//-----------------------------------------------------------------------------
void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable)
{
int i = 0;
lfn->no_of_strings = 0;
#if FATFS_INC_LFN_SUPPORT
// Zero out buffer also
if (wipeTable)
for (i=0;i<MAX_LONGFILENAME_ENTRIES;i++)
memset(lfn->String[i], 0x00, MAX_LFN_ENTRY_LENGTH);
#endif
}
//-----------------------------------------------------------------------------
// fatfs_lfn_cache_entry - Function extracts long file name text from sector
// at a specific offset
//-----------------------------------------------------------------------------
#if FATFS_INC_LFN_SUPPORT
void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer)
{
uint8 LFNIndex, i;
LFNIndex = entryBuffer[0] & 0x1F;
// Limit file name to cache size!
if (LFNIndex > MAX_LONGFILENAME_ENTRIES)
return ;
// This is an error condition
if (LFNIndex == 0)
return ;
if (lfn->no_of_strings == 0)
lfn->no_of_strings = LFNIndex;
lfn->String[LFNIndex-1][0] = entryBuffer[1];
lfn->String[LFNIndex-1][1] = entryBuffer[3];
lfn->String[LFNIndex-1][2] = entryBuffer[5];
lfn->String[LFNIndex-1][3] = entryBuffer[7];
lfn->String[LFNIndex-1][4] = entryBuffer[9];
lfn->String[LFNIndex-1][5] = entryBuffer[0x0E];
lfn->String[LFNIndex-1][6] = entryBuffer[0x10];
lfn->String[LFNIndex-1][7] = entryBuffer[0x12];
lfn->String[LFNIndex-1][8] = entryBuffer[0x14];
lfn->String[LFNIndex-1][9] = entryBuffer[0x16];
lfn->String[LFNIndex-1][10] = entryBuffer[0x18];
lfn->String[LFNIndex-1][11] = entryBuffer[0x1C];
lfn->String[LFNIndex-1][12] = entryBuffer[0x1E];
for (i=0; i<MAX_LFN_ENTRY_LENGTH; i++)
if (lfn->String[LFNIndex-1][i]==0xFF)
lfn->String[LFNIndex-1][i] = 0x20; // Replace with spaces
}
#endif
//-----------------------------------------------------------------------------
// fatfs_lfn_cache_get: Get a reference to the long filename
//-----------------------------------------------------------------------------
#if FATFS_INC_LFN_SUPPORT
char* fatfs_lfn_cache_get(struct lfn_cache *lfn)
{
// Null terminate long filename
if (lfn->no_of_strings == MAX_LONGFILENAME_ENTRIES)
lfn->Null = '\0';
else if (lfn->no_of_strings)
lfn->String[lfn->no_of_strings][0] = '\0';
else
lfn->String[0][0] = '\0';
return (char*)&lfn->String[0][0];
}
#endif
//-----------------------------------------------------------------------------
// fatfs_entry_lfn_text: If LFN text entry found
//-----------------------------------------------------------------------------
#if FATFS_INC_LFN_SUPPORT
int fatfs_entry_lfn_text(struct fat_dir_entry *entry)
{
if ((entry->Attr & FILE_ATTR_LFN_TEXT) == FILE_ATTR_LFN_TEXT)
return 1;
else
return 0;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_entry_lfn_invalid: If SFN found not relating to LFN
//-----------------------------------------------------------------------------
#if FATFS_INC_LFN_SUPPORT
int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry)
{
if ( (entry->Name[0]==FILE_HEADER_BLANK) ||
(entry->Name[0]==FILE_HEADER_DELETED)||
(entry->Attr==FILE_ATTR_VOLUME_ID) ||
(entry->Attr & FILE_ATTR_SYSHID) )
return 1;
else
return 0;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_entry_lfn_exists: If LFN exists and correlation SFN found
//-----------------------------------------------------------------------------
#if FATFS_INC_LFN_SUPPORT
int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry)
{
if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
(entry->Name[0]!=FILE_HEADER_BLANK) &&
(entry->Name[0]!=FILE_HEADER_DELETED) &&
(entry->Attr!=FILE_ATTR_VOLUME_ID) &&
(!(entry->Attr&FILE_ATTR_SYSHID)) &&
(lfn->no_of_strings) )
return 1;
else
return 0;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_entry_sfn_only: If SFN only exists
//-----------------------------------------------------------------------------
int fatfs_entry_sfn_only(struct fat_dir_entry *entry)
{
if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
(entry->Name[0]!=FILE_HEADER_BLANK) &&
(entry->Name[0]!=FILE_HEADER_DELETED) &&
(entry->Attr!=FILE_ATTR_VOLUME_ID) &&
(!(entry->Attr&FILE_ATTR_SYSHID)) )
return 1;
else
return 0;
}
// TODO: FILE_ATTR_SYSHID ?!?!??!
//-----------------------------------------------------------------------------
// fatfs_entry_is_dir: Returns 1 if a directory
//-----------------------------------------------------------------------------
int fatfs_entry_is_dir(struct fat_dir_entry *entry)
{
if (entry->Attr & FILE_TYPE_DIR)
return 1;
else
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_entry_is_file: Returns 1 is a file entry
//-----------------------------------------------------------------------------
int fatfs_entry_is_file(struct fat_dir_entry *entry)
{
if (entry->Attr & FILE_TYPE_FILE)
return 1;
else
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_lfn_entries_required: Calculate number of 13 characters entries
//-----------------------------------------------------------------------------
#if FATFS_INC_LFN_SUPPORT
int fatfs_lfn_entries_required(char *filename)
{
int length = (int)strlen(filename);
if (length)
return (length + MAX_LFN_ENTRY_LENGTH - 1) / MAX_LFN_ENTRY_LENGTH;
else
return 0;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_filename_to_lfn:
//-----------------------------------------------------------------------------
#if FATFS_INC_LFN_SUPPORT
void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk)
{
int i;
int nameIndexes[MAX_LFN_ENTRY_LENGTH] = {1,3,5,7,9,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
// 13 characters entries
int length = (int)strlen(filename);
int entriesRequired = fatfs_lfn_entries_required(filename);
// Filename offset
int start = entry * MAX_LFN_ENTRY_LENGTH;
// Initialise to zeros
memset(buffer, 0x00, FAT_DIR_ENTRY_SIZE);
// LFN entry number
buffer[0] = (uint8)(((entriesRequired-1)==entry)?(0x40|(entry+1)):(entry+1));
// LFN flag
buffer[11] = 0x0F;
// Checksum of short filename
buffer[13] = sfnChk;
// Copy to buffer
for (i=0;i<MAX_LFN_ENTRY_LENGTH;i++)
{
if ( (start+i) < length )
buffer[nameIndexes[i]] = filename[start+i];
else if ( (start+i) == length )
buffer[nameIndexes[i]] = 0x00;
else
{
buffer[nameIndexes[i]] = 0xFF;
buffer[nameIndexes[i]+1] = 0xFF;
}
}
}
#endif
//-----------------------------------------------------------------------------
// fatfs_sfn_create_entry: Create the short filename directory entry
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir)
{
int i;
// Copy short filename
for (i=0;i<FAT_SFN_SIZE_FULL;i++)
entry->Name[i] = shortfilename[i];
// Unless we have a RTC we might as well set these to 1980
entry->CrtTimeTenth = 0x00;
entry->CrtTime[1] = entry->CrtTime[0] = 0x00;
entry->CrtDate[1] = 0x00;
entry->CrtDate[0] = 0x20;
entry->LstAccDate[1] = 0x00;
entry->LstAccDate[0] = 0x20;
entry->WrtTime[1] = entry->WrtTime[0] = 0x00;
entry->WrtDate[1] = 0x00;
entry->WrtDate[0] = 0x20;
if (!dir)
entry->Attr = FILE_TYPE_FILE;
else
entry->Attr = FILE_TYPE_DIR;
entry->NTRes = 0x00;
entry->FstClusHI = FAT_HTONS((uint16)((startCluster>>16) & 0xFFFF));
entry->FstClusLO = FAT_HTONS((uint16)((startCluster>>0) & 0xFFFF));
entry->FileSize = FAT_HTONL(size);
}
#endif
//-----------------------------------------------------------------------------
// fatfs_lfn_create_sfn: Create a padded SFN
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fatfs_lfn_create_sfn(char *sfn_output, char *filename)
{
int i;
int dotPos = -1;
char ext[3];
int pos;
int len = (int)strlen(filename);
// Invalid to start with .
if (filename[0]=='.')
return 0;
memset(sfn_output, ' ', FAT_SFN_SIZE_FULL);
memset(ext, ' ', 3);
// Find dot seperator
for (i = 0; i< len; i++)
{
if (filename[i]=='.')
dotPos = i;
}
// Extract extensions
if (dotPos!=-1)
{
// Copy first three chars of extension
for (i = (dotPos+1); i < (dotPos+1+3); i++)
if (i<len)
ext[i-(dotPos+1)] = filename[i];
// Shorten the length to the dot position
len = dotPos;
}
// Add filename part
pos = 0;
for (i=0;i<len;i++)
{
if ( (filename[i]!=' ') && (filename[i]!='.') )
{
if (filename[i] >= 'a' && filename[i] <= 'z')
sfn_output[pos++] = filename[i] - 'a' + 'A';
else
sfn_output[pos++] = filename[i];
}
// Fill upto 8 characters
if (pos==FAT_SFN_SIZE_PARTIAL)
break;
}
// Add extension part
for (i=FAT_SFN_SIZE_PARTIAL;i<FAT_SFN_SIZE_FULL;i++)
{
if (ext[i-FAT_SFN_SIZE_PARTIAL] >= 'a' && ext[i-FAT_SFN_SIZE_PARTIAL] <= 'z')
sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL] - 'a' + 'A';
else
sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL];
}
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_itoa:
//-----------------------------------------------------------------------------
static void fatfs_itoa(uint32 num, char *s)
{
char* cp;
char outbuf[12];
const char digits[] = "0123456789ABCDEF";
// Build string backwards
cp = outbuf;
do
{
*cp++ = digits[(int)(num % 10)];
}
while ((num /= 10) > 0);
*cp-- = 0;
// Copy in forwards
while (cp >= outbuf)
*s++ = *cp--;
*s = 0;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_lfn_generate_tail:
// sfn_input = Input short filename, spaced format & in upper case
// sfn_output = Output short filename with tail
//-----------------------------------------------------------------------------
#if FATFS_INC_LFN_SUPPORT
#if FATFS_INC_WRITE_SUPPORT
int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum)
{
int tail_chars;
char tail_str[12];
if (tailNum > 99999)
return 0;
// Convert to number
memset(tail_str, 0x00, sizeof(tail_str));
tail_str[0] = '~';
fatfs_itoa(tailNum, tail_str+1);
// Copy in base filename
memcpy(sfn_output, sfn_input, FAT_SFN_SIZE_FULL);
// Overwrite with tail
tail_chars = (int)strlen(tail_str);
memcpy(sfn_output+(FAT_SFN_SIZE_PARTIAL-tail_chars), tail_str, tail_chars);
return 1;
}
#endif
#endif
//-----------------------------------------------------------------------------
// fatfs_convert_from_fat_time: Convert FAT time to h/m/s
//-----------------------------------------------------------------------------
#if FATFS_INC_TIME_DATE_SUPPORT
void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds)
{
*hours = (fat_time >> FAT_TIME_HOURS_SHIFT) & FAT_TIME_HOURS_MASK;
*minutes = (fat_time >> FAT_TIME_MINUTES_SHIFT) & FAT_TIME_MINUTES_MASK;
*seconds = (fat_time >> FAT_TIME_SECONDS_SHIFT) & FAT_TIME_SECONDS_MASK;
*seconds = *seconds * FAT_TIME_SECONDS_SCALE;
}
//-----------------------------------------------------------------------------
// fatfs_convert_from_fat_date: Convert FAT date to d/m/y
//-----------------------------------------------------------------------------
void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year)
{
*day = (fat_date >> FAT_DATE_DAY_SHIFT) & FAT_DATE_DAY_MASK;
*month = (fat_date >> FAT_DATE_MONTH_SHIFT) & FAT_DATE_MONTH_MASK;
*year = (fat_date >> FAT_DATE_YEAR_SHIFT) & FAT_DATE_YEAR_MASK;
*year = *year + FAT_DATE_YEAR_OFFSET;
}
//-----------------------------------------------------------------------------
// fatfs_convert_to_fat_time: Convert h/m/s to FAT time
//-----------------------------------------------------------------------------
uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds)
{
uint16 fat_time = 0;
// Most FAT times are to a resolution of 2 seconds
seconds /= FAT_TIME_SECONDS_SCALE;
fat_time = (hours & FAT_TIME_HOURS_MASK) << FAT_TIME_HOURS_SHIFT;
fat_time|= (minutes & FAT_TIME_MINUTES_MASK) << FAT_TIME_MINUTES_SHIFT;
fat_time|= (seconds & FAT_TIME_SECONDS_MASK) << FAT_TIME_SECONDS_SHIFT;
return fat_time;
}
//-----------------------------------------------------------------------------
// fatfs_convert_to_fat_date: Convert d/m/y to FAT date
//-----------------------------------------------------------------------------
uint16 fatfs_convert_to_fat_date(int day, int month, int year)
{
uint16 fat_date = 0;
// FAT dates are relative to 1980
if (year >= FAT_DATE_YEAR_OFFSET)
year -= FAT_DATE_YEAR_OFFSET;
fat_date = (day & FAT_DATE_DAY_MASK) << FAT_DATE_DAY_SHIFT;
fat_date|= (month & FAT_DATE_MONTH_MASK) << FAT_DATE_MONTH_SHIFT;
fat_date|= (year & FAT_DATE_YEAR_MASK) << FAT_DATE_YEAR_SHIFT;
return fat_date;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_print_sector:
//-----------------------------------------------------------------------------
#ifdef FATFS_DEBUG
void fatfs_print_sector(uint32 sector, uint8 *data)
{
int i;
int j;
FAT_PRINTF(("Sector %d:\n", sector));
for (i=0;i<FAT_SECTOR_SIZE;i++)
{
if (!((i) % 16))
{
FAT_PRINTF((" %04d: ", i));
}
FAT_PRINTF(("%02x", data[i]));
if (!((i+1) % 4))
{
FAT_PRINTF((" "));
}
if (!((i+1) % 16))
{
FAT_PRINTF((" "));
for (j=0;j<16;j++)
{
char ch = data[i-15+j];
// Is printable?
if (ch > 31 && ch < 127)
{
FAT_PRINTF(("%c", ch));
}
else
{
FAT_PRINTF(("."));
}
}
FAT_PRINTF(("\n"));
}
}
}
#endif
#ifndef __FAT_MISC_H__
#define __FAT_MISC_H__
#include "fat_defs.h"
#include "fat_opts.h"
//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
#define MAX_LONGFILENAME_ENTRIES 20
#define MAX_LFN_ENTRY_LENGTH 13
//-----------------------------------------------------------------------------
// Macros
//-----------------------------------------------------------------------------
#define GET_32BIT_WORD(buffer, location) ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
#define GET_16BIT_WORD(buffer, location) ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
#define SET_32BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
buffer[location+1] = (uint8)((value>>8)&0xFF); \
buffer[location+2] = (uint8)((value>>16)&0xFF); \
buffer[location+3] = (uint8)((value>>24)&0xFF); }
#define SET_16BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
buffer[location+1] = (uint8)((value>>8)&0xFF); }
//-----------------------------------------------------------------------------
// Structures
//-----------------------------------------------------------------------------
struct lfn_cache
{
#if FATFS_INC_LFN_SUPPORT
// Long File Name Structure (max 260 LFN length)
uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
uint8 Null;
#endif
uint8 no_of_strings;
};
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
char* fatfs_lfn_cache_get(struct lfn_cache *lfn);
int fatfs_entry_lfn_text(struct fat_dir_entry *entry);
int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
int fatfs_entry_sfn_only(struct fat_dir_entry *entry);
int fatfs_entry_is_dir(struct fat_dir_entry *entry);
int fatfs_entry_is_file(struct fat_dir_entry *entry);
int fatfs_lfn_entries_required(char *filename);
void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
int fatfs_lfn_create_sfn(char *sfn_output, char *filename);
int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
uint16 fatfs_convert_to_fat_date(int day, int month, int year);
void fatfs_print_sector(uint32 sector, uint8 *data);
#endif
#ifndef __FAT_OPTS_H__
#define __FAT_OPTS_H__
#ifdef FATFS_USE_CUSTOM_OPTS_FILE
#include "fat_custom.h"
#endif
//-------------------------------------------------------------
// Configuration
//-------------------------------------------------------------
// Is the processor little endian (1) or big endian (0)
#ifndef FATFS_IS_LITTLE_ENDIAN
#define FATFS_IS_LITTLE_ENDIAN 1
#endif
// Max filename Length
#ifndef FATFS_MAX_LONG_FILENAME
#define FATFS_MAX_LONG_FILENAME 260
#endif
// Max open files (reduce to lower memory requirements)
#ifndef FATFS_MAX_OPEN_FILES
#define FATFS_MAX_OPEN_FILES 2
#endif
// Number of sectors per FAT_BUFFER (min 1)
#ifndef FAT_BUFFER_SECTORS
#define FAT_BUFFER_SECTORS 1
#endif
// Max FAT sectors to buffer (min 1)
// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
#ifndef FAT_BUFFERS
#define FAT_BUFFERS 1
#endif
// Size of cluster chain cache (can be undefined)
// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
// Improves access speed considerably
//#define FAT_CLUSTER_CACHE_ENTRIES 128
// Include support for writing files (1 / 0)?
#ifndef FATFS_INC_WRITE_SUPPORT
#define FATFS_INC_WRITE_SUPPORT 1
#endif
// Support long filenames (1 / 0)?
// (if not (0) only 8.3 format is supported)
#ifndef FATFS_INC_LFN_SUPPORT
#define FATFS_INC_LFN_SUPPORT 1
#endif
// Support directory listing (1 / 0)?
#ifndef FATFS_DIR_LIST_SUPPORT
#define FATFS_DIR_LIST_SUPPORT 1
#endif
// Support time/date (1 / 0)?
#ifndef FATFS_INC_TIME_DATE_SUPPORT
#define FATFS_INC_TIME_DATE_SUPPORT 0
#endif
// Include support for formatting disks (1 / 0)?
#ifndef FATFS_INC_FORMAT_SUPPORT
#define FATFS_INC_FORMAT_SUPPORT 0
#endif
// Sector size used
#define FAT_SECTOR_SIZE 512
// Printf output (directory listing / debug)
#ifndef FAT_PRINTF
void ventoy_syslog_printf(const char *Fmt, ...);
#define FAT_PRINTF(a) ventoy_syslog_printf a
#endif
// Time/Date support requires time.h
#if FATFS_INC_TIME_DATE_SUPPORT
#include <time.h>
#endif
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include <string.h>
#include <assert.h>
#include "fat_string.h"
//-----------------------------------------------------------------------------
// fatfs_total_path_levels: Take a filename and path and count the sub levels
// of folders. E.g. C:\folder\file.zip = 1 level
// Acceptable input formats are:
// c:\folder\file.zip
// /dev/etc/samba.conf
// Returns: -1 = Error, 0 or more = Ok
//-----------------------------------------------------------------------------
int fatfs_total_path_levels(char *path)
{
int levels = 0;
char expectedchar;
if (!path)
return -1;
// Acceptable formats:
// c:\folder\file.zip
// /dev/etc/samba.conf
if (*path == '/')
{
expectedchar = '/';
path++;
}
else if (path[1] == ':' || path[2] == '\\')
{
expectedchar = '\\';
path += 3;
}
else
return -1;
// Count levels in path string
while (*path)
{
// Fast forward through actual subdir text to next slash
for (; *path; )
{
// If slash detected escape from for loop
if (*path == expectedchar) { path++; break; }
path++;
}
// Increase number of subdirs founds
levels++;
}
// Subtract the file itself
return levels-1;
}
//-----------------------------------------------------------------------------
// fatfs_get_substring: Get a substring from 'path' which contains the folder
// (or file) at the specified level.
// E.g. C:\folder\file.zip : Level 0 = C:\folder, Level 1 = file.zip
// Returns: -1 = Error, 0 = Ok
//-----------------------------------------------------------------------------
int fatfs_get_substring(char *path, int levelreq, char *output, int max_len)
{
int i;
int pathlen=0;
int levels=0;
int copypnt=0;
char expectedchar;
if (!path || max_len <= 0)
return -1;
// Acceptable formats:
// c:\folder\file.zip
// /dev/etc/samba.conf
if (*path == '/')
{
expectedchar = '/';
path++;
}
else if (path[1] == ':' || path[2] == '\\')
{
expectedchar = '\\';
path += 3;
}
else
return -1;
// Get string length of path
pathlen = (int)strlen (path);
// Loop through the number of times as characters in 'path'
for (i = 0; i<pathlen; i++)
{
// If a '\' is found then increase level
if (*path == expectedchar) levels++;
// If correct level and the character is not a '\' or '/' then copy text to 'output'
if ( (levels == levelreq) && (*path != expectedchar) && (copypnt < (max_len-1)))
output[copypnt++] = *path;
// Increment through path string
path++;
}
// Null Terminate
output[copypnt] = '\0';
// If a string was copied return 0 else return 1
if (output[0] != '\0')
return 0; // OK
else
return -1; // Error
}
//-----------------------------------------------------------------------------
// fatfs_split_path: Full path contains the passed in string.
// Returned is the path string and file Name string
// E.g. C:\folder\file.zip -> path = C:\folder filename = file.zip
// E.g. C:\file.zip -> path = [blank] filename = file.zip
//-----------------------------------------------------------------------------
int fatfs_split_path(char *full_path, char *path, int max_path, char *filename, int max_filename)
{
int strindex;
// Count the levels to the filepath
int levels = fatfs_total_path_levels(full_path);
if (levels == -1)
return -1;
// Get filename part of string
if (fatfs_get_substring(full_path, levels, filename, max_filename) != 0)
return -1;
// If root file
if (levels == 0)
path[0] = '\0';
else
{
strindex = (int)strlen(full_path) - (int)strlen(filename);
if (strindex > max_path)
strindex = max_path;
memcpy(path, full_path, strindex);
path[strindex-1] = '\0';
}
return 0;
}
//-----------------------------------------------------------------------------
// FileString_StrCmpNoCase: Compare two strings case with case sensitivity
//-----------------------------------------------------------------------------
static int FileString_StrCmpNoCase(char *s1, char *s2, int n)
{
int diff;
char a,b;
while (n--)
{
a = *s1;
b = *s2;
// Make lower case if uppercase
if ((a>='A') && (a<='Z'))
a+= 32;
if ((b>='A') && (b<='Z'))
b+= 32;
diff = a - b;
// If different
if (diff)
return diff;
// If run out of strings
if ( (*s1 == 0) || (*s2 == 0) )
break;
s1++;
s2++;
}
return 0;
}
//-----------------------------------------------------------------------------
// FileString_GetExtension: Get index to extension within filename
// Returns -1 if not found or index otherwise
//-----------------------------------------------------------------------------
static int FileString_GetExtension(char *str)
{
int dotPos = -1;
char *strSrc = str;
// Find last '.' in string (if at all)
while (*strSrc)
{
if (*strSrc=='.')
dotPos = (int)(strSrc-str);
strSrc++;
}
return dotPos;
}
//-----------------------------------------------------------------------------
// FileString_TrimLength: Get length of string excluding trailing spaces
// Returns -1 if not found or index otherwise
//-----------------------------------------------------------------------------
static int FileString_TrimLength(char *str, int strLen)
{
int length = strLen;
char *strSrc = str+strLen-1;
// Find last non white space
while (strLen != 0)
{
if (*strSrc == ' ')
length = (int)(strSrc - str);
else
break;
strSrc--;
strLen--;
}
return length;
}
//-----------------------------------------------------------------------------
// fatfs_compare_names: Compare two filenames (without copying or changing origonals)
// Returns 1 if match, 0 if not
//-----------------------------------------------------------------------------
int fatfs_compare_names(char* strA, char* strB)
{
char *ext1 = NULL;
char *ext2 = NULL;
int ext1Pos, ext2Pos;
int file1Len, file2Len;
// Get both files extension
ext1Pos = FileString_GetExtension(strA);
ext2Pos = FileString_GetExtension(strB);
// NOTE: Extension position can be different for matching
// filename if trailing space are present before it!
// Check that if one has an extension, so does the other
if ((ext1Pos==-1) && (ext2Pos!=-1))
return 0;
if ((ext2Pos==-1) && (ext1Pos!=-1))
return 0;
// If they both have extensions, compare them
if (ext1Pos!=-1)
{
// Set pointer to start of extension
ext1 = strA+ext1Pos+1;
ext2 = strB+ext2Pos+1;
// Verify that the file extension lengths match!
if (strlen(ext1) != strlen(ext2))
return 0;
// If they dont match
if (FileString_StrCmpNoCase(ext1, ext2, (int)strlen(ext1))!=0)
return 0;
// Filelength is upto extensions
file1Len = ext1Pos;
file2Len = ext2Pos;
}
// No extensions
else
{
// Filelength is actual filelength
file1Len = (int)strlen(strA);
file2Len = (int)strlen(strB);
}
// Find length without trailing spaces (before ext)
file1Len = FileString_TrimLength(strA, file1Len);
file2Len = FileString_TrimLength(strB, file2Len);
// Check the file lengths match
if (file1Len!=file2Len)
return 0;
// Compare main part of filenames
if (FileString_StrCmpNoCase(strA, strB, file1Len)!=0)
return 0;
else
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_string_ends_with_slash: Does the string end with a slash (\ or /)
//-----------------------------------------------------------------------------
int fatfs_string_ends_with_slash(char *path)
{
if (path)
{
while (*path)
{
// Last character?
if (!(*(path+1)))
{
if (*path == '\\' || *path == '/')
return 1;
}
path++;
}
}
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_get_sfn_display_name: Get display name for SFN entry
//-----------------------------------------------------------------------------
int fatfs_get_sfn_display_name(char* out, char* in)
{
int len = 0;
while (*in && len <= 11)
{
char a = *in++;
if (a == ' ')
continue;
// Make lower case if uppercase
else if ((a>='A') && (a<='Z'))
a+= 32;
*out++ = a;
len++;
}
*out = '\0';
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_get_extension: Get extension of filename passed in 'filename'.
// Returned extension is always lower case.
// Returns: 1 if ok, 0 if not.
//-----------------------------------------------------------------------------
int fatfs_get_extension(char* filename, char* out, int maxlen)
{
int len = 0;
// Get files extension offset
int ext_pos = FileString_GetExtension(filename);
if (ext_pos > 0 && out && maxlen)
{
filename += ext_pos + 1;
while (*filename && len < (maxlen-1))
{
char a = *filename++;
// Make lowercase if uppercase
if ((a>='A') && (a<='Z'))
a+= 32;
*out++ = a;
len++;
}
*out = '\0';
return 1;
}
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_create_path_string: Append path & filename to create file path string.
// Returns: 1 if ok, 0 if not.
//-----------------------------------------------------------------------------
int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen)
{
int len = 0;
char last = 0;
char seperator = '/';
if (path && filename && out && maxlen > 0)
{
while (*path && len < (maxlen-2))
{
last = *path++;
if (last == '\\')
seperator = '\\';
*out++ = last;
len++;
}
// Add a seperator if trailing one not found
if (last != '\\' && last != '/')
*out++ = seperator;
while (*filename && len < (maxlen-1))
{
*out++ = *filename++;
len++;
}
*out = '\0';
return 1;
}
return 0;
}
//-----------------------------------------------------------------------------
// Test Bench
//-----------------------------------------------------------------------------
#ifdef FAT_STRING_TESTBENCH
void main(void)
{
char output[255];
char output2[255];
assert(fatfs_total_path_levels("C:\\folder\\file.zip") == 1);
assert(fatfs_total_path_levels("C:\\file.zip") == 0);
assert(fatfs_total_path_levels("C:\\folder\\folder2\\file.zip") == 2);
assert(fatfs_total_path_levels("C:\\") == -1);
assert(fatfs_total_path_levels("") == -1);
assert(fatfs_total_path_levels("/dev/etc/file.zip") == 2);
assert(fatfs_total_path_levels("/dev/file.zip") == 1);
assert(fatfs_get_substring("C:\\folder\\file.zip", 0, output, sizeof(output)) == 0);
assert(strcmp(output, "folder") == 0);
assert(fatfs_get_substring("C:\\folder\\file.zip", 1, output, sizeof(output)) == 0);
assert(strcmp(output, "file.zip") == 0);
assert(fatfs_get_substring("/dev/etc/file.zip", 0, output, sizeof(output)) == 0);
assert(strcmp(output, "dev") == 0);
assert(fatfs_get_substring("/dev/etc/file.zip", 1, output, sizeof(output)) == 0);
assert(strcmp(output, "etc") == 0);
assert(fatfs_get_substring("/dev/etc/file.zip", 2, output, sizeof(output)) == 0);
assert(strcmp(output, "file.zip") == 0);
assert(fatfs_split_path("C:\\folder\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
assert(strcmp(output, "C:\\folder") == 0);
assert(strcmp(output2, "file.zip") == 0);
assert(fatfs_split_path("C:\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
assert(output[0] == 0);
assert(strcmp(output2, "file.zip") == 0);
assert(fatfs_split_path("/dev/etc/file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
assert(strcmp(output, "/dev/etc") == 0);
assert(strcmp(output2, "file.zip") == 0);
assert(FileString_GetExtension("C:\\file.zip") == strlen("C:\\file"));
assert(FileString_GetExtension("C:\\file.zip.ext") == strlen("C:\\file.zip"));
assert(FileString_GetExtension("C:\\file.zip.") == strlen("C:\\file.zip"));
assert(FileString_TrimLength("C:\\file.zip", strlen("C:\\file.zip")) == strlen("C:\\file.zip"));
assert(FileString_TrimLength("C:\\file.zip ", strlen("C:\\file.zip ")) == strlen("C:\\file.zip"));
assert(FileString_TrimLength(" ", strlen(" ")) == 0);
assert(fatfs_compare_names("C:\\file.ext", "C:\\file.ext") == 1);
assert(fatfs_compare_names("C:\\file2.ext", "C:\\file.ext") == 0);
assert(fatfs_compare_names("C:\\file .ext", "C:\\file.ext") == 1);
assert(fatfs_compare_names("C:\\file .ext", "C:\\file2.ext") == 0);
assert(fatfs_string_ends_with_slash("C:\\folder") == 0);
assert(fatfs_string_ends_with_slash("C:\\folder\\") == 1);
assert(fatfs_string_ends_with_slash("/path") == 0);
assert(fatfs_string_ends_with_slash("/path/a") == 0);
assert(fatfs_string_ends_with_slash("/path/") == 1);
assert(fatfs_get_extension("/mypath/file.wav", output, 4) == 1);
assert(strcmp(output, "wav") == 0);
assert(fatfs_get_extension("/mypath/file.WAV", output, 4) == 1);
assert(strcmp(output, "wav") == 0);
assert(fatfs_get_extension("/mypath/file.zip", output, 4) == 1);
assert(strcmp(output, "ext") != 0);
assert(fatfs_create_path_string("/mydir1", "myfile.txt", output, sizeof(output)) == 1);
assert(strcmp(output, "/mydir1/myfile.txt") == 0);
assert(fatfs_create_path_string("/mydir2/", "myfile2.txt", output, sizeof(output)) == 1);
assert(strcmp(output, "/mydir2/myfile2.txt") == 0);
assert(fatfs_create_path_string("C:\\mydir3", "myfile3.txt", output, sizeof(output)) == 1);
assert(strcmp(output, "C:\\mydir3\\myfile3.txt") == 0);
}
#endif
#ifndef __FILESTRING_H__
#define __FILESTRING_H__
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
int fatfs_total_path_levels(char *path);
int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
int fatfs_compare_names(char* strA, char* strB);
int fatfs_string_ends_with_slash(char *path);
int fatfs_get_sfn_display_name(char* out, char* in);
int fatfs_get_extension(char* filename, char* out, int maxlen);
int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
#ifndef NULL
#define NULL 0
#endif
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include <string.h>
#include "fat_defs.h"
#include "fat_access.h"
#include "fat_table.h"
#ifndef FAT_BUFFERS
#define FAT_BUFFERS 1
#endif
#ifndef FAT_BUFFER_SECTORS
#define FAT_BUFFER_SECTORS 1
#endif
#if FAT_BUFFERS < 1 || FAT_BUFFER_SECTORS < 1
#error "FAT_BUFFERS & FAT_BUFFER_SECTORS must be at least 1"
#endif
//-----------------------------------------------------------------------------
// FAT Sector Buffer
//-----------------------------------------------------------------------------
#define FAT32_GET_32BIT_WORD(pbuf, location) ( GET_32BIT_WORD(pbuf->ptr, location) )
#define FAT32_SET_32BIT_WORD(pbuf, location, value) { SET_32BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
#define FAT16_GET_16BIT_WORD(pbuf, location) ( GET_16BIT_WORD(pbuf->ptr, location) )
#define FAT16_SET_16BIT_WORD(pbuf, location, value) { SET_16BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
//-----------------------------------------------------------------------------
// fatfs_fat_init:
//-----------------------------------------------------------------------------
void fatfs_fat_init(struct fatfs *fs)
{
int i;
// FAT buffer chain head
fs->fat_buffer_head = NULL;
for (i=0;i<FAT_BUFFERS;i++)
{
// Initialise buffers to invalid
fs->fat_buffers[i].address = FAT32_INVALID_CLUSTER;
fs->fat_buffers[i].dirty = 0;
memset(fs->fat_buffers[i].sector, 0x00, sizeof(fs->fat_buffers[i].sector));
fs->fat_buffers[i].ptr = NULL;
// Add to head of queue
fs->fat_buffers[i].next = fs->fat_buffer_head;
fs->fat_buffer_head = &fs->fat_buffers[i];
}
}
//-----------------------------------------------------------------------------
// fatfs_fat_writeback: Writeback 'dirty' FAT sectors to disk
//-----------------------------------------------------------------------------
static int fatfs_fat_writeback(struct fatfs *fs, struct fat_buffer *pcur)
{
if (pcur)
{
// Writeback sector if changed
if (pcur->dirty)
{
if (fs->disk_io.write_media)
{
uint32 sectors = FAT_BUFFER_SECTORS;
uint32 offset = pcur->address - fs->fat_begin_lba;
// Limit to sectors used for the FAT
if ((offset + FAT_BUFFER_SECTORS) <= fs->fat_sectors)
sectors = FAT_BUFFER_SECTORS;
else
sectors = fs->fat_sectors - offset;
if (!fs->disk_io.write_media(pcur->address, pcur->sector, sectors))
return 0;
}
pcur->dirty = 0;
}
return 1;
}
else
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_fat_read_sector: Read a FAT sector
//-----------------------------------------------------------------------------
static struct fat_buffer *fatfs_fat_read_sector(struct fatfs *fs, uint32 sector)
{
struct fat_buffer *last = NULL;
struct fat_buffer *pcur = fs->fat_buffer_head;
// Itterate through sector buffer list
while (pcur)
{
// Sector within this buffer?
if ((sector >= pcur->address) && (sector < (pcur->address + FAT_BUFFER_SECTORS)))
break;
// End of list?
if (pcur->next == NULL)
{
// Remove buffer from list
if (last)
last->next = NULL;
// We the first and last buffer in the chain?
else
fs->fat_buffer_head = NULL;
}
last = pcur;
pcur = pcur->next;
}
// We found the sector already in FAT buffer chain
if (pcur)
{
pcur->ptr = (uint8 *)(pcur->sector + ((sector - pcur->address) * FAT_SECTOR_SIZE));
return pcur;
}
// Else, we removed the last item from the list
pcur = last;
// Add to start of sector buffer list (now newest sector)
pcur->next = fs->fat_buffer_head;
fs->fat_buffer_head = pcur;
// Writeback sector if changed
if (pcur->dirty)
if (!fatfs_fat_writeback(fs, pcur))
return 0;
// Address is now new sector
pcur->address = sector;
// Read next sector
if (!fs->disk_io.read_media(pcur->address, pcur->sector, FAT_BUFFER_SECTORS))
{
// Read failed, invalidate buffer address
pcur->address = FAT32_INVALID_CLUSTER;
return NULL;
}
pcur->ptr = pcur->sector;
return pcur;
}
//-----------------------------------------------------------------------------
// fatfs_fat_purge: Purge 'dirty' FAT sectors to disk
//-----------------------------------------------------------------------------
int fatfs_fat_purge(struct fatfs *fs)
{
struct fat_buffer *pcur = fs->fat_buffer_head;
// Itterate through sector buffer list
while (pcur)
{
// Writeback sector if changed
if (pcur->dirty)
if (!fatfs_fat_writeback(fs, pcur))
return 0;
pcur = pcur->next;
}
return 1;
}
//-----------------------------------------------------------------------------
// General FAT Table Operations
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// fatfs_find_next_cluster: Return cluster number of next cluster in chain by
// reading FAT table and traversing it. Return 0xffffffff for end of chain.
//-----------------------------------------------------------------------------
uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster)
{
uint32 fat_sector_offset, position;
uint32 nextcluster;
struct fat_buffer *pbuf;
// Why is '..' labelled with cluster 0 when it should be 2 ??
if (current_cluster == 0)
current_cluster = 2;
// Find which sector of FAT table to read
if (fs->fat_type == FAT_TYPE_16)
fat_sector_offset = current_cluster / 256;
else
fat_sector_offset = current_cluster / 128;
// Read FAT sector into buffer
pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
if (!pbuf)
return (FAT32_LAST_CLUSTER);
if (fs->fat_type == FAT_TYPE_16)
{
// Find 32 bit entry of current sector relating to cluster number
position = (current_cluster - (fat_sector_offset * 256)) * 2;
// Read Next Clusters value from Sector Buffer
nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
// If end of chain found
if (nextcluster >= 0xFFF8 && nextcluster <= 0xFFFF)
return (FAT32_LAST_CLUSTER);
}
else
{
// Find 32 bit entry of current sector relating to cluster number
position = (current_cluster - (fat_sector_offset * 128)) * 4;
// Read Next Clusters value from Sector Buffer
nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
// Mask out MS 4 bits (its 28bit addressing)
nextcluster = nextcluster & 0x0FFFFFFF;
// If end of chain found
if (nextcluster >= 0x0FFFFFF8 && nextcluster <= 0x0FFFFFFF)
return (FAT32_LAST_CLUSTER);
}
// Else return next cluster
return (nextcluster);
}
//-----------------------------------------------------------------------------
// fatfs_set_fs_info_next_free_cluster: Write the next free cluster to the FSINFO table
//-----------------------------------------------------------------------------
void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue)
{
if (fs->fat_type == FAT_TYPE_16)
;
else
{
// Load sector to change it
struct fat_buffer *pbuf = fatfs_fat_read_sector(fs, fs->lba_begin+fs->fs_info_sector);
if (!pbuf)
return ;
// Change
FAT32_SET_32BIT_WORD(pbuf, 492, newValue);
fs->next_free_cluster = newValue;
// Write back FSINFO sector to disk
if (fs->disk_io.write_media)
fs->disk_io.write_media(pbuf->address, pbuf->sector, 1);
// Invalidate cache entry
pbuf->address = FAT32_INVALID_CLUSTER;
pbuf->dirty = 0;
}
}
//-----------------------------------------------------------------------------
// fatfs_find_blank_cluster: Find a free cluster entry by reading the FAT
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster)
{
uint32 fat_sector_offset, position;
uint32 nextcluster;
uint32 current_cluster = start_cluster;
struct fat_buffer *pbuf;
do
{
// Find which sector of FAT table to read
if (fs->fat_type == FAT_TYPE_16)
fat_sector_offset = current_cluster / 256;
else
fat_sector_offset = current_cluster / 128;
if ( fat_sector_offset < fs->fat_sectors)
{
// Read FAT sector into buffer
pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
if (!pbuf)
return 0;
if (fs->fat_type == FAT_TYPE_16)
{
// Find 32 bit entry of current sector relating to cluster number
position = (current_cluster - (fat_sector_offset * 256)) * 2;
// Read Next Clusters value from Sector Buffer
nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
}
else
{
// Find 32 bit entry of current sector relating to cluster number
position = (current_cluster - (fat_sector_offset * 128)) * 4;
// Read Next Clusters value from Sector Buffer
nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
// Mask out MS 4 bits (its 28bit addressing)
nextcluster = nextcluster & 0x0FFFFFFF;
}
if (nextcluster !=0 )
current_cluster++;
}
else
// Otherwise, run out of FAT sectors to check...
return 0;
}
while (nextcluster != 0x0);
// Found blank entry
*free_cluster = current_cluster;
return 1;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_fat_set_cluster: Set a cluster link in the chain. NOTE: Immediate
// write (slow).
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster)
{
struct fat_buffer *pbuf;
uint32 fat_sector_offset, position;
// Find which sector of FAT table to read
if (fs->fat_type == FAT_TYPE_16)
fat_sector_offset = cluster / 256;
else
fat_sector_offset = cluster / 128;
// Read FAT sector into buffer
pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
if (!pbuf)
return 0;
if (fs->fat_type == FAT_TYPE_16)
{
// Find 16 bit entry of current sector relating to cluster number
position = (cluster - (fat_sector_offset * 256)) * 2;
// Write Next Clusters value to Sector Buffer
FAT16_SET_16BIT_WORD(pbuf, (uint16)position, ((uint16)next_cluster));
}
else
{
// Find 32 bit entry of current sector relating to cluster number
position = (cluster - (fat_sector_offset * 128)) * 4;
// Write Next Clusters value to Sector Buffer
FAT32_SET_32BIT_WORD(pbuf, (uint16)position, next_cluster);
}
return 1;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_free_cluster_chain: Follow a chain marking each element as free
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster)
{
uint32 last_cluster;
uint32 next_cluster = start_cluster;
// Loop until end of chain
while ( (next_cluster != FAT32_LAST_CLUSTER) && (next_cluster != 0x00000000) )
{
last_cluster = next_cluster;
// Find next link
next_cluster = fatfs_find_next_cluster(fs, next_cluster);
// Clear last link
fatfs_fat_set_cluster(fs, last_cluster, 0x00000000);
}
return 1;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_fat_add_cluster_to_chain: Follow a chain marking and then add a new entry
// to the current tail.
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry)
{
uint32 last_cluster = FAT32_LAST_CLUSTER;
uint32 next_cluster = start_cluster;
if (start_cluster == FAT32_LAST_CLUSTER)
return 0;
// Loop until end of chain
while ( next_cluster != FAT32_LAST_CLUSTER )
{
last_cluster = next_cluster;
// Find next link
next_cluster = fatfs_find_next_cluster(fs, next_cluster);
if (!next_cluster)
return 0;
}
// Add link in for new cluster
fatfs_fat_set_cluster(fs, last_cluster, newEntry);
// Mark new cluster as end of chain
fatfs_fat_set_cluster(fs, newEntry, FAT32_LAST_CLUSTER);
return 1;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_count_free_clusters:
//-----------------------------------------------------------------------------
uint32 fatfs_count_free_clusters(struct fatfs *fs)
{
uint32 i,j;
uint32 count = 0;
struct fat_buffer *pbuf;
for (i = 0; i < fs->fat_sectors; i++)
{
// Read FAT sector into buffer
pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba + i);
if (!pbuf)
break;
for (j = 0; j < FAT_SECTOR_SIZE; )
{
if (fs->fat_type == FAT_TYPE_16)
{
if (FAT16_GET_16BIT_WORD(pbuf, (uint16)j) == 0)
count++;
j += 2;
}
else
{
if (FAT32_GET_32BIT_WORD(pbuf, (uint16)j) == 0)
count++;
j += 4;
}
}
}
return count;
}
#ifndef __FAT_TABLE_H__
#define __FAT_TABLE_H__
#include "fat_opts.h"
#include "fat_misc.h"
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
void fatfs_fat_init(struct fatfs *fs);
int fatfs_fat_purge(struct fatfs *fs);
uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
uint32 fatfs_count_free_clusters(struct fatfs *fs);
#endif
#ifndef __FAT_TYPES_H__
#define __FAT_TYPES_H__
// Detect 64-bit compilation on GCC
#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
#if __SIZEOF_LONG__ == 8
#define FATFS_DEF_UINT32_AS_INT
#endif
#endif
//-------------------------------------------------------------
// System specific types
//-------------------------------------------------------------
#ifndef FATFS_NO_DEF_TYPES
typedef unsigned char uint8;
typedef unsigned short uint16;
// If compiling on a 64-bit machine, use int as 32-bits
#ifdef FATFS_DEF_UINT32_AS_INT
typedef unsigned int uint32;
// Else for 32-bit machines & embedded systems, use long...
#else
typedef unsigned long uint32;
#endif
#endif
#ifndef NULL
#define NULL 0
#endif
//-------------------------------------------------------------
// Endian Macros
//-------------------------------------------------------------
// FAT is little endian so big endian systems need to swap words
// Little Endian - No swap required
#if FATFS_IS_LITTLE_ENDIAN == 1
#define FAT_HTONS(n) (n)
#define FAT_HTONL(n) (n)
// Big Endian - Swap required
#else
#define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
#define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
((((uint32)(n) & 0xFF00)) << 8) | \
((((uint32)(n) & 0xFF0000)) >> 8) | \
((((uint32)(n) & 0xFF000000)) >> 24))
#endif
//-------------------------------------------------------------
// Structure Packing Compile Options
//-------------------------------------------------------------
#ifdef __GNUC__
#define STRUCT_PACK
#define STRUCT_PACK_BEGIN
#define STRUCT_PACK_END
#define STRUCT_PACKED __attribute__ ((packed))
#else
// Other compilers may require other methods of packing structures
#define STRUCT_PACK
#define STRUCT_PACK_BEGIN
#define STRUCT_PACK_END
#define STRUCT_PACKED
#endif
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include <string.h>
#include "fat_defs.h"
#include "fat_access.h"
#include "fat_table.h"
#include "fat_write.h"
#include "fat_string.h"
#include "fat_misc.h"
#if FATFS_INC_WRITE_SUPPORT
//-----------------------------------------------------------------------------
// fatfs_add_free_space: Allocate another cluster of free space to the end
// of a files cluster chain.
//-----------------------------------------------------------------------------
int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters)
{
uint32 i;
uint32 nextcluster;
uint32 start = *startCluster;
// Set the next free cluster hint to unknown
if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
for (i=0;i<clusters;i++)
{
// Start looking for free clusters from the beginning
if (fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
{
// Point last to this
fatfs_fat_set_cluster(fs, start, nextcluster);
// Point this to end of file
fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
// Adjust argument reference
start = nextcluster;
if (i == 0)
*startCluster = nextcluster;
}
else
return 0;
}
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_allocate_free_space: Add an ammount of free space to a file either from
// 'startCluster' if newFile = false, or allocating a new start to the chain if
// newFile = true.
//-----------------------------------------------------------------------------
int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size)
{
uint32 clusterSize;
uint32 clusterCount;
uint32 nextcluster;
if (size==0)
return 0;
// Set the next free cluster hint to unknown
if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
// Work out size and clusters
clusterSize = fs->sectors_per_cluster * FAT_SECTOR_SIZE;
clusterCount = (size / clusterSize);
// If any left over
if (size-(clusterSize*clusterCount))
clusterCount++;
// Allocated first link in the chain if a new file
if (newFile)
{
if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
return 0;
// If this is all that is needed then all done
if (clusterCount==1)
{
fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
*startCluster = nextcluster;
return 1;
}
}
// Allocate from end of current chain (startCluster is end of chain)
else
nextcluster = *startCluster;
if (!fatfs_add_free_space(fs, &nextcluster, clusterCount))
return 0;
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_find_free_dir_offset: Find a free space in the directory for a new entry
// which takes up 'entryCount' blocks (or allocate some more)
//-----------------------------------------------------------------------------
static int fatfs_find_free_dir_offset(struct fatfs *fs, uint32 dirCluster, int entryCount, uint32 *pSector, uint8 *pOffset)
{
struct fat_dir_entry *directoryEntry;
uint8 item=0;
uint16 recordoffset = 0;
uint8 i=0;
int x=0;
int possible_spaces = 0;
int start_recorded = 0;
// No entries required?
if (entryCount == 0)
return 0;
// Main cluster following loop
while (1)
{
// Read sector
if (fatfs_sector_reader(fs, dirCluster, x++, 0))
{
// Analyse Sector
for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
{
// Create the multiplier for sector access
recordoffset = FAT_DIR_ENTRY_SIZE * item;
// Overlay directory entry over buffer
directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
// LFN Entry
if (fatfs_entry_lfn_text(directoryEntry))
{
// First entry?
if (possible_spaces == 0)
{
// Store start
*pSector = x-1;
*pOffset = item;
start_recorded = 1;
}
// Increment the count in-case the file turns
// out to be deleted...
possible_spaces++;
}
// SFN Entry
else
{
// Has file been deleted?
if (fs->currentsector.sector[recordoffset] == FILE_HEADER_DELETED)
{
// First entry?
if (possible_spaces == 0)
{
// Store start
*pSector = x-1;
*pOffset = item;
start_recorded = 1;
}
possible_spaces++;
// We have found enough space?
if (possible_spaces >= entryCount)
return 1;
// Else continue counting until we find a valid entry!
}
// Is the file entry empty?
else if (fs->currentsector.sector[recordoffset] == FILE_HEADER_BLANK)
{
// First entry?
if (possible_spaces == 0)
{
// Store start
*pSector = x-1;
*pOffset = item;
start_recorded = 1;
}
// Increment the blank entries count
possible_spaces++;
// We have found enough space?
if (possible_spaces >= entryCount)
return 1;
}
// File entry is valid
else
{
// Reset all flags
possible_spaces = 0;
start_recorded = 0;
}
}
} // End of for
} // End of if
// Run out of free space in the directory, allocate some more
else
{
uint32 newCluster;
// Get a new cluster for directory
if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &newCluster))
return 0;
// Add cluster to end of directory tree
if (!fatfs_fat_add_cluster_to_chain(fs, dirCluster, newCluster))
return 0;
// Erase new directory cluster
memset(fs->currentsector.sector, 0x00, FAT_SECTOR_SIZE);
for (i=0;i<fs->sectors_per_cluster;i++)
{
if (!fatfs_write_sector(fs, newCluster, i, 0))
return 0;
}
// If non of the name fitted on previous sectors
if (!start_recorded)
{
// Store start
*pSector = (x-1);
*pOffset = 0;
start_recorded = 1;
}
return 1;
}
} // End of while loop
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_add_file_entry: Add a directory entry to a location found by FindFreeOffset
//-----------------------------------------------------------------------------
int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir)
{
uint8 item=0;
uint16 recordoffset = 0;
uint8 i=0;
uint32 x=0;
int entryCount;
struct fat_dir_entry shortEntry;
int dirtySector = 0;
uint32 dirSector = 0;
uint8 dirOffset = 0;
int foundEnd = 0;
uint8 checksum;
uint8 *pSname;
// No write access?
if (!fs->disk_io.write_media)
return 0;
#if FATFS_INC_LFN_SUPPORT
// How many LFN entries are required?
// NOTE: We always request one LFN even if it would fit in a SFN!
entryCount = fatfs_lfn_entries_required(filename);
if (!entryCount)
return 0;
#else
entryCount = 0;
#endif
// Find space in the directory for this filename (or allocate some more)
// NOTE: We need to find space for at least the LFN + SFN (or just the SFN if LFNs not supported).
if (!fatfs_find_free_dir_offset(fs, dirCluster, entryCount + 1, &dirSector, &dirOffset))
return 0;
// Generate checksum of short filename
pSname = (uint8*)shortfilename;
checksum = 0;
for (i=11; i!=0; i--) checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *pSname++;
// Start from current sector where space was found!
x = dirSector;
// Main cluster following loop
while (1)
{
// Read sector
if (fatfs_sector_reader(fs, dirCluster, x++, 0))
{
// Analyse Sector
for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
{
// Create the multiplier for sector access
recordoffset = FAT_DIR_ENTRY_SIZE * item;
// If the start position for the entry has been found
if (foundEnd==0)
if ( (dirSector==(x-1)) && (dirOffset==item) )
foundEnd = 1;
// Start adding filename
if (foundEnd)
{
if (entryCount==0)
{
// Short filename
fatfs_sfn_create_entry(shortfilename, size, startCluster, &shortEntry, dir);
#if FATFS_INC_TIME_DATE_SUPPORT
// Update create, access & modify time & date
fatfs_update_timestamps(&shortEntry, 1, 1, 1);
#endif
memcpy(&fs->currentsector.sector[recordoffset], &shortEntry, sizeof(shortEntry));
// Writeback
return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
}
#if FATFS_INC_LFN_SUPPORT
else
{
entryCount--;
// Copy entry to directory buffer
fatfs_filename_to_lfn(filename, &fs->currentsector.sector[recordoffset], entryCount, checksum);
dirtySector = 1;
}
#endif
}
} // End of if
// Write back to disk before loading another sector
if (dirtySector)
{
if (!fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1))
return 0;
dirtySector = 0;
}
}
else
return 0;
} // End of while loop
return 0;
}
#endif
#ifndef __FAT_WRITE_H__
#define __FAT_WRITE_H__
#include "fat_defs.h"
#include "fat_opts.h"
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/* Copyright (c) 2013-2016 the Civetweb developers
* Copyright (c) 2004-2013 Sergey Lyubka
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef CIVETWEB_HEADER_INCLUDED
#define CIVETWEB_HEADER_INCLUDED
#define CIVETWEB_VERSION "1.8"
#ifndef CIVETWEB_API
#if defined(_WIN32)
#if defined(CIVETWEB_DLL_EXPORTS)
#define CIVETWEB_API __declspec(dllexport)
#elif defined(CIVETWEB_DLL_IMPORTS)
#define CIVETWEB_API __declspec(dllimport)
#else
#define CIVETWEB_API
#endif
#elif __GNUC__ >= 4
#define CIVETWEB_API __attribute__((visibility("default")))
#else
#define CIVETWEB_API
#endif
#endif
#include <stdio.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct mg_context; /* Handle for the HTTP service itself */
struct mg_connection; /* Handle for the individual connection */
/* This structure contains information about the HTTP request. */
struct mg_request_info {
const char *request_method; /* "GET", "POST", etc */
const char *request_uri; /* URL-decoded URI (absolute or relative,
* as in the request) */
const char *local_uri; /* URL-decoded URI (relative). Can be NULL
* if the request_uri does not address a
* resource at the server host. */
const char *uri; /* Deprecated: use local_uri instead */
const char *http_version; /* E.g. "1.0", "1.1" */
const char *query_string; /* URL part after '?', not including '?', or
NULL */
const char *remote_user; /* Authenticated user, or NULL if no auth
used */
char remote_addr[48]; /* Client's IP address as a string. */
#if defined(MG_LEGACY_INTERFACE)
long remote_ip; /* Client's IP address. Deprecated: use remote_addr instead
*/
#endif
long long content_length; /* Length (in bytes) of the request body,
can be -1 if no length was given. */
int remote_port; /* Client's port */
int is_ssl; /* 1 if SSL-ed, 0 if not */
void *user_data; /* User data pointer passed to mg_start() */
void *conn_data; /* Connection-specific user data */
int num_headers; /* Number of HTTP headers */
struct mg_header {
const char *name; /* HTTP header name */
const char *value; /* HTTP header value */
} http_headers[64]; /* Maximum 64 headers */
};
/* This structure needs to be passed to mg_start(), to let civetweb know
which callbacks to invoke. For a detailed description, see
https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md */
struct mg_callbacks {
/* Called when civetweb has received new HTTP request.
If the callback returns one, it must process the request
by sending valid HTTP headers and a body. Civetweb will not do
any further processing. Otherwise it must return zero.
Note that since V1.7 the "begin_request" function is called
before an authorization check. If an authorization check is
required, use a request_handler instead.
Return value:
0: civetweb will process the request itself. In this case,
the callback must not send any data to the client.
1-999: callback already processed the request. Civetweb will
not send any data after the callback returned. The
return code is stored as a HTTP status code for the
access log. */
int (*begin_request)(struct mg_connection *);
/* Called when civetweb has finished processing request. */
void (*end_request)(const struct mg_connection *, int reply_status_code);
/* Called when civetweb is about to log a message. If callback returns
non-zero, civetweb does not log anything. */
int (*log_message)(const struct mg_connection *, const char *message);
/* Called when civetweb is about to log access. If callback returns
non-zero, civetweb does not log anything. */
int (*log_access)(const struct mg_connection *, const char *message);
/* Called when civetweb initializes SSL library.
Parameters:
user_data: parameter user_data passed when starting the server.
Return value:
0: civetweb will set up the SSL certificate.
1: civetweb assumes the callback already set up the certificate.
-1: initializing ssl fails. */
int (*init_ssl)(void *ssl_context, void *user_data);
#if defined(MG_LEGACY_INTERFACE)
/* Called when websocket request is received, before websocket handshake.
Return value:
0: civetweb proceeds with websocket handshake.
1: connection is closed immediately.
This callback is deprecated: Use mg_set_websocket_handler instead. */
int (*websocket_connect)(const struct mg_connection *);
/* Called when websocket handshake is successfully completed, and
connection is ready for data exchange.
This callback is deprecated: Use mg_set_websocket_handler instead. */
void (*websocket_ready)(struct mg_connection *);
/* Called when data frame has been received from the client.
Parameters:
bits: first byte of the websocket frame, see websocket RFC at
http://tools.ietf.org/html/rfc6455, section 5.2
data, data_len: payload, with mask (if any) already applied.
Return value:
1: keep this websocket connection open.
0: close this websocket connection.
This callback is deprecated: Use mg_set_websocket_handler instead. */
int (*websocket_data)(struct mg_connection *,
int bits,
char *data,
size_t data_len);
#endif /* MG_LEGACY_INTERFACE */
/* Called when civetweb is closing a connection. The per-context mutex is
locked when this is invoked. This is primarily useful for noting when
a websocket is closing and removing it from any application-maintained
list of clients.
Using this callback for websocket connections is deprecated: Use
mg_set_websocket_handler instead. */
void (*connection_close)(const struct mg_connection *);
/* Called when civetweb tries to open a file. Used to intercept file open
calls, and serve file data from memory instead.
Parameters:
path: Full path to the file to open.
data_len: Placeholder for the file size, if file is served from
memory.
Return value:
NULL: do not serve file from memory, proceed with normal file open.
non-NULL: pointer to the file contents in memory. data_len must be
initilized with the size of the memory block. */
const char *(*open_file)(const struct mg_connection *,
const char *path,
size_t *data_len);
/* Called when civetweb is about to serve Lua server page, if
Lua support is enabled.
Parameters:
lua_context: "lua_State *" pointer. */
void (*init_lua)(const struct mg_connection *, void *lua_context);
#if defined(MG_LEGACY_INTERFACE)
/* Called when civetweb has uploaded a file to a temporary directory as a
result of mg_upload() call.
Note that mg_upload is deprecated. Use mg_handle_form_request instead.
Parameters:
file_name: full path name to the uploaded file. */
void (*upload)(struct mg_connection *, const char *file_name);
#endif
/* Called when civetweb is about to send HTTP error to the client.
Implementing this callback allows to create custom error pages.
Parameters:
status: HTTP error status code.
Return value:
1: run civetweb error handler.
0: callback already handled the error. */
int (*http_error)(struct mg_connection *, int status);
/* Called after civetweb context has been created, before requests
are processed.
Parameters:
ctx: context handle */
void (*init_context)(const struct mg_context *ctx);
/* Called when a new worker thread is initialized.
Parameters:
ctx: context handle
thread_type:
0 indicates the master thread
1 indicates a worker thread handling client connections
2 indicates an internal helper thread (timer thread)
*/
void (*init_thread)(const struct mg_context *ctx, int thread_type);
/* Called when civetweb context is deleted.
Parameters:
ctx: context handle */
void (*exit_context)(const struct mg_context *ctx);
};
/* Start web server.
Parameters:
callbacks: mg_callbacks structure with user-defined callbacks.
options: NULL terminated list of option_name, option_value pairs that
specify Civetweb configuration parameters.
Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
processing is required for these, signal handlers must be set up
after calling mg_start().
Example:
const char *options[] = {
"document_root", "/var/www",
"listening_ports", "80,443s",
NULL
};
struct mg_context *ctx = mg_start(&my_func, NULL, options);
Refer to https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md
for the list of valid option and their possible values.
Return:
web server context, or NULL on error. */
CIVETWEB_API struct mg_context *mg_start(const struct mg_callbacks *callbacks,
void *user_data,
const char **configuration_options);
/* Stop the web server.
Must be called last, when an application wants to stop the web server and
release all associated resources. This function blocks until all Civetweb
threads are stopped. Context pointer becomes invalid. */
CIVETWEB_API void mg_stop(struct mg_context *);
/* mg_request_handler
Called when a new request comes in. This callback is URI based
and configured with mg_set_request_handler().
Parameters:
conn: current connection information.
cbdata: the callback data configured with mg_set_request_handler().
Returns:
0: the handler could not handle the request, so fall through.
1 - 999: the handler processed the request. The return code is
stored as a HTTP status code for the access log. */
typedef int (*mg_request_handler)(struct mg_connection *conn, void *cbdata);
/* mg_set_request_handler
Sets or removes a URI mapping for a request handler.
This function uses mg_lock_context internally.
URI's are ordered and prefixed URI's are supported. For example,
consider two URIs: /a/b and /a
/a matches /a
/a/b matches /a/b
/a/c matches /a
Parameters:
ctx: server context
uri: the URI (exact or pattern) for the handler
handler: the callback handler to use when the URI is requested.
If NULL, an already registered handler for this URI will be
removed.
The URI used to remove a handler must match exactly the one used
to
register it (not only a pattern match).
cbdata: the callback data to give to the handler when it is called. */
CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx,
const char *uri,
mg_request_handler handler,
void *cbdata);
/* Callback types for websocket handlers in C/C++.
mg_websocket_connect_handler
Is called when the client intends to establish a websocket connection,
before websocket handshake.
Return value:
0: civetweb proceeds with websocket handshake.
1: connection is closed immediately.
mg_websocket_ready_handler
Is called when websocket handshake is successfully completed, and
connection is ready for data exchange.
mg_websocket_data_handler
Is called when a data frame has been received from the client.
Parameters:
bits: first byte of the websocket frame, see websocket RFC at
http://tools.ietf.org/html/rfc6455, section 5.2
data, data_len: payload, with mask (if any) already applied.
Return value:
1: keep this websocket connection open.
0: close this websocket connection.
mg_connection_close_handler
Is called, when the connection is closed.*/
typedef int (*mg_websocket_connect_handler)(const struct mg_connection *,
void *);
typedef void (*mg_websocket_ready_handler)(struct mg_connection *, void *);
typedef int (*mg_websocket_data_handler)(struct mg_connection *,
int,
char *,
size_t,
void *);
typedef void (*mg_websocket_close_handler)(const struct mg_connection *,
void *);
/* mg_set_websocket_handler
Set or remove handler functions for websocket connections.
This function works similar to mg_set_request_handler - see there. */
CIVETWEB_API void
mg_set_websocket_handler(struct mg_context *ctx,
const char *uri,
mg_websocket_connect_handler connect_handler,
mg_websocket_ready_handler ready_handler,
mg_websocket_data_handler data_handler,
mg_websocket_close_handler close_handler,
void *cbdata);
/* mg_authorization_handler
Some description here
Parameters:
conn: current connection information.
cbdata: the callback data configured with mg_set_request_handler().
Returns:
0: access denied
1: access granted
*/
typedef int (*mg_authorization_handler)(struct mg_connection *conn,
void *cbdata);
/* mg_set_auth_handler
Sets or removes a URI mapping for an authorization handler.
This function works similar to mg_set_request_handler - see there. */
CIVETWEB_API void mg_set_auth_handler(struct mg_context *ctx,
const char *uri,
mg_authorization_handler handler,
void *cbdata);
/* Get the value of particular configuration parameter.
The value returned is read-only. Civetweb does not allow changing
configuration at run time.
If given parameter name is not valid, NULL is returned. For valid
names, return value is guaranteed to be non-NULL. If parameter is not
set, zero-length string is returned. */
CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx,
const char *name);
/* Get context from connection. */
CIVETWEB_API struct mg_context *
mg_get_context(const struct mg_connection *conn);
/* Get user data passed to mg_start from context. */
CIVETWEB_API void *mg_get_user_data(const struct mg_context *ctx);
/* Set user data for the current connection. */
CIVETWEB_API void mg_set_user_connection_data(struct mg_connection *conn,
void *data);
/* Get user data set for the current connection. */
CIVETWEB_API void *
mg_get_user_connection_data(const struct mg_connection *conn);
#if defined(MG_LEGACY_INTERFACE)
/* Return array of strings that represent valid configuration options.
For each option, option name and default value is returned, i.e. the
number of entries in the array equals to number_of_options x 2.
Array is NULL terminated. */
/* Deprecated: Use mg_get_valid_options instead. */
CIVETWEB_API const char **mg_get_valid_option_names(void);
#endif
struct mg_option {
const char *name;
int type;
const char *default_value;
};
enum {
CONFIG_TYPE_UNKNOWN = 0x0,
CONFIG_TYPE_NUMBER = 0x1,
CONFIG_TYPE_STRING = 0x2,
CONFIG_TYPE_FILE = 0x3,
CONFIG_TYPE_DIRECTORY = 0x4,
CONFIG_TYPE_BOOLEAN = 0x5,
CONFIG_TYPE_EXT_PATTERN = 0x6
};
/* Return array of struct mg_option, representing all valid configuration
options of civetweb.c.
The array is terminated by a NULL name option. */
CIVETWEB_API const struct mg_option *mg_get_valid_options(void);
struct mg_server_ports {
int protocol; /* 1 = IPv4, 2 = IPv6, 3 = both */
int port; /* port number */
int is_ssl; /* https port: 0 = no, 1 = yes */
int is_redirect; /* redirect all requests: 0 = no, 1 = yes */
int _reserved1;
int _reserved2;
int _reserved3;
int _reserved4;
};
/* Get the list of ports that civetweb is listening on.
The parameter size is the size of the ports array in elements.
The caller is responsibility to allocate the required memory.
This function returns the number of struct mg_server_ports elements
filled in, or <0 in case of an error. */
CIVETWEB_API int mg_get_server_ports(const struct mg_context *ctx,
int size,
struct mg_server_ports *ports);
/* Deprecated: Use mg_get_server_ports instead. */
CIVETWEB_API size_t
mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl);
/* Add, edit or delete the entry in the passwords file.
This function allows an application to manipulate .htpasswd files on the
fly by adding, deleting and changing user records. This is one of the
several ways of implementing authentication on the server side. For another,
cookie-based way please refer to the examples/chat in the source tree.
If password is not NULL, entry is added (or modified if already exists).
If password is NULL, entry is deleted.
Return:
1 on success, 0 on error. */
CIVETWEB_API int mg_modify_passwords_file(const char *passwords_file_name,
const char *domain,
const char *user,
const char *password);
/* Return information associated with the request. */
CIVETWEB_API const struct mg_request_info *
mg_get_request_info(const struct mg_connection *);
/* Send data to the client.
Return:
0 when the connection has been closed
-1 on error
>0 number of bytes written on success */
CIVETWEB_API int mg_write(struct mg_connection *, const void *buf, size_t len);
/* Send data to a websocket client wrapped in a websocket frame. Uses
mg_lock_connection to ensure that the transmission is not interrupted,
i.e., when the application is proactively communicating and responding to
a request simultaneously.
Send data to a websocket client wrapped in a websocket frame.
This function is available when civetweb is compiled with -DUSE_WEBSOCKET
Return:
0 when the connection has been closed
-1 on error
>0 number of bytes written on success */
CIVETWEB_API int mg_websocket_write(struct mg_connection *conn,
int opcode,
const char *data,
size_t data_len);
/* Send data to a websocket server wrapped in a masked websocket frame. Uses
mg_lock_connection to ensure that the transmission is not interrupted,
i.e., when the application is proactively communicating and responding to
a request simultaneously.
Send data to a websocket server wrapped in a masked websocket frame.
This function is available when civetweb is compiled with -DUSE_WEBSOCKET
Return:
0 when the connection has been closed
-1 on error
>0 number of bytes written on success */
CIVETWEB_API int mg_websocket_client_write(struct mg_connection *conn,
int opcode,
const char *data,
size_t data_len);
/* Blocks until unique access is obtained to this connection. Intended for use
with websockets only.
Invoke this before mg_write or mg_printf when communicating with a
websocket if your code has server-initiated communication as well as
communication in direct response to a message. */
CIVETWEB_API void mg_lock_connection(struct mg_connection *conn);
CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn);
#if defined(MG_LEGACY_INTERFACE)
#define mg_lock mg_lock_connection
#define mg_unlock mg_unlock_connection
#endif
/* Lock server context. This lock may be used to protect resources
that are shared between different connection/worker threads. */
CIVETWEB_API void mg_lock_context(struct mg_context *ctx);
CIVETWEB_API void mg_unlock_context(struct mg_context *ctx);
/* Opcodes, from http://tools.ietf.org/html/rfc6455 */
enum {
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
WEBSOCKET_OPCODE_TEXT = 0x1,
WEBSOCKET_OPCODE_BINARY = 0x2,
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
WEBSOCKET_OPCODE_PING = 0x9,
WEBSOCKET_OPCODE_PONG = 0xa
};
/* Macros for enabling compiler-specific checks for printf-like arguments. */
#undef PRINTF_FORMAT_STRING
#if defined(_MSC_VER) && _MSC_VER >= 1400
#include <sal.h>
#if defined(_MSC_VER) && _MSC_VER > 1400
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
#else
#define PRINTF_FORMAT_STRING(s) __format_string s
#endif
#else
#define PRINTF_FORMAT_STRING(s) s
#endif
#ifdef __GNUC__
#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
#else
#define PRINTF_ARGS(x, y)
#endif
/* Send data to the client using printf() semantics.
Works exactly like mg_write(), but allows to do message formatting. */
CIVETWEB_API int mg_printf(struct mg_connection *,
PRINTF_FORMAT_STRING(const char *fmt),
...) PRINTF_ARGS(2, 3);
/* Send contents of the entire file together with HTTP headers. */
CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path);
/* Send contents of the entire file together with HTTP headers.
Parameters:
conn: Current connection information.
path: Full path to the file to send.
mime_type: Content-Type for file. NULL will cause the type to be
looked up by the file extension.
*/
CIVETWEB_API void mg_send_mime_file(struct mg_connection *conn,
const char *path,
const char *mime_type);
/* Store body data into a file. */
CIVETWEB_API long long mg_store_body(struct mg_connection *conn,
const char *path);
/* Read entire request body and stor it in a file "path".
Return:
< 0 Error
>= 0 Number of bytes stored in file "path".
*/
/* Read data from the remote end, return number of bytes read.
Return:
0 connection has been closed by peer. No more data could be read.
< 0 read error. No more data could be read from the connection.
> 0 number of bytes read into the buffer. */
CIVETWEB_API int mg_read(struct mg_connection *, void *buf, size_t len);
/* Get the value of particular HTTP header.
This is a helper function. It traverses request_info->http_headers array,
and if the header is present in the array, returns its value. If it is
not present, NULL is returned. */
CIVETWEB_API const char *mg_get_header(const struct mg_connection *,
const char *name);
/* Get a value of particular form variable.
Parameters:
data: pointer to form-uri-encoded buffer. This could be either POST data,
or request_info.query_string.
data_len: length of the encoded data.
var_name: variable name to decode from the buffer
dst: destination buffer for the decoded variable
dst_len: length of the destination buffer
Return:
On success, length of the decoded variable.
On error:
-1 (variable not found).
-2 (destination buffer is NULL, zero length or too small to hold the
decoded variable).
Destination buffer is guaranteed to be '\0' - terminated if it is not
NULL or zero length. */
CIVETWEB_API int mg_get_var(const char *data,
size_t data_len,
const char *var_name,
char *dst,
size_t dst_len);
/* Get a value of particular form variable.
Parameters:
data: pointer to form-uri-encoded buffer. This could be either POST data,
or request_info.query_string.
data_len: length of the encoded data.
var_name: variable name to decode from the buffer
dst: destination buffer for the decoded variable
dst_len: length of the destination buffer
occurrence: which occurrence of the variable, 0 is the first, 1 the
second...
this makes it possible to parse a query like
b=x&a=y&a=z which will have occurrence values b:0, a:0 and a:1
Return:
On success, length of the decoded variable.
On error:
-1 (variable not found).
-2 (destination buffer is NULL, zero length or too small to hold the
decoded variable).
Destination buffer is guaranteed to be '\0' - terminated if it is not
NULL or zero length. */
CIVETWEB_API int mg_get_var2(const char *data,
size_t data_len,
const char *var_name,
char *dst,
size_t dst_len,
size_t occurrence);
/* Fetch value of certain cookie variable into the destination buffer.
Destination buffer is guaranteed to be '\0' - terminated. In case of
failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
parameter. This function returns only first occurrence.
Return:
On success, value length.
On error:
-1 (either "Cookie:" header is not present at all or the requested
parameter is not found).
-2 (destination buffer is NULL, zero length or too small to hold the
value). */
CIVETWEB_API int mg_get_cookie(const char *cookie,
const char *var_name,
char *buf,
size_t buf_len);
/* Download data from the remote web server.
host: host name to connect to, e.g. "foo.com", or "10.12.40.1".
port: port number, e.g. 80.
use_ssl: wether to use SSL connection.
error_buffer, error_buffer_size: error message placeholder.
request_fmt,...: HTTP request.
Return:
On success, valid pointer to the new connection, suitable for mg_read().
On error, NULL. error_buffer contains error message.
Example:
char ebuf[100];
struct mg_connection *conn;
conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
"%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
*/
CIVETWEB_API struct mg_connection *
mg_download(const char *host,
int port,
int use_ssl,
char *error_buffer,
size_t error_buffer_size,
PRINTF_FORMAT_STRING(const char *request_fmt),
...) PRINTF_ARGS(6, 7);
/* Close the connection opened by mg_download(). */
CIVETWEB_API void mg_close_connection(struct mg_connection *conn);
#if defined(MG_LEGACY_INTERFACE)
/* File upload functionality. Each uploaded file gets saved into a temporary
file and MG_UPLOAD event is sent.
Return number of uploaded files.
Deprecated: Use mg_handle_form_request instead. */
CIVETWEB_API int mg_upload(struct mg_connection *conn,
const char *destination_dir);
#endif
/* This structure contains callback functions for handling form fields.
It is used as an argument to mg_handle_form_request. */
struct mg_form_data_handler {
/* This callback function is called, if a new field has been found.
* The return value of this callback is used to define how the field
* should be processed.
*
* Parameters:
* key: Name of the field ("name" property of the HTML input field).
* filename: Name of a file to upload, at the client computer.
* Only set for input fields of type "file", otherwise NULL.
* path: Output parameter: File name (incl. path) to store the file
* at the server computer. Only used if FORM_FIELD_STORAGE_STORE
* is returned by this callback. Existing files will be
* overwritten.
* pathlen: Length of the buffer for path.
* user_data: Value of the member user_data of mg_form_data_handler
*
* Return value:
* The callback must return the intended storage for this field
* (See FORM_FIELD_STORAGE_*).
*/
int (*field_found)(const char *key,
const char *filename,
char *path,
size_t pathlen,
void *user_data);
/* If the "field_found" callback returned FORM_FIELD_STORAGE_GET,
* this callback will receive the field data.
*
* Parameters:
* key: Name of the field ("name" property of the HTML input field).
* value: Value of the input field.
* user_data: Value of the member user_data of mg_form_data_handler
*
* Return value:
* TODO: Needs to be defined.
*/
int (*field_get)(const char *key,
const char *value,
size_t valuelen,
void *user_data);
/* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE,
* the data will be stored into a file. If the file has been written
* successfully, this callback will be called. This callback will
* not be called for only partially uploaded files. The
* mg_handle_form_request function will either store the file completely
* and call this callback, or it will remove any partial content and
* not call this callback function.
*
* Parameters:
* path: Path of the file stored at the server.
* file_size: Size of the stored file in bytes.
* user_data: Value of the member user_data of mg_form_data_handler
*
* Return value:
* TODO: Needs to be defined.
*/
int (*field_store)(const char *path, long long file_size, void *user_data);
/* User supplied argument, passed to all callback functions. */
void *user_data;
};
/* Return values definition for the "field_found" callback in
* mg_form_data_handler. */
enum {
/* Skip this field (neither get nor store it). Continue with the
* next field. */
FORM_FIELD_STORAGE_SKIP = 0x0,
/* Get the field value. */
FORM_FIELD_STORAGE_GET = 0x1,
/* Store the field value into a file. */
FORM_FIELD_STORAGE_STORE = 0x2,
/* Stop parsing this request. Skip the remaining fields. */
FORM_FIELD_STORAGE_ABORT = 0x10
};
/* Process form data.
* Returns the number of fields handled, or < 0 in case of an error.
* Note: It is possible that several fields are already handled successfully
* (e.g., stored into files), before the request handling is stopped with an
* error. In this case a number < 0 is returned as well.
* In any case, it is the duty of the caller to remove files once they are
* no longer required. */
CIVETWEB_API int mg_handle_form_request(struct mg_connection *conn,
struct mg_form_data_handler *fdh);
/* Convenience function -- create detached thread.
Return: 0 on success, non-0 on error. */
typedef void *(*mg_thread_func_t)(void *);
CIVETWEB_API int mg_start_thread(mg_thread_func_t f, void *p);
/* Return builtin mime type for the given file name.
For unrecognized extensions, "text/plain" is returned. */
CIVETWEB_API const char *mg_get_builtin_mime_type(const char *file_name);
/* Get text representation of HTTP status code. */
CIVETWEB_API const char *mg_get_response_code_text(struct mg_connection *conn,
int response_code);
/* Return CivetWeb version. */
CIVETWEB_API const char *mg_version(void);
/* URL-decode input buffer into destination buffer.
0-terminate the destination buffer.
form-url-encoded data differs from URI encoding in a way that it
uses '+' as character for space, see RFC 1866 section 8.2.1
http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
Return: length of the decoded data, or -1 if dst buffer is too small. */
CIVETWEB_API int mg_url_decode(const char *src,
int src_len,
char *dst,
int dst_len,
int is_form_url_encoded);
/* URL-encode input buffer into destination buffer.
returns the length of the resulting buffer or -1
is the buffer is too small. */
CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len);
/* MD5 hash given strings.
Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
ASCIIz strings. When function returns, buf will contain human-readable
MD5 hash. Example:
char buf[33];
mg_md5(buf, "aa", "bb", NULL); */
CIVETWEB_API char *mg_md5(char buf[33], ...);
/* Print error message to the opened error log stream.
This utilizes the provided logging configuration.
conn: connection
fmt: format string without the line return
...: variable argument list
Example:
mg_cry(conn,"i like %s", "logging"); */
CIVETWEB_API void mg_cry(const struct mg_connection *conn,
PRINTF_FORMAT_STRING(const char *fmt),
...) PRINTF_ARGS(2, 3);
/* utility methods to compare two buffers, case incensitive. */
CIVETWEB_API int mg_strcasecmp(const char *s1, const char *s2);
CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
/* Connect to a websocket as a client
Parameters:
host: host to connect to, i.e. "echo.websocket.org" or "192.168.1.1" or
"localhost"
port: server port
use_ssl: make a secure connection to server
error_buffer, error_buffer_size: buffer for an error message
path: server path you are trying to connect to, i.e. if connection to
localhost/app, path should be "/app"
origin: value of the Origin HTTP header
data_func: callback that should be used when data is received from the
server
user_data: user supplied argument
Return:
On success, valid mg_connection object.
On error, NULL. Se error_buffer for details.
*/
CIVETWEB_API struct mg_connection *
mg_connect_websocket_client(const char *host,
int port,
int use_ssl,
char *error_buffer,
size_t error_buffer_size,
const char *path,
const char *origin,
mg_websocket_data_handler data_func,
mg_websocket_close_handler close_func,
void *user_data);
/* Connect to a TCP server as a client (can be used to connect to a HTTP server)
Parameters:
host: host to connect to, i.e. "www.wikipedia.org" or "192.168.1.1" or
"localhost"
port: server port
use_ssl: make a secure connection to server
error_buffer, error_buffer_size: buffer for an error message
Return:
On success, valid mg_connection object.
On error, NULL. Se error_buffer for details.
*/
CIVETWEB_API struct mg_connection *mg_connect_client(const char *host,
int port,
int use_ssl,
char *error_buffer,
size_t error_buffer_size);
struct mg_client_options {
const char *host;
int port;
const char *client_cert;
const char *server_cert;
/* TODO: add more data */
};
CIVETWEB_API struct mg_connection *
mg_connect_client_secure(const struct mg_client_options *client_options,
char *error_buffer,
size_t error_buffer_size);
enum { TIMEOUT_INFINITE = -1 };
/* Wait for a response from the server
Parameters:
conn: connection
ebuf, ebuf_len: error message placeholder.
timeout: time to wait for a response in milliseconds (if < 0 then wait
forever)
Return:
On success, >= 0
On error/timeout, < 0
*/
CIVETWEB_API int mg_get_response(struct mg_connection *conn,
char *ebuf,
size_t ebuf_len,
int timeout);
/* Check which features where set when civetweb has been compiled.
Parameters:
feature: specifies which feature should be checked
1 serve files (NO_FILES not set)
2 support HTTPS (NO_SSL not set)
4 support CGI (NO_CGI not set)
8 support IPv6 (USE_IPV6 set)
16 support WebSocket (USE_WEBSOCKET set)
32 support Lua scripts and Lua server pages (USE_LUA is set)
64 support server side JavaScript (USE_DUKTAPE is set)
128 support caching (NO_CACHING not set)
The result is undefined for all other feature values.
Return:
If feature is available > 0
If feature is not available = 0
*/
CIVETWEB_API unsigned mg_check_feature(unsigned feature);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CIVETWEB_HEADER_INCLUDED */
/* Copyright (c) 2016 the Civetweb developers
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
static int
url_encoded_field_found(const struct mg_connection *conn,
const char *key,
size_t key_len,
const char *filename,
size_t filename_len,
char *path,
size_t path_len,
struct mg_form_data_handler *fdh)
{
char key_dec[1024];
char filename_dec[1024];
int key_dec_len;
int filename_dec_len;
int ret;
key_dec_len =
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
return FORM_FIELD_STORAGE_SKIP;
}
if (filename) {
filename_dec_len = mg_url_decode(filename,
(int)filename_len,
filename_dec,
(int)sizeof(filename_dec),
1);
if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
|| (filename_dec_len < 0)) {
/* Log error message and skip this field. */
mg_cry(conn, "%s: Cannot decode filename", __func__);
return FORM_FIELD_STORAGE_SKIP;
}
} else {
filename_dec[0] = 0;
}
ret =
fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) {
if (fdh->field_get == NULL) {
mg_cry(conn, "%s: Function \"Get\" not available", __func__);
return FORM_FIELD_STORAGE_SKIP;
}
}
if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) {
if (fdh->field_store == NULL) {
mg_cry(conn, "%s: Function \"Store\" not available", __func__);
return FORM_FIELD_STORAGE_SKIP;
}
}
return ret;
}
static int
url_encoded_field_get(const struct mg_connection *conn,
const char *key,
size_t key_len,
const char *value,
size_t value_len,
struct mg_form_data_handler *fdh)
{
char key_dec[1024];
char *value_dec = mg_malloc(value_len + 1);
int value_dec_len;
if (!value_dec) {
/* Log error message and stop parsing the form data. */
mg_cry(conn,
"%s: Not enough memory (required: %lu)",
__func__,
(unsigned long)(value_len + 1));
return FORM_FIELD_STORAGE_ABORT;
}
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
value_dec_len =
mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
return fdh->field_get(key_dec,
value_dec,
(size_t)value_dec_len,
fdh->user_data);
}
static int
field_stored(const struct mg_connection *conn,
const char *path,
long long file_size,
struct mg_form_data_handler *fdh)
{
/* Equivalent to "upload" callback of "mg_upload". */
(void)conn; /* we do not need mg_cry here, so conn is currently unused */
return fdh->field_store(path, file_size, fdh->user_data);
}
static const char *
search_boundary(const char *buf,
size_t buf_len,
const char *boundary,
size_t boundary_len)
{
/* We must do a binary search here, not a string search, since the buffer
* may contain '\x00' bytes, if binary data is transferred. */
int clen = (int)buf_len - (int)boundary_len - 4;
int i;
for (i = 0; i <= clen; i++) {
if (!memcmp(buf + i, "\r\n--", 4)) {
if (!memcmp(buf + i + 4, boundary, boundary_len)) {
return buf + i;
}
}
}
return NULL;
}
int
mg_handle_form_request(struct mg_connection *conn,
struct mg_form_data_handler *fdh)
{
const char *content_type;
char path[512];
char buf[1024];
int field_storage;
int buf_fill = 0;
int r;
int field_count = 0;
struct file fstore = STRUCT_FILE_INITIALIZER;
int64_t file_size = 0; /* init here, to a avoid a false positive
"uninitialized variable used" warning */
int has_body_data =
(conn->request_info.content_length > 0) || (conn->is_chunked);
/* There are three ways to encode data from a HTML form:
* 1) method: GET (default)
* The form data is in the HTTP query string.
* 2) method: POST, enctype: "application/x-www-form-urlencoded"
* The form data is in the request body.
* The body is url encoded (the default encoding for POST).
* 3) method: POST, enctype: "multipart/form-data".
* The form data is in the request body of a multipart message.
* This is the typical way to handle file upload from a form.
*/
if (!has_body_data) {
const char *data;
if (strcmp(conn->request_info.request_method, "GET")) {
/* No body data, but not a GET request.
* This is not a valid form request. */
return -1;
}
/* GET request: form data is in the query string. */
/* The entire data has already been loaded, so there is no nead to
* call mg_read. We just need to split the query string into key-value
* pairs. */
data = conn->request_info.query_string;
if (!data) {
/* No query string. */
return -1;
}
/* Split data in a=1&b=xy&c=3&c=4 ... */
while (*data) {
const char *val = strchr(data, '=');
const char *next;
ptrdiff_t keylen, vallen;
if (!val) {
break;
}
keylen = val - data;
/* In every "field_found" callback we ask what to do with the
* data ("field_storage"). This could be:
* FORM_FIELD_STORAGE_SKIP (0) ... ignore the value of this field
* FORM_FIELD_STORAGE_GET (1) ... read the data and call the get
* callback function
* FORM_FIELD_STORAGE_STORE (2) ... store the data in a file
* FORM_FIELD_STORAGE_READ (3) ... let the user read the data
* (for parsing long data on the fly)
* (currently not implemented)
* FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing
*/
memset(path, 0, sizeof(path));
field_count++;
field_storage = url_encoded_field_found(conn,
data,
(size_t)keylen,
NULL,
0,
path,
sizeof(path) - 1,
fdh);
val++;
next = strchr(val, '&');
if (next) {
vallen = next - val;
next++;
} else {
vallen = (ptrdiff_t)strlen(val);
next = val + vallen;
}
if (field_storage == FORM_FIELD_STORAGE_GET) {
/* Call callback */
url_encoded_field_get(
conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
/* Store the content to a file */
if (mg_fopen(conn, path, "wb", &fstore) == 0) {
fstore.fp = NULL;
}
file_size = 0;
if (fstore.fp != NULL) {
size_t n =
(size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
mg_cry(conn,
"%s: Cannot write file %s",
__func__,
path);
fclose(fstore.fp);
fstore.fp = NULL;
remove_bad_file(conn, path);
}
file_size += (int64_t)n;
if (fstore.fp) {
r = fclose(fstore.fp);
if (r == 0) {
/* stored successfully */
field_stored(conn, path, file_size, fdh);
} else {
mg_cry(conn,
"%s: Error saving file %s",
__func__,
path);
remove_bad_file(conn, path);
}
fstore.fp = NULL;
}
} else {
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
}
}
/* if (field_storage == FORM_FIELD_STORAGE_READ) { */
/* The idea of "field_storage=read" is to let the API user read
* data chunk by chunk and to some data processing on the fly.
* This should avoid the need to store data in the server:
* It should neither be stored in memory, like
* "field_storage=get" does, nor in a file like
* "field_storage=store".
* However, for a "GET" request this does not make any much
* sense, since the data is already stored in memory, as it is
* part of the query string.
*/
/* } */
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
== FORM_FIELD_STORAGE_ABORT) {
/* Stop parsing the request */
break;
}
/* Proceed to next entry */
data = next;
}
return field_count;
}
content_type = mg_get_header(conn, "Content-Type");
if (!content_type
|| !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")
|| !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) {
/* The form data is in the request body data, encoded in key/value
* pairs. */
int all_data_read = 0;
/* Read body data and split it in keys and values.
* The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
* Here we use "POST", and read the data from the request body.
* The data read on the fly, so it is not required to buffer the
* entire request in memory before processing it. */
for (;;) {
const char *val;
const char *next;
ptrdiff_t keylen, vallen;
ptrdiff_t used;
int end_of_key_value_pair_found = 0;
int get_block;
if ((size_t)buf_fill < (sizeof(buf) - 1)) {
size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
r = mg_read(conn, buf + (size_t)buf_fill, to_read);
if (r < 0) {
/* read error */
return -1;
}
if (r != (int)to_read) {
/* TODO: Create a function to get "all_data_read" from
* the conn object. All data is read if the Content-Length
* has been reached, or if chunked encoding is used and
* the end marker has been read, or if the connection has
* been closed. */
all_data_read = 1;
}
buf_fill += r;
buf[buf_fill] = 0;
if (buf_fill < 1) {
break;
}
}
val = strchr(buf, '=');
if (!val) {
break;
}
keylen = val - buf;
val++;
/* Call callback */
memset(path, 0, sizeof(path));
field_count++;
field_storage = url_encoded_field_found(conn,
buf,
(size_t)keylen,
NULL,
0,
path,
sizeof(path) - 1,
fdh);
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
== FORM_FIELD_STORAGE_ABORT) {
/* Stop parsing the request */
break;
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
if (mg_fopen(conn, path, "wb", &fstore) == 0) {
fstore.fp = NULL;
}
file_size = 0;
if (!fstore.fp) {
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
}
}
get_block = 0;
/* Loop to read values larger than sizeof(buf)-keylen-2 */
do {
next = strchr(val, '&');
if (next) {
vallen = next - val;
next++;
end_of_key_value_pair_found = 1;
} else {
vallen = (ptrdiff_t)strlen(val);
next = val + vallen;
}
if (field_storage == FORM_FIELD_STORAGE_GET) {
#if 0
if (!end_of_key_value_pair_found && !all_data_read) {
/* This callback will deliver partial contents */
}
#else
(void)all_data_read; /* avoid warning */
#endif
/* Call callback */
url_encoded_field_get(conn,
((get_block > 0) ? NULL : buf),
((get_block > 0) ? 0
: (size_t)keylen),
val,
(size_t)vallen,
fdh);
get_block++;
}
if (fstore.fp) {
size_t n =
(size_t)fwrite(val, 1, (size_t)vallen, fstore.fp);
if ((n != (size_t)vallen) || (ferror(fstore.fp))) {
mg_cry(conn,
"%s: Cannot write file %s",
__func__,
path);
fclose(fstore.fp);
fstore.fp = NULL;
remove_bad_file(conn, path);
}
file_size += (int64_t)n;
}
if (!end_of_key_value_pair_found) {
used = next - buf;
memmove(buf,
buf + (size_t)used,
sizeof(buf) - (size_t)used);
buf_fill -= (int)used;
if ((size_t)buf_fill < (sizeof(buf) - 1)) {
size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
r = mg_read(conn, buf + (size_t)buf_fill, to_read);
if (r < 0) {
/* read error */
return -1;
}
if (r != (int)to_read) {
/* TODO: Create a function to get "all_data_read"
* from the conn object. All data is read if the
* Content-Length has been reached, or if chunked
* encoding is used and the end marker has been
* read, or if the connection has been closed. */
all_data_read = 1;
}
buf_fill += r;
buf[buf_fill] = 0;
if (buf_fill < 1) {
break;
}
val = buf;
}
}
} while (!end_of_key_value_pair_found);
if (fstore.fp) {
r = fclose(fstore.fp);
if (r == 0) {
/* stored successfully */
field_stored(conn, path, file_size, fdh);
} else {
mg_cry(conn, "%s: Error saving file %s", __func__, path);
remove_bad_file(conn, path);
}
fstore.fp = NULL;
}
/* Proceed to next entry */
used = next - buf;
memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
buf_fill -= (int)used;
}
return field_count;
}
if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
/* The form data is in the request body data, encoded as multipart
* content (see https://www.ietf.org/rfc/rfc1867.txt,
* https://www.ietf.org/rfc/rfc2388.txt). */
const char *boundary;
size_t bl;
ptrdiff_t used;
struct mg_request_info part_header;
char *hbuf, *hend, *fbeg, *fend, *nbeg, *nend;
const char *content_disp;
const char *next;
memset(&part_header, 0, sizeof(part_header));
/* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
bl = 20;
while (content_type[bl] == ' ') {
bl++;
}
/* There has to be a BOUNDARY definition in the Content-Type header */
if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
/* Malformed request */
return -1;
}
boundary = content_type + bl + 9;
bl = strlen(boundary);
if (bl + 800 > sizeof(buf)) {
/* Sanity check: The algorithm can not work if bl >= sizeof(buf),
* and it will not work effectively, if the buf is only a few byte
* larger than bl, or it buf can not hold the multipart header
* plus the boundary.
* Check some reasonable number here, that should be fulfilled by
* any reasonable request from every browser. If it is not
* fulfilled, it might be a hand-made request, intended to
* interfere with the algorithm. */
return -1;
}
for (;;) {
size_t towrite, n;
int get_block;
r = mg_read(conn,
buf + (size_t)buf_fill,
sizeof(buf) - 1 - (size_t)buf_fill);
if (r < 0) {
/* read error */
return -1;
}
buf_fill += r;
buf[buf_fill] = 0;
if (buf_fill < 1) {
/* No data */
return -1;
}
if (buf[0] != '-' || buf[1] != '-') {
/* Malformed request */
return -1;
}
if (strncmp(buf + 2, boundary, bl)) {
/* Malformed request */
return -1;
}
if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
/* Every part must end with \r\n, if there is another part.
* The end of the request has an extra -- */
if (((size_t)buf_fill != (size_t)(bl + 6))
|| (strncmp(buf + bl + 2, "--\r\n", 4))) {
/* Malformed request */
return -1;
}
/* End of the request */
break;
}
/* Next, we need to get the part header: Read until \r\n\r\n */
hbuf = buf + bl + 4;
hend = strstr(hbuf, "\r\n\r\n");
if (!hend) {
/* Malformed request */
return -1;
}
parse_http_headers(&hbuf, &part_header);
if ((hend + 2) != hbuf) {
/* Malformed request */
return -1;
}
/* Skip \r\n\r\n */
hend += 4;
/* According to the RFC, every part has to have a header field like:
* Content-Disposition: form-data; name="..." */
content_disp = get_header(&part_header, "Content-Disposition");
if (!content_disp) {
/* Malformed request */
return -1;
}
/* Get the mandatory name="..." part of the Content-Disposition
* header. */
nbeg = strstr(content_disp, "name=\"");
if (!nbeg) {
/* Malformed request */
return -1;
}
nbeg += 6;
nend = strchr(nbeg, '\"');
if (!nend) {
/* Malformed request */
return -1;
}
/* Get the optional filename="..." part of the Content-Disposition
* header. */
fbeg = strstr(content_disp, "filename=\"");
if (fbeg) {
fbeg += 10;
fend = strchr(fbeg, '\"');
if (!fend) {
/* Malformed request (the filename field is optional, but if
* it exists, it needs to be terminated correctly). */
return -1;
}
/* TODO: check Content-Type */
/* Content-Type: application/octet-stream */
} else {
fend = fbeg;
}
memset(path, 0, sizeof(path));
field_count++;
field_storage = url_encoded_field_found(conn,
nbeg,
(size_t)(nend - nbeg),
fbeg,
(size_t)(fend - fbeg),
path,
sizeof(path) - 1,
fdh);
/* If the boundary is already in the buffer, get the address,
* otherwise next will be NULL. */
next = search_boundary(hbuf,
(size_t)((buf - hbuf) + buf_fill),
boundary,
bl);
if (field_storage == FORM_FIELD_STORAGE_STORE) {
/* Store the content to a file */
if (mg_fopen(conn, path, "wb", &fstore) == 0) {
fstore.fp = NULL;
}
file_size = 0;
if (!fstore.fp) {
mg_cry(conn, "%s: Cannot create file %s", __func__, path);
}
}
get_block = 0;
while (!next) {
/* Set "towrite" to the number of bytes available
* in the buffer */
towrite = (size_t)(buf - hend + buf_fill);
/* Subtract the boundary length, to deal with
* cases the boundary is only partially stored
* in the buffer. */
towrite -= bl + 4;
if (field_storage == FORM_FIELD_STORAGE_GET) {
url_encoded_field_get(conn,
((get_block > 0) ? NULL : nbeg),
((get_block > 0)
? 0
: (size_t)(nend - nbeg)),
hend,
towrite,
fdh);
get_block++;
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
if (fstore.fp) {
/* Store the content of the buffer. */
n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
if ((n != towrite) || (ferror(fstore.fp))) {
mg_cry(conn,
"%s: Cannot write file %s",
__func__,
path);
fclose(fstore.fp);
fstore.fp = NULL;
remove_bad_file(conn, path);
}
file_size += (int64_t)n;
}
}
memmove(buf, hend + towrite, bl + 4);
buf_fill = (int)(bl + 4);
hend = buf;
/* Read new data */
r = mg_read(conn,
buf + (size_t)buf_fill,
sizeof(buf) - 1 - (size_t)buf_fill);
if (r < 0) {
/* read error */
return -1;
}
buf_fill += r;
buf[buf_fill] = 0;
if (buf_fill < 1) {
/* No data */
return -1;
}
/* Find boundary */
next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
}
towrite = (size_t)(next - hend);
if (field_storage == FORM_FIELD_STORAGE_GET) {
/* Call callback */
url_encoded_field_get(conn,
((get_block > 0) ? NULL : nbeg),
((get_block > 0) ? 0
: (size_t)(nend - nbeg)),
hend,
towrite,
fdh);
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
if (fstore.fp) {
n = (size_t)fwrite(hend, 1, towrite, fstore.fp);
if ((n != towrite) || (ferror(fstore.fp))) {
mg_cry(conn,
"%s: Cannot write file %s",
__func__,
path);
fclose(fstore.fp);
fstore.fp = NULL;
remove_bad_file(conn, path);
}
file_size += (int64_t)n;
}
}
if (field_storage == FORM_FIELD_STORAGE_STORE) {
if (fstore.fp) {
r = fclose(fstore.fp);
if (r == 0) {
/* stored successfully */
field_stored(conn, path, file_size, fdh);
} else {
mg_cry(conn,
"%s: Error saving file %s",
__func__,
path);
remove_bad_file(conn, path);
}
fstore.fp = NULL;
}
}
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
== FORM_FIELD_STORAGE_ABORT) {
/* Stop parsing the request */
break;
}
/* Remove from the buffer */
used = next - buf + 2;
memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
buf_fill -= (int)used;
}
/* All parts handled */
return field_count;
}
/* Unknown Content-Type */
return -1;
}
/*
* This an amalgamation of md5.c and md5.h into a single file
* with all static declaration to reduce linker conflicts
* in Civetweb.
*
* The MD5_STATIC declaration was added to facilitate static
* inclusion.
* No Face Press, LLC
*/
/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.h is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Removed support for non-ANSI compilers; removed
references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>.
1999-05-03 lpd Original version.
*/
#ifndef md5_INCLUDED
#define md5_INCLUDED
/*
* This package supports both compile-time and run-time determination of CPU
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
* defined as non-zero, the code will be compiled to run only on big-endian
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
* run on either big- or little-endian CPUs, but will run slightly less
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
*/
typedef unsigned char md5_byte_t; /* 8-bit byte */
typedef unsigned int md5_word_t; /* 32-bit word */
/* Define the state of the MD5 Algorithm. */
typedef struct md5_state_s {
md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */
} md5_state_t;
#ifdef __cplusplus
extern "C" {
#endif
/* Initialize the algorithm. */
MD5_STATIC void md5_init(md5_state_t *pms);
/* Append a string to the message. */
MD5_STATIC void
md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes);
/* Finish the message and return the digest. */
MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
#ifdef __cplusplus
} /* end extern "C" */
#endif
#endif /* md5_INCLUDED */
/*
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
L. Peter Deutsch
ghost@aladdin.com
*/
/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.c is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
either statically or dynamically; added missing #include <string.h>
in library.
2002-03-11 lpd Corrected argument list for main(), and added int return
type, in test program and T value program.
2002-02-21 lpd Added missing #include <stdio.h> in test program.
2000-07-03 lpd Patched to eliminate warnings about "constant is
unsigned in ANSI C, signed in traditional"; made test program
self-checking.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1999-05-03 lpd Original version.
*/
#ifndef MD5_STATIC
#include <string.h>
#endif
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#ifdef ARCH_IS_BIG_ENDIAN
#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
#else
#define BYTE_ORDER (0)
#endif
#define T_MASK ((md5_word_t)~0)
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
#define T3 (0x242070db)
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
#define T6 (0x4787c62a)
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
#define T9 (0x698098d8)
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
#define T13 (0x6b901122)
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
#define T16 (0x49b40821)
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
#define T19 (0x265e5a51)
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
#define T22 (0x02441453)
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
#define T25 (0x21e1cde6)
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
#define T28 (0x455a14ed)
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
#define T31 (0x676f02d9)
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
#define T35 (0x6d9d6122)
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
#define T38 (0x4bdecfa9)
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
#define T41 (0x289b7ec6)
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
#define T44 (0x04881d05)
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
#define T47 (0x1fa27cf8)
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
#define T50 (0x432aff97)
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
#define T53 (0x655b59c3)
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
#define T57 (0x6fa87e4f)
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
#define T60 (0x4e0811a1)
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
#define T63 (0x2ad7d2bb)
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
static void
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
{
md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2],
d = pms->abcd[3];
md5_word_t t;
#if BYTE_ORDER > 0
/* Define storage only for big-endian CPUs. */
md5_word_t X[16];
#else
/* Define storage for little-endian or both types of CPUs. */
md5_word_t xbuf[16];
const md5_word_t *X;
#endif
{
#if BYTE_ORDER == 0
/*
* Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient
* algorithm on the latter.
*/
static const int w = 1;
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
#endif
#if BYTE_ORDER <= 0 /* little-endian */
{
/*
* On little-endian machines, we can process properly aligned
* data without copying it.
*/
if (!((data - (const md5_byte_t *)0) & 3)) {
/* data are properly aligned, a direct assignment is possible */
/* cast through a (void *) should avoid a compiler warning,
see
https://github.com/bel2125/civetweb/issues/94#issuecomment-98112861
*/
X = (const md5_word_t *)(const void *)data;
} else {
/* not aligned */
memcpy(xbuf, data, 64);
X = xbuf;
}
}
#endif
#if BYTE_ORDER == 0
else /* dynamic big-endian */
#endif
#if BYTE_ORDER >= 0 /* big-endian */
{
/*
* On big-endian machines, we must arrange the bytes in the
* right order.
*/
const md5_byte_t *xp = data;
int i;
#if BYTE_ORDER == 0
X = xbuf; /* (dynamic only) */
#else
#define xbuf X /* (static only) */
#endif
for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = (md5_word_t)(xp[0]) + (md5_word_t)(xp[1] << 8)
+ (md5_word_t)(xp[2] << 16)
+ (md5_word_t)(xp[3] << 24);
}
#endif
}
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
/* Round 1. */
/* Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti) \
t = a + F(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(c, d, a, b, 10, 17, T11);
SET(b, c, d, a, 11, 22, T12);
SET(a, b, c, d, 12, 7, T13);
SET(d, a, b, c, 13, 12, T14);
SET(c, d, a, b, 14, 17, T15);
SET(b, c, d, a, 15, 22, T16);
#undef SET
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti) \
t = a + G(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(c, d, a, b, 15, 14, T23);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 12, 20, T32);
#undef SET
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti) \
t = a + H(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35);
SET(b, c, d, a, 14, 23, T36);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(b, c, d, a, 10, 23, T40);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(d, a, b, c, 12, 11, T46);
SET(c, d, a, b, 15, 16, T47);
SET(b, c, d, a, 2, 23, T48);
#undef SET
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti) \
t = a + I(b, c, d) + X[k] + Ti; \
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(c, d, a, b, 10, 15, T55);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(d, a, b, c, 15, 10, T58);
SET(c, d, a, b, 6, 15, T59);
SET(b, c, d, a, 13, 21, T60);
SET(a, b, c, d, 4, 6, T61);
SET(d, a, b, c, 11, 10, T62);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
#undef SET
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
pms->abcd[0] += a;
pms->abcd[1] += b;
pms->abcd[2] += c;
pms->abcd[3] += d;
}
MD5_STATIC void
md5_init(md5_state_t *pms)
{
pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
pms->abcd[3] = 0x10325476;
}
MD5_STATIC void
md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
{
const md5_byte_t *p = data;
size_t left = nbytes;
size_t offset = (pms->count[0] >> 3) & 63;
md5_word_t nbits = (md5_word_t)(nbytes << 3);
if (nbytes <= 0)
return;
/* Update the message length. */
pms->count[1] += (md5_word_t)(nbytes >> 29);
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
/* Process an initial partial block. */
if (offset) {
size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy(pms->buf + offset, p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
/* Process full blocks. */
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
/* Process a final partial block. */
if (left)
memcpy(pms->buf, p, left);
}
MD5_STATIC void
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
{
static const md5_byte_t pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
md5_byte_t data[8];
int i;
/* Save the length before padding. */
for (i = 0; i < 8; ++i)
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */
md5_append(pms, data, 8);
for (i = 0; i < 16; ++i)
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}
/* This file is part of the CivetWeb web server.
* See https://github.com/civetweb/civetweb/
* (C) 2015 by the CivetWeb authors, MIT license.
*/
#include "duktape.h"
/* TODO: the mg context should be added to duktape as well */
/* Alternative: redefine a new, clean API from scratch (instead of using mg),
* or at least do not add problematic functions. */
/* For evaluation purposes, currently only "send" is supported.
* All other ~50 functions will be added later. */
/* Note: This is only experimental support, so the API may still change. */
static const char *civetweb_conn_id = "\xFF"
"civetweb_conn";
static const char *civetweb_ctx_id = "\xFF"
"civetweb_ctx";
static void *
mg_duk_mem_alloc(void *udata, duk_size_t size)
{
return mg_malloc(size);
}
static void *
mg_duk_mem_realloc(void *udata, void *ptr, duk_size_t newsize)
{
return mg_realloc(ptr, newsize);
}
static void
mg_duk_mem_free(void *udata, void *ptr)
{
mg_free(ptr);
}
static void
mg_duk_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg)
{
/* Script is called "protected" (duk_peval_file), so script errors should
* never yield in a call to this function. Maybe calls prior to executing
* the script could raise a fatal error. */
struct mg_connection *conn;
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
mg_cry(conn, "%s", msg);
}
static duk_ret_t
duk_itf_write(duk_context *ctx)
{
struct mg_connection *conn;
duk_double_t ret;
duk_size_t len = 0;
const char *val = duk_require_lstring(ctx, -1, &len);
/*
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
*/
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
if (!conn) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
ret = mg_write(conn, val, len);
duk_push_number(ctx, ret);
return 1;
}
static duk_ret_t
duk_itf_read(duk_context *ctx)
{
struct mg_connection *conn;
char buf[1024];
int len;
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
if (!conn) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
len = mg_read(conn, buf, sizeof(buf));
duk_push_lstring(ctx, buf, len);
return 1;
}
static duk_ret_t
duk_itf_getoption(duk_context *ctx)
{
struct mg_context *cv_ctx;
const char *ret;
duk_size_t len = 0;
const char *val = duk_require_lstring(ctx, -1, &len);
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, civetweb_ctx_id);
cv_ctx = (struct mg_context *)duk_to_pointer(ctx, -1);
if (!cv_ctx) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
ret = mg_get_option(cv_ctx, val);
if (ret) {
duk_push_string(ctx, ret);
} else {
duk_push_null(ctx);
}
return 1;
}
static void
mg_exec_duktape_script(struct mg_connection *conn, const char *script_name)
{
int i;
duk_context *ctx = NULL;
conn->must_close = 1;
/* Create Duktape interpreter state */
ctx = duk_create_heap(mg_duk_mem_alloc,
mg_duk_mem_realloc,
mg_duk_mem_free,
NULL,
mg_duk_fatal_handler);
if (!ctx) {
mg_cry(conn, "Failed to create a Duktape heap.");
goto exec_duktape_finished;
}
/* Add "conn" object */
duk_push_global_object(ctx);
duk_push_object(ctx); /* create a new table/object ("conn") */
duk_push_c_function(ctx, duk_itf_write, 1 /* 1 = nargs */);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
duk_put_prop_string(ctx, -2, "write"); /* add function conn.write */
duk_push_c_function(ctx, duk_itf_read, 0 /* 0 = nargs */);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
duk_put_prop_string(ctx, -2, "read"); /* add function conn.read */
duk_push_string(ctx, conn->request_info.request_method);
duk_put_prop_string(ctx, -2, "request_method"); /* add string conn.r... */
duk_push_string(ctx, conn->request_info.request_uri);
duk_put_prop_string(ctx, -2, "request_uri");
duk_push_string(ctx, conn->request_info.local_uri);
duk_put_prop_string(ctx, -2, "uri");
duk_push_string(ctx, conn->request_info.http_version);
duk_put_prop_string(ctx, -2, "http_version");
duk_push_string(ctx, conn->request_info.query_string);
duk_put_prop_string(ctx, -2, "query_string");
duk_push_string(ctx, conn->request_info.remote_addr);
duk_put_prop_string(ctx, -2, "remote_addr");
duk_push_int(ctx, conn->request_info.remote_port);
duk_put_prop_string(ctx, -2, "remote_port");
duk_push_int(ctx, ntohs(conn->client.lsa.sin.sin_port));
duk_put_prop_string(ctx, -2, "server_port");
duk_push_object(ctx); /* subfolder "conn.http_headers" */
for (i = 0; i < conn->request_info.num_headers; i++) {
duk_push_string(ctx, conn->request_info.http_headers[i].value);
duk_put_prop_string(ctx, -2, conn->request_info.http_headers[i].name);
}
duk_put_prop_string(ctx, -2, "http_headers");
duk_put_prop_string(ctx, -2, "conn"); /* call the table "conn" */
/* Add "civetweb" object */
duk_push_global_object(ctx);
duk_push_object(ctx); /* create a new table/object ("conn") */
duk_push_string(ctx, CIVETWEB_VERSION);
duk_put_prop_string(ctx, -2, "version");
duk_push_string(ctx, script_name);
duk_put_prop_string(ctx, -2, "script_name");
if (conn->ctx != NULL) {
duk_push_c_function(ctx, duk_itf_getoption, 1 /* 1 = nargs */);
duk_push_pointer(ctx, (void *)(conn->ctx));
duk_put_prop_string(ctx, -2, civetweb_ctx_id);
duk_put_prop_string(ctx, -2, "getoption"); /* add function conn.write */
if (conn->ctx->systemName != NULL) {
duk_push_string(ctx, conn->ctx->systemName);
duk_put_prop_string(ctx, -2, "system");
}
}
duk_put_prop_string(ctx, -2, "civetweb"); /* call the table "civetweb" */
duk_push_global_stash(ctx);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
if (duk_peval_file(ctx, script_name) != 0) {
mg_cry(conn, "%s", duk_safe_to_string(ctx, -1));
goto exec_duktape_finished;
}
duk_pop(ctx); /* ignore result */
exec_duktape_finished:
duk_destroy_heap(ctx);
}
#include "civetweb_lua.h"
#include "civetweb_private_lua.h"
#ifdef _WIN32
static void *
mmap(void *addr, int64_t len, int prot, int flags, int fd, int offset)
{
/* TODO (low): This is an incomplete implementation of mmap for windows.
* Currently it is sufficient, but there are a lot of unused parameters.
* Better use a function "mg_map" which only has the required parameters,
* and implement it using mmap in Linux and CreateFileMapping in Windows.
* Noone should expect a full mmap for Windows here.
*/
HANDLE fh = (HANDLE)_get_osfhandle(fd);
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t)len);
CloseHandle(mh);
/* unused parameters */
(void)addr;
(void)prot;
(void)flags;
(void)offset;
return p;
}
static void
munmap(void *addr, int64_t length)
{
/* unused parameters */
(void)length;
UnmapViewOfFile(addr);
}
#define MAP_FAILED (NULL)
#define MAP_PRIVATE (0)
#define PROT_READ (0)
#else
#include <sys/mman.h>
#endif
static const char *LUASOCKET = "luasocket";
static const char lua_regkey_ctx = 1;
static const char lua_regkey_connlist = 2;
/* Forward declarations */
static void handle_request(struct mg_connection *);
static int handle_lsp_request(struct mg_connection *,
const char *,
struct file *,
struct lua_State *);
static void
reg_string(struct lua_State *L, const char *name, const char *val)
{
if (name != NULL && val != NULL) {
lua_pushstring(L, name);
lua_pushstring(L, val);
lua_rawset(L, -3);
}
}
static void
reg_int(struct lua_State *L, const char *name, int val)
{
if (name != NULL) {
lua_pushstring(L, name);
lua_pushinteger(L, val);
lua_rawset(L, -3);
}
}
static void
reg_boolean(struct lua_State *L, const char *name, int val)
{
if (name != NULL) {
lua_pushstring(L, name);
lua_pushboolean(L, val != 0);
lua_rawset(L, -3);
}
}
static void
reg_conn_function(struct lua_State *L,
const char *name,
lua_CFunction func,
struct mg_connection *conn)
{
if (name != NULL && func != NULL && conn != NULL) {
lua_pushstring(L, name);
lua_pushlightuserdata(L, conn);
lua_pushcclosure(L, func, 1);
lua_rawset(L, -3);
}
}
static void
reg_function(struct lua_State *L, const char *name, lua_CFunction func)
{
if (name != NULL && func != NULL) {
lua_pushstring(L, name);
lua_pushcclosure(L, func, 0);
lua_rawset(L, -3);
}
}
static void
lua_cry(struct mg_connection *conn,
int err,
lua_State *L,
const char *lua_title,
const char *lua_operation)
{
switch (err) {
case LUA_OK:
case LUA_YIELD:
break;
case LUA_ERRRUN:
mg_cry(conn,
"%s: %s failed: runtime error: %s",
lua_title,
lua_operation,
lua_tostring(L, -1));
break;
case LUA_ERRSYNTAX:
mg_cry(conn,
"%s: %s failed: syntax error: %s",
lua_title,
lua_operation,
lua_tostring(L, -1));
break;
case LUA_ERRMEM:
mg_cry(conn, "%s: %s failed: out of memory", lua_title, lua_operation);
break;
case LUA_ERRGCMM:
mg_cry(conn,
"%s: %s failed: error during garbage collection",
lua_title,
lua_operation);
break;
case LUA_ERRERR:
mg_cry(conn,
"%s: %s failed: error in error handling: %s",
lua_title,
lua_operation,
lua_tostring(L, -1));
break;
default:
mg_cry(conn, "%s: %s failed: error %i", lua_title, lua_operation, err);
break;
}
}
static int
lsp_sock_close(lua_State *L)
{
int num_args = lua_gettop(L);
if ((num_args == 1) && lua_istable(L, -1)) {
lua_getfield(L, -1, "sock");
closesocket((SOCKET)lua_tonumber(L, -1));
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static int
lsp_sock_recv(lua_State *L)
{
int num_args = lua_gettop(L);
char buf[2000];
int n;
if ((num_args == 1) && lua_istable(L, -1)) {
lua_getfield(L, -1, "sock");
n = recv((SOCKET)lua_tonumber(L, -1), buf, sizeof(buf), 0);
if (n <= 0) {
lua_pushnil(L);
} else {
lua_pushlstring(L, buf, n);
}
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static int
lsp_sock_send(lua_State *L)
{
int num_args = lua_gettop(L);
const char *buf;
size_t len, sent = 0;
int n = 0, sock;
if ((num_args == 2) && lua_istable(L, -2) && lua_isstring(L, -1)) {
buf = lua_tolstring(L, -1, &len);
lua_getfield(L, -2, "sock");
sock = (int)lua_tonumber(L, -1);
while (sent < len) {
if ((n = send(sock, buf + sent, (int)(len - sent), 0)) <= 0) {
break;
}
sent += n;
}
lua_pushnumber(L, n);
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static const struct luaL_Reg luasocket_methods[] = {{"close", lsp_sock_close},
{"send", lsp_sock_send},
{"recv", lsp_sock_recv},
{NULL, NULL}};
static int
lsp_connect(lua_State *L)
{
int num_args = lua_gettop(L);
char ebuf[100];
SOCKET sock;
union usa sa;
int ok;
if ((num_args == 3) && lua_isstring(L, -3) && lua_isnumber(L, -2)
&& lua_isnumber(L, -1)) {
ok = connect_socket(NULL,
lua_tostring(L, -3),
(int)lua_tonumber(L, -2),
(int)lua_tonumber(L, -1),
ebuf,
sizeof(ebuf),
&sock,
&sa);
if (!ok) {
return luaL_error(L, ebuf);
} else {
lua_newtable(L);
reg_int(L, "sock", (int)sock);
reg_string(L, "host", lua_tostring(L, -4));
luaL_getmetatable(L, LUASOCKET);
lua_setmetatable(L, -2);
/* TODO (high): The metatable misses a _gc method to free the
* sock object -> currently lsp_connect is a resource leak. */
}
} else {
return luaL_error(
L, "connect(host,port,is_ssl): invalid parameter given.");
}
return 1;
}
static int
lsp_error(lua_State *L)
{
lua_getglobal(L, "mg");
lua_getfield(L, -1, "onerror");
lua_pushvalue(L, -3);
lua_pcall(L, 1, 0, 0);
return 0;
}
/* Silently stop processing chunks. */
static void
lsp_abort(lua_State *L)
{
int top = lua_gettop(L);
lua_getglobal(L, "mg");
lua_pushnil(L);
lua_setfield(L, -2, "onerror");
lua_settop(L, top);
lua_pushstring(L, "aborting");
lua_error(L);
}
struct lsp_var_reader_data {
const char *begin;
unsigned len;
unsigned state;
};
static const char *
lsp_var_reader(lua_State *L, void *ud, size_t *sz)
{
struct lsp_var_reader_data *reader = (struct lsp_var_reader_data *)ud;
const char *ret;
(void)(L); /* unused */
switch (reader->state) {
case 0:
ret = "mg.write(";
*sz = strlen(ret);
break;
case 1:
ret = reader->begin;
*sz = reader->len;
break;
case 2:
ret = ")";
*sz = strlen(ret);
break;
default:
ret = 0;
*sz = 0;
}
reader->state++;
return ret;
}
static int
lsp(struct mg_connection *conn,
const char *path,
const char *p,
int64_t len,
lua_State *L)
{
int i, j, pos = 0, lines = 1, lualines = 0, is_var, lua_ok;
char chunkname[MG_BUF_LEN];
struct lsp_var_reader_data data;
for (i = 0; i < len; i++) {
if (p[i] == '\n')
lines++;
if ((i + 1) < len && p[i] == '<' && p[i + 1] == '?') {
/* <?= ?> means a variable is enclosed and its value should be
* printed */
is_var = ((i + 2) < len && p[i + 2] == '=');
if (is_var)
j = i + 2;
else
j = i + 1;
while (j < len) {
if (p[j] == '\n')
lualines++;
if ((j + 1) < len && p[j] == '?' && p[j + 1] == '>') {
mg_write(conn, p + pos, i - pos);
mg_snprintf(conn,
NULL, /* name only used for debugging */
chunkname,
sizeof(chunkname),
"@%s+%i",
path,
lines);
lua_pushlightuserdata(L, conn);
lua_pushcclosure(L, lsp_error, 1);
if (is_var) {
data.begin = p + (i + 3);
data.len = j - (i + 3);
data.state = 0;
lua_ok = mg_lua_load(
L, lsp_var_reader, &data, chunkname, NULL);
} else {
lua_ok = luaL_loadbuffer(L,
p + (i + 2),
j - (i + 2),
chunkname);
}
if (lua_ok) {
/* Syntax error or OOM. Error message is pushed on
* stack. */
lua_pcall(L, 1, 0, 0);
} else {
/* Success loading chunk. Call it. */
lua_pcall(L, 0, 0, 1);
}
pos = j + 2;
i = pos - 1;
break;
}
j++;
}
if (lualines > 0) {
lines += lualines;
lualines = 0;
}
}
}
if (i > pos) {
mg_write(conn, p + pos, i - pos);
}
return 0;
}
/* mg.write: Send data to the client */
static int
lsp_write(lua_State *L)
{
struct mg_connection *conn =
(struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
int num_args = lua_gettop(L);
const char *str;
size_t size;
int i;
for (i = 1; i <= num_args; i++) {
if (lua_isstring(L, i)) {
str = lua_tolstring(L, i, &size);
mg_write(conn, str, size);
}
}
return 0;
}
/* mg.read: Read data from the client (e.g., from a POST request) */
static int
lsp_read(lua_State *L)
{
struct mg_connection *conn =
(struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
char buf[1024];
int len = mg_read(conn, buf, sizeof(buf));
if (len <= 0)
return 0;
lua_pushlstring(L, buf, len);
return 1;
}
/* mg.keep_alive: Allow Lua pages to use the http keep-alive mechanism */
static int
lsp_keep_alive(lua_State *L)
{
struct mg_connection *conn =
(struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
int num_args = lua_gettop(L);
/* This function may be called with one parameter (boolean) to set the
keep_alive state.
Or without a parameter to just query the current keep_alive state. */
if ((num_args == 1) && lua_isboolean(L, 1)) {
conn->must_close = !lua_toboolean(L, 1);
} else if (num_args != 0) {
/* Syntax error */
return luaL_error(L, "invalid keep_alive() call");
}
/* Return the current "keep_alive" state. This may be false, even it
* keep_alive(true) has been called. */
lua_pushboolean(L, should_keep_alive(conn));
return 1;
}
/* mg.include: Include another .lp file */
static int
lsp_include(lua_State *L)
{
struct mg_connection *conn =
(struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
int num_args = lua_gettop(L);
struct file file = STRUCT_FILE_INITIALIZER;
const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
if (filename) {
if (handle_lsp_request(conn, filename, &file, L)) {
/* handle_lsp_request returned an error code, meaning an error
occured in
the included page and mg.onerror returned non-zero. Stop processing.
*/
lsp_abort(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid include() call");
}
return 0;
}
/* mg.cry: Log an error. Default value for mg.onerror. */
static int
lsp_cry(lua_State *L)
{
struct mg_connection *conn =
(struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
int num_args = lua_gettop(L);
const char *text = (num_args == 1) ? lua_tostring(L, 1) : NULL;
if (text) {
mg_cry(conn, "%s", lua_tostring(L, -1));
} else {
/* Syntax error */
return luaL_error(L, "invalid cry() call");
}
return 0;
}
/* mg.redirect: Redirect the request (internally). */
static int
lsp_redirect(lua_State *L)
{
struct mg_connection *conn =
(struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
int num_args = lua_gettop(L);
const char *target = (num_args == 1) ? lua_tostring(L, 1) : NULL;
if (target) {
conn->request_info.local_uri = target;
handle_request(conn);
lsp_abort(L);
} else {
/* Syntax error */
return luaL_error(L, "invalid redirect() call");
}
return 0;
}
/* mg.send_file */
static int
lsp_send_file(lua_State *L)
{
struct mg_connection *conn =
(struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
int num_args = lua_gettop(L);
const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
if (filename) {
mg_send_file(conn, filename);
} else {
/* Syntax error */
return luaL_error(L, "invalid send_file() call");
}
return 0;
}
/* mg.get_time */
static int
lsp_get_time(lua_State *L)
{
int num_args = lua_gettop(L);
int monotonic = (num_args > 0) ? lua_toboolean(L, 1) : 0;
struct timespec ts;
double d;
clock_gettime(monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME, &ts);
d = (double)ts.tv_sec + ((double)ts.tv_nsec * 1.0E-9);
lua_pushnumber(L, d);
return 1;
}
/* mg.get_var */
static int
lsp_get_var(lua_State *L)
{
int num_args = lua_gettop(L);
const char *data, *var_name;
size_t data_len, occurrence;
int ret;
char dst[512];
if (num_args >= 2 && num_args <= 3) {
data = lua_tolstring(L, 1, &data_len);
var_name = lua_tostring(L, 2);
occurrence = (num_args > 2) ? (long)lua_tonumber(L, 3) : 0;
ret =
mg_get_var2(data, data_len, var_name, dst, sizeof(dst), occurrence);
if (ret >= 0) {
/* Variable found: return value to Lua */
lua_pushstring(L, dst);
} else {
/* Variable not found (TODO (mid): may be string too long) */
lua_pushnil(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid get_var() call");
}
return 1;
}
/* mg.get_mime_type */
static int
lsp_get_mime_type(lua_State *L)
{
int num_args = lua_gettop(L);
struct vec mime_type = {0, 0};
struct mg_context *ctx;
const char *text;
lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
lua_gettable(L, LUA_REGISTRYINDEX);
ctx = (struct mg_context *)lua_touserdata(L, -1);
if (num_args == 1) {
text = lua_tostring(L, 1);
if (text) {
if (ctx) {
get_mime_type(ctx, text, &mime_type);
lua_pushlstring(L, mime_type.ptr, mime_type.len);
} else {
text = mg_get_builtin_mime_type(text);
lua_pushstring(L, text);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid argument for get_mime_type() call");
}
} else {
/* Syntax error */
return luaL_error(L, "invalid get_mime_type() call");
}
return 1;
}
/* mg.get_cookie */
static int
lsp_get_cookie(lua_State *L)
{
int num_args = lua_gettop(L);
const char *cookie;
const char *var_name;
int ret;
char dst[512];
if (num_args == 2) {
cookie = lua_tostring(L, 1);
var_name = lua_tostring(L, 2);
if (cookie != NULL && var_name != NULL) {
ret = mg_get_cookie(cookie, var_name, dst, sizeof(dst));
} else {
ret = -1;
}
if (ret >= 0) {
lua_pushlstring(L, dst, ret);
} else {
lua_pushnil(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid get_cookie() call");
}
return 1;
}
/* mg.md5 */
static int
lsp_md5(lua_State *L)
{
int num_args = lua_gettop(L);
const char *text;
md5_byte_t hash[16];
md5_state_t ctx;
size_t text_len;
char buf[40];
if (num_args == 1) {
text = lua_tolstring(L, 1, &text_len);
if (text) {
md5_init(&ctx);
md5_append(&ctx, (const md5_byte_t *)text, text_len);
md5_finish(&ctx, hash);
bin2str(buf, hash, sizeof(hash));
lua_pushstring(L, buf);
} else {
lua_pushnil(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid md5() call");
}
return 1;
}
/* mg.url_encode */
static int
lsp_url_encode(lua_State *L)
{
int num_args = lua_gettop(L);
const char *text;
size_t text_len;
char dst[512];
if (num_args == 1) {
text = lua_tolstring(L, 1, &text_len);
if (text) {
mg_url_encode(text, dst, sizeof(dst));
lua_pushstring(L, dst);
} else {
lua_pushnil(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid url_encode() call");
}
return 1;
}
/* mg.url_decode */
static int
lsp_url_decode(lua_State *L)
{
int num_args = lua_gettop(L);
const char *text;
size_t text_len;
int is_form;
char dst[512];
if (num_args == 1 || (num_args == 2 && lua_isboolean(L, 2))) {
text = lua_tolstring(L, 1, &text_len);
is_form = (num_args == 2) ? lua_isboolean(L, 2) : 0;
if (text) {
mg_url_decode(text, text_len, dst, (int)sizeof(dst), is_form);
lua_pushstring(L, dst);
} else {
lua_pushnil(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid url_decode() call");
}
return 1;
}
/* mg.base64_encode */
static int
lsp_base64_encode(lua_State *L)
{
int num_args = lua_gettop(L);
const char *text;
size_t text_len;
char *dst;
if (num_args == 1) {
text = lua_tolstring(L, 1, &text_len);
if (text) {
dst = (char *)mg_malloc(text_len * 8 / 6 + 4);
if (dst) {
base64_encode((const unsigned char *)text, (int)text_len, dst);
lua_pushstring(L, dst);
mg_free(dst);
} else {
return luaL_error(L, "out of memory in base64_encode() call");
}
} else {
lua_pushnil(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid base64_encode() call");
}
return 1;
}
/* mg.base64_encode */
static int
lsp_base64_decode(lua_State *L)
{
int num_args = lua_gettop(L);
const char *text;
size_t text_len, dst_len;
int ret;
char *dst;
if (num_args == 1) {
text = lua_tolstring(L, 1, &text_len);
if (text) {
dst = (char *)mg_malloc(text_len);
if (dst) {
ret = base64_decode((const unsigned char *)text,
(int)text_len,
dst,
&dst_len);
if (ret != -1) {
mg_free(dst);
return luaL_error(
L, "illegal character in lsp_base64_decode() call");
} else {
lua_pushlstring(L, dst, dst_len);
mg_free(dst);
}
} else {
return luaL_error(L,
"out of memory in lsp_base64_decode() call");
}
} else {
lua_pushnil(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid lsp_base64_decode() call");
}
return 1;
}
/* mg.get_response_code_text */
static int
lsp_get_response_code_text(lua_State *L)
{
int num_args = lua_gettop(L);
int type1;
double code;
const char *text;
if (num_args == 1) {
type1 = lua_type(L, 1);
if (type1 == LUA_TNUMBER) {
/* If the first argument is a number,
convert it to the corresponding text. */
code = lua_tonumber(L, 1);
text = mg_get_response_code_text(NULL, (int)code);
if (text)
lua_pushstring(L, text);
return text ? 1 : 0;
}
}
/* Syntax error */
return luaL_error(L, "invalid get_response_code_text() call");
}
/* mg.random - might be better than math.random on some systems */
static int
lsp_random(lua_State *L)
{
int num_args = lua_gettop(L);
if (num_args == 0) {
/* The civetweb internal random number generator will generate
* a 64 bit random number. */
uint64_t r = get_random();
/* Lua "number" is a IEEE 754 double precission float:
* https://en.wikipedia.org/wiki/Double-precision_floating-point_format
* Thus, mask with 2^53-1 to get an integer with the maximum
* precission available. */
r &= ((((uint64_t)1) << 53) - 1);
lua_pushnumber(L, (double)r);
return 1;
}
/* Syntax error */
return luaL_error(L, "invalid random() call");
}
union {
void *p;
void (*f)(unsigned char uuid[16]);
} pf_uuid_generate;
/* mg.uuid */
static int
lsp_uuid(lua_State *L)
{
union {
unsigned char uuid_array[16];
struct uuid_struct_type {
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
} uuid_struct;
} uuid;
char uuid_str[40];
int num_args = lua_gettop(L);
memset(&uuid, 0, sizeof(uuid));
memset(uuid_str, 0, sizeof(uuid_str));
if (num_args == 0) {
pf_uuid_generate.f(uuid.uuid_array);
sprintf(uuid_str,
"{%08lX-%04X-%04X-%02X%02X-"
"%02X%02X%02X%02X%02X%02X}",
(unsigned long)uuid.uuid_struct.data1,
(unsigned)uuid.uuid_struct.data2,
(unsigned)uuid.uuid_struct.data3,
(unsigned)uuid.uuid_struct.data4[0],
(unsigned)uuid.uuid_struct.data4[1],
(unsigned)uuid.uuid_struct.data4[2],
(unsigned)uuid.uuid_struct.data4[3],
(unsigned)uuid.uuid_struct.data4[4],
(unsigned)uuid.uuid_struct.data4[5],
(unsigned)uuid.uuid_struct.data4[6],
(unsigned)uuid.uuid_struct.data4[7]);
lua_pushstring(L, uuid_str);
return 1;
}
/* Syntax error */
return luaL_error(L, "invalid random() call");
}
#ifdef USE_WEBSOCKET
struct lua_websock_data {
lua_State *state;
char *script;
unsigned references;
struct mg_connection *conn[MAX_WORKER_THREADS];
pthread_mutex_t ws_mutex;
};
#endif
/* mg.write for websockets */
static int
lwebsock_write(lua_State *L)
{
#ifdef USE_WEBSOCKET
int num_args = lua_gettop(L);
struct lua_websock_data *ws;
const char *str;
size_t size;
int opcode = -1;
unsigned i;
struct mg_connection *client = NULL;
lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
lua_gettable(L, LUA_REGISTRYINDEX);
ws = (struct lua_websock_data *)lua_touserdata(L, -1);
(void)pthread_mutex_lock(&(ws->ws_mutex));
if (num_args == 1) {
/* just one text: send it to all client */
if (lua_isstring(L, 1)) {
opcode = WEBSOCKET_OPCODE_TEXT;
}
} else if (num_args == 2) {
if (lua_isnumber(L, 1)) {
/* opcode number and message text */
opcode = (int)lua_tointeger(L, 1);
} else if (lua_isstring(L, 1)) {
/* opcode string and message text */
str = lua_tostring(L, 1);
if (!mg_strncasecmp(str, "text", 4))
opcode = WEBSOCKET_OPCODE_TEXT;
else if (!mg_strncasecmp(str, "bin", 3))
opcode = WEBSOCKET_OPCODE_BINARY;
else if (!mg_strncasecmp(str, "close", 5))
opcode = WEBSOCKET_OPCODE_CONNECTION_CLOSE;
else if (!mg_strncasecmp(str, "ping", 4))
opcode = WEBSOCKET_OPCODE_PING;
else if (!mg_strncasecmp(str, "pong", 4))
opcode = WEBSOCKET_OPCODE_PONG;
else if (!mg_strncasecmp(str, "cont", 4))
opcode = WEBSOCKET_OPCODE_CONTINUATION;
} else if (lua_isuserdata(L, 1)) {
/* client id and message text */
client = (struct mg_connection *)lua_touserdata(L, 1);
opcode = WEBSOCKET_OPCODE_TEXT;
}
} else if (num_args == 3) {
if (lua_isuserdata(L, 1)) {
client = (struct mg_connection *)lua_touserdata(L, 1);
if (lua_isnumber(L, 2)) {
/* client id, opcode number and message text */
opcode = (int)lua_tointeger(L, 2);
} else if (lua_isstring(L, 2)) {
/* client id, opcode string and message text */
str = lua_tostring(L, 2);
if (!mg_strncasecmp(str, "text", 4))
opcode = WEBSOCKET_OPCODE_TEXT;
else if (!mg_strncasecmp(str, "bin", 3))
opcode = WEBSOCKET_OPCODE_BINARY;
else if (!mg_strncasecmp(str, "close", 5))
opcode = WEBSOCKET_OPCODE_CONNECTION_CLOSE;
else if (!mg_strncasecmp(str, "ping", 4))
opcode = WEBSOCKET_OPCODE_PING;
else if (!mg_strncasecmp(str, "pong", 4))
opcode = WEBSOCKET_OPCODE_PONG;
else if (!mg_strncasecmp(str, "cont", 4))
opcode = WEBSOCKET_OPCODE_CONTINUATION;
}
}
}
if (opcode >= 0 && opcode < 16 && lua_isstring(L, num_args)) {
str = lua_tolstring(L, num_args, &size);
if (client) {
for (i = 0; i < ws->references; i++) {
if (client == ws->conn[i]) {
mg_websocket_write(ws->conn[i], opcode, str, size);
}
}
} else {
for (i = 0; i < ws->references; i++) {
mg_websocket_write(ws->conn[i], opcode, str, size);
}
}
} else {
(void)pthread_mutex_unlock(&(ws->ws_mutex));
return luaL_error(L, "invalid websocket write() call");
}
(void)pthread_mutex_unlock(&(ws->ws_mutex));
#else
(void)(L); /* unused */
#endif
return 0;
}
struct laction_arg {
lua_State *state;
const char *script;
pthread_mutex_t *pmutex;
char txt[1];
};
static int
lua_action(struct laction_arg *arg)
{
int err, ok;
struct mg_context *ctx;
(void)pthread_mutex_lock(arg->pmutex);
lua_pushlightuserdata(arg->state, (void *)&lua_regkey_ctx);
lua_gettable(arg->state, LUA_REGISTRYINDEX);
ctx = (struct mg_context *)lua_touserdata(arg->state, -1);
err = luaL_loadstring(arg->state, arg->txt);
if (err != 0) {
lua_cry(fc(ctx), err, arg->state, arg->script, "timer");
(void)pthread_mutex_unlock(arg->pmutex);
mg_free(arg);
return 0;
}
err = lua_pcall(arg->state, 0, 1, 0);
if (err != 0) {
lua_cry(fc(ctx), err, arg->state, arg->script, "timer");
(void)pthread_mutex_unlock(arg->pmutex);
mg_free(arg);
return 0;
}
ok = lua_type(arg->state, -1);
if (lua_isboolean(arg->state, -1)) {
ok = lua_toboolean(arg->state, -1);
} else {
ok = 0;
}
lua_pop(arg->state, 1);
(void)pthread_mutex_unlock(arg->pmutex);
if (!ok) {
mg_free(arg);
}
return ok;
}
static int
lua_action_free(struct laction_arg *arg)
{
if (lua_action(arg)) {
mg_free(arg);
}
return 0;
}
static int
lwebsocket_set_timer(lua_State *L, int is_periodic)
{
#if defined(USE_TIMERS) && defined(USE_WEBSOCKET)
int num_args = lua_gettop(L);
struct lua_websock_data *ws;
int type1, type2, ok = 0;
double timediff;
struct mg_context *ctx;
struct laction_arg *arg;
const char *txt;
size_t txt_len;
lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
lua_gettable(L, LUA_REGISTRYINDEX);
ctx = (struct mg_context *)lua_touserdata(L, -1);
lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
lua_gettable(L, LUA_REGISTRYINDEX);
ws = (struct lua_websock_data *)lua_touserdata(L, -1);
if (num_args < 2) {
return luaL_error(L,
"not enough arguments for set_timer/interval() call");
}
type1 = lua_type(L, 1);
type2 = lua_type(L, 2);
if (type1 == LUA_TSTRING && type2 == LUA_TNUMBER && num_args == 2) {
timediff = (double)lua_tonumber(L, 2);
txt = lua_tostring(L, 1);
txt_len = strlen(txt);
arg = (struct laction_arg *)mg_malloc(sizeof(struct laction_arg)
+ txt_len + 10);
arg->state = L;
arg->script = ws->script;
arg->pmutex = &(ws->ws_mutex);
memcpy(arg->txt, "return(", 7);
memcpy(arg->txt + 7, txt, txt_len);
arg->txt[txt_len + 7] = ')';
arg->txt[txt_len + 8] = 0;
ok =
(0
== timer_add(ctx,
timediff,
is_periodic,
1,
(taction)(is_periodic ? lua_action : lua_action_free),
(void *)arg));
} else if (type1 == LUA_TFUNCTION && type2 == LUA_TNUMBER) {
/* TODO (mid): not implemented yet */
return luaL_error(L, "invalid arguments for set_timer/interval() call");
} else {
return luaL_error(L, "invalid arguments for set_timer/interval() call");
}
lua_pushboolean(L, ok);
return 1;
#else
(void)(L); /* unused */
(void)(is_periodic); /* unused */
return 0;
#endif
}
/* mg.set_timeout for websockets */
static int
lwebsocket_set_timeout(lua_State *L)
{
return lwebsocket_set_timer(L, 0);
}
/* mg.set_interval for websockets */
static int
lwebsocket_set_interval(lua_State *L)
{
return lwebsocket_set_timer(L, 1);
}
enum {
LUA_ENV_TYPE_LUA_SERVER_PAGE = 0,
LUA_ENV_TYPE_PLAIN_LUA_PAGE = 1,
LUA_ENV_TYPE_LUA_WEBSOCKET = 2,
};
static void
prepare_lua_request_info(struct mg_connection *conn, lua_State *L)
{
const char *s;
int i;
/* Export mg.request_info */
lua_pushstring(L, "request_info");
lua_newtable(L);
reg_string(L, "request_method", conn->request_info.request_method);
reg_string(L, "request_uri", conn->request_info.request_uri);
reg_string(L, "uri", conn->request_info.local_uri);
reg_string(L, "http_version", conn->request_info.http_version);
reg_string(L, "query_string", conn->request_info.query_string);
#if defined(MG_LEGACY_INTERFACE)
reg_int(L, "remote_ip", conn->request_info.remote_ip); /* remote_ip is
deprecated, use
remote_addr
instead */
#endif
reg_string(L, "remote_addr", conn->request_info.remote_addr);
/* TODO (high): ip version */
reg_int(L, "remote_port", conn->request_info.remote_port);
reg_int(L, "num_headers", conn->request_info.num_headers);
reg_int(L, "server_port", ntohs(conn->client.lsa.sin.sin_port));
if (conn->request_info.content_length >= 0) {
/* reg_int64: content_length */
lua_pushstring(L, "content_length");
lua_pushnumber(
L,
(lua_Number)conn->request_info
.content_length); /* lua_Number may be used as 52 bit integer */
lua_rawset(L, -3);
}
if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
reg_string(L, "content_type", s);
}
if (conn->request_info.remote_user != NULL) {
reg_string(L, "remote_user", conn->request_info.remote_user);
reg_string(L, "auth_type", "Digest");
}
reg_boolean(L, "https", conn->ssl != NULL);
if (conn->status_code > 0) {
/* Lua error handler should show the status code */
reg_int(L, "status", conn->status_code);
}
lua_pushstring(L, "http_headers");
lua_newtable(L);
for (i = 0; i < conn->request_info.num_headers; i++) {
reg_string(L,
conn->request_info.http_headers[i].name,
conn->request_info.http_headers[i].value);
}
lua_rawset(L, -3);
lua_rawset(L, -3);
}
void
civetweb_open_lua_libs(lua_State *L)
{
{
extern void luaL_openlibs(lua_State *);
luaL_openlibs(L);
}
#ifdef USE_LUA_SQLITE3
{
extern int luaopen_lsqlite3(lua_State *);
luaopen_lsqlite3(L);
}
#endif
#ifdef USE_LUA_LUAXML
{
extern int luaopen_LuaXML_lib(lua_State *);
luaopen_LuaXML_lib(L);
}
#endif
#ifdef USE_LUA_FILE_SYSTEM
{
extern int luaopen_lfs(lua_State *);
luaopen_lfs(L);
}
#endif
#ifdef USE_LUA_BINARY
{
/* TODO (low): Test if this could be used as a replacement for bit32.
* Check again with Lua 5.3 later. */
extern int luaopen_binary(lua_State *);
luaL_requiref(L, "binary", luaopen_binary, 1);
lua_pop(L, 1);
}
#endif
}
static void
prepare_lua_environment(struct mg_context *ctx,
struct mg_connection *conn,
struct lua_websock_data *ws_conn_list,
lua_State *L,
const char *script_name,
int lua_env_type)
{
civetweb_open_lua_libs(L);
#if LUA_VERSION_NUM == 502
/* Keep the "connect" method for compatibility,
* but do not backport it to Lua 5.1.
* TODO: Redesign the interface.
*/
luaL_newmetatable(L, LUASOCKET);
lua_pushliteral(L, "__index");
luaL_newlib(L, luasocket_methods);
lua_rawset(L, -3);
lua_pop(L, 1);
lua_register(L, "connect", lsp_connect);
#endif
/* Store context in the registry */
if (ctx != NULL) {
lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
lua_pushlightuserdata(L, (void *)ctx);
lua_settable(L, LUA_REGISTRYINDEX);
}
if (ws_conn_list != NULL) {
lua_pushlightuserdata(L, (void *)&lua_regkey_connlist);
lua_pushlightuserdata(L, (void *)ws_conn_list);
lua_settable(L, LUA_REGISTRYINDEX);
}
/* Register mg module */
lua_newtable(L);
switch (lua_env_type) {
case LUA_ENV_TYPE_LUA_SERVER_PAGE:
reg_string(L, "lua_type", "page");
break;
case LUA_ENV_TYPE_PLAIN_LUA_PAGE:
reg_string(L, "lua_type", "script");
break;
case LUA_ENV_TYPE_LUA_WEBSOCKET:
reg_string(L, "lua_type", "websocket");
break;
}
if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE
|| lua_env_type == LUA_ENV_TYPE_PLAIN_LUA_PAGE) {
reg_conn_function(L, "cry", lsp_cry, conn);
reg_conn_function(L, "read", lsp_read, conn);
reg_conn_function(L, "write", lsp_write, conn);
reg_conn_function(L, "keep_alive", lsp_keep_alive, conn);
reg_conn_function(L, "send_file", lsp_send_file, conn);
}
if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE) {
reg_conn_function(L, "include", lsp_include, conn);
reg_conn_function(L, "redirect", lsp_redirect, conn);
}
if (lua_env_type == LUA_ENV_TYPE_LUA_WEBSOCKET) {
reg_function(L, "write", lwebsock_write);
#ifdef USE_TIMERS
reg_function(L, "set_timeout", lwebsocket_set_timeout);
reg_function(L, "set_interval", lwebsocket_set_interval);
#endif
/* reg_conn_function(L, "send_file", lsp_send_file, conn); */
}
reg_function(L, "time", lsp_get_time);
reg_function(L, "get_var", lsp_get_var);
reg_function(L, "get_mime_type", lsp_get_mime_type);
reg_function(L, "get_cookie", lsp_get_cookie);
reg_function(L, "md5", lsp_md5);
reg_function(L, "url_encode", lsp_url_encode);
reg_function(L, "url_decode", lsp_url_decode);
reg_function(L, "base64_encode", lsp_base64_encode);
reg_function(L, "base64_decode", lsp_base64_decode);
reg_function(L, "get_response_code_text", lsp_get_response_code_text);
reg_function(L, "random", lsp_random);
if (pf_uuid_generate.f) {
reg_function(L, "uuid", lsp_uuid);
}
reg_string(L, "version", CIVETWEB_VERSION);
reg_string(L, "script_name", script_name);
if (ctx != NULL) {
reg_string(L, "document_root", ctx->config[DOCUMENT_ROOT]);
reg_string(L, "auth_domain", ctx->config[AUTHENTICATION_DOMAIN]);
#if defined(USE_WEBSOCKET)
reg_string(L, "websocket_root", ctx->config[WEBSOCKET_ROOT]);
#endif
if (ctx->systemName != NULL) {
reg_string(L, "system", ctx->systemName);
}
}
/* Export connection specific info */
if (conn != NULL) {
prepare_lua_request_info(conn, L);
}
lua_setglobal(L, "mg");
/* Register default mg.onerror function */
IGNORE_UNUSED_RESULT(
luaL_dostring(L,
"mg.onerror = function(e) mg.write('\\nLua error:\\n', "
"debug.traceback(e, 1)) end"));
if (ctx != NULL) {
/* Preload */
if (ctx->config[LUA_PRELOAD_FILE] != NULL) {
IGNORE_UNUSED_RESULT(luaL_dofile(L, ctx->config[LUA_PRELOAD_FILE]));
}
if (ctx->callbacks.init_lua != NULL) {
ctx->callbacks.init_lua(conn, L);
}
}
}
static int
lua_error_handler(lua_State *L)
{
const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
lua_getglobal(L, "mg");
if (!lua_isnil(L, -1)) {
lua_getfield(L, -1, "write"); /* call mg.write() */
lua_pushstring(L, error_msg);
lua_pushliteral(L, "\n");
lua_call(L, 2, 0);
IGNORE_UNUSED_RESULT(
luaL_dostring(L, "mg.write(debug.traceback(), '\\n')"));
} else {
printf("Lua error: [%s]\n", error_msg);
IGNORE_UNUSED_RESULT(
luaL_dostring(L, "print(debug.traceback(), '\\n')"));
}
/* TODO(lsm, low): leave the stack balanced */
return 0;
}
static void *
lua_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
{
(void)ud;
(void)osize; /* not used */
if (nsize == 0) {
mg_free(ptr);
return NULL;
}
return mg_realloc(ptr, nsize);
}
static void
mg_exec_lua_script(struct mg_connection *conn,
const char *path,
const void **exports)
{
int i;
lua_State *L;
/* Assume the script does not support keep_alive. The script may change this
* by calling mg.keep_alive(true). */
conn->must_close = 1;
/* Execute a plain Lua script. */
if (path != NULL && (L = lua_newstate(lua_allocator, NULL)) != NULL) {
prepare_lua_environment(
conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE);
lua_pushcclosure(L, &lua_error_handler, 0);
if (exports != NULL) {
#if LUA_VERSION_NUM > 501
lua_pushglobaltable(L);
for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
lua_CFunction func;
lua_pushstring(L, (const char *)(exports[i]));
*(const void **)(&func) = exports[i + 1];
lua_pushcclosure(L, func, 0);
lua_rawset(L, -3);
}
#else
for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
lua_CFunction func;
const char *name = (const char *)(exports[i]);
*(const void **)(&func) = exports[i + 1];
lua_register(L, name, func);
}
#endif
}
if (luaL_loadfile(L, path) != 0) {
lua_error_handler(L);
}
lua_pcall(L, 0, 0, -2);
lua_close(L);
}
}
static int
handle_lsp_request(struct mg_connection *conn,
const char *path,
struct file *filep,
struct lua_State *ls)
{
void *p = NULL;
lua_State *L = NULL;
int error = 1;
struct file filesize = STRUCT_FILE_INITIALIZER;
/* Assume the script does not support keep_alive. The script may change this
* by calling mg.keep_alive(true). */
conn->must_close = 1;
/* We need both mg_stat to get file size, and mg_fopen to get fd */
if (!mg_stat(conn, path, &filesize)) {
/* File not found */
if (ls == NULL) {
send_http_error(conn, 500, "Error: File %s not found", path);
} else {
luaL_error(ls, "File [%s] not found", path);
}
goto cleanup_handle_lsp_request;
}
if (!mg_fopen(conn, path, "r", filep)) {
/* File not found or not accessible */
if (ls == NULL) {
send_http_error(conn,
500,
"Error: Cannot open script file %s",
path);
} else {
luaL_error(ls, "Cannot [%s] not found", path);
}
goto cleanup_handle_lsp_request;
}
/* TODO: Operations mg_fopen and mg_stat should do what their names
* indicate. They should not fill in different members of the same
* struct file.
* See Github issue #225 */
filep->size = filesize.size;
if (filep->membuf == NULL
&& (p = mmap(NULL,
(size_t)filep->size,
PROT_READ,
MAP_PRIVATE,
fileno(filep->fp),
0)) == MAP_FAILED) {
/* mmap failed */
if (ls == NULL) {
send_http_error(
conn,
500,
"Error: Cannot open script\nFile %s can not be mapped",
path);
} else {
luaL_error(ls,
"mmap(%s, %zu, %d): %s",
path,
(size_t)filep->size,
fileno(filep->fp),
strerror(errno));
}
goto cleanup_handle_lsp_request;
}
if (ls != NULL) {
L = ls;
} else {
L = lua_newstate(lua_allocator, NULL);
if (L == NULL) {
send_http_error(
conn,
500,
"%s",
"Error: Cannot execute script\nlua_newstate failed");
goto cleanup_handle_lsp_request;
}
prepare_lua_environment(
conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_LUA_SERVER_PAGE);
}
/* Lua state is ready to use */
/* We're not sending HTTP headers here, Lua page must do it. */
error = lsp(conn,
path,
(filep->membuf == NULL) ? (const char *)p
: (const char *)filep->membuf,
filep->size,
L);
cleanup_handle_lsp_request:
if (L != NULL && ls == NULL)
lua_close(L);
if (p != NULL)
munmap(p, filep->size);
mg_fclose(filep);
return error;
}
#ifdef USE_WEBSOCKET
struct mg_shared_lua_websocket_list {
struct lua_websock_data ws;
struct mg_shared_lua_websocket_list *next;
};
static void *
lua_websocket_new(const char *script, struct mg_connection *conn)
{
struct mg_shared_lua_websocket_list **shared_websock_list =
&(conn->ctx->shared_lua_websockets);
struct lua_websock_data *ws;
int err, ok = 0;
assert(conn->lua_websocket_state == NULL);
/* lock list (mg_context global) */
mg_lock_context(conn->ctx);
while (*shared_websock_list) {
/* check if ws already in list */
if (0 == strcmp(script, (*shared_websock_list)->ws.script)) {
break;
}
shared_websock_list = &((*shared_websock_list)->next);
}
if (*shared_websock_list == NULL) {
/* add ws to list */
*shared_websock_list = (struct mg_shared_lua_websocket_list *)
mg_calloc(sizeof(struct mg_shared_lua_websocket_list), 1);
if (*shared_websock_list == NULL) {
mg_unlock_context(conn->ctx);
mg_cry(conn, "Cannot create shared websocket struct, OOM");
return NULL;
}
/* init ws list element */
ws = &(*shared_websock_list)->ws;
ws->script = mg_strdup(script); /* TODO (low): handle OOM */
pthread_mutex_init(&(ws->ws_mutex), &pthread_mutex_attr);
(void)pthread_mutex_lock(&(ws->ws_mutex));
ws->state = lua_newstate(lua_allocator, NULL);
ws->conn[0] = conn;
ws->references = 1;
prepare_lua_environment(
conn->ctx, NULL, ws, ws->state, script, LUA_ENV_TYPE_LUA_WEBSOCKET);
err = luaL_loadfile(ws->state, script);
if (err != 0) {
lua_cry(conn, err, ws->state, script, "load");
}
err = lua_pcall(ws->state, 0, 0, 0);
if (err != 0) {
lua_cry(conn, err, ws->state, script, "init");
}
} else {
/* inc ref count */
ws = &(*shared_websock_list)->ws;
(void)pthread_mutex_lock(&(ws->ws_mutex));
(*shared_websock_list)->ws.conn[(ws->references)++] = conn;
}
mg_unlock_context(conn->ctx);
/* call add */
lua_getglobal(ws->state, "open");
lua_newtable(ws->state);
prepare_lua_request_info(conn, ws->state);
lua_pushstring(ws->state, "client");
lua_pushlightuserdata(ws->state, (void *)conn);
lua_rawset(ws->state, -3);
err = lua_pcall(ws->state, 1, 1, 0);
if (err != 0) {
lua_cry(conn, err, ws->state, script, "open handler");
} else {
if (lua_isboolean(ws->state, -1)) {
ok = lua_toboolean(ws->state, -1);
}
lua_pop(ws->state, 1);
}
if (!ok) {
/* Remove from ws connection list. */
/* TODO (mid): Check if list entry and Lua state needs to be deleted
* (see websocket_close). */
(*shared_websock_list)->ws.conn[--(ws->references)] = 0;
}
(void)pthread_mutex_unlock(&(ws->ws_mutex));
return ok ? (void *)ws : NULL;
}
static int
lua_websocket_data(struct mg_connection *conn,
int bits,
char *data,
size_t data_len,
void *ws_arg)
{
struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
int err, ok = 0;
assert(ws != NULL);
assert(ws->state != NULL);
(void)pthread_mutex_lock(&(ws->ws_mutex));
lua_getglobal(ws->state, "data");
lua_newtable(ws->state);
lua_pushstring(ws->state, "client");
lua_pushlightuserdata(ws->state, (void *)conn);
lua_rawset(ws->state, -3);
lua_pushstring(ws->state, "bits"); /* TODO: dont use "bits" but fields with
a meaning according to
http://tools.ietf.org/html/rfc6455,
section 5.2 */
lua_pushnumber(ws->state, bits);
lua_rawset(ws->state, -3);
lua_pushstring(ws->state, "data");
lua_pushlstring(ws->state, data, data_len);
lua_rawset(ws->state, -3);
err = lua_pcall(ws->state, 1, 1, 0);
if (err != 0) {
lua_cry(conn, err, ws->state, ws->script, "data handler");
} else {
if (lua_isboolean(ws->state, -1)) {
ok = lua_toboolean(ws->state, -1);
}
lua_pop(ws->state, 1);
}
(void)pthread_mutex_unlock(&(ws->ws_mutex));
return ok;
}
static int
lua_websocket_ready(struct mg_connection *conn, void *ws_arg)
{
struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
int err, ok = 0;
assert(ws != NULL);
assert(ws->state != NULL);
(void)pthread_mutex_lock(&(ws->ws_mutex));
lua_getglobal(ws->state, "ready");
lua_newtable(ws->state);
lua_pushstring(ws->state, "client");
lua_pushlightuserdata(ws->state, (void *)conn);
lua_rawset(ws->state, -3);
err = lua_pcall(ws->state, 1, 1, 0);
if (err != 0) {
lua_cry(conn, err, ws->state, ws->script, "ready handler");
} else {
if (lua_isboolean(ws->state, -1)) {
ok = lua_toboolean(ws->state, -1);
}
lua_pop(ws->state, 1);
}
(void)pthread_mutex_unlock(&(ws->ws_mutex));
return ok;
}
static void
lua_websocket_close(struct mg_connection *conn, void *ws_arg)
{
struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg);
struct mg_shared_lua_websocket_list **shared_websock_list =
&(conn->ctx->shared_lua_websockets);
int err = 0;
unsigned i;
assert(ws != NULL);
assert(ws->state != NULL);
(void)pthread_mutex_lock(&(ws->ws_mutex));
lua_getglobal(ws->state, "close");
lua_newtable(ws->state);
lua_pushstring(ws->state, "client");
lua_pushlightuserdata(ws->state, (void *)conn);
lua_rawset(ws->state, -3);
err = lua_pcall(ws->state, 1, 0, 0);
if (err != 0) {
lua_cry(conn, err, ws->state, ws->script, "close handler");
}
for (i = 0; i < ws->references; i++) {
if (ws->conn[i] == conn) {
ws->references--;
ws->conn[i] = ws->conn[ws->references];
}
}
/* TODO: Delete lua_websock_data and remove it from the websocket list.
This must only be done, when all connections are closed, and all
asynchronous operations and timers are completed/expired. */
(void)shared_websock_list; /* shared_websock_list unused (see open TODO) */
(void)pthread_mutex_unlock(&(ws->ws_mutex));
}
#endif
static void
lua_init_optional_libraries(void)
{
#if !defined(_WIN32)
void *dll_handle = dlopen("libuuid.so", RTLD_LAZY);
pf_uuid_generate.p = dlsym(dll_handle, "uuid_generate");
#else
pf_uuid_generate.p = 0;
#endif
}
#if !defined(MAX_TIMERS)
#define MAX_TIMERS MAX_WORKER_THREADS
#endif
typedef int (*taction)(void *arg);
struct ttimer {
double time;
double period;
taction action;
void *arg;
};
struct ttimers {
pthread_t threadid; /* Timer thread ID */
pthread_mutex_t mutex; /* Protects timer lists */
struct ttimer timers[MAX_TIMERS]; /* List of timers */
unsigned timer_count; /* Current size of timer list */
};
static int
timer_add(struct mg_context *ctx,
double next_time,
double period,
int is_relative,
taction action,
void *arg)
{
unsigned u, v;
int error = 0;
struct timespec now;
if (ctx->stop_flag) {
return 0;
}
if (is_relative) {
clock_gettime(CLOCK_MONOTONIC, &now);
next_time += now.tv_sec;
next_time += now.tv_nsec * 1.0E-9;
}
pthread_mutex_lock(&ctx->timers->mutex);
if (ctx->timers->timer_count == MAX_TIMERS) {
error = 1;
} else {
for (u = 0; u < ctx->timers->timer_count; u++) {
if (ctx->timers->timers[u].time < next_time) {
for (v = ctx->timers->timer_count; v > u; v--) {
ctx->timers->timers[v] = ctx->timers->timers[v - 1];
}
break;
}
}
ctx->timers->timers[u].time = next_time;
ctx->timers->timers[u].period = period;
ctx->timers->timers[u].action = action;
ctx->timers->timers[u].arg = arg;
ctx->timers->timer_count++;
}
pthread_mutex_unlock(&ctx->timers->mutex);
return error;
}
static void
timer_thread_run(void *thread_func_param)
{
struct mg_context *ctx = (struct mg_context *)thread_func_param;
struct timespec now;
double d;
unsigned u;
int re_schedule;
struct ttimer t;
mg_set_thread_name("timer");
if (ctx->callbacks.init_thread) {
/* Timer thread */
ctx->callbacks.init_thread(ctx, 2);
}
#if defined(HAVE_CLOCK_NANOSLEEP) /* Linux with librt */
/* TODO */
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &request, &request)
== EINTR) { /*nop*/
;
}
#else
clock_gettime(CLOCK_MONOTONIC, &now);
d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
while (ctx->stop_flag == 0) {
pthread_mutex_lock(&ctx->timers->mutex);
if (ctx->timers->timer_count > 0 && d >= ctx->timers->timers[0].time) {
t = ctx->timers->timers[0];
for (u = 1; u < ctx->timers->timer_count; u++) {
ctx->timers->timers[u - 1] = ctx->timers->timers[u];
}
ctx->timers->timer_count--;
pthread_mutex_unlock(&ctx->timers->mutex);
re_schedule = t.action(t.arg);
if (re_schedule && (t.period > 0)) {
timer_add(ctx, t.time + t.period, t.period, 0, t.action, t.arg);
}
continue;
} else {
pthread_mutex_unlock(&ctx->timers->mutex);
}
mg_sleep(1);
clock_gettime(CLOCK_MONOTONIC, &now);
d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
}
#endif
}
#ifdef _WIN32
static unsigned __stdcall timer_thread(void *thread_func_param)
{
timer_thread_run(thread_func_param);
return 0;
}
#else
static void *
timer_thread(void *thread_func_param)
{
timer_thread_run(thread_func_param);
return NULL;
}
#endif /* _WIN32 */
static int
timers_init(struct mg_context *ctx)
{
ctx->timers = (struct ttimers *)mg_calloc(sizeof(struct ttimers), 1);
(void)pthread_mutex_init(&ctx->timers->mutex, NULL);
/* Start timer thread */
mg_start_thread_with_id(timer_thread, ctx, &ctx->timers->threadid);
return 0;
}
static void
timers_exit(struct mg_context *ctx)
{
if (ctx->timers) {
(void)pthread_mutex_destroy(&ctx->timers->mutex);
mg_free(ctx->timers);
}
}
Licensing of XZ Embedded
========================
All the files in this package have been written by Lasse Collin
and/or Igor Pavlov. All these files have been put into the
public domain. You can do whatever you want with these files.
As usual, this software is provided "as is", without any warranty.
XZ Embedded
===========
XZ Embedded is a relatively small, limited implementation of the .xz
file format. Currently only decoding is implemented.
XZ Embedded was written for use in the Linux kernel, but the code can
be easily used in other environments too, including regular userspace
applications. See userspace/xzminidec.c for an example program.
This README contains information that is useful only when the copy
of XZ Embedded isn't part of the Linux kernel tree. You should also
read linux/Documentation/xz.txt even if you aren't using XZ Embedded
as part of Linux; information in that file is not repeated in this
README.
Compiling the Linux kernel module
The xz_dec module depends on crc32 module, so make sure that you have
it enabled (CONFIG_CRC32).
Building the xz_dec and xz_dec_test modules without support for BCJ
filters:
cd linux/lib/xz
make -C /path/to/kernel/source \
KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m
Building the xz_dec and xz_dec_test modules with support for BCJ
filters:
cd linux/lib/xz
make -C /path/to/kernel/source \
KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m CONFIG_XZ_DEC_BCJ=y \
CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y \
CONFIG_XZ_DEC_IA64=y CONFIG_XZ_DEC_ARM=y \
CONFIG_XZ_DEC_ARMTHUMB=y CONFIG_XZ_DEC_SPARC=y
If you want only one or a few of the BCJ filters, omit the appropriate
variables. CONFIG_XZ_DEC_BCJ=y is always required to build the support
code shared between all BCJ filters.
Most people don't need the xz_dec_test module. You can skip building
it by omitting CONFIG_XZ_DEC_TEST=m from the make command line.
Compiler requirements
XZ Embedded should compile as either GNU-C89 (used in the Linux
kernel) or with any C99 compiler. Getting the code to compile with
non-GNU C89 compiler or a C++ compiler should be quite easy as
long as there is a data type for unsigned 64-bit integer (or the
code is modified not to support large files, which needs some more
care than just using 32-bit integer instead of 64-bit).
If you use GCC, try to use a recent version. For example, on x86-32,
xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when
compiled with GCC 4.3.3.
Embedding into userspace applications
To embed the XZ decoder, copy the following files into a single
directory in your source code tree:
linux/include/linux/xz.h
linux/lib/xz/xz_crc32.c
linux/lib/xz/xz_dec_lzma2.c
linux/lib/xz/xz_dec_stream.c
linux/lib/xz/xz_lzma2.h
linux/lib/xz/xz_private.h
linux/lib/xz/xz_stream.h
userspace/xz_config.h
Alternatively, xz.h may be placed into a different directory but then
that directory must be in the compiler include path when compiling
the .c files.
Your code should use only the functions declared in xz.h. The rest of
the .h files are meant only for internal use in XZ Embedded.
You may want to modify xz_config.h to be more suitable for your build
environment. Probably you should at least skim through it even if the
default file works as is.
Integrity check support
XZ Embedded always supports the integrity check types None and
CRC32. Support for CRC64 is optional. SHA-256 is currently not
supported in XZ Embedded although the .xz format does support it.
The xz tool from XZ Utils uses CRC64 by default, but CRC32 is usually
enough in embedded systems to keep the code size smaller.
If you want support for CRC64, you need to copy linux/lib/xz/xz_crc64.c
into your application, and #define XZ_USE_CRC64 in xz_config.h or in
compiler flags.
When using the internal CRC32 or CRC64, their lookup tables need to be
initialized with xz_crc32_init() and xz_crc64_init(), respectively.
See xz.h for details.
To use external CRC32 or CRC64 code instead of the code from
xz_crc32.c or xz_crc64.c, the following #defines may be used
in xz_config.h or in compiler flags:
#define XZ_INTERNAL_CRC32 0
#define XZ_INTERNAL_CRC64 0
Then it is up to you to provide compatible xz_crc32() or xz_crc64()
functions.
If the .xz file being decompressed uses an integrity check type that
isn't supported by XZ Embedded, it is treated as an error and the
file cannot be decompressed. For multi-call mode, this can be modified
by #defining XZ_DEC_ANY_CHECK. Then xz_dec_run() will return
XZ_UNSUPPORTED_CHECK when unsupported check type is detected. After
that decompression can be continued normally except that the
integrity check won't be verified. In single-call mode there's
no way to continue decoding, so XZ_DEC_ANY_CHECK is almost useless
in single-call mode.
BCJ filter support
If you want support for one or more BCJ filters, you need to copy also
linux/lib/xz/xz_dec_bcj.c into your application, and use appropriate
#defines in xz_config.h or in compiler flags. You don't need these
#defines in the code that just uses XZ Embedded via xz.h, but having
them always #defined doesn't hurt either.
#define Instruction set BCJ filter endianness
XZ_DEC_X86 x86-32 or x86-64 Little endian only
XZ_DEC_POWERPC PowerPC Big endian only
XZ_DEC_IA64 Itanium (IA-64) Big or little endian
XZ_DEC_ARM ARM Little endian only
XZ_DEC_ARMTHUMB ARM-Thumb Little endian only
XZ_DEC_SPARC SPARC Big or little endian
While some architectures are (partially) bi-endian, the endianness
setting doesn't change the endianness of the instructions on all
architectures. That's why Itanium and SPARC filters work for both big
and little endian executables (Itanium has little endian instructions
and SPARC has big endian instructions).
There currently is no filter for little endian PowerPC or big endian
ARM or ARM-Thumb. Implementing filters for them can be considered if
there is a need for such filters in real-world applications.
Notes about shared libraries
If you are including XZ Embedded into a shared library, you very
probably should rename the xz_* functions to prevent symbol
conflicts in case your library is linked against some other library
or application that also has XZ Embedded in it (which may even be
a different version of XZ Embedded). TODO: Provide an easy way
to do this.
Please don't create a shared library of XZ Embedded itself unless
it is fine to rebuild everything depending on that shared library
everytime you upgrade to a newer version of XZ Embedded. There are
no API or ABI stability guarantees between different versions of
XZ Embedded.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment