512 lines
12 KiB
C
512 lines
12 KiB
C
#include "../include/config.h"
|
|
#include "../include/common.h"
|
|
|
|
/*
|
|
* This file holds random utility functions shared by cgi's and
|
|
* core.
|
|
*/
|
|
extern int date_format;
|
|
|
|
/* fix the problem with strtok() skipping empty options between tokens */
|
|
char *my_strtok(char *buffer, char *tokens) {
|
|
char *token_position = NULL;
|
|
char *sequence_head = NULL;
|
|
static char *my_strtok_buffer = NULL;
|
|
static char *original_my_strtok_buffer = NULL;
|
|
|
|
if(buffer != NULL) {
|
|
my_free(original_my_strtok_buffer);
|
|
if((my_strtok_buffer = (char *)strdup(buffer)) == NULL)
|
|
return NULL;
|
|
original_my_strtok_buffer = my_strtok_buffer;
|
|
}
|
|
|
|
sequence_head = my_strtok_buffer;
|
|
|
|
if(sequence_head[0] == '\x0')
|
|
return NULL;
|
|
|
|
token_position = strchr(my_strtok_buffer, tokens[0]);
|
|
|
|
if(token_position == NULL) {
|
|
my_strtok_buffer = strchr(my_strtok_buffer, '\x0');
|
|
return sequence_head;
|
|
}
|
|
|
|
token_position[0] = '\x0';
|
|
my_strtok_buffer = token_position + 1;
|
|
|
|
return sequence_head;
|
|
}
|
|
|
|
/* fixes compiler problems under Solaris, since strsep() isn't included */
|
|
/* this code is taken from the glibc source */
|
|
char *my_strsep(char **stringp, const char *delim) {
|
|
char *begin, *end;
|
|
|
|
begin = *stringp;
|
|
if(begin == NULL)
|
|
return NULL;
|
|
|
|
/* A frequent case is when the delimiter string contains only one
|
|
* character. Here we don't need to call the expensive `strpbrk'
|
|
* function and instead work using `strchr'. */
|
|
if(delim[0] == '\0' || delim[1] == '\0') {
|
|
char ch = delim[0];
|
|
|
|
if(ch == '\0' || begin[0] == '\0')
|
|
end = NULL;
|
|
else {
|
|
if(*begin == ch)
|
|
end = begin;
|
|
else
|
|
end = strchr(begin + 1, ch);
|
|
}
|
|
}
|
|
else {
|
|
/* find the end of the token. */
|
|
end = strpbrk(begin, delim);
|
|
}
|
|
|
|
if(end) {
|
|
/* terminate the token and set *STRINGP past NUL character. */
|
|
*end++ = '\0';
|
|
*stringp = end;
|
|
}
|
|
else
|
|
/* no more delimiters; this is the last token. */
|
|
*stringp = NULL;
|
|
|
|
return begin;
|
|
}
|
|
|
|
/* open a file read-only via mmap() */
|
|
mmapfile *mmap_fopen(char *filename) {
|
|
mmapfile *new_mmapfile = NULL;
|
|
int fd = 0;
|
|
void *mmap_buf = NULL;
|
|
struct stat statbuf;
|
|
int mode = O_RDONLY;
|
|
unsigned long file_size = 0L;
|
|
|
|
if(filename == NULL)
|
|
return NULL;
|
|
|
|
/* allocate memory */
|
|
if((new_mmapfile = (mmapfile *) malloc(sizeof(mmapfile))) == NULL)
|
|
return NULL;
|
|
|
|
/* open the file */
|
|
if((fd = open(filename, mode)) == -1) {
|
|
my_free(new_mmapfile);
|
|
return NULL;
|
|
}
|
|
|
|
/* get file info */
|
|
if((fstat(fd, &statbuf)) == -1) {
|
|
close(fd);
|
|
my_free(new_mmapfile);
|
|
return NULL;
|
|
}
|
|
|
|
/* get file size */
|
|
file_size = (unsigned long)statbuf.st_size;
|
|
|
|
/* only mmap() if we have a file greater than 0 bytes */
|
|
if(file_size > 0) {
|
|
|
|
/* mmap() the file - allocate one extra byte for processing zero-byte files */
|
|
if((mmap_buf =
|
|
(void *)mmap(0, file_size, PROT_READ, MAP_PRIVATE, fd,
|
|
0)) == MAP_FAILED) {
|
|
close(fd);
|
|
my_free(new_mmapfile);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
mmap_buf = NULL;
|
|
|
|
/* populate struct info for later use */
|
|
new_mmapfile->path = (char *)strdup(filename);
|
|
new_mmapfile->fd = fd;
|
|
new_mmapfile->file_size = (unsigned long)file_size;
|
|
new_mmapfile->current_position = 0L;
|
|
new_mmapfile->current_line = 0L;
|
|
new_mmapfile->mmap_buf = mmap_buf;
|
|
|
|
return new_mmapfile;
|
|
}
|
|
|
|
/* close a file originally opened via mmap() */
|
|
int mmap_fclose(mmapfile * temp_mmapfile) {
|
|
|
|
if(temp_mmapfile == NULL)
|
|
return ERROR;
|
|
|
|
/* un-mmap() the file */
|
|
if(temp_mmapfile->file_size > 0L)
|
|
munmap(temp_mmapfile->mmap_buf, temp_mmapfile->file_size);
|
|
|
|
/* close the file */
|
|
close(temp_mmapfile->fd);
|
|
|
|
/* free memory */
|
|
my_free(temp_mmapfile->path);
|
|
my_free(temp_mmapfile);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/* gets one line of input from an mmap()'ed file */
|
|
char *mmap_fgets(mmapfile * temp_mmapfile) {
|
|
char *buf = NULL;
|
|
unsigned long x = 0L;
|
|
int len = 0;
|
|
|
|
if(temp_mmapfile == NULL)
|
|
return NULL;
|
|
|
|
/* size of file is 0 bytes */
|
|
if(temp_mmapfile->file_size == 0L)
|
|
return NULL;
|
|
|
|
/* we've reached the end of the file */
|
|
if(temp_mmapfile->current_position >= temp_mmapfile->file_size)
|
|
return NULL;
|
|
|
|
/* find the end of the string (or buffer) */
|
|
for(x = temp_mmapfile->current_position; x < temp_mmapfile->file_size;
|
|
x++) {
|
|
if(*((char *)(temp_mmapfile->mmap_buf) + x) == '\n') {
|
|
x++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* calculate length of line we just read */
|
|
len = (int)(x - temp_mmapfile->current_position);
|
|
|
|
/* allocate memory for the new line */
|
|
if((buf = (char *)malloc(len + 1)) == NULL)
|
|
return NULL;
|
|
|
|
/* copy string to newly allocated memory and terminate the string */
|
|
memcpy(buf,
|
|
((char *)(temp_mmapfile->mmap_buf) +
|
|
temp_mmapfile->current_position), len);
|
|
buf[len] = '\x0';
|
|
|
|
/* update the current position */
|
|
temp_mmapfile->current_position = x;
|
|
|
|
/* increment the current line */
|
|
temp_mmapfile->current_line++;
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* gets one line of input from an mmap()'ed file (may be contained on more than one line in the source file) */
|
|
char *mmap_fgets_multiline(mmapfile * temp_mmapfile) {
|
|
char *buf = NULL;
|
|
char *tempbuf = NULL;
|
|
char *stripped = NULL;
|
|
int len = 0;
|
|
int len2 = 0;
|
|
int end = 0;
|
|
|
|
if(temp_mmapfile == NULL)
|
|
return NULL;
|
|
|
|
while(1) {
|
|
|
|
my_free(tempbuf);
|
|
|
|
if((tempbuf = mmap_fgets(temp_mmapfile)) == NULL)
|
|
break;
|
|
|
|
if(buf == NULL) {
|
|
len = strlen(tempbuf);
|
|
if((buf = (char *)malloc(len + 1)) == NULL)
|
|
break;
|
|
memcpy(buf, tempbuf, len);
|
|
buf[len] = '\x0';
|
|
}
|
|
else {
|
|
/* strip leading white space from continuation lines */
|
|
stripped = tempbuf;
|
|
while(*stripped == ' ' || *stripped == '\t')
|
|
stripped++;
|
|
len = strlen(stripped);
|
|
len2 = strlen(buf);
|
|
if((buf =
|
|
(char *)realloc(buf, len + len2 + 1)) == NULL)
|
|
break;
|
|
strcat(buf, stripped);
|
|
len += len2;
|
|
buf[len] = '\x0';
|
|
}
|
|
|
|
if(len == 0)
|
|
break;
|
|
|
|
/* handle Windows/DOS CR/LF */
|
|
if(len >= 2 && buf[len - 2] == '\r')
|
|
end = len - 3;
|
|
/* normal Unix LF */
|
|
else if(len >= 1 && buf[len - 1] == '\n')
|
|
end = len - 2;
|
|
else
|
|
end = len - 1;
|
|
|
|
/* two backslashes found. unescape first backslash first and break */
|
|
if(end >= 1 && buf[end - 1] == '\\' && buf[end] == '\\') {
|
|
buf[end] = '\n';
|
|
buf[end + 1] = '\x0';
|
|
break;
|
|
}
|
|
|
|
/* one backslash found. continue reading the next line */
|
|
else if(end > 0 && buf[end] == '\\')
|
|
buf[end] = '\x0';
|
|
|
|
/* no continuation marker was found, so break */
|
|
else
|
|
break;
|
|
}
|
|
|
|
my_free(tempbuf);
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* strip newline, carriage return, and tab characters from beginning and end of a string */
|
|
void strip(char *buffer) {
|
|
register int x, z;
|
|
int len;
|
|
|
|
if(buffer == NULL || buffer[0] == '\x0')
|
|
return;
|
|
|
|
/* strip end of string */
|
|
len = (int)strlen(buffer);
|
|
for(x = len - 1; x >= 0; x--) {
|
|
switch(buffer[x]) {
|
|
case ' ':
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
buffer[x] = '\x0';
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* if we stripped all of it, just return */
|
|
if(!x)
|
|
return;
|
|
|
|
/* save last position for later... */
|
|
z = x;
|
|
|
|
/* strip beginning of string (by shifting) */
|
|
/* NOTE: this is very expensive to do, so avoid it whenever possible */
|
|
for(x = 0;; x++) {
|
|
switch(buffer[x]) {
|
|
case ' ':
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(x > 0 && z > 0) {
|
|
/* new length of the string after we stripped the end */
|
|
len = z + 1;
|
|
|
|
/* shift chars towards beginning of string to remove leading whitespace */
|
|
for(z = x; z < len; z++)
|
|
buffer[z - x] = buffer[z];
|
|
buffer[len - x] = '\x0';
|
|
}
|
|
}
|
|
|
|
/**************************************************
|
|
*************** HASH FUNCTIONS *******************
|
|
**************************************************/
|
|
/* dual hash function */
|
|
int hashfunc(const char *name1, const char *name2, int hashslots) {
|
|
unsigned int i, result;
|
|
|
|
result = 0;
|
|
|
|
if(name1)
|
|
for(i = 0; i < strlen(name1); i++)
|
|
result += name1[i];
|
|
|
|
if(name2)
|
|
for(i = 0; i < strlen(name2); i++)
|
|
result += name2[i];
|
|
|
|
result = result % hashslots;
|
|
|
|
return result;
|
|
}
|
|
|
|
/* dual hash data comparison */
|
|
int compare_hashdata(const char *val1a, const char *val1b, const char *val2a,
|
|
const char *val2b) {
|
|
int result = 0;
|
|
|
|
/* NOTE: If hash calculation changes, update the compare_strings() function! */
|
|
|
|
/* check first name */
|
|
if(val1a == NULL && val2a == NULL)
|
|
result = 0;
|
|
else if(val1a == NULL)
|
|
result = 1;
|
|
else if(val2a == NULL)
|
|
result = -1;
|
|
else
|
|
result = strcmp(val1a, val2a);
|
|
|
|
/* check second name if necessary */
|
|
if(result == 0) {
|
|
if(val1b == NULL && val2b == NULL)
|
|
result = 0;
|
|
else if(val1b == NULL)
|
|
result = 1;
|
|
else if(val2b == NULL)
|
|
result = -1;
|
|
else
|
|
result = strcmp(val1b, val2b);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
/*
|
|
* given a date/time in time_t format, produce a corresponding
|
|
* date/time string, including timezone
|
|
*/
|
|
void get_datetime_string(time_t * raw_time, char *buffer, int buffer_length,
|
|
int type) {
|
|
time_t t;
|
|
struct tm *tm_ptr, tm_s;
|
|
int hour;
|
|
int minute;
|
|
int second;
|
|
int month;
|
|
int day;
|
|
int year;
|
|
char *weekdays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
|
char *months[12] = {
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept",
|
|
"Oct", "Nov", "Dec"
|
|
};
|
|
char *tzone = "";
|
|
|
|
if(raw_time == NULL)
|
|
time(&t);
|
|
else
|
|
t = *raw_time;
|
|
|
|
if(type == HTTP_DATE_TIME)
|
|
tm_ptr = gmtime_r(&t, &tm_s);
|
|
else
|
|
tm_ptr = localtime_r(&t, &tm_s);
|
|
|
|
hour = tm_ptr->tm_hour;
|
|
minute = tm_ptr->tm_min;
|
|
second = tm_ptr->tm_sec;
|
|
month = tm_ptr->tm_mon + 1;
|
|
day = tm_ptr->tm_mday;
|
|
year = tm_ptr->tm_year + 1900;
|
|
|
|
#ifdef HAVE_TM_ZONE
|
|
tzone = (char *)(tm_ptr->tm_zone);
|
|
#else
|
|
tzone = (tm_ptr->tm_isdst) ? tzname[1] : tzname[0];
|
|
#endif
|
|
|
|
/* ctime() style date/time */
|
|
if(type == LONG_DATE_TIME)
|
|
snprintf(buffer, buffer_length, "%s %s %d %02d:%02d:%02d %s %d",
|
|
weekdays[tm_ptr->tm_wday], months[tm_ptr->tm_mon], day,
|
|
hour, minute, second, tzone, year);
|
|
|
|
/* short date/time */
|
|
else if(type == SHORT_DATE_TIME) {
|
|
if(date_format == DATE_FORMAT_EURO)
|
|
snprintf(buffer, buffer_length,
|
|
"%02d-%02d-%04d %02d:%02d:%02d", day, month,
|
|
year, hour, minute, second);
|
|
else if(date_format == DATE_FORMAT_ISO8601
|
|
|| date_format == DATE_FORMAT_STRICT_ISO8601)
|
|
snprintf(buffer, buffer_length,
|
|
"%04d-%02d-%02d%c%02d:%02d:%02d", year, month,
|
|
day,
|
|
(date_format ==
|
|
DATE_FORMAT_STRICT_ISO8601) ? 'T' : ' ', hour,
|
|
minute, second);
|
|
else
|
|
snprintf(buffer, buffer_length,
|
|
"%02d-%02d-%04d %02d:%02d:%02d", month, day,
|
|
year, hour, minute, second);
|
|
}
|
|
|
|
/* short date */
|
|
else if(type == SHORT_DATE) {
|
|
if(date_format == DATE_FORMAT_EURO)
|
|
snprintf(buffer, buffer_length, "%02d-%02d-%04d", day,
|
|
month, year);
|
|
else if(date_format == DATE_FORMAT_ISO8601
|
|
|| date_format == DATE_FORMAT_STRICT_ISO8601)
|
|
snprintf(buffer, buffer_length, "%04d-%02d-%02d", year,
|
|
month, day);
|
|
else
|
|
snprintf(buffer, buffer_length, "%02d-%02d-%04d", month,
|
|
day, year);
|
|
}
|
|
|
|
/* expiration date/time for HTTP headers */
|
|
else if(type == HTTP_DATE_TIME)
|
|
snprintf(buffer, buffer_length,
|
|
"%s, %02d %s %d %02d:%02d:%02d GMT",
|
|
weekdays[tm_ptr->tm_wday], day, months[tm_ptr->tm_mon],
|
|
year, hour, minute, second);
|
|
|
|
/* short time */
|
|
else
|
|
snprintf(buffer, buffer_length, "%02d:%02d:%02d", hour, minute,
|
|
second);
|
|
|
|
buffer[buffer_length - 1] = '\x0';
|
|
}
|
|
|
|
/* get days, hours, minutes, and seconds from a raw time_t format or total seconds */
|
|
void get_time_breakdown(unsigned long raw_time, int *days, int *hours,
|
|
int *minutes, int *seconds) {
|
|
unsigned long temp_time;
|
|
int temp_days;
|
|
int temp_hours;
|
|
int temp_minutes;
|
|
int temp_seconds;
|
|
|
|
temp_time = raw_time;
|
|
|
|
temp_days = temp_time / 86400;
|
|
temp_time -= (temp_days * 86400);
|
|
temp_hours = temp_time / 3600;
|
|
temp_time -= (temp_hours * 3600);
|
|
temp_minutes = temp_time / 60;
|
|
temp_time -= (temp_minutes * 60);
|
|
temp_seconds = (int)temp_time;
|
|
|
|
*days = temp_days;
|
|
*hours = temp_hours;
|
|
*minutes = temp_minutes;
|
|
*seconds = temp_seconds;
|
|
}
|