/********************************************************************
Name: elogd.c
Created by: Stefan Ritt
Copyright 2000 + Stefan Ritt
ELOG 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 3 of the License, or
(at your option) any later version.
ELOG 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.
In addition, as a special exception, the copyright holders give
permission to link the code of portions of this program with the
OpenSSL library under certain conditions as described in each
individual source file, and distribute linked combinations
including the two.
You must obey the GNU General Public License in all respects
for all of the code used other than OpenSSL. If you modify
file(s) with this exception, you may extend this exception to your
version of the file(s), but you are not obligated to do so. If you
do not wish to do so, delete this exception statement from your
version. If you delete this exception statement from all source
files in the program, then also delete it here.
You should have received a copy of the GNU General Public License
along with ELOG. If not, see .
Contents: Web server program for Electronic Logbook ELOG
\********************************************************************/
#include "elogd.h"
#include "git-revision.h"
const char *_git_revision = GIT_REVISION;
BOOL running_as_daemon; /* Running as a daemon/service? */
int elog_tcp_port; /* Server's TCP port */
static void (*printf_handler)(const char *); /* Handler to printf for logging */
static void (*fputs_handler)(const char *); /* Handler to fputs for logging */
static FILE *current_output_stream = NULL; /* Currently used output stream */
char *return_buffer;
int return_buffer_size;
int strlen_retbuf;
int keep_alive;
char header_buffer[20000];
int return_length;
char host_name[256];
char referer[256];
char browser[256];
char config_file[256];
char resource_dir[256];
char logbook_dir[256];
char listen_interface[256];
char theme_name[80];
char http_host[256];
char http_user[256];
char _param[MAX_PARAM][NAME_LENGTH];
char _value[MAX_PARAM][NAME_LENGTH];
char _mtext[TEXT_SIZE];
char _cmdline[CMD_SIZE];
char *_attachment_buffer;
int _attachment_size;
int _max_content_length = MAX_CONTENT_LENGTH;
struct in_addr rem_addr;
char rem_host[256];
char rem_host_ip[256];
int _sock;
BOOL use_keepalive, enable_execute = FALSE;
BOOL ckedit_exist, image_magick_exist;
int _verbose_level, _current_message_id;
int _logging_level, _ssl_flag;
LOGBOOK *lb_list = NULL;
#define VERBOSE_URL 1
#define VERBOSE_INFO 2
#define VERBOSE_DEBUG 3
#ifdef HAVE_SSL
SSL *_ssl_con;
#endif
char *mname[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September",
"October", "November", "December"
};
char attr_list[MAX_N_ATTR][NAME_LENGTH];
char attr_options[MAX_N_ATTR][MAX_N_LIST][NAME_LENGTH];
int attr_flags[MAX_N_ATTR];
char attr_list_default[][NAME_LENGTH] = {"Author", "Type", "Category", "Subject", ""};
char attr_options_default[][MAX_N_LIST][NAME_LENGTH] = {{""},
{"Routine", "Other"},
{"General", "Other"},
{""}
};
int attr_flags_default[] = {AF_REQUIRED, 0, 0, 0};
struct {
char ext[32];
char type[80];
} filetype[] = {
{
".AI", "application/postscript"},
{
".ASC", "text/plain"},
{
".BZ2", "application/x-bzip2"},
{
".CFG", "text/plain"},
{
".CHRT", "application/x-kchart"},
{
".CONF", "text/plain"},
{
".CSH", "application/x-csh"},
{
".CSS", "text/css"},
{
".DOC", "application/msword"},
{
".DVI", "application/x-dvi"},
{
".EPS", "application/postscript"},
{
".GIF", "image/gif"},
{
".GZ", "application/x-gzip"},
{
".HTM", "text/html"},
{
".HTML", "text/html"},
{
".ICO", "image/x-icon"},
{
".JPEG", "image/jpeg"},
{
".JPG", "image/jpeg"},
{
".JS", "application/x-javascript"},
{
".KPR", "application/x-kpresenter"},
{
".KSP", "application/x-kspread"},
{
".KWD", "application/x-kword"},
{
".MP3", "audio/mpeg"},
{
".OGG", "application/x-ogg"},
{
".PDF", "application/pdf"},
{
".PNG", "image/png"},
{
".PS", "application/postscript"},
{
".RAM", "audio/x-pn-realaudio"},
{
".RM", "audio/x-pn-realaudio"},
{
".RM", "audio/x-pn-realaudio"},
{
".RM", "audio/x-pn-realaudio"},
{
".RPM", "application/x-rpm"},
{
".RTF", "application/rtf"},
{
".SH", "application/x-sh"},
{
".SVG", "image/svg+xml"},
{
".TAR", "application/x-tar"},
{
".TCL", "application/x-tcl"},
{
".TEX", "application/x-tex"},
{
".TGZ", "application/x-gzip"},
{
".TIF", "image/tiff"},
{
".TIFF", "image/tiff"},
{
".TXT", "text/plain"},
{
".WAV", "audio/x-wav"},
{
".XLS", "application/x-msexcel"},
{
".XML", "text/xml"},
{
".XSL", "text/xml"},
{
".ZIP", "application/x-zip-compressed"},
{
/* Open XML file types */
".DOCM", "application/vnd.ms-word.document.macroEnabled.12"},
{
".DOCX", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{
".DOTM", "application/vnd.ms-word.template.macroEnabled.12"},
{
".DOTX", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
{
".PPSM", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
{
".PPSX", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
{
".PPTM", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
{
".PPTX", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
{
".XLSB", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
{
".XLSM", "application/vnd.ms-excel.sheet.macroEnabled.12"},
{
".XLSX", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{
".XPS", "application/vnd.ms-xpsdocument"},
{
"", ""},};
struct {
char language[32];
char abbrev[32];
} lang_table[] = {
{"brazilian", "br"},
{"bulgarian", "bg"},
{"czech", "cz"},
{"danish", "dk"},
{"dutch", "nl"},
{"french", "fr"},
{"german", "de"},
{"indonesia", "id"},
{"italian", "it"},
{"japanese", "jp"},
{"polish", "pl"},
{"ru_CP1251", "ru"},
{"slowak", "sk"},
{"spanish", "es"},
{"swedish", "se"},
{"turkish", "tr"},
{"zh_CN-GB2314", "zh"},
{"zh_CN-UTF8", "zh"},
{"", ""}
};
char _convert_cmd[256];
char _identify_cmd[256];
#ifdef OS_WINNT
int run_service(void);
#endif
#ifdef OS_UNIX
gid_t orig_gid; /* Original effective GID before dropping privilege */
uid_t orig_uid; /* Original effective UID before dropping privilege */
char pidfile[256]; /* Pidfile name */
#endif
#ifdef __CYGWIN__ /* bug in cygwin, 'timezone' not linked automatically */
long _timezone;
#endif
/*---- Funcions from the MIDAS library -----------------------------*/
#define my_toupper(_c) ( ((_c)>='a' && (_c)<='z') ? ((_c)-'a'+'A') : (_c) )
#define my_tolower(_c) ( ((_c)>='A' && (_c)<='Z') ? ((_c)-'A'+'a') : (_c) )
BOOL strieq(const char *str1, const char *str2) {
char c1, c2;
if (str1 == NULL && str2 == NULL)
return TRUE;
if (str1 == NULL || str2 == NULL)
return FALSE;
if (strlen(str1) != strlen(str2))
return FALSE;
while (*str1) {
c1 = *str1++;
c2 = *str2++;
if (my_toupper(c1) != my_toupper(c2))
return FALSE;
}
if (*str2)
return FALSE;
return TRUE;
}
BOOL strnieq(const char *str1, const char *str2, int n) {
char c1, c2;
int i;
if (str1 == NULL && str2 == NULL && n == 0)
return TRUE;
if (str1 == NULL || str2 == NULL)
return FALSE;
if ((int) strlen(str1) < n || (int) strlen(str2) < n)
return FALSE;
for (i = 0; i < n && *str1; i++) {
c1 = *str1++;
c2 = *str2++;
if (my_toupper(c1) != my_toupper(c2))
return FALSE;
}
if (i < n)
return FALSE;
return TRUE;
}
char *stristr(const char *str, const char *pattern) {
size_t i;
if (!*pattern)
return (char *) str;
for (; *str; str++) {
if (toupper(*str) == toupper(*pattern)) {
for (i = 1;; i++) {
if (!pattern[i])
return (char *) str;
if (toupper(str[i]) != toupper(pattern[i]))
break;
}
}
}
return NULL;
}
void strextract(const char *str, char delim, char *extr, int size)
/* extract a substinr "extr" from "str" until "delim" is found */
{
int i;
for (i = 0; str[i] != delim && i < size - 1; i++)
extr[i] = str[i];
extr[i] = 0;
}
static BOOL chkext(const char *str, const char *ext) {
int extl, strl;
char c1, c2;
if (ext == NULL || str == NULL)
return FALSE;
extl = strlen(ext);
strl = strlen(str);
if (extl >= strl)
return FALSE;
str = str + strl - extl;
while (*str) {
c1 = *str++;
c2 = *ext++;
if (my_toupper(c1) != my_toupper(c2))
return FALSE;
}
return TRUE;
}
int get_verbose(void) {
return _verbose_level;
}
void set_verbose(int v) {
_verbose_level = v;
}
/* workaround for some gcc versions bug for "%c" format (see strftime(3) */
size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm) {
return strftime(s, max, fmt, tm);
}
/* signal save read function */
int my_read(int fh, void *buffer, unsigned int bytes) {
#ifdef OS_UNIX
int i, n = 0;
do {
i = read(fh, (char *) buffer + n, bytes - n);
/* don't return if an alarm signal was cought */
if (i == -1 && errno == EINTR)
continue;
if (i == -1)
return -1;
if (i == 0)
return n;
n += i;
} while (n < (int) bytes);
return n;
#else
return read(fh, buffer, bytes);
#endif
return 0;
}
/* workaround for wong timezone under MacOSX */
long my_timezone() {
#if defined(OS_MACOSX) || defined(__FreeBSD__) || defined(__OpenBSD__)
time_t tp;
time(&tp);
return -localtime(&tp)->tm_gmtoff;
#elif defined(OS_WINNT)
return _timezone;
#else
return timezone;
#endif
}
/*---- Compose RFC2822 compliant date ---*/
void get_rfc2822_date(char *date, int size, time_t ltime) {
time_t now;
char buf[256];
int offset;
struct tm *ts;
/* switch locale temporarily back to english to comply with RFC2822 date format */
setlocale(LC_ALL, "C");
if (ltime == 0)
time(&now);
else
now = ltime;
ts = localtime(&now);
assert(ts);
strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", ts);
offset = (-(int) my_timezone());
if (ts->tm_isdst)
offset += 3600;
snprintf(date, size - 1, "%s %+03d%02d", buf, (int) (offset / 3600),
(int) ((abs((int) offset) / 60) % 60));
}
/*---- Safe malloc wrappers with out of memory checking from GNU ---*/
static void memory_error_and_abort(char *func) {
eprintf("%s: not enough memory\n", func);
exit(EXIT_FAILURE);
}
/* Return a pointer to free()able block of memory large enough
to hold BYTES number of bytes. If the memory cannot be allocated,
print an error message and abort. */
void *xmalloc(size_t bytes) {
char *temp;
/* Align buffer on 4 byte boundery for HP UX and other 64 bit systems to prevent Bus error (core dump) */
if (bytes & 3)
bytes += 4 - (bytes & 3);
temp = (char *) malloc(bytes + 12);
if (temp == 0)
memory_error_and_abort("xmalloc");
/* put magic number around array and put array size */
*(unsigned int *) (temp + 0) = bytes;
*(unsigned int *) (temp + 4) = 0xdeadc0de;
*(unsigned int *) (temp + bytes + 8) = 0xdeadc0de;
return (temp + 8);
}
void *xcalloc(size_t count, size_t bytes) {
char *temp;
/* Align buffer on 4 byte boundery for HP UX and other 64 bit systems to prevent Bus error (core dump) */
if (bytes & 3)
bytes += 4 - (bytes & 3);
temp = (char *) malloc(count * bytes + 12);
if (temp == 0)
memory_error_and_abort("xcalloc");
memset(temp, 0, count * bytes + 12);
/* put magic number around array */
*(unsigned int *) (temp + 0) = count * bytes;
*(unsigned int *) (temp + 4) = 0xdeadc0de;
*(unsigned int *) (temp + count * bytes + 8) = 0xdeadc0de;
return (temp + 8);
}
void *xrealloc(void *pointer, size_t bytes) {
char *temp;
int old_size;
/* Align buffer on 4 byte boundery for HP UX and other 64 bit systems to prevent Bus error (core dump) */
if (bytes & 3)
bytes += 4 - (bytes & 3);
if (pointer == NULL)
return xmalloc(bytes);
/* check old magic number */
temp = pointer;
assert(*((unsigned int *) (temp - 4)) == 0xdeadc0de);
old_size = *((unsigned int *) (temp - 8));
assert(*((unsigned int *) (temp + old_size)) == 0xdeadc0de);
temp = (char *) realloc(temp - 8, bytes + 12);
if (temp == 0)
memory_error_and_abort("xrealloc");
/* put magic number around array */
*(unsigned int *) (temp + 0) = bytes;
*(unsigned int *) (temp + 4) = 0xdeadc0de;
*(unsigned int *) (temp + bytes + 8) = 0xdeadc0de;
return (temp + 8);
}
void xfree(void *pointer) {
char *temp;
int old_size;
if (!pointer)
return;
/* check for magic byte */
temp = pointer;
assert(*((unsigned int *) (temp - 4)) == 0xdeadc0de);
old_size = *((unsigned int *) (temp - 8));
assert(*((unsigned int *) (temp + old_size)) == 0xdeadc0de);
free(temp - 8);
}
char *xstrdup(const char *string) {
char *s;
s = (char *) xmalloc(strlen(string) + 1);
strcpy(s, string);
return s;
}
/*----------------------- Message handling -------------------------*/
/* Have vasprintf? (seems that only libc6 based Linux has this) */
#ifdef __linux__
#define HAVE_VASPRintF
#endif
#ifndef HAVE_VASPRintF
/* vasprintf implementation taken (and adapted) from GNU libiberty */
static int int_vasprintf(char **result, const char *format, va_list args) {
const char *p = format;
/* Add one to make sure that it is never zero, which might cause malloc
to return NULL. */
int total_width = strlen(format) + 1;
va_list ap;
#ifdef va_copy
va_copy(ap, args);
#else
memcpy(&ap, &args, sizeof(va_list));
#endif
while (*p != '\0') {
if (*p++ == '%') {
while (strchr("-+ #0", *p))
++p;
if (*p == '*') {
++p;
total_width += abs(va_arg(ap, int));
} else
total_width += strtoul(p, (char **) &p, 10);
if (*p == '.') {
++p;
if (*p == '*') {
++p;
total_width += abs(va_arg(ap, int));
} else
total_width += strtoul(p, (char **) &p, 10);
}
while (strchr("hlL", *p))
++p;
/*
* Should be big enough for any format specifier
* except %s and floats.
*/
total_width += 30;
switch (*p) {
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
case 'c':
(void) va_arg(ap, int);
break;
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
(void) va_arg(ap, double);
/*
* Since an ieee double can have an exponent of 307, we'll
* make the buffer wide enough to cover the gross case.
*/
total_width += 307;
break;
case 's':
total_width += strlen(va_arg(ap, char *));
break;
case 'p':
case 'n':
(void) va_arg(ap, char *);
break;
}
p++;
}
}
#ifdef va_copy
va_end(ap);
#endif
*result = (char *) malloc(total_width);
if (*result != NULL)
return vsprintf(*result, format, args);
else
return -1;
}
#if defined (_BSD_VA_LIST_) && defined (__FreeBSD__)
int vasprintf(char **result, const char *format, _BSD_VA_LIST_ args)
#else
int vasprintf(char **result, const char *format, va_list args)
#endif
{
return int_vasprintf(result, format, args);
}
#endif /* ! HAVE_VASPRintF */
/* Safe replacement for vasprintf (adapted code from Samba) */
int xvasprintf(char **ptr, const char *format, va_list ap) {
int n;
va_list save;
#ifdef va_copy
va_copy(save, ap);
#else
#ifdef __va_copy
__va_copy(save, ap);
#else
save = ap;
#endif
#endif
n = vasprintf(ptr, format, save);
if (n == -1 || !*ptr) {
printf("Not enough memory");
exit(EXIT_FAILURE);
}
return n;
}
/* Driver for printf_handler, drop-in replacement for printf */
void eprintf(const char *format, ...) {
va_list ap;
char *msg;
va_start(ap, format);
xvasprintf(&msg, format, ap);
va_end(ap);
(*printf_handler)(msg);
free(msg);
}
/* Driver for fputs_handler, drop-in replacement for fputs(buf, fd) */
void efputs(const char *buf) {
(*fputs_handler)(buf);
}
/* Dump with the newline, drop-in replacement for puts(buf) */
void eputs(const char *buf) {
char *p;
p = xmalloc(strlen(buf) + 2);
strcpy(p, buf);
strcat(p, "\n");
(*fputs_handler)(p);
xfree(p);
}
/* Flush the current output stream */
void eflush(void) {
/* Do this only for non-NULL streams (uninitiated stream or a syslog) */
if (current_output_stream != NULL)
fflush(current_output_stream);
}
#ifdef OS_WINNT
HANDLE hEventLog;
#endif
/* Print MSG to syslog */
void print_syslog(const char *msg) {
char *p;
/* strip trailing \r and \n */
p = xstrdup(msg);
while (p[strlen(p) - 1] == '\r' || p[strlen(p) - 1] == '\n')
p[strlen(p) - 1] = 0;
#ifdef OS_UNIX
syslog(SYSLOG_PRIORITY, "%s", p);
#else
ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, &p, NULL);
#endif
xfree(p);
}
/* Print MSG to stderr */
void print_stderr(const char *msg) {
fprintf(stderr, "%s", msg);
}
/* Dump BUF to syslog */
void fputs_syslog(const char *buf) {
char *p;
/* strip trailing \r and \n */
p = xstrdup(buf);
while (p[strlen(p) - 1] == '\r' || p[strlen(p) - 1] == '\n')
p[strlen(p) - 1] = 0;
#ifdef OS_UNIX
syslog(SYSLOG_PRIORITY, "%s", p);
#else
ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, &p, NULL);
#endif
xfree(p);
}
/* Dump BUF to stderr */
void fputs_stderr(const char *buf) {
fputs(buf, stderr);
}
/* Redirect all messages handled with eprintf/efputs
to syslog (Unix) or event log (Windows) */
void redirect_to_syslog(void) {
static int has_inited = 0;
/* initiate syslog */
if (!has_inited) {
#ifdef OS_UNIX
setlogmask(LOG_UPTO(SYSLOG_PRIORITY));
openlog("elogd", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
#else
hEventLog = RegisterEventSource(NULL, "ELOG");
#endif
}
has_inited = 1;
printf_handler = print_syslog;
fputs_handler = fputs_syslog;
/* tells that the syslog facility is currently used as output */
current_output_stream = NULL;
}
/* Redirect all messages handled with eprintf/efputs to stderr */
void redirect_to_stderr(void) {
printf_handler = print_stderr;
fputs_handler = fputs_stderr;
current_output_stream = stderr;
}
/*------------------------------------------------------------------*/
int my_shell(char *cmd, char *result, int size) {
#ifdef OS_WINNT
HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
hChildStdoutRd, hChildStdoutWr, hChildStderrRd, hChildStderrWr, hSaveStdin, hSaveStdout, hSaveStderr;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
char buffer[10000];
DWORD dwRead, dwAvail, i;
/* Set the bInheritHandle flag so pipe handles are inherited. */
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
/* Save the handle to the current STDOUT. */
hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
/* Create a pipe for the child's STDOUT. */
if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
return 0;
/* Set a write handle to the pipe to be STDOUT. */
if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
return 0;
/* Save the handle to the current STDERR. */
hSaveStderr = GetStdHandle(STD_ERROR_HANDLE);
/* Create a pipe for the child's STDERR. */
if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0))
return 0;
/* Set a read handle to the pipe to be STDERR. */
if (!SetStdHandle(STD_ERROR_HANDLE, hChildStderrWr))
return 0;
/* Save the handle to the current STDIN. */
hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
/* Create a pipe for the child's STDIN. */
if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
return 0;
/* Set a read handle to the pipe to be STDIN. */
if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
return 0;
/* Duplicate the write handle to the pipe so it is not inherited. */
if (!DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, FALSE, /* not inherited */
DUPLICATE_SAME_ACCESS))
return 0;
CloseHandle(hChildStdinWr);
/* Now create the child process. */
memset(&siStartInfo, 0, sizeof(siStartInfo));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.lpReserved = NULL;
siStartInfo.lpReserved2 = NULL;
siStartInfo.cbReserved2 = 0;
siStartInfo.lpDesktop = NULL;
siStartInfo.dwFlags = 0;
/* command to execute */
sprintf(buffer, "cmd /q /c %s", cmd);
if (!CreateProcess(NULL, buffer, /* command line */
NULL, /* process security attributes */
NULL, /* primary thread security attributes */
TRUE, /* handles are inherited */
0, /* creation flags */
NULL, /* use parent's environment */
NULL, /* use parent's current directory */
&siStartInfo, /* STARTUPINFO pointer */
&piProcInfo)) /* receives PROCESS_INFORMATION */
return 0;
/* After process creation, restore the saved STDIN and STDOUT. */
SetStdHandle(STD_INPUT_HANDLE, hSaveStdin);
SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout);
SetStdHandle(STD_ERROR_HANDLE, hSaveStderr);
memset(result, 0, size);
do {
/* query stdout */
do {
if (!PeekNamedPipe(hChildStdoutRd, buffer, 256, &dwRead, &dwAvail, NULL))
break;
if (dwRead > 0) {
ReadFile(hChildStdoutRd, buffer, 256, &dwRead, NULL);
buffer[dwRead] = 0;
strlcat(result, buffer, size);
}
} while (dwAvail > 0);
/* query stderr */
do {
if (!PeekNamedPipe(hChildStderrRd, buffer, 256, &dwRead, &dwAvail, NULL))
break;
if (dwRead > 0) {
ReadFile(hChildStderrRd, buffer, 256, &dwRead, NULL);
buffer[dwRead] = 0;
strlcat(result, buffer, size);
}
} while (dwAvail > 0);
/* check if subprocess still alive */
if (!GetExitCodeProcess(piProcInfo.hProcess, &i))
break;
if (i != STILL_ACTIVE)
break;
/* give some CPU to subprocess */
Sleep(10);
} while (TRUE);
CloseHandle(hChildStdinWrDup);
CloseHandle(hChildStdinRd);
CloseHandle(hChildStderrRd);
CloseHandle(hChildStdoutRd);
/* strip trailing CR/LF */
while (strlen(result) > 0 && (result[strlen(result) - 1] == '\r' || result[strlen(result) - 1] == '\n'))
result[strlen(result) - 1] = 0;
return 1;
#endif /* OS_WINNT */
#ifdef OS_UNIX
pid_t child_pid;
int fh, status, wait_status;
char str[1024];
char tmp_filename[1024];
strlcpy(tmp_filename, "/tmp/elog_XXXXXX", sizeof(tmp_filename));
fh = mkstemp(tmp_filename);
if (fh == 0) {
eprintf("Error getting TMP file name.\n");
return 0;
}
close(fh);
if ((child_pid = fork()) < 0)
return 0;
else if (child_pid > 0) {
/* parent process waits for child */
do {
wait_status = waitpid(child_pid, &status, 0);
} while (wait_status == -1 && errno == EINTR);
/* read back result */
memset(result, 0, size);
fh = open(tmp_filename, O_RDONLY);
if (fh > 0) {
read(fh, result, size - 1);
close(fh);
}
/* remove temporary file */
remove(tmp_filename);
/* strip trailing CR/LF */
while (strlen(result) > 0 && (result[strlen(result) - 1] == '\r' || result[strlen(result) - 1] == '\n'))
result[strlen(result) - 1] = 0;
} else {
/* child process */
/* restore original UID/GID */
if (setregid(-1, orig_gid) < 0 || setreuid(-1, orig_uid) < 0)
eprintf("Cannot restore original GID/UID.\n");
/* give up root privilege permanently */
if (geteuid() == 0) {
if (!getcfg("global", "Grp", str, sizeof(str)) || setgroup(str) < 0) {
eprintf("Falling back to default group \"elog\"\n");
if (setgroup("elog") < 0) {
eprintf("Falling back to default group \"%s\"\n", DEFAULT_GROUP);
if (setgroup(DEFAULT_GROUP) < 0) {
eprintf("Refuse to run as setgid root.\n");
eprintf("Please consider to define a Grp statement in configuration file\n");
exit(EXIT_FAILURE);
}
}
} else if (get_verbose() >= VERBOSE_INFO)
eprintf("Falling back to group \"%s\"\n", str);
if (!getcfg("global", "Usr", str, sizeof(str)) || setuser(str) < 0) {
eprintf("Falling back to default user \"elog\"\n");
if (setuser("elog") < 0) {
eprintf("Falling back to default user \"%s\"\n", DEFAULT_USER);
if (setuser(DEFAULT_USER) < 0) {
eprintf("Refuse to run as setuid root.\n");
eprintf("Please consider to define a Usr statement in configuration file\n");
exit(EXIT_FAILURE);
}
}
} else if (get_verbose() >= VERBOSE_INFO)
eprintf("Falling back to user \"%s\"\n", str);
}
/* execute shell with redirection to /tmp/elog-shell */
sprintf(str, "/bin/sh -c \"%s\" > %s 2>&1", cmd, tmp_filename);
if (get_verbose() >= VERBOSE_INFO) {
efputs("Going to execute: ");
efputs(str);
efputs("\n");
}
system(str);
_exit(0);
}
return 1;
#endif /* OS_UNIX */
}
/*------------------------------------------------------------------*/
void strsubst_list(char *string, int size, char name[][NAME_LENGTH], char value[][NAME_LENGTH], int n)
/* subsitute "$name" with value corresponding to name */
{
int i, j;
char tmp[2 * NAME_LENGTH], str[2 * NAME_LENGTH], uattr[2 * NAME_LENGTH], *ps, *pt, *p, result[10000];
pt = tmp;
ps = string;
for (p = strchr(ps, '$'); p != NULL; p = strchr(ps, '$')) {
/* copy leading characters */
j = (int) (p - ps);
if (j >= (int) sizeof(tmp))
return;
memcpy(pt, ps, j);
pt += j;
p++;
/* extract name */
strlcpy(str, p, sizeof(str));
for (j = 0; j < (int) strlen(str); j++)
str[j] = toupper(str[j]);
/* do shell substituion at the end, so that shell parameter can
contain substituted attributes */
if (strncmp(str, "SHELL(", 6) == 0) {
strlcpy(pt, "$shell(", sizeof(tmp) - (pt - tmp));
ps += 7;
pt += 7;
continue;
}
/* search name */
for (i = 0; i < n; i++) {
strlcpy(uattr, name[i], sizeof(uattr));
for (j = 0; j < (int) strlen(uattr); j++)
uattr[j] = toupper(uattr[j]);
if (strncmp(str, uattr, strlen(uattr)) == 0)
break;
}
/* copy value */
if (i < n) {
strlcpy(pt, value[i], sizeof(tmp) - (pt - tmp));
pt += strlen(pt);
ps = p + strlen(uattr);
} else {
*pt++ = '$';
ps = p;
}
}
/* copy remainder */
strlcpy(pt, ps, sizeof(tmp) - (pt - tmp));
strlcpy(string, tmp, size);
/* check for $shell() subsitution */
pt = tmp;
ps = string;
for (p = strchr(ps, '$'); p != NULL; p = strchr(ps, '$')) {
/* copy leading characters */
j = (int) (p - ps);
if (j >= (int) sizeof(tmp))
return;
memcpy(pt, ps, j);
pt += j;
p++;
/* extract name */
strlcpy(str, p, sizeof(str));
for (j = 0; j < (int) strlen(str); j++)
str[j] = toupper(str[j]);
if (strncmp(str, "SHELL(", 6) == 0) {
ps += 7;
if (strrchr(p, '\"')) {
ps += strrchr(p, '\"') - p - 5;
if (strchr(ps, ')'))
ps = strchr(ps, ')') + 1;
} else {
if (strchr(ps, ')'))
ps = strchr(ps, ')') + 1;
}
if (str[6] == '"') {
strcpy(str, p + 7);
if (strrchr(str, '\"'))
*strrchr(str, '\"') = 0;
} else {
strcpy(str, p + 6);
if (strrchr(str, ')'))
*strrchr(str, ')') = 0;
}
if (!enable_execute) {
strlcpy(result, loc("Shell execution not enabled via -x flag"), sizeof(result));
eprintf("Shell execution not enabled via -x flag.\n");
} else
my_shell(str, result, sizeof(result));
strlcpy(pt, result, sizeof(tmp) - (pt - tmp));
pt += strlen(pt);
} else {
*pt++ = '$';
ps = p;
}
}
/* copy remainder */
strlcpy(pt, ps, sizeof(tmp) - (pt - tmp));
/* return result */
strlcpy(string, tmp, size);
}
/*------------------------------------------------------------------*/
void strsubst(char *string, int size, char *pattern, char *subst)
/* subsitute "pattern" with "subst" in "string" */
{
char *tail, *p;
int s;
p = string;
for (p = stristr(p, pattern); p != NULL; p = stristr(p, pattern)) {
if (strlen(pattern) == strlen(subst)) {
memcpy(p, subst, strlen(subst));
} else if (strlen(pattern) > strlen(subst)) {
memcpy(p, subst, strlen(subst));
memmove(p + strlen(subst), p + strlen(pattern), strlen(p + strlen(pattern)) + 1);
} else {
tail = (char *) xmalloc(strlen(p) - strlen(pattern) + 1);
strcpy(tail, p + strlen(pattern));
s = size - (p - string);
strlcpy(p, subst, s);
strlcat(p, tail, s);
xfree(tail);
}
p += strlen(subst);
}
}
/*------------------------------------------------------------------*/
void url_decode(char *p)
/********************************************************************\
Decode the given string in-place by expanding %XX escapes
\********************************************************************/
{
char *pD, str[3];
int i;
pD = p;
while (*p) {
if (*p == '%') {
/* Escape: next 2 chars are hex representation of the actual character */
p++;
if (isxdigit(p[0]) && isxdigit(p[1])) {
str[0] = p[0];
str[1] = p[1];
str[2] = 0;
sscanf(str, "%02X", &i);
*pD++ = (char) i;
p += 2;
} else
*pD++ = '%';
} else if (*p == '+') {
/* convert '+' to ' ' */
*pD++ = ' ';
p++;
} else {
*pD++ = *p++;
}
}
*pD = '\0';
}
void url_encode(char *ps, int size)
/********************************************************************\
Encode the given string in-place by adding %XX escapes
\********************************************************************/
{
unsigned char *pd, *p;
unsigned char str[NAME_LENGTH];
pd = str;
p = (unsigned char *) ps;
while (*p && pd < str + 250) {
if (strchr("%&=#?+<>", *p) || *p > 127) {
sprintf((char *) pd, "%%%02X", *p);
pd += 3;
p++;
} else if (*p == ' ') {
*pd++ = '+';
p++;
} else {
*pd++ = *p++;
}
}
*pd = '\0';
strlcpy(ps, (char *) str, size);
}
void url_slash_encode(char *ps, int size)
/********************************************************************\
Do the same including '/' characters
\********************************************************************/
{
unsigned char *pd, *p;
unsigned char str[NAME_LENGTH];
pd = str;
p = (unsigned char *) ps;
while (*p && pd < str + 250) {
if (strchr("%&=#?+<>/", *p) || *p > 127) {
sprintf((char *) pd, "%%%02X", *p);
pd += 3;
p++;
} else if (*p == ' ') {
*pd++ = '+';
p++;
} else {
*pd++ = *p++;
}
}
*pd = '\0';
strlcpy(ps, (char *) str, size);
}
/*-------------------------------------------------------------------*/
void str_escape(char *ps, int size)
/********************************************************************\
Encode the given string in-place by adding \\ escapes for `$"\
\********************************************************************/
{
unsigned char *pd, *p;
unsigned char str[NAME_LENGTH];
pd = str;
p = (unsigned char *) ps;
while (*p && pd < str + 250) {
if (strchr("'$\"\\", *p)) {
*pd++ = '\\';
*pd++ = *p++;
} else {
*pd++ = *p++;
}
}
*pd = '\0';
strlcpy(ps, (char *) str, size);
}
void btou(char *str)
/* convert all blanks to underscores in a string */
{
int i;
for (i = 0; i < (int) strlen(str); i++)
if (str[i] == ' ')
str[i] = '_';
}
/*-------------------------------------------------------------------*/
void stou(char *str)
/* convert all special characters to underscores in a string */
{
int i;
for (i = 0; i < (int) strlen(str); i++)
if (!isalnum(str[i]))
str[i] = '_';
}
/*-------------------------------------------------------------------*/
char *map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int cind(char c) {
int i;
if (c == '=')
return 0;
for (i = 0; i < 64; i++)
if (map[i] == c)
return i;
return -1;
}
void base64_decode(char *s, char *d) {
unsigned int t;
while (s && *s) {
t = cind(*s) << 18;
s++;
t |= cind(*s) << 12;
s++;
t |= cind(*s) << 6;
s++;
t |= cind(*s) << 0;
s++;
*(d + 2) = (char) (t & 0xFF);
t >>= 8;
*(d + 1) = (char) (t & 0xFF);
t >>= 8;
*d = (char) (t & 0xFF);
d += 3;
}
*d = 0;
}
void base64_encode(unsigned char *s, unsigned char *d, int size) {
unsigned int t, pad;
unsigned char *p;
pad = 3 - strlen((char *) s) % 3;
if (pad == 3)
pad = 0;
p = d;
while (*s) {
t = (*s++) << 16;
if (*s)
t |= (*s++) << 8;
if (*s)
t |= (*s++) << 0;
*(d + 3) = map[t & 63];
t >>= 6;
*(d + 2) = map[t & 63];
t >>= 6;
*(d + 1) = map[t & 63];
t >>= 6;
*(d + 0) = map[t & 63];
d += 4;
if (d - p >= size - 3)
return;
}
*d = 0;
while (pad--)
*(--d) = '=';
}
void base64_bufenc(unsigned char *s, int len, char *d) {
unsigned int t, pad;
int i;
pad = 3 - len % 3;
if (pad == 3)
pad = 0;
for (i = 0; i < len;) {
t = s[i++] << 16;
if (i < len)
t |= s[i++] << 8;
if (i < len)
t |= s[i++] << 0;
*(d + 3) = map[t & 63];
t >>= 6;
*(d + 2) = map[t & 63];
t >>= 6;
*(d + 1) = map[t & 63];
t >>= 6;
*(d + 0) = map[t & 63];
d += 4;
}
*d = 0;
while (pad--)
*(--d) = '=';
}
char *sha256_crypt(const char *key, const char *salt);
void do_crypt(const char *s, char *d, int size) {
strlcpy(d, sha256_crypt(s, "$5$") + 4, size);
}
/*------------------------------------------------------------------*
MD5 Checksum Routines
\*------------------------------------------------------------------*/
typedef struct {
unsigned int state[4]; // state (ABCD)
unsigned int count[2]; // number of bits, modulo 2^64 (lsb first)
unsigned char buffer[64]; // input buffer
} MD5_CONTEXT;
/*------------------------------------------------------------------*/
/* prototypes of the support routines */
void _MD5_update(MD5_CONTEXT *, const void *, unsigned int);
void _MD5_transform(unsigned int[4], unsigned char[64]);
void _MD5_encode(unsigned char *, unsigned int *, unsigned int);
void _MD5_decode(unsigned int *, unsigned char *, unsigned int);
/* F, G, H and I are basic MD5 functions */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
/* ROTATE_LEFT rotates x left n bits */
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
/* Rotation is separate from addition to prevent recomputation */
#define FF(a, b, c, d, x, s, ac) { \
(a) += F ((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
(a) += G ((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
(a) += H ((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
(a) += I ((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
/*------------------------------------------------------------------*/
/* main MD5 checksum routine, returns digest from pdata buffer */
void MD5_checksum(const void *pdata, unsigned int len, unsigned char digest[16]) {
MD5_CONTEXT ctx;
unsigned char bits[8];
unsigned int i, padlen;
/* to allow multithreading we have to locate the padding memory here */
unsigned char PADDING[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
};
memset(&ctx, 0, sizeof(MD5_CONTEXT));
ctx.count[0] = ctx.count[1] = 0;
/* load magic initialization constants */
ctx.state[0] = 0x67452301;
ctx.state[1] = 0xefcdab89;
ctx.state[2] = 0x98badcfe;
ctx.state[3] = 0x10325476;
_MD5_update(&ctx, pdata, len);
// save number of bits
_MD5_encode(bits, ctx.count, 8);
// pad out to 56 mod 64
i = (unsigned int) ((ctx.count[0] >> 3) & 0x3f);
padlen = (i < 56) ? (56 - i) : (120 - i);
_MD5_update(&ctx, PADDING, padlen);
// append length (before padding)
_MD5_update(&ctx, bits, 8);
// store state in digest
_MD5_encode(digest, ctx.state, 16);
}
/*------------------------------------------------------------------*/
void _MD5_update(MD5_CONTEXT *pctx, const void *pdata, unsigned int len) {
unsigned char *pin;
unsigned int i, index, partlen;
pin = (unsigned char *) pdata;
// compute number of bytes mod 64
index = (unsigned int) ((pctx->count[0] >> 3) & 0x3F);
// update number of bits
if ((pctx->count[0] += ((unsigned int) len << 3)) < ((unsigned int) len << 3))
pctx->count[1]++;
pctx->count[1] += ((unsigned int) len >> 29);
partlen = 64 - index;
// transform as many times as possible.
if (len >= partlen) {
memcpy(&pctx->buffer[index], pin, partlen);
_MD5_transform(pctx->state, pctx->buffer);
for (i = partlen; i + 63 < len; i += 64)
_MD5_transform(pctx->state, &pin[i]);
index = 0;
} else
i = 0;
/* buffer remaining input */
memcpy(&pctx->buffer[index], &pin[i], len - i);
}
/*------------------------------------------------------------------*/
/* basic transformation, transforms state based on block */
void _MD5_transform(unsigned int state[4], unsigned char block[64]) {
unsigned int lA = state[0], lB = state[1], lC = state[2], lD = state[3];
unsigned int x[16];
_MD5_decode(x, block, 64);
/* round 1 */
FF(lA, lB, lC, lD, x[0], 7, 0xd76aa478); // 1
FF(lD, lA, lB, lC, x[1], 12, 0xe8c7b756); // 2
FF(lC, lD, lA, lB, x[2], 17, 0x242070db); // 3
FF(lB, lC, lD, lA, x[3], 22, 0xc1bdceee); // 4
FF(lA, lB, lC, lD, x[4], 7, 0xf57c0faf); // 5
FF(lD, lA, lB, lC, x[5], 12, 0x4787c62a); // 6
FF(lC, lD, lA, lB, x[6], 17, 0xa8304613); // 7
FF(lB, lC, lD, lA, x[7], 22, 0xfd469501); // 8
FF(lA, lB, lC, lD, x[8], 7, 0x698098d8); // 9
FF(lD, lA, lB, lC, x[9], 12, 0x8b44f7af); // 10
FF(lC, lD, lA, lB, x[10], 17, 0xffff5bb1); // 11
FF(lB, lC, lD, lA, x[11], 22, 0x895cd7be); // 12
FF(lA, lB, lC, lD, x[12], 7, 0x6b901122); // 13
FF(lD, lA, lB, lC, x[13], 12, 0xfd987193); // 14
FF(lC, lD, lA, lB, x[14], 17, 0xa679438e); // 15
FF(lB, lC, lD, lA, x[15], 22, 0x49b40821); // 16
/* round 2 */
GG(lA, lB, lC, lD, x[1], 5, 0xf61e2562); // 17
GG(lD, lA, lB, lC, x[6], 9, 0xc040b340); // 18
GG(lC, lD, lA, lB, x[11], 14, 0x265e5a51); // 19
GG(lB, lC, lD, lA, x[0], 20, 0xe9b6c7aa); // 20
GG(lA, lB, lC, lD, x[5], 5, 0xd62f105d); // 21
GG(lD, lA, lB, lC, x[10], 9, 0x2441453); // 22
GG(lC, lD, lA, lB, x[15], 14, 0xd8a1e681); // 23
GG(lB, lC, lD, lA, x[4], 20, 0xe7d3fbc8); // 24
GG(lA, lB, lC, lD, x[9], 5, 0x21e1cde6); // 25
GG(lD, lA, lB, lC, x[14], 9, 0xc33707d6); // 26
GG(lC, lD, lA, lB, x[3], 14, 0xf4d50d87); // 27
GG(lB, lC, lD, lA, x[8], 20, 0x455a14ed); // 28
GG(lA, lB, lC, lD, x[13], 5, 0xa9e3e905); // 29
GG(lD, lA, lB, lC, x[2], 9, 0xfcefa3f8); // 30
GG(lC, lD, lA, lB, x[7], 14, 0x676f02d9); // 31
GG(lB, lC, lD, lA, x[12], 20, 0x8d2a4c8a); // 32
/* round 3 */
HH(lA, lB, lC, lD, x[5], 4, 0xfffa3942); // 33
HH(lD, lA, lB, lC, x[8], 11, 0x8771f681); // 34
HH(lC, lD, lA, lB, x[11], 16, 0x6d9d6122); // 35
HH(lB, lC, lD, lA, x[14], 23, 0xfde5380c); // 36
HH(lA, lB, lC, lD, x[1], 4, 0xa4beea44); // 37
HH(lD, lA, lB, lC, x[4], 11, 0x4bdecfa9); // 38
HH(lC, lD, lA, lB, x[7], 16, 0xf6bb4b60); // 39
HH(lB, lC, lD, lA, x[10], 23, 0xbebfbc70); // 40
HH(lA, lB, lC, lD, x[13], 4, 0x289b7ec6); // 41
HH(lD, lA, lB, lC, x[0], 11, 0xeaa127fa); // 42
HH(lC, lD, lA, lB, x[3], 16, 0xd4ef3085); // 43
HH(lB, lC, lD, lA, x[6], 23, 0x4881d05); // 44
HH(lA, lB, lC, lD, x[9], 4, 0xd9d4d039); // 45
HH(lD, lA, lB, lC, x[12], 11, 0xe6db99e5); // 46
HH(lC, lD, lA, lB, x[15], 16, 0x1fa27cf8); // 47
HH(lB, lC, lD, lA, x[2], 23, 0xc4ac5665); // 48
/* round 4 */
II(lA, lB, lC, lD, x[0], 6, 0xf4292244); // 49
II(lD, lA, lB, lC, x[7], 10, 0x432aff97); // 50
II(lC, lD, lA, lB, x[14], 15, 0xab9423a7); // 51
II(lB, lC, lD, lA, x[5], 21, 0xfc93a039); // 52
II(lA, lB, lC, lD, x[12], 6, 0x655b59c3); // 53
II(lD, lA, lB, lC, x[3], 10, 0x8f0ccc92); // 54
II(lC, lD, lA, lB, x[10], 15, 0xffeff47d); // 55
II(lB, lC, lD, lA, x[1], 21, 0x85845dd1); // 56
II(lA, lB, lC, lD, x[8], 6, 0x6fa87e4f); // 57
II(lD, lA, lB, lC, x[15], 10, 0xfe2ce6e0); // 58
II(lC, lD, lA, lB, x[6], 15, 0xa3014314); // 59
II(lB, lC, lD, lA, x[13], 21, 0x4e0811a1); // 60
II(lA, lB, lC, lD, x[4], 6, 0xf7537e82); // 61
II(lD, lA, lB, lC, x[11], 10, 0xbd3af235); // 62
II(lC, lD, lA, lB, x[2], 15, 0x2ad7d2bb); // 63
II(lB, lC, lD, lA, x[9], 21, 0xeb86d391); // 64
state[0] += lA;
state[1] += lB;
state[2] += lC;
state[3] += lD;
/* lClear sensitive information */
memset(x, 0, sizeof(x));
}
/*------------------------------------------------------------------*/
/* encodes input (unsigned int) into output (unsigned char),
assumes that lLen is a multiple of 4 */
void _MD5_encode(unsigned char *pout, unsigned int *pin, unsigned int len) {
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4) {
pout[j] = (unsigned char) (pin[i] & 0x0ff);
pout[j + 1] = (unsigned char) ((pin[i] >> 8) & 0x0ff);
pout[j + 2] = (unsigned char) ((pin[i] >> 16) & 0x0ff);
pout[j + 3] = (unsigned char) ((pin[i] >> 24) & 0x0ff);
}
}
/*------------------------------------------------------------------*/
/* encodes input (unsigned char) into output (unsigned int),
assumes that lLen is a multiple of 4 */
void _MD5_decode(unsigned int *pout, unsigned char *pin, unsigned int len) {
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
pout[i] = ((unsigned int) pin[j]) | (((unsigned int) pin[j + 1]) << 8) | (((unsigned int) pin[j + 2])
<< 16) | (((unsigned int)
pin[j + 3]) << 24);
}
/*------------------------------------------------------------------*/
BOOL file_exist(char *file_name) {
int fh;
fh = open(file_name, O_RDONLY);
if (fh < 0)
return FALSE;
close(fh);
return TRUE;
}
/*------------------------------------------------------------------*/
void serialdate2date(double days, int *day, int *month, int *year)
/* convert days since 1.1.1900 to date */
{
int i, j, l, n;
l = (int) days + 68569 + 2415019;
n = (int) ((4 * l) / 146097);
l = l - (int) ((146097 * n + 3) / 4);
i = (int) ((4000 * (l + 1)) / 1461001);
l = l - (int) ((1461 * i) / 4) + 31;
j = (int) ((80 * l) / 2447);
*day = l - (int) ((2447 * j) / 80);
l = (int) (j / 11);
*month = j + 2 - (12 * l);
*year = 100 * (n - 49) + i + l;
}
double date2serialdate(int day, int month, int year)
/* convert date to days since 1.1.1900 */
{
int serialdate;
serialdate = (int) ((1461 * (year + 4800 + (int) ((month - 14) / 12))) / 4) + (int) ((367 * (month - 2
-
12 *
((month -
14) /
12))) / 12) -
(int) ((3 * ((int) ((year + 4900 + (int) ((month - 14) / 12))
/ 100))) / 4) + day - 2415019 - 32075;
return serialdate;
}
/*------------------------------------------------------------------*/
/* Wrapper for setegid. */
int setegroup(char *str) {
#ifdef OS_UNIX
struct group *gr;
gr = getgrnam(str);
if (gr != NULL) {
chown(logbook_dir, -1, gr->gr_gid);
if (setregid(-1, gr->gr_gid) >= 0 && initgroups(gr->gr_name, gr->gr_gid) >= 0)
return 0;
else {
eprintf("Cannot set effective GID to group \"%s\"\n", gr->gr_name);
eprintf("setgroup: %s\n", strerror(errno));
}
} else
eprintf("Group \"%s\" not found\n", str);
return -1;
#else
return 0;
#endif
}
/* Wrapper for seteuid. */
int seteuser(char *str) {
#ifdef OS_UNIX
struct passwd *pw;
pw = getpwnam(str);
if (pw != NULL) {
chown(logbook_dir, pw->pw_uid, -1);
if (setreuid(-1, pw->pw_uid) >= 0) {
return 0;
} else {
eprintf("Cannot set effective UID to user \"%s\"\n", str);
eprintf("setuser: %s\n", strerror(errno));
}
} else
eprintf("User \"%s\" not found\n", str);
return -1;
#else
return 0;
#endif
}
/* Wrapper for setgid. */
int setgroup(char *str) {
#ifdef OS_UNIX
struct group *gr;
gr = getgrnam(str);
if (gr != NULL)
if (setgid(gr->gr_gid) >= 0 && initgroups(gr->gr_name, gr->gr_gid) >= 0)
return 0;
else {
eprintf("Cannot set effective GID to group \"%s\"\n", gr->gr_name);
eprintf("setgroup: %s\n", strerror(errno));
}
else
eprintf("Group \"%s\" not found\n", str);
return -1;
#else
return 0;
#endif
}
/* Wrapper for setuid. */
int setuser(char *str) {
#ifdef OS_UNIX
struct passwd *pw;
pw = getpwnam(str);
if (pw != NULL)
if (setuid(pw->pw_uid) >= 0)
return 0;
else {
eprintf("Cannot set effective UID to user \"%s\"\n", str);
eprintf("setuser: %s\n", strerror(errno));
}
else
eprintf("User \"%s\" not found\n", str);
return -1;
#else
return 0;
#endif
}
/*-------------------------------------------------------------------*/
int send_with_timeout(void *p, int sock, char *buf, int buf_size) {
int status, sent, send_size, send_packet;
time_t start, now;
char *pbuf;
#ifdef HAVE_SSL
SSL *ssl;
#endif
time(&start);
sent = 0;
send_size = buf_size;
pbuf = p; // fix compiler warning
pbuf = buf;
do {
if (send_size > 65536)
send_packet = 65536;
else
send_packet = send_size;
#ifdef HAVE_SSL
ssl = (SSL *) p;
if (ssl)
status = SSL_write(ssl, pbuf, send_packet);
else
#endif
status = send(sock, pbuf, send_packet, 0);
// abort after 30 seconds
time(&now);
if (now > start + 30) {
printf("Timeout after 30 seconds\n");
break;
}
// repeat if we were interrupted by alarm() signal
if (status == -1 && errno == EINTR) {
continue;
}
if (status == -1)
break;
if (status > 0)
sent += status;
if (status > 0 && sent < buf_size) {
pbuf += status;
send_size -= status;
}
} while (sent < buf_size);
return sent;
}
/*-------------------------------------------------------------------*/
int recv_string(int sock, char *buffer, int buffer_size, int millisec) {
int i, n;
fd_set readfds;
struct timeval timeout;
n = 0;
memset(buffer, 0, buffer_size);
do {
if (millisec > 0) {
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
timeout.tv_sec = millisec / 1000;
timeout.tv_usec = (millisec % 1000) * 1000;
select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout);
if (!FD_ISSET(sock, &readfds))
break;
}
i = recv(sock, buffer + n, 1, 0);
if (i <= 0)
break;
n++;
if (n >= buffer_size)
break;
} while (buffer[n - 1] && buffer[n - 1] != 10);
return n - 1;
}
/*-------------------------------------------------------------------*/
SESSION_ID *_sid = NULL;
int _n_sid;
int sid_new(LOGBOOK *lbs, const char *user, const char *host, char *sid) {
double exp;
time_t now;
int i, new_i;
char str[256];
time(&now);
new_i = 0;
if (_sid == NULL) {
_sid = (SESSION_ID *) malloc(sizeof(SESSION_ID));
_n_sid = 1;
new_i = 0;
} else {
exp = 24;
str[0] = 0;
if (lbs == NULL)
getcfg("global", "Login expiration", str, sizeof(str));
else
getcfg(lbs->name, "Login expiration", str, sizeof(str));
if (atof(str) > 0)
exp = atof(str);
if (exp < 24)
exp = 24; /* one day minimum for dangling edit pages */
/* search for expired sid */
for (i = 0; i < _n_sid; i++) {
if (_sid[i].time + exp * 3600 < now) {
new_i = i;
break;
}
}
if (i == _n_sid) {
_sid = (SESSION_ID *) realloc(_sid, sizeof(SESSION_ID) * (_n_sid + 1));
new_i = _n_sid;
_n_sid++;
}
}
strlcpy(_sid[new_i].user_name, user, sizeof(_sid[0].user_name));
strlcpy(_sid[new_i].host_ip, host, sizeof(_sid[0].host_ip));
for (i = 0; i < 4; i++)
sprintf(sid + i * 4, "%04X", rand() % 0x10000);
sid[16] = 0;
strlcpy(_sid[new_i].session_id, sid, sizeof(_sid[0].session_id));
_sid[new_i].time = now;
return 1;
}
/*-------------------------------------------------------------------*/
int sid_check(char *sid, char *user_name) {
int i;
time_t now;
if (sid == NULL)
return FALSE;
time(&now);
for (i = 0; i < _n_sid; i++) {
if (strcmp(_sid[i].host_ip, (char *) inet_ntoa(rem_addr)) == 0 && strcmp(_sid[i].session_id, sid) == 0) {
strcpy(user_name, _sid[i].user_name);
_sid[i].time = now;
return TRUE;
}
}
return FALSE;
}
/*-------------------------------------------------------------------*/
int sid_remove(char *sid) {
int i;
if (sid == NULL)
return FALSE;
for (i = 0; i < _n_sid; i++) {
if (strcmp(_sid[i].session_id, sid) == 0) {
memset(&_sid[i], 0, sizeof(SESSION_ID));
return TRUE;
}
}
return FALSE;
}
/*-------------------------------------------------------------------*/
void compose_email_header(LOGBOOK *lbs, char *subject, char *from, char *to, char *url, char *mail_text,
int size, int mail_encoding, int n_attachments, char *multipart_boundary,
int message_id, int reply_id) {
char buffer[256], charset[256], subject_enc[5000];
char buf[80], str[256];
int i, offset, multipart;
time_t now;
struct tm *ts;
i = 0;
if (mail_encoding & 1)
i++;
if (mail_encoding & 2)
i++;
if (mail_encoding & 4)
i++;
multipart = i > 1;
if (!getcfg("global", "charset", charset, sizeof(charset)))
strcpy(charset, DEFAULT_HTTP_CHARSET);
/* switch locale temporarily back to english to comply with RFC2822 date format */
setlocale(LC_ALL, "C");
time(&now);
ts = localtime(&now);
assert(ts);
strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", ts);
offset = (-(int) my_timezone());
if (ts->tm_isdst)
offset += 3600;
if (get_verbose() >= VERBOSE_INFO) {
sprintf(str, "timezone: %d, offset: %d\n", (int) my_timezone(), (int) offset);
efputs(str);
}
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Date: %s %+03d%02d\r\n", buf,
(int) (offset / 3600), (int) ((abs((int) offset) / 60) % 60));
getcfg("global", "Language", str, sizeof(str));
if (str[0])
setlocale(LC_ALL, str);
strlcat(mail_text, "To: ", size);
if (getcfg(lbs->name, "Omit Email to", str, sizeof(str)) && atoi(str) == 1)
strlcat(mail_text, "ELOG", size);
else
strlcat(mail_text, to, size);
strlcat(mail_text, "\r\n", size);
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "From: %s\r\n", from);
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "User-Agent: Elog Version %s\r\n",
VERSION);
strlcat(mail_text, "MIME-Version: 1.0\r\n", size);
memset(subject_enc, 0, sizeof(subject_enc));
for (i = 0; i < (int) strlen(subject); i++)
if (subject[i] < 0)
break;
if (i < (int) strlen(subject)) {
/* subject contains local characters, so encode it using charset */
for (i = 0; i < (int) strlen(subject); i += 40) {
strlcpy(buffer, subject + i, sizeof(buffer));
buffer[40] = 0;
strlcat(subject_enc, "=?", sizeof(subject_enc));
strlcat(subject_enc, charset, sizeof(subject_enc));
strlcat(subject_enc, "?B?", sizeof(subject_enc));
base64_encode((unsigned char *) buffer, (unsigned char *) (subject_enc + strlen(subject_enc)),
sizeof(subject_enc) - strlen(subject_enc));
strlcat(subject_enc, "?=", sizeof(subject_enc));
if (strlen(subject + i) < 40)
break;
strlcat(subject_enc, "\r\n ", sizeof(subject_enc)); // another encoded-word
}
} else
strlcpy(subject_enc, subject, sizeof(subject_enc));
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Subject: %s\r\n", subject_enc);
if (strchr(from, '@')) {
strlcpy(str, strchr(from, '@') + 1, sizeof(str));
if (strchr(str, '>'))
*strchr(str, '>') = 0;
} else
strlcpy(str, "elog.org", sizeof(str));
if (message_id)
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Message-ID: <%s-%d@%s>\r\n",
lbs->name_enc, message_id, str);
if (reply_id)
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "In-Reply-To: <%s-%d@%s>\r\n",
lbs->name_enc, reply_id, str);
if (url)
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "X-Elog-URL: %s\r\n", url);
strlcat(mail_text, "X-Elog-submit-type: web|elog\r\n", size);
if (multipart) {
sprintf(multipart_boundary, "------------%04X%04X%04X", rand(), rand(), rand());
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
"MIME-Version: 1.0\r\nContent-Type: multipart/alternative;\r\n boundary=\"%s\"\r\n\r\n",
multipart_boundary);
strlcat(mail_text, "This is a multi-part message in MIME format.\r\n", size);
} else {
if (n_attachments) {
sprintf(multipart_boundary, "------------%04X%04X%04X", rand(), rand(), rand());
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
"MIME-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n boundary=\"%s\"\r\n\r\n",
multipart_boundary);
strlcat(mail_text, "This is a multi-part message in MIME format.\r\n", size);
} else {
if (multipart_boundary)
multipart_boundary[0] = 0;
}
}
}
/*-------------------------------------------------------------------*/
int check_smtp_error(char *str, int expected, char *error, int error_size) {
if (atoi(str) != expected) {
if (error)
strlcpy(error, str + 4, error_size);
return 0;
}
return 1;
}
/*-------------------------------------------------------------------*/
int sendmail(LOGBOOK *lbs, char *smtp_host, char *from, char *to, char *text, char *error, int error_size) {
struct sockaddr_in bind_addr;
struct hostent *phe;
int i, n, s, strsize;
char *str;
char list[MAX_N_EMAIL][NAME_LENGTH], buffer[10000], decoded[256];
memset(error, 0, error_size);
if (get_verbose() >= VERBOSE_INFO)
eprintf("\n\nEmail from %s to %s, SMTP host %s:\n", from, to, smtp_host);
sprintf(buffer, "Email from %s to ", from);
strlcat(buffer, to, sizeof(buffer));
strlcat(buffer, ", SMTP host ", sizeof(buffer));
strlcat(buffer, smtp_host, sizeof(buffer));
strlcat(buffer, ":\n", sizeof(buffer));
write_logfile(lbs, buffer);
/* create a new socket for connecting to remote server */
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == -1)
return -1;
strsize = MAX_CONTENT_LENGTH + 1000;
str = xmalloc(strsize);
/* connect to remote node port on SMTP port */
int smtp_port = 25;
if (getcfg(lbs->name, "SMTP port", str, strsize))
smtp_port = atoi(str);
memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons((short) smtp_port);
phe = gethostbyname(smtp_host);
if (phe == NULL) {
if (error)
strlcpy(error, loc("Cannot lookup server name"), error_size);
return -1;
}
memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
if (connect(s, (void *) &bind_addr, sizeof(bind_addr)) < 0) {
closesocket(s);
if (error)
strlcpy(error, loc("Cannot connect to server"), error_size);
return -1;
}
recv_string(s, str, strsize, 10000);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
/* drain server messages */
do {
str[0] = 0;
recv_string(s, str, strsize, 300);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
} while (str[0]);
if (getcfg(lbs->name, "SMTP username", str, strsize)) {
snprintf(str, strsize - 1, "EHLO %s\r\n", host_name);
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
do {
recv_string(s, str, strsize, 3000);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
if (!check_smtp_error(str, 250, error, error_size))
goto smtp_error;
} while (stristr(str, "250 ") == NULL);
} else {
snprintf(str, strsize - 1, "HELO %s\r\n", host_name);
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
recv_string(s, str, strsize, 3000);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
if (!check_smtp_error(str, 250, error, error_size))
goto smtp_error;
}
/* optional authentication */
if (getcfg(lbs->name, "SMTP username", str, strsize)) {
snprintf(str, strsize - 1, "AUTH LOGIN\r\n");
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
recv_string(s, str, strsize, 3000);
if (strchr(str, '\r'))
*strchr(str, '\r') = 0;
if (atoi(str) != 334) {
strcat(str, "\n");
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
} else {
base64_decode(str + 4, decoded);
strcat(decoded, "\n");
if (get_verbose() >= VERBOSE_INFO)
efputs(decoded);
write_logfile(lbs, decoded);
}
if (!check_smtp_error(str, 334, error, error_size))
goto smtp_error;
getcfg(lbs->name, "SMTP username", decoded, sizeof(decoded));
base64_encode((unsigned char *) decoded, (unsigned char *) str, strsize);
strcat(str, "\r\n");
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(decoded);
write_logfile(lbs, decoded);
recv_string(s, str, strsize, 3000);
if (strchr(str, '\r'))
*strchr(str, '\r') = 0;
base64_decode(str + 4, decoded);
strcat(decoded, "\n");
if (get_verbose() >= VERBOSE_INFO)
efputs(decoded);
write_logfile(lbs, decoded);
if (!check_smtp_error(str, 334, error, error_size))
goto smtp_error;
getcfg(lbs->name, "SMTP password", str, strsize);
strcat(str, "\r\n");
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
recv_string(s, str, strsize, 3000);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
if (!check_smtp_error(str, 235, error, error_size))
goto smtp_error;
}
snprintf(str, strsize - 1, "MAIL FROM: %s\r\n", from);
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
recv_string(s, str, strsize, 3000);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
if (!check_smtp_error(str, 250, error, error_size))
goto smtp_error;
/* break recipients into list */
n = strbreak(to, list, MAX_N_EMAIL, ",", FALSE);
for (i = 0; i < n; i++) {
if (list[i] == 0 || strchr(list[i], '@') == NULL)
continue;
snprintf(str, strsize - 1, "RCPT TO: <%s>\r\n", list[i]);
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
/* increased timeout for SMTP servers with long alias lists */
recv_string(s, str, strsize, 30000);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
if (!check_smtp_error(str, 250, error, error_size))
goto smtp_error;
}
snprintf(str, strsize - 1, "DATA\r\n");
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
recv_string(s, str, strsize, 3000);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
if (!check_smtp_error(str, 354, error, error_size))
goto smtp_error;
/* replace "." at beginning of line by ".." */
strlcpy(str, text, strsize);
strsubst(str, strsize, "\n.", "\n..");
/* add "." to signal end of message */
strlcat(str, ".\r\n", strsize);
/* check if buffer exceeded */
if ((int) strlen(str) == strsize - 1) {
strlcpy(error, loc("Entry size too large for email notification"), error_size);
goto smtp_error;
}
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
recv_string(s, str, strsize, 10000);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
if (!check_smtp_error(str, 250, error, error_size))
goto smtp_error;
snprintf(str, strsize - 1, "QUIT\r\n");
send(s, str, strlen(str), 0);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
recv_string(s, str, strsize, 3000);
if (get_verbose() >= VERBOSE_INFO)
efputs(str);
write_logfile(lbs, str);
if (!check_smtp_error(str, 221, error, error_size))
goto smtp_error;
closesocket(s);
xfree(str);
return 1;
smtp_error:
closesocket(s);
xfree(str);
return -1;
}
/*------------------------------------------------------------------*/
int elog_connect(char *host, int port) {
int status, sock;
struct hostent *phe;
struct sockaddr_in bind_addr;
/* create socket */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("cannot create socket");
return -1;
}
/* compose remote address */
memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_addr.s_addr = 0;
bind_addr.sin_port = htons((unsigned short) port);
phe = gethostbyname(host);
if (phe == NULL) {
perror("cannot get host name");
return -1;
}
memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
/* connect to server */
status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
if (status != 0)
return -1;
return sock;
}
/*------------------------------------------------------------------*/
#ifdef HAVE_SSL
int ssl_connect(int sock, SSL **ssl_con) {
SSL_METHOD *meth;
SSL_CTX *ctx;
X509 *cert = NULL;
int i;
SSL_library_init();
SSL_load_error_strings();
#if OPENSSL_VERSION_NUMBER > 0x1010000fL
meth = (SSL_METHOD *) TLS_method();
#else
meth = (SSL_METHOD *) TLSv1_2_method();
#endif
ctx = SSL_CTX_new(meth);
*ssl_con = SSL_new(ctx);
SSL_set_fd(*ssl_con, sock);
if (SSL_connect(*ssl_con) <= 0)
return -1;
cert = SSL_get_peer_certificate(*ssl_con);
if (cert == NULL)
return -1;
i = SSL_get_verify_result(*ssl_con);
if (i != X509_V_OK)
printf("Possibly invalid certificate, continue on your own risk!\n");
return 0;
}
#endif
/*-------------------------------------------------------------------*/
void split_url(const char *url, char *host, int *port, char *subdir, char *param) {
const char *p;
char str[256];
if (host)
host[0] = 0;
if (port)
*port = 80;
if (subdir)
subdir[0] = 0;
if (param)
param[0] = 0;
p = url;
if (strncmp(url, "http://", 7) == 0)
p += 7;
if (strncmp(url, "https://", 8) == 0)
p += 8;
strncpy(str, p, sizeof(str));
if (strchr(str, '/')) {
if (subdir)
strncpy(subdir, strchr(str, '/'), 256);
*strchr(str, '/') = 0;
}
if (strchr(str, '?')) {
if (subdir)
strncpy(subdir, strchr(str, '?'), 256);
*strchr(str, '?') = 0;
}
if (strchr(str, ':')) {
if (port)
*port = atoi(strchr(str, ':') + 1);
*strchr(str, ':') = 0;
}
if (host)
strcpy(host, str);
if (subdir) {
if (strchr(subdir, '?')) {
strncpy(param, strchr(subdir, '?'), 256);
*strchr(subdir, '?') = 0;
}
if (subdir[0] == 0)
strcpy(subdir, "/");
}
}
/*-------------------------------------------------------------------*/
int retrieve_url(LOGBOOK *lbs, const char *url, int ssl, char **buffer, BOOL send_unm) {
char str[1000], unm[256], upwd[256], host[256], subdir[256], param[256];
int port, bufsize;
int i, n;
fd_set readfds;
struct timeval timeout;
UNUSED(ssl);
#ifdef HAVE_SSL
SSL *ssl_con = NULL;
#else
void *ssl_con = NULL;
#endif
int sock;
*buffer = NULL;
split_url(url, host, &port, subdir, param);
/* create a new socket for connecting to remote server */
sock = elog_connect(host, port);
if (sock == -1)
return -1;
#ifdef HAVE_SSL
if (ssl)
if (ssl_connect(sock, &ssl_con) < 0) {
SSL_free(ssl_con);
printf("Error initiating SSL connection\n");
return -1;
}
#endif
/* compose GET request, avoid chunked data in HTTP/1.1 protocol */
sprintf(str, "GET %s%s HTTP/1.0\r\nConnection: Close\r\n", subdir, param);
/* add local username/password */
if (send_unm) {
if (isparam("unm")) {
strlcpy(unm, getparam("unm"), sizeof(unm));
if (isparam("upwd"))
strlcpy(upwd, getparam("upwd"), sizeof(upwd));
else
get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL);
sprintf(str + strlen(str), "Cookie: unm=%s; upwd=%s\r\n", unm, upwd);
}
}
/* add host (RFC2616, Sec. 14) */
sprintf(str + strlen(str), "Host: %s:%d\r\n", host, port);
strcat(str, "\r\n");
send_with_timeout(ssl_con, sock, (char *) str, strlen(str));
bufsize = TEXT_SIZE + 1000;
*buffer = xmalloc(bufsize);
memset(*buffer, 0, bufsize);
n = 0;
do {
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
timeout.tv_sec = 30; /* 30 sec. timeout */
timeout.tv_usec = 0;
select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout);
if (!FD_ISSET(sock, &readfds)) {
closesocket(sock);
sock = 0;
xfree(*buffer);
*buffer = NULL;
return -1;
}
#ifdef HAVE_SSL
if (ssl)
i = SSL_read(ssl_con, *buffer + n, bufsize - n);
else
#endif
i = recv(sock, *buffer + n, bufsize - n, 0);
if (i <= 0)
break;
n += i;
if (n >= bufsize) {
/* increase buffer size */
bufsize += 10000;
*buffer = (char *) xrealloc(*buffer, bufsize);
if (*buffer == NULL) {
closesocket(sock);
return -1;
}
}
} while (1);
#ifdef HAVE_SSL
if (ssl)
SSL_free(ssl_con);
#endif
return n;
}
/*-------------------------------------------------------------------*/
int ss_daemon_init() {
#ifdef OS_UNIX
/* only implemented for UNIX */
int i, fd, pid;
if ((pid = fork()) < 0)
return FAILURE;
else if (pid != 0)
_exit(EXIT_SUCCESS); /* parent finished, exit without atexit hook */
/* child continues here */
/* try and use up stdin, stdout and stderr, so other
routines writing to stdout etc won't cause havoc. Copied from smbd */
for (i = 0; i < 3; i++) {
close(i);
fd = open("/dev/null", O_RDWR, 0);
if (fd < 0)
fd = open("/dev/null", O_WRONLY, 0);
if (fd < 0) {
eprintf("Can't open /dev/null");
return FAILURE;
}
if (fd != i) {
eprintf("Did not get file descriptor");
return FAILURE;
}
}
setsid(); /* become session leader */
chdir("/"); /* change working direcotry (not on NFS!) */
umask(0); /* clear our file mode creation mask */
#endif
return SUCCESS;
}
/*------------------------------------------------------------------*/
/* Parameter extraction from configuration file similar to win.ini */
typedef struct {
char *param;
char *uparam;
char *value;
} CONFIG_PARAM;
typedef struct {
char *section_name;
int n_params;
CONFIG_PARAM *config_param;
} LB_CONFIG;
LB_CONFIG *lb_config = NULL;
int n_lb_config = 0;
char _topgroup[256];
char _condition[256];
time_t cfgfile_mtime = 0;
/*-------------------------------------------------------------------*/
void check_config_file(BOOL force) {
struct stat cfg_stat;
if (force) {
parse_config_file(config_file);
return;
}
/* force re-read configuration file if changed */
if (stat(config_file, &cfg_stat) == 0) {
if (cfgfile_mtime < cfg_stat.st_mtime) {
cfgfile_mtime = cfg_stat.st_mtime;
parse_config_file(config_file);
}
} else
eprintf("Cannot stat() config file \"%s\": %s\n", config_file, strerror(errno));
}
/*-------------------------------------------------------------------*/
void setcfg_topgroup(char *topgroup) {
strcpy(_topgroup, topgroup);
}
char *getcfg_topgroup() {
if (_topgroup[0])
return _topgroup;
return NULL;
}
/*------------------------------------------------------------------*/
int is_logbook(char *logbook) {
char str[256];
if (strieq(logbook, "global"))
return 0;
/* check for 'global group' or 'global_xxx' */
strlcpy(str, logbook, sizeof(str));
str[7] = 0;
return !strieq(str, "global ");
}
/*-------------------------------------------------------------------*/
void set_condition(char *c) {
strlcpy(_condition, c, sizeof(_condition));
}
/*-------------------------------------------------------------------*/
void evaluate_conditions(LOGBOOK *lbs, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
char condition[256], str[256];
int index, i;
condition[0] = 0;
set_condition("");
for (index = 0; index < lbs->n_attr; index++) {
for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
if (strchr(attr_options[index][i], '{') && strchr(attr_options[index][i], '}')) {
strlcpy(str, attr_options[index][i], sizeof(str));
*strchr(str, '{') = 0;
if (strieq(str, attrib[index])) {
strlcpy(str, strchr(attr_options[index][i], '{') + 1, sizeof(str));
if (*strchr(str, '}'))
*strchr(str, '}') = 0;
if (condition[0] == 0)
strlcpy(condition, str, sizeof(condition));
else {
strlcat(condition, ",", sizeof(condition));
strlcat(condition, str, sizeof(condition));
}
set_condition(condition);
scan_attributes(lbs->name);
}
}
}
}
}
/*-------------------------------------------------------------------*/
BOOL match_param(char *str, char *param, int conditional_only) {
int ncl, npl, nand, i, j, k;
char *p, pcond[256], clist[10][NAME_LENGTH], plist[10][NAME_LENGTH], alist[10][NAME_LENGTH];
if (conditional_only && str[0] != '{')
return FALSE;
if (!_condition[0] || str[0] != '{')
return (stricmp(str, param) == 0);
p = str;
if (strchr(p, '}'))
p = strchr(p, '}') + 1;
while (*p == ' ')
p++;
strlcpy(pcond, str, sizeof(pcond));
if (strchr(pcond, '}'))
*strchr(pcond, '}') = 0;
if (strchr(pcond, '{'))
*strchr(pcond, '{') = ' ';
npl = strbreak(pcond, plist, 10, ",", FALSE);
ncl = strbreak(_condition, clist, 10, ",", FALSE);
for (i = 0; i < ncl; i++)
for (j = 0; j < npl; j++)
if (stricmp(clist[i], plist[j]) == 0) {
/* condition matches */
return stricmp(p, param) == 0;
}
/* check and'ed conditions */
for (i = 0; i < npl; i++)
if (strchr(plist[i], '&')) {
nand = strbreak(plist[i], alist, 10, "&", FALSE);
for (j = 0; j < nand; j++) {
for (k = 0; k < ncl; k++)
if (stricmp(clist[k], alist[j]) == 0)
break;
if (k == ncl)
return FALSE;
}
if (j == nand)
return stricmp(p, param) == 0;
}
return 0;
}
/*-------------------------------------------------------------------*/
int param_compare(const void *p1, const void *p2) {
return stricmp(((CONFIG_PARAM *) p1)->uparam, ((CONFIG_PARAM *) p2)->uparam);
}
/*------------------------------------------------------------------*/
void free_config() {
int i, j;
for (i = 0; i < n_lb_config; i++) {
for (j = 0; j < lb_config[i].n_params; j++) {
xfree(lb_config[i].config_param[j].param);
xfree(lb_config[i].config_param[j].uparam);
xfree(lb_config[i].config_param[j].value);
}
if (lb_config[i].config_param)
xfree(lb_config[i].config_param);
xfree(lb_config[i].section_name);
}
xfree(lb_config);
lb_config = NULL;
n_lb_config = 0;
}
/*------------------------------------------------------------------*/
int parse_config_file(char *file_name)
/* parse whole config file and store options in sorted list */
{
char *str, *buffer, *p, *pstr;
int index, i, j, fh, length;
str = xmalloc(20000);
/* open configuration file */
fh = open(file_name, O_RDONLY | O_BINARY);
if (fh < 0)
return 0;
length = lseek(fh, 0, SEEK_END);
lseek(fh, 0, SEEK_SET);
buffer = xmalloc(length + 1);
read(fh, buffer, length);
buffer[length] = 0;
close(fh);
/* release previously allocated memory */
if (lb_config)
free_config();
/* search group */
p = buffer;
index = 0;
do {
if (*p == '#' || *p == ';') {
/* skip comment */
while (*p && *p != '\n' && *p != '\r')
p++;
} else if (*p == '[') {
p++;
pstr = str;
while (*p && *p != ']' && *p != '\n' && *p != '\r' && pstr - str < 10000)
*pstr++ = *p++;
*pstr = 0;
/* allocate new group */
if (!lb_config)
lb_config = xmalloc(sizeof(LB_CONFIG));
else
lb_config = xrealloc(lb_config, sizeof(LB_CONFIG) * (n_lb_config + 1));
lb_config[n_lb_config].section_name = xmalloc(strlen(str) + 1);
lb_config[n_lb_config].n_params = 0;
lb_config[n_lb_config].config_param = NULL;
strcpy(lb_config[n_lb_config].section_name, str);
/* enumerate parameters */
i = 0;
p = strchr(p, '\n');
if (p)
p++;
while (p && *p && *p != '[') {
pstr = str;
while (*p && *p != '=' && *p != '\n' && *p != '\r' && pstr - str < 10000)
*pstr++ = *p++;
*pstr-- = 0;
while (pstr > str && (*pstr == ' ' || *pstr == '\t' || *pstr == '='))
*pstr-- = 0;
if (*p == '=') {
if (lb_config[n_lb_config].n_params == 0)
lb_config[n_lb_config].config_param = xmalloc(sizeof(CONFIG_PARAM));
else
lb_config[n_lb_config].config_param = xrealloc(lb_config[n_lb_config].config_param,
sizeof(CONFIG_PARAM) *
(lb_config[n_lb_config].n_params + 1));
lb_config[n_lb_config].config_param[i].param = xmalloc(strlen(str) + 1);
lb_config[n_lb_config].config_param[i].uparam = xmalloc(strlen(str) + 1);
strcpy(lb_config[n_lb_config].config_param[i].param, str);
for (j = 0; j < (int) strlen(str); j++)
lb_config[n_lb_config].config_param[i].uparam[j] = toupper(str[j]);
lb_config[n_lb_config].config_param[i].uparam[j] = 0;
p++;
while (*p == ' ' || *p == '\t')
p++;
pstr = str;
while (*p && *p != '\n' && *p != '\r' && pstr - str < 10000)
*pstr++ = *p++;
*pstr-- = 0;
while (*pstr == ' ' || *pstr == '\t')
*pstr-- = 0;
lb_config[n_lb_config].config_param[i].value = xmalloc(strlen(str) + 1);
strcpy(lb_config[n_lb_config].config_param[i].value, str);
i++;
lb_config[n_lb_config].n_params = i;
}
/* search for next line beginning */
while (*p && *p != '\r' && *p != '\n')
p++;
while (*p && (*p == '\r' || *p == '\n'))
p++;
}
/* sort parameter */
// outcommented: not needed, might screw up group ordering
//qsort(lb_config[n_lb_config].config_param, lb_config[n_lb_config].n_params, sizeof(CONFIG_PARAM),
// param_compare);
n_lb_config++;
index++;
}
/* search for next line beginning */
while (*p && *p != '\r' && *p != '\n' && *p != '[')
p++;
while (*p && (*p == '\r' || *p == '\n'))
p++;
} while (*p);
xfree(str);
xfree(buffer);
return 0;
}
/*-------------------------------------------------------------------*/
int getcfg_simple(char *group, char *param, char *value, int vsize, int conditional) {
int i, j, status;
char uparam[256];
if (strlen(param) >= sizeof(uparam))
return 0;
for (i = 0; i < (int) strlen(param); i++)
uparam[i] = toupper(param[i]);
uparam[i] = 0;
value[0] = 0;
for (i = 0; i < n_lb_config; i++)
if (strieq(lb_config[i].section_name, group))
break;
if (i == n_lb_config)
return 0;
for (j = 0; j < lb_config[i].n_params; j++)
if (match_param(lb_config[i].config_param[j].uparam, uparam, conditional)) {
status = strchr(lb_config[i].config_param[j].uparam, '{') ? 2 : 1;
strlcpy(value, lb_config[i].config_param[j].value, vsize);
return status;
}
return 0;
}
/*-------------------------------------------------------------------*/
int enumgrp(int index, char *group) {
if (index < n_lb_config) {
strcpy(group, lb_config[index].section_name);
return 1;
}
return 0;
}
/*-------------------------------------------------------------------*/
int getcfg(char *group, char *param, char *value, int vsize)
/*
Read parameter from configuration file.
- if group == [global] and top group exists, read
from [global ]
- if parameter not in [global ], read from [global]
- if group is logbook, read from logbook section
- if parameter not in [], read from [global ]
or [global]
*/
{
char str[256];
int status;
/* if group is [global] and top group exists, read from there */
if (strieq(group, "global") && getcfg_topgroup()) {
sprintf(str, "global %s", getcfg_topgroup());
status = getcfg(str, param, value, vsize);
if (status)
return status;
}
/* first check if parameter is under condition */
if (_condition[0]) {
status = getcfg_simple(group, param, value, vsize, TRUE);
if (status)
return status;
}
status = getcfg_simple(group, param, value, vsize, FALSE);
if (status)
return status;
/* if parameter not found in logbook, look in [global] section */
if (!group || is_logbook(group))
return getcfg("global", param, value, vsize);
return 0;
}
/*-------------------------------------------------------------------*/
char *find_param(char *buf, char *group, char *param) {
char *str, *p, *pstr, *pstart;
/* search group */
str = xmalloc(10000);
p = buf;
do {
if (*p == '[') {
p++;
pstr = str;
while (*p && *p != ']' && *p != '\n')
*pstr++ = *p++;
*pstr = 0;
if (strieq(str, group)) {
/* search parameter */
p = strchr(p, '\n');
if (p)
p++;
while (p && *p && *p != '[') {
pstr = str;
pstart = p;
while (*p && *p != '=' && *p != '\n')
*pstr++ = *p++;
*pstr-- = 0;
while (pstr > str && (*pstr == ' ' || *pstr == '=' || *pstr == '\t'))
*pstr-- = 0;
if (match_param(str, param, FALSE)) {
xfree(str);
return pstart;
}
if (p)
p = strchr(p, '\n');
if (p)
p++;
}
}
}
if (p)
p = strchr(p, '\n');
if (p)
p++;
} while (p);
xfree(str);
/* now search if in [global] section */
if (!strieq(group, "global"))
return find_param(buf, "global", param);
return NULL;
}
/*-------------------------------------------------------------------*/
int is_group(char *group) {
int i;
for (i = 0; i < n_lb_config; i++)
if (strieq(group, lb_config[i].section_name))
return 1;
return 0;
}
/*------------------------------------------------------------------*/
int enumcfg(char *group, char *param, int psize, char *value, int vsize, int index) {
int i;
for (i = 0; i < n_lb_config; i++)
if (strieq(group, lb_config[i].section_name)) {
if (index < lb_config[i].n_params) {
strlcpy(param, lb_config[i].config_param[index].param, psize);
strlcpy(value, lb_config[i].config_param[index].value, vsize);
return 1;
}
return 0;
}
return 0;
}
/*-------------------------------------------------------------------*/
int exist_top_group() {
int i;
char str[256];
for (i = 0;; i++) {
if (!enumcfg("global", str, sizeof(str), NULL, 0, i))
break;
str[9] = 0;
if (strieq(str, "top group"))
return 1;
}
return 0;
}
/*-------------------------------------------------------------------*/
char *_locbuffer = NULL;
char **_porig, **_ptrans;
time_t _locfile_mtime = 0;
/* check if language file changed and if so reload it */
int check_language() {
char language[256], file_name[256], *p;
int fh, length, n;
struct stat cfg_stat;
getcfg("global", "Language", language, sizeof(language));
/* set locale for strftime */
if (language[0])
setlocale(LC_ALL, language);
else
setlocale(LC_ALL, "english");
/* force re-read configuration file if changed */
strlcpy(file_name, resource_dir, sizeof(file_name));
strlcat(file_name, "resources", sizeof(file_name));
strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
strlcat(file_name, "eloglang.", sizeof(file_name));
strlcat(file_name, language, sizeof(file_name));
if (stat(file_name, &cfg_stat) == 0) {
if (_locfile_mtime != cfg_stat.st_mtime) {
_locfile_mtime = cfg_stat.st_mtime;
if (_locbuffer) {
xfree(_locbuffer);
_locbuffer = NULL;
}
}
}
if (strieq(language, "english") || language[0] == 0) {
if (_locbuffer) {
xfree(_locbuffer);
_locbuffer = NULL;
}
} else {
if (_locbuffer == NULL) {
fh = open(file_name, O_RDONLY | O_BINARY);
if (fh < 0)
return -1;
length = lseek(fh, 0, SEEK_END);
lseek(fh, 0, SEEK_SET);
_locbuffer = xmalloc(length + 1);
read(fh, _locbuffer, length);
_locbuffer[length] = 0;
close(fh);
/* scan lines, setup orig-translated pointers */
p = _locbuffer;
n = 0;
do {
while (*p && (*p == '\r' || *p == '\n'))
p++;
if (*p && (*p == ';' || *p == '#' || *p == ' ' || *p == '\t')) {
while (*p && *p != '\n' && *p != '\r')
p++;
continue;
}
if (n == 0) {
_porig = xmalloc(sizeof(char *) * 2);
_ptrans = xmalloc(sizeof(char *) * 2);
} else {
_porig = xrealloc(_porig, sizeof(char *) * (n + 2));
_ptrans = xrealloc(_ptrans, sizeof(char *) * (n + 2));
}
_porig[n] = p;
while (*p && (*p != '=' && *p != '\r' && *p != '\n'))
p++;
if (*p && *p != '=')
continue;
_ptrans[n] = p + 1;
while (*_ptrans[n] == ' ' || *_ptrans[n] == '\t')
_ptrans[n]++;
/* remove '=' and surrounding blanks */
while (*p == '=' || *p == ' ' || *p == '\t')
*p-- = 0;
p = _ptrans[n];
while (*p && *p != '\n' && *p != '\r')
p++;
if (p)
*p++ = 0;
n++;
} while (p && *p);
_porig[n] = NULL;
_ptrans[n] = NULL;
}
}
return 0;
}
/*-------------------------------------------------------------------*/
/* localization support */
char *loc(char *orig) {
int n;
char language[256];
static char result[256];
if (!_locbuffer)
return orig;
/* search string and return translation */
for (n = 0; _porig[n]; n++)
if (strcmp(orig, _porig[n]) == 0) {
if (*_ptrans[n])
return _ptrans[n];
return orig;
}
/* special case: "Change %s" */
if (strstr(orig, "Change ") && strcmp(orig, "Change %s") != 0) {
sprintf(result, loc("Change %s"), orig + 7);
return result;
}
/* special case: some intrinsic commands */
if (strstr(orig, "GetPwdFile")) {
strcpy(result, orig);
return result;
}
getcfg("global", "Language", language, sizeof(language));
eprintf("Language error: string \"%s\" not found for language \"%s\"\n", orig, language);
return orig;
}
/*-------------------------------------------------------------------*/
char *month_name(int m)
/* return name of month in current locale, m=0..11 */
{
struct tm ts;
static char name[32];
memset(&ts, 0, sizeof(ts));
ts.tm_mon = m;
ts.tm_mday = 15;
ts.tm_year = 2000;
mktime(&ts);
strftime(name, sizeof(name), "%B", &ts);
return name;
}
/*-------------------------------------------------------------------*/
time_t date_to_ltime(char *date) {
struct tm tms;
int i, date_zone, local_zone;
time_t ltime;
memset(&tms, 0, sizeof(struct tm));
if (strlen(date) > 25) {
/* RFC2822 compliant date */
for (i = 0; i < 12; i++)
if (strncmp(date + 8, mname[i], 3) == 0)
break;
tms.tm_mon = i;
tms.tm_mday = atoi(date + 5);
tms.tm_hour = atoi(date + 17);
tms.tm_min = atoi(date + 20);
tms.tm_sec = atoi(date + 23);
tms.tm_year = atoi(date + 12) - 1900;
tms.tm_isdst = -1;
if (tms.tm_year < 90)
tms.tm_year += 100;
ltime = mktime(&tms);
/* correct for difference between local time zone (used by mktime) and time zone of date */
date_zone = atoi(date + 26);
/* correct for incorrect date_zone */
if (date_zone > 2400 || date_zone < -2400)
date_zone = 0;
date_zone = (abs(date_zone) % 100) * 60 + (date_zone) / 100 * 3600;
local_zone = my_timezone();
if (tms.tm_isdst)
local_zone -= 3600;
ltime = ltime - local_zone - date_zone;
} else {
/* ctime() complient date */
for (i = 0; i < 12; i++)
if (strncmp(date + 4, mname[i], 3) == 0)
break;
tms.tm_mon = i;
tms.tm_mday = atoi(date + 8);
tms.tm_hour = atoi(date + 11);
tms.tm_min = atoi(date + 14);
tms.tm_sec = atoi(date + 17);
tms.tm_year = atoi(date + 20) - 1900;
tms.tm_isdst = -1;
if (tms.tm_year < 90)
tms.tm_year += 100;
ltime = mktime(&tms);
}
return ltime;
}
/*-------------------------------------------------------------------*/
void check_config() {
check_config_file(FALSE);
check_language();
}
/*-------------------------------------------------------------------*/
void retrieve_domain(char *ret, int size) {
char smtp_host[80];
strlcpy(ret, "tmp.org", size);
if (getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) {
if (strchr(smtp_host, '.'))
strlcpy(ret, strchr(smtp_host, '.') + 1, size);
}
}
/*-------------------------------------------------------------------*/
void retrieve_email_from(LOGBOOK *lbs, char *ret, char *ret_name, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
char email_from[256], email_from_name[256], str[256], *p, login_name[256],
slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH],
full_name[256], user_email[256];
int i;
if (getcfg(lbs->name, "Use Email from", str, sizeof(str))) {
if (str[0] != '<') {
strlcpy(email_from, "<", sizeof(email_from));
strlcat(email_from, str, sizeof(email_from));
strlcat(email_from, ">", sizeof(email_from));
} else
strlcpy(email_from, str, sizeof(email_from));
strlcpy(email_from_name, str, sizeof(email_from));
} else if (isparam("unm")) {
get_user_line(lbs, getparam("unm"), NULL, full_name, user_email, NULL, NULL, NULL);
strlcpy(email_from_name, full_name, sizeof(email_from_name));
strlcat(email_from_name, " <", sizeof(email_from_name));
strlcat(email_from_name, user_email, sizeof(email_from_name));
strlcat(email_from_name, ">", sizeof(email_from_name));
strlcpy(email_from, "<", sizeof(email_from));
strlcat(email_from, user_email, sizeof(email_from));
strlcat(email_from, ">", sizeof(email_from));
} else if (getcfg(lbs->name, "Default Email from", str, sizeof(str))) {
if (str[0] != '<') {
strlcpy(email_from, "<", sizeof(email_from));
strlcat(email_from, str, sizeof(email_from));
strlcat(email_from, ">", sizeof(email_from));
} else
strlcpy(email_from, str, sizeof(email_from));
strlcpy(email_from_name, str, sizeof(email_from));
} else {
sprintf(email_from_name, "ELog ", host_name);
sprintf(email_from, "", host_name);
}
if (attrib) {
i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
strsubst_list(email_from_name, sizeof(email_from_name), slist, svalue, i);
strsubst_list(email_from, sizeof(email_from), slist, svalue, i);
/* remove possible 'mailto:' */
if ((p = strstr(email_from_name, "mailto:")) != NULL)
memmove(p, p + 7, strlen(p + 7) + 1);
if ((p = strstr(email_from, "mailto:")) != NULL)
memmove(p, p + 7, strlen(p + 7) + 1);
}
/* if nothing available, figure out email from an administrator */
if (strchr(email_from, '@') == NULL) {
for (i = 0;; i++) {
if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
break;
get_user_line(lbs, login_name, NULL, NULL, email_from, NULL, NULL, NULL);
sprintf(email_from_name, "%s <%s>", login_name, email_from);
if (is_admin_user(lbs, login_name) && strchr(email_from, '@'))
break;
}
}
if (ret)
strcpy(ret, email_from);
if (ret_name)
strcpy(ret_name, email_from_name);
}
/*------------------------------------------------------------------*/
void el_decode(char *message, char *key, char *result, int size) {
char *pc, *ph;
int i;
if (result == NULL)
return;
*result = 0;
ph = strstr(message, "========================================");
if (ph == NULL)
return;
do {
if (ph[40] == '\r' || ph[40] == '\n')
break;
ph = strstr(ph + 40, "========================================");
if (ph == NULL)
return;
} while (1);
/* go through all lines */
for (pc = message; pc < ph;) {
if (strncmp(pc, key, strlen(key)) == 0) {
pc += strlen(key);
for (i = 0; *pc != '\n' && *pc != '\r' && i < size - 1; i++)
result[i] = *pc++;
result[i] = 0;
return;
}
pc = strchr(pc, '\n');
if (pc == NULL)
return;
while (*pc && (*pc == '\n' || *pc == '\r'))
pc++;
}
}
/*------------------------------------------------------------------*/
void el_decode_int(char *message, char *key, char *result, int size) {
char str[80];
if (result == NULL)
return;
*result = 0;
el_decode(message, key, str, sizeof(str));
if (str[0])
sprintf(str, "%d", atoi(str));
strlcpy(result, str, size);
}
/*------------------------------------------------------------------*/
void el_decode_intlist(char *message, char *key, char *result, int size) {
int i;
if (result == NULL)
return;
*result = 0;
el_decode(message, key, result, size);
/* remove any non allowed characters */
for (i = 0; i < size && i < (int) strlen(result); i++)
if (!isdigit(result[i]) && result[i] != ' ' && result[i] != ',')
result[i] = ' ';
}
/*------------------------------------------------------------------*/
void el_enum_attr(char *message, int n, char *attr_name, char *attr_value) {
char *p, str[NAME_LENGTH], tmp[NAME_LENGTH];
int i;
p = message;
for (i = 0; i <= n; i++) {
strlcpy(str, p, sizeof(str));
if (strchr(str, '\n'))
*strchr(str, '\n') = 0;
if (strchr(str, '\r'))
*strchr(str, '\r') = 0;
if (strcmp(str, "========================================") == 0)
break;
p = strchr(p, '\n');
if (!p) {
str[0] = 0; /* not a valid line */
break;
}
while (*p == '\n' || *p == '\r')
p++;
if (strchr(str, ':')) {
strcpy(tmp, str);
*strchr(tmp, ':') = 0;
if (strieq(tmp, "$@MID@$") || strieq(tmp, "Date") || strieq(tmp, "Attachment") || strieq(tmp,
"Reply To")
|| strieq(tmp, "In Reply To") || strieq(tmp, "Encoding") || strieq(tmp, "Locked by"))
i--;
}
}
attr_name[0] = 0;
attr_value[0] = 0;
if (strchr(str, ':')) {
strlcpy(attr_name, str, NAME_LENGTH);
*strchr(attr_name, ':') = 0;
strlcpy(attr_value, strchr(str, ':') + 2, NAME_LENGTH);
}
}
/*------------------------------------------------------------------*/
/* Simplified copy of fnmatch() for Cygwin where fnmatch is not defined */
#define EOS '\0'
int fnmatch1(const char *pattern, const char *string) {
char c, test;
for (;;)
switch (c = *pattern++) {
case EOS:
return (*string == EOS ? 0 : 1);
case '?':
if (*string == EOS)
return (1);
++string;
break;
case '*':
c = *pattern;
/* Collapse multiple stars. */
while (c == '*')
c = *++pattern;
/* Optimize for pattern with * at end or before /. */
if (c == EOS)
return (0);
/* General case, use recursion. */
while ((test = *string) != EOS) {
if (!fnmatch1(pattern, string))
return (0);
++string;
}
return (1);
/* FALLTHROUGH */
default:
if (c != *string)
return (1);
string++;
break;
}
}
/*------------------------------------------------------------------*/
int ss_file_find(const char *path, char *pattern, char **plist)
/********************************************************************
Routine: ss_file_find
Purpose: Return list of files matching 'pattern' from the 'path' location
Input:
char *path Name of a file in file system to check
char *pattern pattern string (wildcard allowed)
Output:
char **plist pointer to the file list
Function value:
int Number of files matching request
\********************************************************************/
{
#ifdef OS_UNIX
DIR *dir_pointer;
struct dirent *dp;
int i;
if ((dir_pointer = opendir(path)) == NULL)
return 0;
*plist = (char *) xmalloc(MAX_PATH_LENGTH);
i = 0;
for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) {
if (fnmatch1(pattern, dp->d_name) == 0) {
*plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH);
strncpy(*plist + (i * MAX_PATH_LENGTH), dp->d_name, strlen(dp->d_name));
*(*plist + (i * MAX_PATH_LENGTH) + strlen(dp->d_name)) = '\0';
i++;
}
}
closedir(dir_pointer);
return i;
#endif
#ifdef OS_WINNT
HANDLE pffile;
LPWIN32_FIND_DATA lpfdata;
char str[255];
int i, first;
strlcpy(str, path, sizeof(str));
strlcat(str, "\\", sizeof(str));
strlcat(str, pattern, sizeof(str));
first = 1;
i = 0;
lpfdata = xmalloc(sizeof(WIN32_FIND_DATA));
*plist = (char *) xmalloc(MAX_PATH_LENGTH);
pffile = FindFirstFile(str, lpfdata);
if (pffile == INVALID_HANDLE_VALUE)
return 0;
first = 0;
*plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH);
strncpy(*plist + (i * MAX_PATH_LENGTH), lpfdata->cFileName, strlen(lpfdata->cFileName));
*(*plist + (i * MAX_PATH_LENGTH) + strlen(lpfdata->cFileName)) = '\0';
i++;
while (FindNextFile(pffile, lpfdata)) {
*plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH);
strncpy(*plist + (i * MAX_PATH_LENGTH), lpfdata->cFileName, strlen(lpfdata->cFileName));
*(*plist + (i * MAX_PATH_LENGTH) + strlen(lpfdata->cFileName)) = '\0';
i++;
}
xfree(lpfdata);
return i;
#endif
}
/*------------------------------------------------------------------*/
int eli_compare(const void *e1, const void *e2) {
if (((EL_INDEX *) e1)->file_time < ((EL_INDEX *) e2)->file_time)
return -1;
if (((EL_INDEX *) e1)->file_time >= ((EL_INDEX *) e2)->file_time)
return 1;
return 0;
}
/*------------------------------------------------------------------*/
void generate_subdir_name(char *file_name, char *subdir, int size) {
char fn[MAX_PATH_LENGTH], path[MAX_PATH_LENGTH];
int year;
// extract path from file_name
strlcpy(path, file_name, size);
if (strrchr(path, DIR_SEPARATOR))
*(strrchr(path, DIR_SEPARATOR) + 1) = 0;
// extract file name
if (strrchr(file_name, DIR_SEPARATOR))
strlcpy(fn, strrchr(file_name, DIR_SEPARATOR) + 1, sizeof(fn));
else
strlcpy(fn, file_name, sizeof(fn));
// create subdir from name
year = (fn[0] - '0') * 10 + (fn[1] - '0');
// month = (fn[2]-'0')*10+(fn[3]-'0');
if (year < 80)
sprintf(subdir, "20%02d", year);
else
sprintf(subdir, "19%02d", year);
strlcat(subdir, DIR_SEPARATOR_STR, size);
}
/*------------------------------------------------------------------*/
int restructure_dir(char *dir) {
char *file_list;
int n1, n2, index, status;
char old_path[MAX_PATH_LENGTH], new_path[MAX_PATH_LENGTH],
subdir[MAX_PATH_LENGTH];
static int first = TRUE;
/* go through all entry files */
n1 = ss_file_find(dir, "??????a.log", &file_list);
for (index = 0; index < n1; index++) {
generate_subdir_name(file_list + index * MAX_PATH_LENGTH, subdir, sizeof(subdir));
// create new subdir
strlcpy(new_path, dir, MAX_PATH_LENGTH);
strlcat(new_path, subdir, MAX_PATH_LENGTH);
#ifdef OS_WINNT
status = mkdir(new_path);
#else
status = mkdir(new_path, 0755);
#endif
if (status == 0) {
if (first) {
eprintf("\nFound old directory structure. Creating subdirectories and moving files...\n");
first = FALSE;
}
eprintf("Created directory \"%s\"\n", new_path);
} else {
if (errno != EEXIST) {
eprintf("generate_subdir_name: %s\n", strerror(errno));
eprintf("Cannot create directory \"%s\"\n", new_path);
}
}
strlcpy(old_path, dir, sizeof(old_path));
strlcat(old_path, file_list + index * MAX_PATH_LENGTH, sizeof(old_path));
strlcpy(new_path, dir, sizeof(new_path));
strlcat(new_path, subdir, sizeof(new_path));
strlcat(new_path, file_list + index * MAX_PATH_LENGTH, sizeof(new_path));
rename(old_path, new_path);
}
if (file_list)
xfree(file_list);
/* go through all attachment files */
n2 = ss_file_find(dir, "??????_??????_*", &file_list);
for (index = 0; index < n2; index++) {
generate_subdir_name(file_list + index * MAX_PATH_LENGTH, subdir, sizeof(subdir));
// create new subdir
strlcpy(new_path, dir, MAX_PATH_LENGTH);
strlcat(new_path, subdir, MAX_PATH_LENGTH);
#ifdef OS_WINNT
status = mkdir(new_path);
#else
status = mkdir(new_path, 0755);
#endif
strlcpy(old_path, dir, sizeof(old_path));
strlcat(old_path, file_list + index * MAX_PATH_LENGTH, sizeof(old_path));
strlcpy(new_path, dir, sizeof(new_path));
strlcat(new_path, subdir, sizeof(new_path));
strlcat(new_path, file_list + index * MAX_PATH_LENGTH, sizeof(new_path));
rename(old_path, new_path);
}
if (file_list)
xfree(file_list);
return n1 + n2;
}
/*------------------------------------------------------------------*/
int parse_file(LOGBOOK *lbs, char *file_name) {
char str[256], date[256], *buffer, *p, *pn, in_reply_to[80];
int length, i, fh, len;
fh = open(file_name, O_RDONLY | O_BINARY, 0644);
if (fh < 0) {
sprintf(str, "Cannot open file \"%s\"", file_name);
eprintf("%s; %s\n", str, strerror(errno));
return EL_FILE_ERROR;
}
/* read file into buffer */
length = lseek(fh, 0, SEEK_END);
if (length > 0) {
buffer = xmalloc(length + 1);
lseek(fh, 0, SEEK_SET);
read(fh, buffer, length);
buffer[length] = 0;
close(fh);
/* go through buffer */
p = buffer;
do {
p = strstr(p, "$@MID@$:");
if (p) {
lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index + 1));
if (lbs->el_index == NULL) {
eprintf("Not enough memory to allocate entry index\n");
return EL_MEM_ERROR;
}
strlcpy(lbs->el_index[*lbs->n_el_index].subdir, file_name + strlen(lbs->data_dir), 256);
if (strrchr(lbs->el_index[*lbs->n_el_index].subdir, DIR_SEPARATOR))
*(strrchr(lbs->el_index[*lbs->n_el_index].subdir, DIR_SEPARATOR) + 1) = 0;
if (strrchr(file_name, DIR_SEPARATOR))
strlcpy(str, strrchr(file_name, DIR_SEPARATOR) + 1, sizeof(str));
else
strlcpy(str, file_name, sizeof(str));
strcpy(lbs->el_index[*lbs->n_el_index].file_name, str);
el_decode(p, "Date: ", date, sizeof(date));
el_decode_int(p, "In reply to: ", in_reply_to, sizeof(in_reply_to));
lbs->el_index[*lbs->n_el_index].file_time = date_to_ltime(date);
lbs->el_index[*lbs->n_el_index].message_id = atoi(p + 8);
lbs->el_index[*lbs->n_el_index].offset = p - buffer;
lbs->el_index[*lbs->n_el_index].in_reply_to = atoi(in_reply_to);
pn = strstr(p + 8, "$@MID@$:");
if (pn)
len = pn - p;
else
len = strlen(p);
MD5_checksum(p, len, lbs->el_index[*lbs->n_el_index].md5_digest);
if (lbs->el_index[*lbs->n_el_index].message_id > 0) {
if (get_verbose() == VERBOSE_DEBUG) {
eprintf(" ID %3d, %s, ofs %5d, %s, MD5=", lbs->el_index[*lbs->n_el_index].message_id,
str, lbs->el_index[*lbs->n_el_index].offset,
lbs->el_index[*lbs->n_el_index].in_reply_to ? "reply" : "thead");
for (i = 0; i < 16; i++)
eprintf("%02X", lbs->el_index[*lbs->n_el_index].md5_digest[i]);
eprintf("\n");
}
/* valid ID */
(*lbs->n_el_index)++;
}
p += 8;
}
} while (p);
xfree(buffer);
}
return SUCCESS;
}
/*------------------------------------------------------------------*/
int scan_dir_tree(LOGBOOK *lbs, const char *dir, char **file_list, int *n) {
int index, n_files;
char str[MAX_PATH_LENGTH];
char *fl, *p;
fl = NULL;
n_files = ss_file_find(dir, "*", &fl);
if (n_files == 0) {
if (fl)
xfree(fl);
return 0;
}
if (*file_list == NULL)
*file_list = (char *) xmalloc(n_files * MAX_PATH_LENGTH);
else
*file_list = (char *) xrealloc(*file_list, ((*n) + n_files) * MAX_PATH_LENGTH);
/* go through all files */
for (index = 0; index < n_files; index++) {
if (fnmatch1("??????a.log", &fl[index * MAX_PATH_LENGTH]) == 0) {
p = *file_list + ((*n) * MAX_PATH_LENGTH);
strlcpy(p, dir, MAX_PATH_LENGTH);
if (p[strlen(p) - 1] != DIR_SEPARATOR)
strlcat(p, DIR_SEPARATOR_STR, MAX_PATH_LENGTH);
strlcat(p, fl + index * MAX_PATH_LENGTH, MAX_PATH_LENGTH);
(*n)++;
}
}
/* go through all sub-directories */
for (index = 0; index < n_files; index++) {
if (fnmatch1("????", &fl[index * MAX_PATH_LENGTH]) == 0 ||
fnmatch1("??", &fl[index * MAX_PATH_LENGTH]) == 0) {
if (strieq(fl + index * MAX_PATH_LENGTH, ".."))
continue;
strlcpy(str, dir, sizeof(str));
if (str[strlen(str) - 1] != DIR_SEPARATOR)
strlcat(str, DIR_SEPARATOR_STR, sizeof(str));
strlcat(str, fl + index * MAX_PATH_LENGTH, sizeof(str));
scan_dir_tree(lbs, str, file_list, n);
}
}
if (fl)
xfree(fl);
return *n;
}
/*------------------------------------------------------------------*/
int el_build_index(LOGBOOK *lbs, BOOL rebuild)
/* scan all ??????a.log files and build an index table in eli[] */
{
char *file_list, error_str[256], base_dir[256], *buffer;
int index, n;
int i, status;
unsigned char digest[16];
if (rebuild) {
xfree(lbs->el_index);
xfree(lbs->n_el_index);
}
lbs->n_el_index = xmalloc(sizeof(int));
*lbs->n_el_index = 0;
lbs->el_index = xmalloc(0);
/* get data directory */
strcpy(base_dir, lbs->data_dir);
if (get_verbose() >= VERBOSE_DEBUG) {
/* show MD5 from config file */
load_config_section(lbs->name, &buffer, error_str);
if (error_str[0])
eprintf(error_str);
else {
remove_crlf(buffer);
MD5_checksum(buffer, strlen(buffer), digest);
eprintf("\n\nConfig [%s], MD5=", lbs->name);
for (i = 0; i < 16; i++)
eprintf("%02X", digest[i]);
eprintf("\n\n");
}
if (buffer)
xfree(buffer);
}
if (get_verbose() >= VERBOSE_DEBUG)
eprintf("Entries:\n");
// move files to directories if (new layout to reduce number of files per directory)
restructure_dir(base_dir);
file_list = NULL;
n = 0;
scan_dir_tree(lbs, base_dir, &file_list, &n);
/* go through all files */
for (index = 0; index < n; index++) {
status = parse_file(lbs, file_list + index * MAX_PATH_LENGTH);
if (status != SUCCESS) {
if (file_list)
xfree(file_list);
return status;
}
}
if (file_list)
xfree(file_list);
/* sort entries according to date */
qsort(lbs->el_index, *lbs->n_el_index, sizeof(EL_INDEX), eli_compare);
if (get_verbose() >= VERBOSE_DEBUG) {
eprintf("After sort:\n");
for (i = 0; i < *lbs->n_el_index; i++)
eprintf(" ID %3d, %s, ofs %5d\n", lbs->el_index[i].message_id, lbs->el_index[i].file_name,
lbs->el_index[i].offset);
}
if (rebuild && n == 0) {
eprintf("Logbook files seem to have disappeared, aborting program.\n");
assert(rebuild && n > 0);
}
return EL_SUCCESS;
}
/*------------------------------------------------------------------*/
int el_index_logbooks() {
char str[256], data_dir[256], logbook[256], cwd[256], *p;
int i, j, n, status = 0;
if (lb_list) {
for (i = 0; lb_list[i].name[0]; i++) {
if (lb_list[i].el_index != NULL) {
xfree(lb_list[i].el_index);
xfree(lb_list[i].n_el_index);
/* check if other logbook uses same index */
for (j = i + 1; lb_list[j].name[0]; j++) {
/* mark that logbook already freed */
if (lb_list[j].el_index == lb_list[i].el_index)
lb_list[j].el_index = NULL;
}
}
}
xfree(lb_list);
}
/* count logbooks */
for (i = n = 0;; i++) {
if (!enumgrp(i, str))
break;
if (!is_logbook(str))
continue;
n++;
}
lb_list = xcalloc(sizeof(LOGBOOK), n + 1);
for (i = n = 0;; i++) {
if (!enumgrp(i, logbook))
break;
if (!is_logbook(logbook))
continue;
/* check for duplicate name */
for (j = 0; j < i && lb_list[j].name[0]; j++)
if (strieq(lb_list[j].name, logbook)) {
eprintf("Error in configuration file: Duplicate logbook \"%s\"\n", logbook);
return EL_DUPLICATE;
}
/* store logbook in list */
strcpy(lb_list[n].name, logbook);
strcpy(lb_list[n].name_enc, logbook);
url_encode(lb_list[n].name_enc, sizeof(lb_list[n].name_enc));
/* get data dir from configuration file (old method) */
if (getcfg(logbook, "Data dir", str, sizeof(str))) {
if (str[0] == DIR_SEPARATOR || str[1] == ':')
strlcpy(data_dir, str, sizeof(data_dir));
else {
strlcpy(data_dir, resource_dir, sizeof(data_dir));
strlcat(data_dir, str, sizeof(data_dir));
}
} else {
/* use logbook_dir + "Subdir" (new method) */
strlcpy(data_dir, logbook_dir, sizeof(data_dir));
if (data_dir[strlen(data_dir) - 1] != DIR_SEPARATOR)
strlcat(data_dir, DIR_SEPARATOR_STR, sizeof(data_dir));
if (getcfg(logbook, "Subdir", str, sizeof(str))) {
if (str[0] == DIR_SEPARATOR)
strlcpy(data_dir, str, sizeof(data_dir));
else
strlcat(data_dir, str, sizeof(data_dir));
} else
strlcat(data_dir, logbook, sizeof(data_dir)); /* use logbook name as default */
}
if (data_dir[strlen(data_dir) - 1] != DIR_SEPARATOR)
strlcat(data_dir, DIR_SEPARATOR_STR, sizeof(data_dir));
/* create data directory if not existing */
getcwd(cwd, sizeof(cwd));
j = chdir(data_dir);
if (j < 0) {
p = data_dir;
if (*p == DIR_SEPARATOR) {
chdir(DIR_SEPARATOR_STR);
p++;
}
if (p[1] == ':') {
strcpy(str, p);
if (str[2] == DIR_SEPARATOR)
str[3] = 0;
else
str[2] = 0;
chdir(str);
p += strlen(str);
}
do {
if (strchr(p, DIR_SEPARATOR)) {
strlcpy(str, p, sizeof(str));
*strchr(str, DIR_SEPARATOR) = 0;
p = strchr(p, DIR_SEPARATOR) + 1;
} else {
strlcpy(str, p, sizeof(str));
p = NULL;
}
j = chdir(str);
if (j < 0) {
#ifdef OS_WINNT
j = mkdir(str);
#else
j = mkdir(str, 0755);
#endif
if (j == 0) {
if (get_verbose() >= VERBOSE_INFO)
eprintf("Created directory \"%s\"\n", str);
} else {
eprintf("el_index_logbooks: %s\n", strerror(errno));
eprintf("Cannot create directory \"%s\"\n", str);
}
chdir(str);
}
} while (p && *p);
}
chdir(cwd);
strcpy(lb_list[n].data_dir, data_dir);
lb_list[n].el_index = NULL;
/* check if other logbook uses the same directory */
for (j = 0; j < n; j++)
if (strcmp(lb_list[j].data_dir, lb_list[n].data_dir) == 0) {
if (get_verbose() >= VERBOSE_INFO)
eprintf("Logbook \"%s\" uses same directory as logbook \"%s\"\n", logbook, lb_list[j].name);
lb_list[n].el_index = lb_list[j].el_index;
lb_list[n].n_el_index = lb_list[j].n_el_index;
break;
}
if (j == n) {
if (get_verbose() >= VERBOSE_INFO)
eprintf("Indexing logbook \"%s\" in \"%s\" ... ", logbook, lb_list[n].data_dir);
eflush();
status = el_build_index(&lb_list[n], FALSE);
if (get_verbose() >= VERBOSE_INFO)
if (status == EL_SUCCESS)
eprintf("ok\n");
}
if (status == EL_EMPTY) {
if (get_verbose() >= VERBOSE_INFO)
eprintf("Found empty logbook \"%s\"\n", logbook);
} else if (status != EL_SUCCESS) {
eprintf("Error generating index.\n");
return status;
}
n++;
}
/* if top groups defined, set top group in logbook */
if (exist_top_group()) {
LBLIST phier;
phier = get_logbook_hierarchy();
for (i = 0; i < phier->n_members; i++)
if (phier->member[i]->is_top)
for (j = 0; lb_list[j].name[0]; j++)
if (is_logbook_in_group(phier->member[i], lb_list[j].name))
strcpy(lb_list[j].top_group, phier->member[i]->name);
free_logbook_hierarchy(phier);
}
if (!load_password_files())
return EL_INVAL_FILE;
return EL_SUCCESS;
}
/*------------------------------------------------------------------*/
int el_search_message(LOGBOOK *lbs, int mode, int message_id, BOOL head_only)
/********************************************************************
Routine: el_search_message
Purpose: Search for a specific message in a logbook
Input:
int mode Search mode, EL_FIRST, EL_LAST, EL_NEXT, EL_PREV
int message_id Message id for EL_NEXT and EL_PREV
Function value:
int New message id
\********************************************************************/
{
int i;
if (lbs->n_el_index == 0)
return 0;
if (mode == EL_FIRST) {
if (head_only) {
for (i = 0; i < *lbs->n_el_index; i++)
if (lbs->el_index[i].in_reply_to == 0)
return lbs->el_index[i].message_id;
return 0;
}
if (*lbs->n_el_index == 0)
return 0;
return lbs->el_index[0].message_id;
}
if (mode == EL_LAST) {
if (head_only) {
for (i = *lbs->n_el_index - 1; i >= 0; i--)
if (lbs->el_index[i].in_reply_to == 0)
return lbs->el_index[i].message_id;
return 0;
}
if (*lbs->n_el_index == 0)
return 0;
return lbs->el_index[*lbs->n_el_index - 1].message_id;
}
if (mode == EL_NEXT) {
for (i = 0; i < *lbs->n_el_index; i++)
if (lbs->el_index[i].message_id == message_id)
break;
if (i == *lbs->n_el_index)
return 0; // message not found
if (i == *lbs->n_el_index - 1)
return 0; // last message
if (head_only) {
for (i++; i < *lbs->n_el_index; i++)
if (lbs->el_index[i].in_reply_to == 0)
return lbs->el_index[i].message_id;
return 0;
}
return lbs->el_index[i + 1].message_id;
}
if (mode == EL_PREV) {
for (i = 0; i < *lbs->n_el_index; i++)
if (lbs->el_index[i].message_id == message_id)
break;
if (i == *lbs->n_el_index)
return 0; // message not found
if (i == 0)
return 0; // first message
if (head_only) {
for (i--; i >= 0; i--)
if (lbs->el_index[i].in_reply_to == 0)
return lbs->el_index[i].message_id;
return 0;
}
return lbs->el_index[i - 1].message_id;
}
return 0;
}
/*------------------------------------------------------------------*/
int el_retrieve(LOGBOOK *lbs, int message_id, char *date, char attr_list[MAX_N_ATTR][NAME_LENGTH],
char attrib[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, int *textsize,
char *in_reply_to, char *reply_to, char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH],
char *encoding, char *locked_by, char *draft)
/********************************************************************
Routine: el_retrieve
Purpose: Retrieve an ELog entry by its message tab
Input:
LOGBOOK lbs Logbook structure
int message_id Message ID to retrieve
int *size Size of text buffer
Output:
char *tag tag of retrieved message
char *date Date/time of message recording
char attr_list Names of attributes
char attrib Values of attributes
int n_attr Number of attributes
char *text Message text
char *in_reply_to Original message if this one is a reply
char *reply_to Replies for current message
char *attachment[] File attachments
char *encoding Encoding of message
char *locked_by User/Host if locked for editing
char *draft User who drafted that message
int *size Actual message text size
Function value:
EL_SUCCESS Successful completion
EL_EMPTY Logbook is empty
EL_NO_MSG Message doesn't exist
EL_FILE_ERROR Internal error
\********************************************************************/
{
int i, index, size, fh;
char str[NAME_LENGTH], file_name[MAX_PATH_LENGTH * 3], *p;
char *message, attachment_all[64 * MAX_ATTACHMENTS];
if (message_id == 0)
/* open most recent message */
message_id = el_search_message(lbs, EL_LAST, 0, FALSE);
if (message_id == 0)
return EL_EMPTY;
for (index = 0; index < *lbs->n_el_index; index++)
if (lbs->el_index[index].message_id == message_id)
break;
if (index == *lbs->n_el_index)
return EL_NO_MSG;
snprintf(file_name, sizeof(file_name), "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir,
lbs->el_index[index].file_name);
fh = open(file_name, O_RDONLY | O_BINARY, 0644);
if (fh < 0) {
/* file might have been deleted, rebuild index */
el_build_index(lbs, TRUE);
return el_retrieve(lbs, message_id, date, attr_list, attrib, n_attr, text, textsize, in_reply_to,
reply_to, attachment, encoding, locked_by, draft);
}
message = xmalloc(TEXT_SIZE + 1000);
lseek(fh, lbs->el_index[index].offset, SEEK_SET);
i = my_read(fh, message, TEXT_SIZE + 1000 - 1);
if (i <= 0) {
xfree(message);
close(fh);
return EL_FILE_ERROR;
}
message[i] = 0;
close(fh);
if (strncmp(message, "$@MID@$:", 8) != 0) {
xfree(message);
/* file might have been edited, rebuild index */
el_build_index(lbs, TRUE);
return el_retrieve(lbs, message_id, date, attr_list, attrib, n_attr, text, textsize, in_reply_to,
reply_to, attachment, encoding, locked_by, draft);
}
/* check for correct ID */
if (atoi(message + 8) != message_id) {
xfree(message);
return EL_FILE_ERROR;
}
/* decode message size */
p = strstr(message + 8, "$@MID@$:");
if (p == NULL)
size = strlen(message);
else
size = p - message;
message[size] = 0;
/* decode message */
if (date)
el_decode(message, "Date: ", date, 80);
if (reply_to)
el_decode_intlist(message, "Reply to: ", reply_to, MAX_REPLY_TO * 10);
if (in_reply_to)
el_decode_int(message, "In reply to: ", in_reply_to, 80);
if (n_attr == -1) {
/* derive attribute names from message */
for (i = 0;; i++) {
el_enum_attr(message, i, attr_list[i], attrib[i]);
if (!attr_list[i][0])
break;
}
n_attr = i;
} else {
if (attrib)
for (i = 0; i < n_attr; i++) {
sprintf(str, "%s: ", attr_list[i]);
el_decode(message, str, attrib[i], NAME_LENGTH);
}
}
el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
if (encoding)
el_decode(message, "Encoding: ", encoding, 80);
if (attachment) {
/* break apart attachements */
for (i = 0; i < MAX_ATTACHMENTS; i++)
if (attachment[i] != NULL)
attachment[i][0] = 0;
for (i = 0; i < MAX_ATTACHMENTS; i++) {
if (attachment[i] != NULL) {
if (i == 0)
p = strtok(attachment_all, ",");
else
p = strtok(NULL, ",");
if (p != NULL)
strcpy(attachment[i], p);
else
break;
}
}
}
if (locked_by)
el_decode(message, "Locked by: ", locked_by, 80);
if (draft)
el_decode(message, "Draft: ", draft, 80);
p = strstr(message, "========================================\n");
/* check for \n -> \r conversion (e.g. zipping/unzipping) */
if (p == NULL)
p = strstr(message, "========================================\r");
if (text) {
if (p != NULL) {
p += 41;
if ((int) strlen(p) >= *textsize) {
strlcpy(text, p, *textsize);
show_error("Entry too long to display. Please increase TEXT_SIZE and recompile elogd.");
xfree(message);
return EL_FILE_ERROR;
} else {
strlcpy(text, p, *textsize);
/* strip CR at end */
if (text[strlen(text) - 1] == '\n') {
text[strlen(text) - 1] = 0;
if (text[strlen(text) - 1] == '\r')
text[strlen(text) - 1] = 0;
}
*textsize = strlen(text);
}
} else {
text[0] = 0;
*textsize = 0;
}
}
xfree(message);
return EL_SUCCESS;
}
/*------------------------------------------------------------------*/
int el_submit_attachment(LOGBOOK *lbs, const char *afilename, const char *buffer, int buffer_size,
char *full_name) {
char file_name[MAX_PATH_LENGTH], ext_file_name[MAX_PATH_LENGTH + 100], str[MAX_PATH_LENGTH],
*p, subdir[MAX_PATH_LENGTH], path_name[MAX_PATH_LENGTH];
int fh;
time_t now;
struct tm tms;
/* strip directory, add date and time to filename */
strlcpy(str, afilename, sizeof(str));
p = str;
while (strchr(p, ':'))
p = strchr(p, ':') + 1;
while (strchr(p, '\\'))
p = strchr(p, '\\') + 1; /* NT */
while (strchr(p, '/'))
p = strchr(p, '/') + 1; /* Unix */
strlcpy(file_name, p, sizeof(file_name));
/* assemble ELog filename */
if (file_name[0]) {
if (file_name[6] == '_' && file_name[13] == '_' && isdigit(file_name[0]) && isdigit(file_name[1]))
strlcpy(ext_file_name, file_name, sizeof(ext_file_name));
else {
time(&now);
memcpy(&tms, localtime(&now), sizeof(struct tm));
sprintf(ext_file_name, "%02d%02d%02d_%02d%02d%02d_%s", tms.tm_year % 100, tms.tm_mon + 1,
tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec, file_name);
}
strlcpy(path_name, lbs->data_dir, sizeof(str));
generate_subdir_name(ext_file_name, subdir, sizeof(subdir));
strlcat(path_name, subdir, sizeof(str));
if (strlen(path_name) > 0 && path_name[strlen(path_name) - 1] == DIR_SEPARATOR)
path_name[strlen(path_name) - 1] = 0;
#ifdef OS_WINNT
mkdir(path_name);
#else
mkdir(path_name, 0755);
#endif
strlcat(path_name, DIR_SEPARATOR_STR, sizeof(path_name));
/* test if file exists */
do {
strlcpy(str, path_name, sizeof(str));
strlcat(str, ext_file_name, sizeof(path_name));
fh = open(str, O_RDONLY, 0644);
if (fh > 0) {
close(fh);
strlcpy(str, ext_file_name, sizeof(str));
if (strchr(str, '.')) {
*strchr(str, '.') = 0;
strlcat(str, "_1", sizeof(str));
strlcat(str, strchr(ext_file_name, '.'), sizeof(str));
strlcpy(ext_file_name, str, sizeof(ext_file_name));
}
}
} while (fh > 0);
if (full_name)
strlcpy(full_name, ext_file_name, MAX_PATH_LENGTH);
strlcpy(str, path_name, sizeof(str));
strlcat(str, ext_file_name, sizeof(path_name));
/* save attachment */
fh = open(str, O_CREAT | O_RDWR | O_BINARY, 0644);
if (fh < 0) {
strencode2(file_name, str, sizeof(file_name));
sprintf(str, "Cannot write attachment file \"%s\"", file_name);
show_error(str);
return -1;
} else {
write(fh, buffer, buffer_size);
close(fh);
}
}
return 0;
}
/*------------------------------------------------------------------*/
void el_delete_attachment(LOGBOOK *lbs, char *file_name) {
int i;
char str[MAX_PATH_LENGTH], subdir[MAX_PATH_LENGTH];
strlcpy(str, lbs->data_dir, sizeof(str));
generate_subdir_name(file_name, subdir, sizeof(subdir));
strlcat(str, subdir, sizeof(str));
strlcat(str, file_name, sizeof(str));
remove(str);
strlcat(str, ".png", sizeof(str));
remove(str);
for (i = 0;; i++) {
strlcpy(str, lbs->data_dir, sizeof(str));
strlcat(str, subdir, sizeof(str));
strlcat(str, file_name, sizeof(str));
sprintf(str + strlen(str), "-%d.png", i);
if (file_exist(str)) {
remove(str);
continue;
}
strlcpy(str, lbs->data_dir, sizeof(str));
strlcat(str, subdir, sizeof(str));
strlcat(str, file_name, sizeof(str));
if (strrchr(str, '.'))
*strrchr(str, '.') = 0;
sprintf(str + strlen(str), "-%d.png", i);
if (file_exist(str)) {
remove(str);
continue;
}
break;
}
}
/*------------------------------------------------------------------*/
int el_retrieve_attachment(LOGBOOK *lbs, int message_id, int n, char name[MAX_PATH_LENGTH]) {
int i, index, size, fh;
char file_name[MAX_PATH_LENGTH * 3], *p;
char message[TEXT_SIZE + 1000], attachment_all[64 * MAX_ATTACHMENTS];
if (message_id == 0)
return EL_EMPTY;
for (index = 0; index < *lbs->n_el_index; index++)
if (lbs->el_index[index].message_id == message_id)
break;
if (index == *lbs->n_el_index)
return EL_NO_MSG;
snprintf(file_name, sizeof(file_name), "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir,
lbs->el_index[index].file_name);
fh = open(file_name, O_RDONLY | O_BINARY, 0644);
if (fh < 0) {
/* file might have been deleted, rebuild index */
el_build_index(lbs, TRUE);
return el_retrieve_attachment(lbs, message_id, n, name);
}
lseek(fh, lbs->el_index[index].offset, SEEK_SET);
i = my_read(fh, message, sizeof(message) - 1);
if (i <= 0) {
close(fh);
return EL_FILE_ERROR;
}
message[i] = 0;
close(fh);
if (strncmp(message, "$@MID@$:", 8) != 0) {
/* file might have been edited, rebuild index */
el_build_index(lbs, TRUE);
return el_retrieve_attachment(lbs, message_id, n, name);
}
/* check for correct ID */
if (atoi(message + 8) != message_id)
return EL_FILE_ERROR;
/* decode message size */
p = strstr(message + 8, "$@MID@$:");
if (p == NULL)
size = strlen(message);
else
size = p - message;
message[size] = 0;
el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
name[0] = 0;
for (i = 0; i <= n; i++) {
if (i == 0)
p = strtok(attachment_all, ",");
else
p = strtok(NULL, ",");
if (p == NULL)
break;
}
if (p)
strlcpy(name, p, MAX_PATH_LENGTH);
return EL_SUCCESS;
}
/*------------------------------------------------------------------*/
int el_submit(LOGBOOK *lbs, int message_id, BOOL bedit, char *date, char attr_name[MAX_N_ATTR][NAME_LENGTH],
char attr_value[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, char *in_reply_to,
char *reply_to, char *encoding, char afilename[MAX_ATTACHMENTS][256], BOOL mark_original,
char *locked_by, char *draft)
/********************************************************************
Routine: el_submit
Purpose: Submit an ELog entry
Input:
LOGBOOK lbs Logbook structure
int message_id Message id
BOOL bedit TRUE for existing message, FALSE for new message
char *date Message date
char attr_name[][] Name of attributes
char attr_value[][] Value of attributes
int n_attr Number of attributes
char *text Message text
char *in_reply_to In reply to this message
char *reply_to Replie(s) to this message
char *encoding Text encoding, either HTML or plain
char *afilename[] File name of attachments
char *tag If given, edit existing message
int *tag_size Maximum size of tag
BOOL mark_original Tag original message for replies
char *locked_by User/Host which locked message for edit
char *draft User which drafted message
Function value:
int New message ID
\********************************************************************/
{
int n, i, j, size, fh, index, tail_size, orig_size, delta, reply_id;
char file_name[MAX_PATH_LENGTH * 3], dir[256], str[NAME_LENGTH], date1[256], attrib[MAX_N_ATTR][NAME_LENGTH],
reply_to1[MAX_REPLY_TO * 10], in_reply_to1[MAX_REPLY_TO * 10], encoding1[80], *message, *p,
*old_text, *buffer, locked_by1[256];
char attachment_all[64 * MAX_ATTACHMENTS], subdir[MAX_PATH_LENGTH];
time_t ltime;
tail_size = orig_size = 0;
buffer = NULL;
message = xmalloc(TEXT_SIZE + 100);
old_text = NULL;
memcpy(attrib, attr_value, sizeof(attrib));
strlcpy(reply_to1, reply_to, sizeof(reply_to1));
strlcpy(in_reply_to1, in_reply_to, sizeof(in_reply_to1));
strlcpy(encoding1, encoding, sizeof(encoding1));
strlcpy(date1, date, sizeof(date1));
if (locked_by)
strlcpy(locked_by1, locked_by, sizeof(locked_by1));
else
locked_by1[0] = 0;
/* generate new file name YYMMDD.log in data directory */
strcpy(dir, lbs->data_dir);
if (bedit) {
/* edit existing message */
for (index = 0; index < *lbs->n_el_index; index++)
if (lbs->el_index[index].message_id == message_id)
break;
if (index == *lbs->n_el_index) {
xfree(message);
return -1;
}
snprintf(file_name, sizeof(file_name), "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir,
lbs->el_index[index].file_name);
fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
if (fh < 0) {
xfree(message);
return -1;
}
lseek(fh, lbs->el_index[index].offset, SEEK_SET);
i = my_read(fh, message, TEXT_SIZE + 100 - 1);
if (i >= 0)
message[i] = 0;
else
message[0] = 0;
/* check for valid message */
if (strncmp(message, "$@MID@$:", 8) != 0) {
close(fh);
xfree(message);
/* file might have been edited, rebuild index */
el_build_index(lbs, TRUE);
return el_submit(lbs, message_id, bedit, date, attr_name, attrib, n_attr, text, in_reply_to,
reply_to, encoding, afilename, mark_original, locked_by, draft);
}
/* check for correct ID */
if (atoi(message + 8) != message_id) {
close(fh);
xfree(message);
return -1;
}
/* decode message size */
p = strstr(message + 8, "$@MID@$:");
if (p == NULL)
size = strlen(message);
else
size = p - message;
message[size] = 0;
if (strcmp(text, "") == 0) {
p = strstr(message, "========================================\n");
/* check for \n -> \r conversion (e.g. zipping/unzipping) */
if (p == NULL)
p = strstr(message, "========================================\r");
if (p) {
p += 41;
old_text = xmalloc(size + 1);
strlcpy(old_text, p, size);
if (old_text[strlen(old_text) - 1] == '\n' || old_text[strlen(old_text) - 1] == '\r')
old_text[strlen(old_text) - 1] = 0;
}
}
if (strieq(date1, ""))
el_decode(message, "Date: ", date1, sizeof(date1));
else
strlcpy(date1, date, sizeof(date1));
if (strieq(locked_by1, ""))
el_decode_intlist(message, "Locked by: ", locked_by1, sizeof(locked_by1));
if (strieq(reply_to1, ""))
el_decode_intlist(message, "Reply to: ", reply_to1, sizeof(reply_to1));
if (strieq(in_reply_to1, ""))
el_decode_int(message, "In reply to: ", in_reply_to1, sizeof(in_reply_to1));
if (strieq(encoding1, ""))
el_decode(message, "Encoding: ", encoding1, sizeof(encoding1));
el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
for (i = 0; i < n_attr; i++) {
sprintf(str, "%s: ", attr_name[i]);
if (strieq(attrib[i], ""))
el_decode(message, str, attrib[i], NAME_LENGTH);
}
/* buffer tail of logfile */
lseek(fh, 0, SEEK_END);
orig_size = size;
tail_size = TELL(fh) - (lbs->el_index[index].offset + size);
if (tail_size > 0) {
buffer = xmalloc(tail_size);
lseek(fh, lbs->el_index[index].offset + size, SEEK_SET);
n = my_read(fh, buffer, tail_size);
}
lseek(fh, lbs->el_index[index].offset, SEEK_SET);
} else {
/* create new message */
if (!date[0]) {
get_rfc2822_date(date1, sizeof(date1), 0);
ltime = date_to_ltime(date1);
} else {
ltime = date_to_ltime(date);
get_rfc2822_date(date1, sizeof(date1), ltime);
}
for (i = 0; i < 12; i++)
if (strncmp(date1 + 8, mname[i], 3) == 0)
break;
snprintf(file_name, sizeof(file_name), "%c%c%02d%c%ca.log", date1[14], date1[15], i + 1, date1[5], date1[6]);
generate_subdir_name(file_name, subdir, sizeof(subdir));
sprintf(str, "%s%s", dir, subdir);
if (strlen(str) > 0 && str[strlen(str) - 1] == DIR_SEPARATOR)
str[strlen(str) - 1] = 0;
#ifdef OS_WINNT
mkdir(str);
#else
mkdir(str, 0755);
#endif
sprintf(str, "%s%s%s", dir, subdir, file_name);
fh = open(str, O_CREAT | O_RDWR | O_BINARY, 0644);
if (fh < 0) {
xfree(message);
if (old_text)
xfree(old_text);
return -1;
}
lseek(fh, 0, SEEK_END);
/* new message id is old plus one */
if (message_id == 0) {
message_id = 1;
for (i = 0; i < *lbs->n_el_index; i++)
if (lbs->el_index[i].message_id >= message_id)
message_id = lbs->el_index[i].message_id + 1;
}
/* enter message in index */
index = *lbs->n_el_index;
(*lbs->n_el_index)++;
lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index));
lbs->el_index[index].message_id = message_id;
strlcpy(lbs->el_index[index].file_name, file_name, sizeof(lbs->el_index[index].file_name));
strlcpy(lbs->el_index[index].subdir, subdir, sizeof(lbs->el_index[index].subdir));
lbs->el_index[index].file_time = ltime;
lbs->el_index[index].offset = TELL(fh);
lbs->el_index[index].in_reply_to = atoi(in_reply_to1);
/* if index not ordered, sort it */
i = *lbs->n_el_index;
if (i > 1 && lbs->el_index[i - 1].file_time < lbs->el_index[i - 2].file_time) {
qsort(lbs->el_index, i, sizeof(EL_INDEX), eli_compare);
/* search message again, index could have been changed by sorting */
for (index = 0; index < *lbs->n_el_index; index++)
if (lbs->el_index[index].message_id == message_id)
break;
}
/* if other logbook has same index, update pointers */
for (i = 0; lb_list[i].name[0]; i++)
if (&lb_list[i] != lbs && lb_list[i].n_el_index == lbs->n_el_index)
lb_list[i].el_index = lbs->el_index;
}
/* compose message */
sprintf(message, "$@MID@$: %d\n", message_id);
sprintf(message + strlen(message), "Date: %s\n", date1);
if (reply_to1[0])
sprintf(message + strlen(message), "Reply to: %s\n", reply_to1);
if (in_reply_to1[0])
sprintf(message + strlen(message), "In reply to: %s\n", in_reply_to1);
for (i = 0; i < n_attr; i++)
sprintf(message + strlen(message), "%s: %s\n", attr_name[i], attrib[i]);
sprintf(message + strlen(message), "Attachment: ");
if (afilename) {
sprintf(message + strlen(message), "%s", afilename[0]);
for (i = 1; i < MAX_ATTACHMENTS; i++)
if (afilename[i][0])
sprintf(message + strlen(message), ",%s", afilename[i]);
}
sprintf(message + strlen(message), "\n");
sprintf(message + strlen(message), "Encoding: %s\n", encoding1);
if (locked_by1[0])
sprintf(message + strlen(message), "Locked by: %s\n", locked_by1);
if (draft && draft[0])
sprintf(message + strlen(message), "Draft: %s\n", draft);
sprintf(message + strlen(message), "========================================\n");
if (strieq(text, "") && old_text)
strlcat(message, old_text, TEXT_SIZE + 100);
else
strlcat(message, text, TEXT_SIZE + 100);
strlcat(message, "\n", TEXT_SIZE + 100);
if (old_text)
xfree(old_text);
n = write(fh, message, strlen(message));
if (n != (int) strlen(message)) {
if (tail_size > 0)
xfree(buffer);
close(fh);
return -1;
}
/* update MD5 checksum */
MD5_checksum(message, strlen(message), lbs->el_index[index].md5_digest);
if (bedit) {
if (tail_size > 0) {
n = write(fh, buffer, tail_size);
xfree(buffer);
/* correct offsets for remaining messages in same file */
delta = strlen(message) - orig_size;
for (i = 0; i < *lbs->n_el_index; i++)
if (lbs->el_index[i].message_id == message_id)
break;
for (j = i + 1; j < *lbs->n_el_index && strieq(lbs->el_index[i].file_name,
lbs->el_index[j].file_name); j++)
lbs->el_index[j].offset += delta;
}
/* truncate file here */
TRUNCATE(fh);
}
close(fh);
/* if reply, mark original message */
reply_id = atoi(in_reply_to);
if (mark_original && in_reply_to[0] && !bedit && atoi(in_reply_to) > 0) {
char date[80], attr[MAX_N_ATTR][NAME_LENGTH], enc[80], att[MAX_ATTACHMENTS][256],
reply_to[MAX_REPLY_TO * 10], in_reply_to[MAX_REPLY_TO * 10], lock[256], draft[256];
/* retrieve original message */
size = TEXT_SIZE + 100;
el_retrieve(lbs, reply_id, date, attr_list, attr, n_attr, message, &size, in_reply_to, reply_to, att,
enc, lock, draft);
if (reply_to[0])
strcat(reply_to, ", ");
sprintf(reply_to + strlen(reply_to), "%d", message_id);
/* write modified message */
el_submit(lbs, reply_id, TRUE, date, attr_list, attr, n_attr, message, in_reply_to, reply_to, enc, att,
TRUE, lock, draft);
}
xfree(message);
return message_id;
}
/*------------------------------------------------------------------*/
void remove_reference(LOGBOOK *lbs, int message_id, int remove_id, BOOL reply_to_flag) {
char date[80], attr[MAX_N_ATTR][NAME_LENGTH], enc[80], in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
att[MAX_ATTACHMENTS][256], lock[256], draft[256], *p, *ps, *message;
int size, status;
/* retrieve original message */
size = TEXT_SIZE + 1000;
message = (char *) xmalloc(size);
status = el_retrieve(lbs, message_id, date, attr_list, attr, lbs->n_attr, message, &size, in_reply_to,
reply_to, att, enc, lock, draft);
if (status != EL_SUCCESS)
return;
if (reply_to_flag)
p = reply_to;
else
p = in_reply_to;
while (*p) {
while (*p && (*p == ',' || *p == ' '))
p++;
ps = p;
while (isdigit(*ps))
ps++;
while (*ps && (*ps == ',' || *ps == ' '))
ps++;
if (atoi(p) == remove_id)
strcpy(p, ps);
else
while (isdigit(*p))
p++;
}
/* write modified message */
el_submit(lbs, message_id, TRUE, date, attr_list, attr, lbs->n_attr, message, in_reply_to, reply_to, enc,
att, TRUE, lock, NULL);
xfree(message);
}
/*------------------------------------------------------------------*/
int el_delete_message(LOGBOOK *lbs, int message_id, BOOL delete_attachments,
char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], BOOL delete_bw_ref,
BOOL delete_reply_to)
/********************************************************************
Routine: el_delete_message
Purpose: Delete an ELog entry including attachments
Input:
LOGBOOK *lbs Pointer to logbook structure
int message_id Message ID
BOOL delete_attachments Delete attachments if TRUE
char attachment Used to return attachments (on move)
BOOL delete_bw_ref If true, delete backward references
BOOL delete_reply_to If true, delete replies to this message
Output:
Function value:
EL_SUCCESS Successful completion
\********************************************************************/
{
int i, index, size, fh, tail_size, old_offset;
char str[MAX_PATH_LENGTH], file_name[MAX_PATH_LENGTH * 3], reply_to[MAX_REPLY_TO * 10], in_reply_to[256];
char *buffer, *p;
char *message, attachment_all[64 * MAX_ATTACHMENTS];
char attrib[MAX_N_ATTR][NAME_LENGTH];
for (index = 0; index < *lbs->n_el_index; index++)
if (lbs->el_index[index].message_id == message_id)
break;
if (index == *lbs->n_el_index)
return -1;
snprintf(file_name, sizeof(file_name), "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir,
lbs->el_index[index].file_name);
fh = open(file_name, O_RDWR | O_BINARY, 0644);
if (fh < 0)
return EL_FILE_ERROR;
message = xmalloc(TEXT_SIZE + 1000);
lseek(fh, lbs->el_index[index].offset, SEEK_SET);
i = my_read(fh, message, TEXT_SIZE + 1000 - 1);
if (i <= 0) {
xfree(message);
close(fh);
return EL_FILE_ERROR;
}
if (_logging_level > 1) {
sprintf(str, "DELETE entry #%d", message_id);
write_logfile(lbs, str);
}
message[i] = 0;
if (strncmp(message, "$@MID@$:", 8) != 0) {
close(fh);
xfree(message);
/* file might have been edited, rebuild index */
el_build_index(lbs, TRUE);
return el_delete_message(lbs, message_id, delete_attachments, attachment, delete_bw_ref,
delete_reply_to);
}
/* check for correct ID */
if (atoi(message + 8) != message_id) {
close(fh);
xfree(message);
return EL_FILE_ERROR;
}
/* decode message size */
p = strstr(message + 8, "$@MID@$:");
if (p == NULL)
size = strlen(message);
else
size = p - message;
message[size] = 0;
/* delete attachments */
el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
for (i = 0; i < MAX_ATTACHMENTS; i++) {
if (i == 0)
p = strtok(attachment_all, ",");
else
p = strtok(NULL, ",");
if (attachment != NULL) {
if (attachment[i][0] && p) {
/* delete old attachment if new one exists */
el_delete_attachment(lbs, p);
}
/* return old attachment if no new one */
if (!attachment[i][0] && p)
strcpy(attachment[i], p);
}
if (delete_attachments && p)
el_delete_attachment(lbs, p);
}
/* decode references */
el_decode_intlist(message, "Reply to: ", reply_to, sizeof(reply_to));
el_decode_int(message, "In reply to: ", in_reply_to, sizeof(in_reply_to));
/* decoded attributes */
for (i = 0;; i++) {
el_enum_attr(message, i, attr_list[i], attrib[i]);
if (!attr_list[i][0])
break;
}
/* buffer tail of logfile */
lseek(fh, 0, SEEK_END);
tail_size = TELL(fh) - (lbs->el_index[index].offset + size);
buffer = NULL;
if (tail_size > 0) {
buffer = xmalloc(tail_size);
lseek(fh, lbs->el_index[index].offset + size, SEEK_SET);
my_read(fh, buffer, tail_size);
}
lseek(fh, lbs->el_index[index].offset, SEEK_SET);
if (tail_size > 0) {
write(fh, buffer, tail_size);
xfree(buffer);
}
/* truncate file here */
TRUNCATE(fh);
/* if file length gets zero, delete file */
tail_size = lseek(fh, 0, SEEK_END);
close(fh);
xfree(message);
if (tail_size == 0)
remove(file_name);
/* remove message from index */
strcpy(str, lbs->el_index[index].file_name);
old_offset = lbs->el_index[index].offset;
for (i = index; i < *lbs->n_el_index - 1; i++)
memcpy(&lbs->el_index[i], &lbs->el_index[i + 1], sizeof(EL_INDEX));
(*lbs->n_el_index)--;
if (*lbs->n_el_index > 0)
lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index));
/* correct all offsets after deleted message */
for (i = 0; i < *lbs->n_el_index; i++)
if (strieq(lbs->el_index[i].file_name, str) && lbs->el_index[i].offset > old_offset)
lbs->el_index[i].offset -= size;
/* if other logbook has same index, update pointers */
for (i = 0; lb_list[i].name[0]; i++)
if (&lb_list[i] != lbs && lb_list[i].n_el_index == lbs->n_el_index)
lb_list[i].el_index = lbs->el_index;
/* delete also replies to this message */
if (delete_reply_to && reply_to[0]) {
p = reply_to;
if (isdigit(*p))
do {
if (atoi(p))
el_delete_message(lbs, atoi(p), TRUE, NULL, FALSE, TRUE);
while (*p && isdigit(*p))
p++;
while (*p && (*p == ',' || *p == ' '))
p++;
} while (*p);
}
/* delete backward references */
if (in_reply_to[0] && delete_bw_ref) {
p = in_reply_to;
do {
if (atoi(p))
remove_reference(lbs, atoi(p), message_id, TRUE);
while (*p && isdigit(*p))
p++;
while (*p && (*p == ',' || *p == ' '))
p++;
} while (*p);
}
/* execute shell if requested */
if (getcfg(lbs->name, "Execute delete", str, sizeof(str)))
execute_shell(lbs, message_id, attrib, NULL, str);
return EL_SUCCESS;
}
/*------------------------------------------------------------------*/
int el_correct_links(LOGBOOK *lbs, int old_id, int new_id)
/* If a message gets resubmitted, the links to that message are wrong.
This routine corrects that. */
{
int i, i1, n, n1, size;
char date[80], *attrib, *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10], encoding[80],
locked_by[256], draft[256];
char list[MAX_N_ATTR][NAME_LENGTH], list1[MAX_N_ATTR][NAME_LENGTH];
char *att_file;
attrib = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH);
text = (char *) xmalloc(TEXT_SIZE);
att_file = (char *) xmalloc(MAX_ATTACHMENTS * 256);
el_retrieve(lbs, new_id, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, NULL, 0, in_reply_to,
reply_to, (char (*)[256]) att_file, encoding, locked_by, draft);
/* go through in_reply_to list */
n = strbreak(in_reply_to, list, MAX_N_ATTR, ",", FALSE);
for (i = 0; i < n; i++) {
size = TEXT_SIZE;
el_retrieve(lbs, atoi(list[i]), date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text, &size,
in_reply_to, reply_to, (char (*)[256]) att_file, encoding, locked_by, draft);
n1 = strbreak(reply_to, list1, MAX_N_ATTR, ",", FALSE);
reply_to[0] = 0;
for (i1 = 0; i1 < n1; i1++) {
/* replace old ID by new ID */
if (atoi(list1[i1]) == old_id)
sprintf(reply_to + strlen(reply_to), "%d", new_id);
else
strcat(reply_to, list1[i1]);
if (i1 < n1 - 1)
strcat(reply_to, ", ");
}
el_submit(lbs, atoi(list[i]), TRUE, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text,
in_reply_to, reply_to, encoding, (char (*)[256]) att_file, TRUE, locked_by, draft);
}
el_retrieve(lbs, new_id, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, NULL, 0, in_reply_to,
reply_to, (char (*)[256]) att_file, encoding, locked_by, draft);
/* go through reply_to list */
n = strbreak(reply_to, list, MAX_N_ATTR, ",", FALSE);
for (i = 0; i < n; i++) {
size = sizeof(text);
el_retrieve(lbs, atoi(list[i]), date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text, &size,
in_reply_to, reply_to, (char (*)[256]) att_file, encoding, locked_by, draft);
n1 = strbreak(in_reply_to, list1, MAX_N_ATTR, ",", FALSE);
in_reply_to[0] = 0;
for (i1 = 0; i1 < n1; i1++) {
/* replace old ID by new ID */
if (atoi(list1[i1]) == old_id)
sprintf(in_reply_to + strlen(in_reply_to), "%d", new_id);
else
strcat(in_reply_to, list1[i1]);
if (i1 < n1 - 1)
strcat(in_reply_to, ", ");
}
el_submit(lbs, atoi(list[i]), TRUE, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text,
in_reply_to, reply_to, encoding, (char (*)[256]) att_file, TRUE, locked_by, draft);
}
xfree(text);
xfree(attrib);
xfree(att_file);
return EL_SUCCESS;
}
/*------------------------------------------------------------------*/
int el_move_message_thread(LOGBOOK *lbs, int message_id) {
int i, n, size, new_id;
char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
encoding[80], locked_by[256], draft[256];
char list[MAX_N_ATTR][NAME_LENGTH], str[256];
char att_file[MAX_ATTACHMENTS][256];
/* retrieve message */
text = xmalloc(TEXT_SIZE);
size = TEXT_SIZE;
el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to,
att_file, encoding, locked_by, draft);
/* submit as new message */
date[0] = 0;
new_id = el_submit(lbs, 0, FALSE, date, attr_list, attrib, lbs->n_attr, text, in_reply_to, reply_to,
encoding, att_file, FALSE, locked_by, draft);
xfree(text);
/* correct links */
el_correct_links(lbs, message_id, new_id);
/* delete original message */
el_delete_message(lbs, message_id, FALSE, NULL, FALSE, FALSE);
/* move all replies recursively */
if (getcfg(lbs->name, "Resubmit replies", str, sizeof(str)) && atoi(str) == 1) {
if (reply_to[0]) {
n = strbreak(reply_to, list, MAX_N_ATTR, ",", FALSE);
for (i = 0; i < n; i++)
el_move_message_thread(lbs, atoi(list[i]));
}
}
return new_id;
}
/*------------------------------------------------------------------*/
int el_move_message(LOGBOOK *lbs, int old_id, int new_id) {
int status, size;
char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
encoding[80], locked_by[256], draft[256], att_file[MAX_ATTACHMENTS][256];
/* retrieve message */
text = xmalloc(TEXT_SIZE);
size = TEXT_SIZE;
status = el_retrieve(lbs, old_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to,
reply_to, att_file, encoding, locked_by, draft);
if (status != EL_SUCCESS)
return 0;
/* submit as new message */
status = el_submit(lbs, new_id, FALSE, date, attr_list, attrib, lbs->n_attr, text, in_reply_to, reply_to,
encoding, att_file, FALSE, locked_by, draft);
xfree(text);
if (status != new_id)
return 0;
/* correct links */
el_correct_links(lbs, old_id, new_id);
/* delete original message */
el_delete_message(lbs, old_id, FALSE, NULL, FALSE, FALSE);
return 1;
}
/*------------------------------------------------------------------*/
int el_lock_message(LOGBOOK *lbs, int message_id, char *user, BOOL lock)
/* lock message for editing */
{
int size;
char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], text[TEXT_SIZE], in_reply_to[80],
reply_to[MAX_REPLY_TO * 10], encoding[80], locked_by[256], draft[256];
char att_file[MAX_ATTACHMENTS][256];
/* retrieve message */
size = sizeof(text);
el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to,
att_file, encoding, locked_by, draft);
/* submit message, unlocked if block == FALSE */
el_submit(lbs, message_id, TRUE, date, attr_list, attrib, lbs->n_attr, text, in_reply_to, reply_to,
encoding, att_file, FALSE, lock ? user : NULL, draft);
return EL_SUCCESS;
}
/*------------------------------------------------------------------*/
int el_draft_message(LOGBOOK *lbs, int message_id, char *user, BOOL bdraft)
/* lock message for editing */
{
int size;
char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], text[TEXT_SIZE], in_reply_to[80],
reply_to[MAX_REPLY_TO * 10], encoding[80], locked_by[256], draft[256];
char att_file[MAX_ATTACHMENTS][256];
/* retrieve message */
size = sizeof(text);
el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to,
att_file, encoding, locked_by, draft);
/* submit message, undraft if bdraft == FALSE */
el_submit(lbs, message_id, TRUE, date, attr_list, attrib, lbs->n_attr, text, in_reply_to, reply_to,
encoding, att_file, FALSE, locked_by, bdraft ? user : NULL);
return EL_SUCCESS;
}
/*------------------------------------------------------------------*/
void write_logfile(LOGBOOK *lbs, const char *text) {
char file_name[MAX_PATH_LENGTH];
char str[MAX_PATH_LENGTH], unm[256];
int fh;
time_t now;
char buf[10000];
if (lbs == NULL) {
if (!getcfg("global", "logfile", str, sizeof(str)))
return;
} else if (!getcfg(lbs->name, "logfile", str, sizeof(str)))
return;
if (str[0] == DIR_SEPARATOR || str[1] == ':')
strlcpy(file_name, str, sizeof(file_name));
else {
strlcpy(file_name, logbook_dir, sizeof(file_name));
strlcat(file_name, str, sizeof(file_name));
}
fh = open(file_name, O_RDWR | O_BINARY | O_CREAT | O_APPEND, 0644);
if (fh < 0)
return;
now = time(0);
strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", localtime(&now));
strcat(buf, " ");
if (isparam("unm") && rem_host[0]) {
strlcpy(unm, getparam("unm"), sizeof(unm));
if (rem_host_ip[0])
sprintf(buf + strlen(buf), "[%s@%s(%s)] ", unm, rem_host, rem_host_ip);
else
sprintf(buf + strlen(buf), "[%s@%s] ", unm, rem_host);
} else if (rem_host[0]) {
if (rem_host_ip[0])
sprintf(buf + strlen(buf), "[%s(%s)] ", rem_host, rem_host_ip);
else
sprintf(buf + strlen(buf), "[%s] ", rem_host);
} else
sprintf(buf + strlen(buf), "[%s] ", rem_host_ip);
if (lbs)
sprintf(buf + strlen(buf), "{%s} ", lbs->name);
strlcat(buf, text, sizeof(buf) - 1);
#ifdef OS_WINNT
if (strlen(buf) > 0 && buf[strlen(buf) - 1] != '\n')
strlcat(buf, "\r\n", sizeof(buf));
else if (strlen(buf) > 1 && buf[strlen(buf) - 2] != '\r')
strlcpy(buf + strlen(buf) - 2, "\r\n", sizeof(buf) - (strlen(buf) - 2));
#else
if (strlen(buf) > 1 && buf[strlen(buf) - 1] != '\n')
strlcat(buf, "\n", sizeof(buf));
#endif
write(fh, buf, strlen(buf));
close(fh);
}
/*------------------------------------------------------------------*/
/*
void logd(const char *format, ...)
{
va_list argptr;
char str[10000];
FILE *f;
time_t now;
char buf[1000];
va_start(argptr, format);
vsprintf(str, (char *) format, argptr);
va_end(argptr);
f = fopen("c:\\tmp\\elogd.log", "a");
if (!f)
return;
now = time(0);
strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", localtime(&now));
strcat(buf, " ");
strlcat(buf, str, sizeof(buf));
if (buf[strlen(buf) - 1] != '\n')
strlcat(buf, "\n", sizeof(buf));
fprintf(f, buf);
fclose(f);
}
*/
/*------------------------------------------------------------------*/
char *html_tags[] = {"", "", "", "
", "
", ""};
int is_html(char *s) {
char *str, *p;
int i;
str = xstrdup(s);
for (i = 0; i < (int) strlen(s); i++)
str[i] = toupper(s[i]);
str[i] = 0;
for (i = 0; html_tags[i][0]; i++) {
p = strstr(str, html_tags[i]);
if (p && strchr(p, '>') && (p == str || (p > str && *(p - 1) != '\\'))) {
xfree(str);
return TRUE;
}
}
if (strstr(str, "") && strchr(strstr(str, ""), ';')) {
xfree(str);
return TRUE;
}
xfree(str);
return FALSE;
}
/*------------------------------------------------------------------*/
int html_allowed(LOGBOOK *lbs) {
char str[80];
return (getcfg(lbs->name, "Allow HTML", str, sizeof(str)) && atoi(str) == 1);
}
/*------------------------------------------------------------------*/
char *script_tags[] = {"onerror", "onabort", "onchange", "onclick", "ondblclick", "onfocus", "onkeydown",
"onkeyup", "onload", "onmousedonw", "onmousemove", "onmouseover", "onmouseup",
"onreset", "onselect", "onsubmit", "onunload", "javascript", NULL
};
int is_script(char *s) {
char *str;
int i;
str = xstrdup(s);
for (i = 0; i < (int) strlen(s); i++)
str[i] = tolower(s[i]);
str[i] = 0;
for (i = 0; script_tags[i] != NULL; i++) {
if (strstr(str, script_tags[i])) {
xfree(str);
return TRUE;
}
}
xfree(str);
return FALSE;
}
/*------------------------------------------------------------------*/
char *full_html_tags[] = {"", "", "", ""};
int is_full_html(char *file_name) {
char *str, *p;
int i, fh, length;
unsigned char *buf;
fh = open(file_name, O_RDONLY | O_BINARY);
if (fh < 0)
return FALSE;
lseek(fh, 0, SEEK_END);
length = TELL(fh);
lseek(fh, 0, SEEK_SET);
if (length > 1000)
length = 1000;
buf = xmalloc(length);
read(fh, buf, length);
close(fh);
str = xstrdup((char *) buf);
for (i = 0; i < (int) strlen((char *) buf); i++)
str[i] = toupper(buf[i]);
str[i] = 0;
xfree(buf);
for (i = 0; full_html_tags[i][0]; i++) {
p = strstr(str, full_html_tags[i]);
if (p && strchr(p, '>') && (p == str || (p > str && *(p - 1) != '\\'))) {
xfree(str);
return TRUE;
}
}
xfree(str);
return FALSE;
}
/*------------------------------------------------------------------*/
int is_ascii(char *file_name) {
int i, fh, length;
unsigned char *buf;
fh = open(file_name, O_RDONLY | O_BINARY);
if (fh < 0)
return FALSE;
lseek(fh, 0, SEEK_END);
length = TELL(fh);
lseek(fh, 0, SEEK_SET);
if (length > 1000)
length = 1000;
buf = xmalloc(length);
read(fh, buf, length);
close(fh);
for (i = 0; i < length; i++) {
if (buf[i] < 32 && buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t') {
xfree(buf);
return FALSE;
}
}
xfree(buf);
return TRUE;
}
/*------------------------------------------------------------------*/
int is_image(char *att) {
return (stristr(att, ".GIF") != NULL) || (stristr(att, ".JPG") != NULL) || (stristr(att, ".JPEG") != NULL)
|| (stristr(att, ".PNG") != NULL) || (stristr(att, ".SVG") != NULL);
}
/*------------------------------------------------------------------*/
void strip_html(char *s) {
char *p;
while ((p = strchr(s, '<')) != NULL) {
if (strchr(p, '>'))
memmove(p, strchr(p, '>') + 1, strlen(strchr(p, '>') + 1) + 1);
else
*p = 0;
}
}
/*------------------------------------------------------------------*/
int line_break(char *str, char *encoding) {
if (strieq(encoding, "plain") || strieq(encoding, "ELCode")) {
return str[0] == '\n';
}
// HTML encoding
if (strncmp(str, "
", 4) == 0 || strncmp(str, "
", 4) == 0 || strncmp(str, "
", 6) == 0)
return 1;
return 0;
}
/*------------------------------------------------------------------*/
void insert_breaks(char *str, int n, int size) {
int i, j, i_last;
i_last = 0;
for (i = 0; i < (int) strlen(str); i++) {
if (str[i] == '\r')
i_last = i;
/* if more than n chars without return, insert one */
if (i - i_last >= n && (int) strlen(str) + 3 < size) {
/* find previous blank */
while (i > i_last && str[i] != ' ')
i--;
if (str[i] == ' ')
i++;
/* move trailing string one char further */
for (j = strlen(str) + 2; j > i; j--)
str[j] = str[j - 2];
/* set CR */
str[i++] = '\r';
str[i++] = '\n';
i_last = i;
}
}
}
/*------------------------------------------------------------------*/
void replace_inline_img(LOGBOOK *lbs, char *str) {
char *p, *pn, *pa, old[256], link[256], base_url[256], domain[256];
int index;
p = str;
do {
p = strstr(p, "')
pn++;
if (*pn == '>')
pn++;
retrieve_domain(domain, sizeof(domain));
sprintf(p, "", index, domain);
memmove(p + strlen(p), pn, strlen(pn) + 1);
/* now change href to absolute link */
pa = p - 1;
while (pa > str && *pa != '<')
// search ''
pa--;
pn = strstr(pa, "href=");
if (pn && pn - pa < 10) {
strlcpy(old, pn + 6, sizeof(old));
if (strchr(old, '\"'))
*strchr(old, '\"') = 0;
compose_base_url(lbs, base_url, sizeof(base_url), FALSE);
strlcpy(link, base_url, sizeof(link));
strlcat(link, old, sizeof(link));
if (strchr(link, '?'))
*strchr(link, '?') = 0;
strsubst(pn + 6, TEXT_SIZE, old, link);
if (strlen(link) > strlen(old))
p += strlen(link) - strlen(old);
}
p++;
} else
p++;
}
} while (p != NULL);
}
/*------------------------------------------------------------------*/
void convert_elog_link(LOGBOOK *lbs, char *link, char *link_text, char *result, int absolute_link,
int message_id) {
char str[256], base_url[256];
int i;
strlcpy(str, link, sizeof(str));
if (strchr(str, '/'))
*strchr(str, '/') = 0;
for (i = 0; i < (int) strlen(str); i++)
if (!isdigit(str[i]))
break;
if (i < (int) strlen(str)) {
/* if link contains reference to other logbook, put logbook explicitly */
if (absolute_link)
compose_base_url(NULL, base_url, sizeof(base_url), FALSE);
else
strcpy(base_url, "../");
sprintf(result, "elog:%s", base_url, link, link_text);
} else if (link[0] == '/') {
if (absolute_link)
compose_base_url(NULL, base_url, sizeof(base_url), FALSE);
else
base_url[0] = 0;
sprintf(result, "elog:%s", base_url, lbs->name_enc, message_id, link,
link_text);
} else {
if (absolute_link)
compose_base_url(lbs, base_url, sizeof(base_url), FALSE);
else
base_url[0] = 0;
sprintf(result, "elog:%s", base_url, link, link_text);
}
}
/*------------------------------------------------------------------*/
void rsputs(const char *str) {
while (strlen_retbuf + (int) strlen(str) + 1 >= return_buffer_size) {
return_buffer = xrealloc(return_buffer, return_buffer_size + (int) strlen(str) + 100000);
memset(return_buffer + return_buffer_size, 0, (int) strlen(str) + 100000);
return_buffer_size += (int) strlen(str) + 100000;
}
strcpy(return_buffer + strlen_retbuf, str);
strlen_retbuf += strlen(str);
}
/*------------------------------------------------------------------*/
char *key_list[] = {"http://", "https://", "ftp://", "mailto:", "elog:", "file://", ""};
void rsputs2(LOGBOOK *lbs, int absolute_link, const char *str) {
int i, j, k, l, n;
char *p, *pd, link[1000], link_text[1000];
while (strlen_retbuf + (int) (2 * strlen(str) + 1000) >= return_buffer_size) {
return_buffer = xrealloc(return_buffer, return_buffer_size + 100000);
memset(return_buffer + return_buffer_size, 0, 100000);
return_buffer_size += 100000;
}
j = strlen_retbuf;
for (i = 0; i < (int) strlen(str); i++) {
for (l = 0; key_list[l][0]; l++) {
if (strncmp(str + i, key_list[l], strlen(key_list[l])) == 0) {
/* check for escape character */
if (i > 0 && *(str + i - 1) == '\\') {
j--;
*(return_buffer + j) = 0;
continue;
}
p = (char *) (str + i + strlen(key_list[l]));
i += strlen(key_list[l]);
for (k = 0; *p && strcspn(p, " \t\n\r({[)}]\"") && k < (int) sizeof(link); k++, i++)
link[k] = *p++;
link[k] = 0;
i--;
/* link may not end with a '.'/',' (like in a sentence) */
if (link[k - 1] == '.' || link[k - 1] == ',') {
link[k - 1] = 0;
k--;
i--;
}
/* check if link contains coloring */
p = strchr(link, '\001');
if (p != NULL) {
strlcpy(link_text, link, sizeof(link_text));
/* skip everything between '<' and '>' */
pd = p;
while (*pd && *pd != '\002')
*p = *pd++;
memmove(p, pd + 1, strlen(pd + 1) + 1);
/* skip '' */
p = strchr(link, '\001');
if (p != NULL) {
pd = p;
while (*pd && *pd != '\002')
*p = *pd++;
memmove(p, pd + 1, strlen(pd + 1) + 1);
}
/* correct link text */
for (n = 0; n < (int) strlen(link_text); n++) {
switch (link_text[n]) {
/* the translation for the search highliting */
case '\001':
link_text[n] = '<';
break;
case '\002':
link_text[n] = '>';
break;
case '\003':
link_text[n] = '\"';
break;
case '\004':
link_text[n] = ' ';
break;
}
}
} else
strlcpy(link_text, link, sizeof(link_text));
if (strcmp(key_list[l], "elog:") == 0) {
convert_elog_link(lbs, link, link_text, return_buffer + j, absolute_link, _current_message_id);
} else if (strcmp(key_list[l], "mailto:") == 0) {
sprintf(return_buffer + j, "%s", link, link_text);
} else {
sprintf(return_buffer + j, "%s", key_list[l]);
j += strlen(return_buffer + j);
strlen_retbuf = j;
/* link_text can contain special characters */
rsputs2(lbs, absolute_link, link_text);
j = strlen_retbuf;
sprintf(return_buffer + j, "");
}
j += strlen(return_buffer + j);
break;
}
}
if (!key_list[l][0]) {
if (strncmp(str + i, "
", 4) == 0) {
strcpy(return_buffer + j, "
");
j += 4;
i += 3;
} else
switch (str[i]) {
case '&':
strcat(return_buffer, "&");
j += 5;
break;
case '<':
strcat(return_buffer, "<");
j += 4;
break;
case '>':
strcat(return_buffer, ">");
j += 4;
break;
/* suppress escape character '\' in front of HTML or ELCode tag */
case '\\':
if (str[i + 1] != '<' && str[i + 1] != '[')
return_buffer[j++] = str[i];
break;
/* the translation for the search highliting */
case '\001':
strcat(return_buffer, "<");
j++;
break;
case '\002':
strcat(return_buffer, ">");
j++;
break;
case '\003':
strcat(return_buffer, "\"");
j++;
break;
case '\004':
strcat(return_buffer, " ");
j++;
break;
default:
return_buffer[j++] = str[i];
}
}
}
return_buffer[j] = 0;
strlen_retbuf = j;
}
/*------------------------------------------------------------------*/
void rsputs3(const char *text) {
int i;
char str[2];
str[1] = 0;
for (i = 0; i < (int) strlen(text); i++) {
switch (text[i]) {
case '<':
rsputs("<");
break;
case '>':
rsputs(">");
break;
case '&':
rsputs("&");
break;
case '\"':
rsputs(""");
break;
default:
str[0] = text[i];
rsputs(str);
}
}
}
/*------------------------------------------------------------------*/
typedef struct {
char *pattern;
char *subst;
} PATTERN_LIST;
PATTERN_LIST pattern_list[] = {
/* smileys */
{":))", ""},
{":-))", ""},
{":)", ""},
{":-)", ""},
{":(", ""},
{":-(", ""},
{";)", ""},
{";-)", ""},
{":d", ""},
{"?-)", ""},
{";(", ""},
{";-(", ""},
{":]", ""},
{":-]", ""},
{":o", ""},
{":-o", ""},
{"8-)", ""},
{"8o", ""},
{"x-(", ""},
{":p", ""},
{":-p", ""},
/* formatting */
{"[b]", ""},
{"[/b]", ""},
{"[u]", ""},
{"[/u]", ""},
{"[i]", ""},
{"[/i]", ""},
{"[center]", ""},
{"[/center]", ""},
{"[color=", ""},
{"[/color]", ""},
{"[size=", ""},
{"[/size]", ""},
{"[font=", ""},
{"[/font]", ""},
{"\r\n[code]", ""},
{"[code]", ""},
{"[/code]\r\n", "
"},
{"[/code]", "
"},
{"\r\n[code1]", ""},
{"[code1]", ""},
{"[/code1]\r\n", "
"},
{"[/code1]", "
"},
/* lists */
{"[list]\r", ""},
{"[list]", ""},
{"[*]", "- "},
{"[/list]\r", "#>"}, // either
or
{"[/list]", "#>"},
{"[list=", ""},
/* headings */
{"[h1]", ""},
{"[/h1]", "
"},
{"[h2]", ""},
{"[/h2]", "
"},
{"[h3]", ""},
{"[/h3]", "
"},
/* URLs */
{"[url=", "%s"},
{"[url]", "%s"},
{"[/url]", ""},
{"[email]", "%s"},
{"[/email]", ""},
{"[img]", ""},
{"[/img]", ""},
/* quote */
{"[quote=",
"
%s: |
"},
{"[quote]",
"
\r\n"},
{"[/quote]", " |
\r\n"},
/* table */
{"[table]", ""},
{"[table ", ""},
{"|-", " | "},
{"|", " | "},
{"[/table]", " | "},
/* horizontal line */
{"[line]", " "},
/* anchor */
{"[anchor]", ""},
{"[/anchor]", ""},
{"", ""}
};
char
*email_quote_table =
"%s: | ";
void rsputs_elcode(LOGBOOK *lbs, BOOL email_notify, const char *str) {
int i, j, k, l, m, elcode_disabled, elcode_disabled1, ordered_list, substituted, inside_table,
smileys_enabled;
char *p, *pd, link[1000], link_text[1000], tmp[1000], attrib[1000], hattrib[1000], value[1000],
subst[1000], base_url[256], param[256], *lstr, domain[256];
while (strlen_retbuf + (int) (2 * strlen(str) + 1000) >= return_buffer_size) {
return_buffer = xrealloc(return_buffer, return_buffer_size + 100000);
memset(return_buffer + return_buffer_size, 0, 100000);
return_buffer_size += 100000;
}
elcode_disabled = FALSE;
elcode_disabled1 = FALSE;
ordered_list = FALSE;
smileys_enabled = TRUE;
inside_table = 0;
j = strlen_retbuf;
m = 0;
/* check for smileys */
if (getcfg(lbs->name, "Enable smileys", tmp, sizeof(tmp)) && atoi(tmp) == 0)
smileys_enabled = FALSE;
/* make lower case copy of str */
lstr = xmalloc(strlen(str) + 1);
for (pd = lstr, p = (char *) str; *p; p++, pd++)
*pd = tolower(*p);
*pd = 0;
for (i = 0; i < (int) strlen(str); i++) {
for (l = 0; key_list[l][0]; l++) {
if (strncmp(lstr + i, key_list[l], strlen(key_list[l])) == 0) {
/* check for escape character */
if (i > 0 && *(str + i - 1) == '\\') {
j--;
*(return_buffer + j) = 0;
continue;
}
p = (char *) (str + i + strlen(key_list[l]));
i += strlen(key_list[l]);
for (k = 0; *p && strcspn(p, " \t\n\r({[)}]\"") && k < (int) sizeof(link); k++, i++)
link[k] = *p++;
link[k] = 0;
i--;
/* link may not end with a '.'/',' (like in a sentence) */
if (link[k - 1] == '.' || link[k - 1] == ',') {
link[k - 1] = 0;
k--;
i--;
}
strlcpy(link_text, link, sizeof(link_text));
/* check if link contains coloring */
while ((p = strchr(link, '\001')) != NULL) {
/* skip everything between '<' and '>' */
pd = p;
while (*pd && *pd != '\002')
*p = *pd++;
memmove(p, pd + 1, strlen(pd + 1) + 1);
/* skip '' */
p = strchr(link, '\001');
if (p != NULL) {
pd = p;
while (*pd && *pd != '\002')
*p = *pd++;
memmove(p, pd + 1, strlen(pd + 1) + 1);
}
}
if (strcmp(key_list[l], "elog:") == 0) {
strlcpy(tmp, link, sizeof(tmp));
if (strchr(tmp, '/'))
*strchr(tmp, '/') = 0;
for (m = 0; m < (int) strlen(tmp); m++)
if (!isdigit(tmp[m]))
break;
if (m < (int) strlen(tmp) && tmp[m] != '#') {
/* if link contains reference to other logbook, put logbook explicitly */
if (email_notify)
compose_base_url(NULL, base_url, sizeof(base_url), TRUE);
else
strcpy(base_url, "../");
sprintf(return_buffer + j, "elog:%s", base_url, link, link_text);
} else if (link[0] == '/') {
if (email_notify)
compose_base_url(NULL, base_url, sizeof(base_url), TRUE);
else
base_url[0] = 0;
sprintf(return_buffer + j, "elog:%s", base_url, lbs->name_enc,
_current_message_id, link, link_text);
} else {
if (email_notify)
compose_base_url(lbs, base_url, sizeof(base_url), TRUE);
else
base_url[0] = 0;
sprintf(return_buffer + j, "elog:%s", base_url, link, link_text);
}
} else if (strcmp(key_list[l], "mailto:") == 0) {
sprintf(return_buffer + j, "%s", link, link_text);
} else {
sprintf(return_buffer + j, "%s", key_list[l]);
j += strlen(return_buffer + j);
strlen_retbuf = j;
/* link_text can contain special characters */
rsputs2(lbs, email_notify, link_text);
j = strlen_retbuf;
sprintf(return_buffer + j, "");
}
j += strlen(return_buffer + j);
break;
}
}
if (key_list[l][0])
continue;
substituted = FALSE;
for (l = 0; pattern_list[l].pattern[0]; l++) {
if (strncmp(lstr + i, pattern_list[l].pattern, strlen(pattern_list[l].pattern)) == 0) {
if (stristr(pattern_list[l].pattern, "[/code]"))
elcode_disabled = FALSE;
if (stristr(pattern_list[l].pattern, "[/code1]"))
elcode_disabled1 = FALSE;
/* check for escape character */
if (i > 0 && str[i - 1] == '\\') {
j--;
strncpy(return_buffer + j, str + i, strlen(pattern_list[l].pattern));
j += strlen(pattern_list[l].pattern);
i += strlen(pattern_list[l].pattern) - 1; // 1 gets added in for loop...
substituted = TRUE;
break;
}
/* check for blank before smiley and if smileys are allowed */
if (l <= 20 &&
((str[i - 1] != ' ' && str[i - 1] != '\r' && str[i - 1] != '\n') ||
(smileys_enabled == FALSE))) {
strncpy(return_buffer + j, str + i, strlen(pattern_list[l].pattern));
j += strlen(pattern_list[l].pattern);
i += strlen(pattern_list[l].pattern) - 1; // 1 gets added in for loop...
substituted = TRUE;
break;
}
if (!elcode_disabled && !elcode_disabled1) {
substituted = TRUE;
if (stristr(pattern_list[l].pattern, "[list="))
ordered_list = TRUE;
if (stristr(pattern_list[l].pattern, "[table"))
inside_table++;
if (stristr(pattern_list[l].pattern, "[/table]"))
inside_table--;
if (stristr(pattern_list[l].pattern, "[quote")) {
if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] == '=') {
i += strlen(pattern_list[l].pattern);
strextract(str + i, ']', attrib, sizeof(attrib));
i += strlen(attrib);
if (attrib[0] == '\"')
memmove(attrib, attrib + 1, strlen(attrib + 1) + 1);
if (attrib[strlen(attrib) - 1] == '\"')
attrib[strlen(attrib) - 1] = 0;
sprintf(value, loc("%s wrote"), attrib);
if (email_notify)
sprintf(return_buffer + j, email_quote_table, value);
else
sprintf(return_buffer + j, pattern_list[l].subst, value);
j += strlen(return_buffer + j);
} else {
if (email_notify)
sprintf(return_buffer + j, email_quote_table, loc("Quote"));
else
sprintf(return_buffer + j, pattern_list[l].subst, loc("Quote"));
j += strlen(return_buffer + j);
i += strlen(pattern_list[l].pattern) - 1; // 1 gets added in for loop...
}
} else if (strstr(pattern_list[l].subst, "%#")) {
/* special substitutions */
if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] == '=') {
i += strlen(pattern_list[l].pattern);
strextract(str + i, ']', attrib, sizeof(attrib));
i += strlen(attrib) + 1;
if (strncmp(attrib, "elog:", 5) == 0) { /* eval elog: */
strlcpy(tmp, attrib + 5, sizeof(tmp));
if (strchr(tmp, '/'))
*strchr(tmp, '/') = 0;
for (m = 0; m < (int) strlen(tmp); m++)
if (!isdigit(tmp[m]))
break;
if (m < (int) strlen(tmp))
/* if link contains reference to other logbook, add ".." in front */
sprintf(hattrib, "../%s", attrib + 5);
else if (attrib[5] == '/')
sprintf(hattrib, "%d%s", _current_message_id, attrib + 5);
else
sprintf(hattrib, "%s", attrib + 5);
} else if (strstr(attrib, "://") == 0 && attrib[0] != '#') { /* add http:// if missing */
if (_ssl_flag)
sprintf(hattrib, "https://%s", attrib);
else
sprintf(hattrib, "http://%s", attrib);
} else
strlcpy(hattrib, attrib, sizeof(hattrib));
strextract(str + i, '[', value, sizeof(value));
i += strlen(value) - 1;
strlcpy(subst, pattern_list[l].subst, sizeof(subst));
*strchr(subst, '#') = 's';
sprintf(return_buffer + j, subst, hattrib, value);
j += strlen(return_buffer + j);
} else if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] != '=') {
i += strlen(pattern_list[l].pattern);
strextract(str + i, '[', attrib, sizeof(attrib));
i += strlen(attrib) - 1;
strlcpy(hattrib, attrib, sizeof(hattrib));
/* replace elog:x/x for images */
if (strnieq(attrib, "elog:", 5)) {
if (email_notify) {
retrieve_domain(domain, sizeof(domain));
char* xx = strchr(attrib, '/') + 1;
char buffer[3];
memcpy(buffer, xx, strlen(attrib) - (xx - attrib));
sprintf(hattrib, "cid:att%d@%s", atoi(buffer) - 1, domain);
} else {
if (email_notify)
compose_base_url(lbs, hattrib, sizeof(hattrib), TRUE);
else
hattrib[0] = 0;
if (attrib[5] == '/') {
if (_current_message_id == 0) {
sprintf(param, "attachment%d", atoi(attrib + 6) - 1);
if (isparam(param))
strlcat(hattrib, getparam(param), sizeof(hattrib));
} else
sprintf(hattrib + strlen(hattrib), "%d%s", _current_message_id, attrib + 5);
} else {
strlcat(hattrib, attrib + 5, sizeof(hattrib));
}
}
}
/* add http:// if missing */
else if ((!strnieq(attrib, "http://", 7) && !strnieq(attrib, "https://", 8)) &&
strstr(pattern_list[l].subst, "mailto") == NULL &&
strstr(pattern_list[l].subst, "img") == NULL &&
strncmp(pattern_list[l].subst, "", sizeof(subst));
strsubst(subst, sizeof(subst), "%#", hattrib);
sprintf(return_buffer + j, subst, attrib);
j += strlen(return_buffer + j);
}
} else if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] == '=') {
/* extract sting after '=' and put it into '%s' of subst */
i += strlen(pattern_list[l].pattern);
strextract(str + i, ']', attrib, sizeof(attrib));
i += strlen(attrib);
sprintf(return_buffer + j, pattern_list[l].subst, attrib);
j += strlen(return_buffer + j);
} else if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] == ' ') {
/* extract sting after ' ' and put it into '%s' of subst */
i += strlen(pattern_list[l].pattern);
strextract(str + i, ']', attrib, sizeof(attrib));
i += strlen(attrib);
sprintf(return_buffer + j, pattern_list[l].subst, attrib);
j += strlen(return_buffer + j);
} else if (strncmp(pattern_list[l].pattern, "[/list]", 7) == 0) {
if (ordered_list)
strcpy(subst, "");
else
strcpy(subst, "");
ordered_list = FALSE;
strcpy(return_buffer + j, subst);
j += strlen(subst);
i += strlen(pattern_list[l].pattern) - 1; // 1 gets added in for loop...
} else if (strncmp(pattern_list[l].pattern, "|", 1) == 0) {
if (inside_table) {
strcpy(link, pattern_list[l].subst);
if (strstr(link, "%s")) {
strcpy(tmp, link);
if (email_notify)
compose_base_url(lbs, base_url, sizeof(base_url), TRUE);
else
base_url[0] = 0;
sprintf(link, tmp, base_url);
}
strcpy(return_buffer + j, link);
j += strlen(link);
i += strlen(pattern_list[l].pattern) - 1; // 1 gets added in for loop...
} else {
strcpy(return_buffer + j, pattern_list[l].pattern);
j += strlen(pattern_list[l].pattern);
i += strlen(pattern_list[l].pattern) - 1; // 1 gets added in for loop...
}
} else {
/* simple substitution */
strcpy(link, pattern_list[l].subst);
if (strstr(link, "%s")) {
strcpy(tmp, link);
if (email_notify)
compose_base_url(lbs, base_url, sizeof(base_url), TRUE);
else
base_url[0] = 0;
sprintf(link, tmp, base_url);
}
strcpy(return_buffer + j, link);
j += strlen(link);
i += strlen(pattern_list[l].pattern) - 1; // 1 gets added in for loop...
}
} // !elcode_disabled && !elcode_disabled1
else if (!elcode_disabled) {
substituted = TRUE;
/* simple substitution */
strcpy(link, pattern_list[l].subst);
if (strstr(link, "%s")) {
strcpy(tmp, link);
if (email_notify)
compose_base_url(lbs, base_url, sizeof(base_url), TRUE);
else
base_url[0] = 0;
sprintf(link, tmp, base_url);
}
strcpy(return_buffer + j, link);
j += strlen(link);
i += strlen(pattern_list[l].pattern) - 1; // 1 gets added in for loop...
}
if (stristr(pattern_list[l].pattern, "[code]"))
elcode_disabled = TRUE;
if (stristr(pattern_list[l].pattern, "[code1]"))
elcode_disabled1 = TRUE;
break;
}
}
/* don't output tags if already subsituted by HTML code */
if (substituted)
continue;
if (strnieq(str + i, " ", 4)) {
strcpy(return_buffer + j, " ");
j += 6;
i += 3;
} else
switch (str[i]) {
case '\r':
if (!elcode_disabled && !elcode_disabled1 && !inside_table) {
strcat(return_buffer, " \r\n");
j += 8;
} else {
strcat(return_buffer, "\r\n");
j += 2;
}
break;
case '\n':
break;
case '&':
strcat(return_buffer, "&");
j += 5;
break;
case '<':
strcat(return_buffer, "<");
j += 4;
break;
case '>':
strcat(return_buffer, ">");
j += 4;
break;
/* the translation for the search highliting */
case '\001':
strcat(return_buffer, "<");
j++;
break;
case '\002':
strcat(return_buffer, ">");
j++;
break;
case '\003':
strcat(return_buffer, "\"");
j++;
break;
case '\004':
strcat(return_buffer, " ");
j++;
break;
default:
return_buffer[j++] = str[i];
}
}
xfree(lstr);
return_buffer[j] = 0;
strlen_retbuf = j;
}
/*------------------------------------------------------------------*/
void rsprintf(const char *format, ...) {
va_list argptr;
char str[10000];
va_start(argptr, format);
vsprintf(str, (char *) format, argptr);
va_end(argptr);
while (strlen_retbuf + (int) strlen(str) + 1 >= return_buffer_size) {
return_buffer = xrealloc(return_buffer, return_buffer_size + 100000);
memset(return_buffer + return_buffer_size, 0, 100000);
return_buffer_size += 100000;
}
strcpy(return_buffer + strlen_retbuf, str);
strlen_retbuf += strlen(str);
}
/*------------------------------------------------------------------*/
void flush_return_buffer() {
#ifdef HAVE_SSL
send_with_timeout(_ssl_con, _sock, return_buffer, strlen_retbuf);
#else
send_with_timeout(NULL, _sock, return_buffer, strlen_retbuf);
#endif
memset(return_buffer, 0, return_buffer_size);
strlen_retbuf = 0;
}
/*------------------------------------------------------------------*/
/* Parameter handling functions similar to setenv/getenv */
void initparam() {
memset(_param, 0, sizeof(_param));
memset(_value, 0, sizeof(_value));
_mtext[0] = 0;
_cmdline[0] = 0;
}
int setparam(char *param, char *value) {
int i;
char str[10000];
if (strieq(param, "text")) {
if (strlen(value) >= TEXT_SIZE) {
sprintf(str,
"Error: Entry text too big (%lu bytes). Please increase TEXT_SIZE and recompile elogd\n",
(unsigned long) strlen(value));
show_error(str);
return 0;
}
strlcpy(_mtext, value, TEXT_SIZE);
return 1;
}
if (strieq(param, "cmdline")) {
if (strlen(value) >= CMD_SIZE) {
sprintf(str,
"Error: Command line too big (%lu bytes). Please increase CMD_SIZE and recompile elogd\n",
(unsigned long) strlen(value));
show_error(str);
return 0;
}
strlcpy(_cmdline, value, CMD_SIZE);
return 1;
}
/* paremeters can be superseeded */
for (i = 0; i < MAX_PARAM; i++)
if (_param[i][0] == 0 || strieq(param, _param[i]))
break;
if (i < MAX_PARAM) {
if (strlen(param) >= NAME_LENGTH) {
sprintf(str, "Error: Parameter name too big (%lu bytes).\n", (unsigned long) strlen(param));
show_error(str);
return 0;
}
strlcpy(_param[i], param, NAME_LENGTH);
if (strlen(value) >= NAME_LENGTH) {
sprintf(str,
"Error: Parameter value for parameter %s too big (%lu bytes). Please increase NAME_LENGTH and recompile elogd\n",
param, (unsigned long) strlen(value));
show_error(str);
return 0;
}
strlcpy(_value[i], value, NAME_LENGTH);
} else {
sprintf(str, "Error: Too many parameters (> %d). Cannot perform operation.\n", MAX_PARAM);
show_error(str);
return 0;
}
return 1;
}
char *getparam(char *param) {
int i;
if (strieq(param, "text"))
return _mtext;
if (strieq(param, "cmdline"))
return _cmdline;
for (i = 0; i < MAX_PARAM && _param[i][0]; i++)
if (strieq(param, _param[i]))
break;
if (i < MAX_PARAM && _param[i][0])
return _value[i];
return NULL;
}
BOOL enumparam(int n, char *param, char *value) {
param[0] = value[0] = 0;
if (n >= MAX_PARAM)
return FALSE;
if (_param[n][0] == 0)
return FALSE;
strcpy(param, _param[n]);
strcpy(value, _value[n]);
return TRUE;
}
BOOL isparam(char *param) {
int i;
if (strieq(param, "text"))
return _mtext[0] != 0;
for (i = 0; i < MAX_PARAM && _param[i][0]; i++)
if (strieq(param, _param[i]))
break;
if (i < MAX_PARAM && _param[i][0])
return TRUE;
return FALSE;
}
void unsetparam(char *param) {
int i;
for (i = 0; i < MAX_PARAM; i++)
if (strieq(param, _param[i]))
break;
if (i < MAX_PARAM) {
for (; i < MAX_PARAM - 1; i++) {
strlcpy(_param[i], _param[i + 1], NAME_LENGTH);
strlcpy(_value[i], _value[i + 1], NAME_LENGTH);
}
_param[MAX_PARAM - 1][0] = 0;
_value[MAX_PARAM - 1][0] = 0;
}
}
/*------------------------------------------------------------------*/
void extract_path(char *str) {
char *p, str2[256];
p = NULL;
if (strstr(str, "http://"))
p = str + 7;
if (strstr(str, "https://"))
p = str + 8;
if (p) {
while (*p && *p != '/')
p++;
if (*p == '/')
p++;
strcpy(str2, p);
strcpy(str, str2);
if (str[strlen(str) - 1] == '/')
str[strlen(str) - 1] = 0;
}
}
/*------------------------------------------------------------------*/
void extract_host(char *str) {
char *p, *ph, str2[256];
p = NULL;
if (strstr(str, "http://"))
p = str + 7;
else if (strstr(str, "https://"))
p = str + 8;
if (p) {
ph = p;
while (*p && *p != '/' && *p != ':')
p++;
*p = 0;
strcpy(str2, ph);
strcpy(str, str2);
}
}
/*------------------------------------------------------------------*/
void compose_base_url(LOGBOOK *lbs, char *base_url, int size, BOOL email_notify)
/* return URL for elogd with optional logbook subdirectory */
{
if (email_notify)
if (getcfg(lbs->name, "Use Email URL", base_url, size))
return;
if (!getcfg("global", "URL", base_url, size)) {
if (referer[0]) {
/* get URL from referer */
strlcpy(base_url, referer, size);
if (strchr(base_url, '?'))
*strchr(base_url, '?') = 0;
if (strrchr(base_url, '/'))
*(strrchr(base_url, '/') + 1) = 0;
} else {
if (_ssl_flag)
strcpy(base_url, "https://");
else
strcpy(base_url, "http://");
if (elog_tcp_port == 80)
sprintf(base_url + strlen(base_url), "%s/", host_name);
else
sprintf(base_url + strlen(base_url), "%s:%d/", host_name, elog_tcp_port);
if (lbs) {
strlcat(base_url, lbs->name_enc, size);
strlcat(base_url, "/", size);
}
}
} else {
if (base_url[strlen(base_url) - 1] != '/')
strlcat(base_url, "/", size);
if (lbs) {
strlcat(base_url, lbs->name_enc, size);
strlcat(base_url, "/", size);
}
}
}
/*------------------------------------------------------------------*/
void set_location(LOGBOOK *lbs, char *rp) {
char str[NAME_LENGTH], group[NAME_LENGTH], list[NAME_LENGTH], *p, rel_path[NAME_LENGTH];
int i;
/* remove any CR/LF from path */
strlcpy(rel_path, rp, sizeof(rel_path));
if (strchr(rel_path, '\r'))
*strchr(rel_path, '\r') = 0;
if (strchr(rel_path, '\n'))
*strchr(rel_path, '\n') = 0;
if (getcfg(lbs->name, "Relative redirect", str, sizeof(str)) && atoi(str) == 1) {
if (rel_path[0])
strlcpy(str, rel_path, sizeof(str));
else
strlcpy(str, ".", sizeof(str));
rsputs("Location: ");
rsputs(str);
} else {
/* Absolute redirect */
/* if path contains http(s), go directly there */
if (strncmp(rel_path, "http://", 7) == 0) {
rsputs("Location: ");
rsputs(rel_path);
} else if (strncmp(rel_path, "https://", 8) == 0) {
rsputs("Location: ");
rsputs(rel_path);
} else {
/* check for URL options */
str[0] = 0;
if (lbs)
getcfg(lbs->name, "URL", str, sizeof(str));
else
getcfg("global", "URL", str, sizeof(str));
if (str[0] == 0) {
/* get redirection from referer and host */
if (referer[0]) {
strlcpy(str, referer, sizeof(str));
/* strip any parameter */
if (strchr(str, '?'))
*strchr(str, '?') = 0;
/* strip rightmost '/' */
if (str[strlen(str) - 1] == '/')
str[strlen(str) - 1] = 0;
/* extract last subdir */
p = str + strlen(str);
while (p > str && *p != '/')
p--;
if (*p == '/')
p++;
/* if last subdir equals any logbook name, strip it */
for (i = 0; lb_list[i].name[0]; i++)
if (stricmp(p, lb_list[i].name_enc) == 0) {
*p = 0;
break;
}
/* if last subdir equals any group, strip it */
snprintf(group, sizeof(group), "Group %s", p);
if (getcfg("global", group, list, sizeof(list)))
*p = 0;
/* if last subdir equals any top group, strip it */
snprintf(group, sizeof(group), "Top group %s", p);
if (getcfg("global", group, list, sizeof(list)))
*p = 0;
} else {
/* assemble absolute path from host name and port */
if (_ssl_flag)
snprintf(str, sizeof(str), "https://%s", http_host);
else
snprintf(str, sizeof(str), "http://%s", http_host);
if (elog_tcp_port != 80 && strchr(str, ':') == NULL)
sprintf(str + strlen(str), ":%d", elog_tcp_port);
strlcat(str, "/", sizeof(str));
}
/* add trailing '/' if not present */
if (str[strlen(str) - 1] != '/')
strlcat(str, "/", sizeof(str));
/* add top group if existing and not logbook */
if (!lbs && getcfg_topgroup()) {
strlcat(str, getcfg_topgroup(), sizeof(str));
strlcat(str, "/", sizeof(str));
}
if (strncmp(rel_path, "../", 3) == 0)
strlcat(str, rel_path + 3, sizeof(str));
else if (strcmp(rel_path, ".") == 0) {
if (lbs)
strlcat(str, lbs->name_enc, sizeof(str));
} else if (rel_path[0] == '/')
strlcat(str, rel_path + 1, sizeof(str));
else {
if (lbs) {
strlcat(str, lbs->name_enc, sizeof(str));
strlcat(str, "/", sizeof(str));
strlcat(str, rel_path, sizeof(str));
} else
strlcat(str, rel_path, sizeof(str));
}
rsputs("Location: ");
rsputs(str);
} else {
/* use redirection via URL */
/* if HTTP request comes from localhost, use localhost as
absolute link (needed if running on DSL at home) */
if (!str[0] && strstr(http_host, "localhost")) {
if (_ssl_flag)
strlcpy(str, "https://localhost", sizeof(str));
else
strlcpy(str, "http://localhost", sizeof(str));
if (elog_tcp_port != 80)
sprintf(str + strlen(str), ":%d", elog_tcp_port);
strlcat(str, "/", sizeof(str));
}
/* add trailing '/' if not present */
if (str[strlen(str) - 1] != '/')
strlcat(str, "/", sizeof(str));
/* add top group if existing and not logbook */
if (!lbs && getcfg_topgroup()) {
strlcat(str, getcfg_topgroup(), sizeof(str));
strlcat(str, "/", sizeof(str));
}
if (strncmp(rel_path, "../", 3) == 0)
strlcat(str, rel_path + 3, sizeof(str));
else if (strcmp(rel_path, ".") == 0) {
if (lbs)
strlcat(str, lbs->name_enc, sizeof(str));
} else if (rel_path[0] == '/')
strlcat(str, rel_path + 1, sizeof(str));
else {
if (lbs) {
strlcat(str, lbs->name_enc, sizeof(str));
strlcat(str, "/", sizeof(str));
strlcat(str, rel_path, sizeof(str));
} else
strlcat(str, rel_path, sizeof(str));
}
rsputs("Location: ");
rsputs(str);
}
}
}
rsprintf("\r\n\r\nredir\r\n");
}
/*------------------------------------------------------------------*/
void set_redir(LOGBOOK *lbs, char *redir) {
char str[NAME_LENGTH];
str[0] = 0;
/* prepare relative path */
if (redir[0])
strlcpy(str, redir, sizeof(str));
else {
if (lbs)
snprintf(str, sizeof(str), "../%s/", lbs->name_enc);
else if (getcfg_topgroup())
sprintf(str, ".");
}
set_location(lbs, str);
}
/*------------------------------------------------------------------*/
void set_cookie(LOGBOOK *lbs, char *name, char *value, BOOL global, char *expiration) {
char lb_name[256], str[NAME_LENGTH], format[80];
double exp;
time_t now;
struct tm *gmt;
if (lbs)
strcpy(lb_name, lbs->name);
else
strcpy(lb_name, "global");
if (value != NULL)
rsprintf("Set-Cookie: %s=%s;", name, value);
else
rsprintf("Set-Cookie: %s;", name);
/* add path */
if (global) {
/* path for all logbooks */
if (getcfg(lb_name, "URL", str, sizeof(str))) {
extract_path(str);
url_encode(str, sizeof(str));
rsprintf(" path=/%s;", str);
} else
rsprintf(" path=/;");
} else {
/* path for individual logbook */
if (getcfg(lb_name, "URL", str, sizeof(str))) {
extract_path(str);
url_encode(str, sizeof(str));
if (str[0])
rsprintf(" path=/%s/%s;", str, lbs->name_enc);
else
rsprintf(" path=/%s;", lbs->name_enc);
} else
rsprintf(" path=/%s;", lbs->name_enc);
}
exp = atof(expiration);
/* to clear a cookie, set expiration date to yesterday */
if (value != NULL && value[0] == 0)
exp = -24;
/* add expriation date */
if (exp != 0 && exp < 100000) {
time(&now);
now += (int) (3600 * exp);
gmt = gmtime(&now);
strcpy(format, "%A, %d-%b-%y %H:%M:%S GMT");
strftime(str, sizeof(str), format, gmt);
rsprintf(" expires=%s;", str);
}
rsprintf("\r\n");
}
/*------------------------------------------------------------------*/
const char *git_revision() {
const char *p = _git_revision;
if (strrchr(p, '-'))
p = strrchr(p, '-') + 2;
return p;
}
/*------------------------------------------------------------------*/
void redirect(LOGBOOK *lbs, char *rel_path) {
/* redirect */
rsprintf("HTTP/1.1 302 Found\r\n");
rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
if (keep_alive) {
rsprintf("Connection: Keep-Alive\r\n");
rsprintf("Keep-Alive: timeout=60, max=10\r\n");
}
set_location(lbs, rel_path);
}
/*------------------------------------------------------------------*/
int strbreak(char *str, char list[][NAME_LENGTH], int size, char *brk, BOOL ignore_quotes)
/* break comma-separated list into char array, stripping leading
and trailing blanks */
{
int i, j;
char *p;
memset(list, 0, size * NAME_LENGTH);
p = str;
if (!p || !*p)
return 0;
while (*p == ' ')
p++;
for (i = 0; *p && i < size; i++) {
if (*p == '"' && !ignore_quotes) {
p++;
j = 0;
memset(list[i], 0, NAME_LENGTH);
do {
/* convert two '"' to one */
if (*p == '"' && *(p + 1) == '"') {
list[i][j++] = '"';
p += 2;
} else if (*p == '"') {
break;
} else
list[i][j++] = *p++;
} while (j < NAME_LENGTH - 1);
list[i][j] = 0;
/* skip second '"' */
p++;
/* skip blanks and break character */
while (*p == ' ')
p++;
if (*p && strchr(brk, *p))
p++;
while (*p == ' ')
p++;
} else {
strlcpy(list[i], p, NAME_LENGTH);
for (j = 0; j < (int) strlen(list[i]); j++)
if (strchr(brk, list[i][j])) {
list[i][j] = 0;
break;
}
p += strlen(list[i]);
while (*p == ' ')
p++;
if (*p && strchr(brk, *p))
p++;
while (*p == ' ')
p++;
}
while (list[i][strlen(list[i]) - 1] == ' ')
list[i][strlen(list[i]) - 1] = 0;
if (!*p)
break;
}
if (i == size)
return size;
return i + 1;
}
/*------------------------------------------------------------------*/
int scan_attributes(char *logbook)
/* scan configuration file for attributes and fill attr_list, attr_options
and attr_flags arrays */
{
char list[10000], str[NAME_LENGTH], str2[NAME_LENGTH], type[NAME_LENGTH],
tmp_list[MAX_N_ATTR][NAME_LENGTH];
int i, j, n, m, n_options;
if (getcfg(logbook, "Attributes", list, sizeof(list))) {
/* reset attribute flags */
memset(attr_flags, 0, sizeof(attr_flags));
/* get attribute list */
memset(attr_list, 0, sizeof(attr_list));
n = strbreak(list, attr_list, MAX_N_ATTR, ",", FALSE);
/* check for forbidden attributes */
for (i = 0; i < n; i++) {
if (strieq(attr_list[i], "id") ||
strieq(attr_list[i], "text") ||
strieq(attr_list[i], "date") ||
strieq(attr_list[i], "encoding") ||
strieq(attr_list[i], "reply to") ||
strieq(attr_list[i], "locked by") ||
strieq(attr_list[i], "in reply to") || strieq(attr_list[i], "attachment")) {
snprintf(str, sizeof(str), loc("Attribute \"%s\" is not allowed in config file"), attr_list[i]);
show_error(str);
return -1;
}
}
/* get options lists for attributes */
memset(attr_options, 0, sizeof(attr_options));
for (i = 0; i < n; i++) {
n_options = 0;
snprintf(str, sizeof(str), "Options %s", attr_list[i]);
if (getcfg(logbook, str, list, sizeof(list)))
n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
snprintf(str, sizeof(str), "MOptions %s", attr_list[i]);
if (getcfg(logbook, str, list, sizeof(list))) {
n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
attr_flags[i] |= AF_MULTI;
}
snprintf(str, sizeof(str), "ROptions %s", attr_list[i]);
if (getcfg(logbook, str, list, sizeof(list))) {
n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
attr_flags[i] |= AF_RADIO;
}
snprintf(str, sizeof(str), "IOptions %s", attr_list[i]);
if (getcfg(logbook, str, list, sizeof(list))) {
n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
attr_flags[i] |= AF_ICON;
}
snprintf(str2, sizeof(str2), "Sort Attribute Options %s", attr_list[i]);
if (n_options && getcfg(logbook, str2, str, sizeof(str)) && atoi(str) == 1) {
qsort(attr_options[i], n_options, NAME_LENGTH, ascii_compare2);
}
}
/* check if attribute required */
getcfg(logbook, "Required Attributes", list, sizeof(list));
m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++)
if (strieq(attr_list[j], tmp_list[i]))
attr_flags[j] |= AF_REQUIRED;
}
/* check if locked attribute */
getcfg(logbook, "Locked Attributes", list, sizeof(list));
m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++)
if (strieq(attr_list[j], tmp_list[i]))
attr_flags[j] |= AF_LOCKED;
}
/* check if fixed attribute for Edit */
getcfg(logbook, "Fixed Attributes Edit", list, sizeof(list));
m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++)
if (strieq(attr_list[j], tmp_list[i]))
attr_flags[j] |= AF_FIXED_EDIT;
}
/* check if fixed attribute for Reply */
getcfg(logbook, "Fixed Attributes Reply", list, sizeof(list));
m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++)
if (strieq(attr_list[j], tmp_list[i]))
attr_flags[j] |= AF_FIXED_REPLY;
}
/* check for extendable options */
getcfg(logbook, "Extendable Options", list, sizeof(list));
m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++)
if (strieq(attr_list[j], tmp_list[i]))
attr_flags[j] |= AF_EXTENDABLE;
}
for (i = 0; i < n; i++) {
snprintf(str, sizeof(str), "Type %s", attr_list[i]);
if (getcfg(logbook, str, type, sizeof(type))) {
if (strieq(type, "date"))
attr_flags[i] |= AF_DATE;
if (strieq(type, "datetime"))
attr_flags[i] |= AF_DATETIME;
if (strieq(type, "time"))
attr_flags[i] |= AF_TIME;
if (strieq(type, "numeric"))
attr_flags[i] |= AF_NUMERIC;
if (strieq(type, "userlist"))
attr_flags[i] |= AF_USERLIST;
if (strieq(type, "muserlist"))
attr_flags[i] |= AF_MUSERLIST;
if (strieq(type, "useremail"))
attr_flags[i] |= AF_USEREMAIL;
if (strieq(type, "museremail"))
attr_flags[i] |= AF_MUSEREMAIL;
}
}
} else {
memcpy(attr_list, attr_list_default, sizeof(attr_list_default));
memcpy(attr_options, attr_options_default, sizeof(attr_options_default));
memcpy(attr_flags, attr_flags_default, sizeof(attr_flags_default));
n = 4;
}
return n;
}
/*------------------------------------------------------------------*/
void show_http_header(LOGBOOK *lbs, BOOL expires, char *cookie) {
char str[256];
rsprintf("HTTP/1.1 200 Document follows\r\n");
rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
if (getcfg("global", "charset", str, sizeof(str)))
rsprintf("Content-Type: text/html;charset=%s\r\n", str);
else
rsprintf("Content-Type: text/html;charset=%s\r\n", DEFAULT_HTTP_CHARSET);
if (cookie && cookie[0])
set_cookie(lbs, cookie, NULL, FALSE, "99999"); /* ten years by default */
if (keep_alive) {
rsprintf("Connection: Keep-Alive\r\n");
rsprintf("Keep-Alive: timeout=60, max=10\r\n");
}
if (expires) {
rsprintf("Pragma: no-cache\r\n");
rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n");
}
rsprintf("\r\n");
}
void show_plain_header(int size, char *file_name) {
/* header */
rsprintf("HTTP/1.1 200 Document follows\r\n");
rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
rsprintf("Accept-Ranges: bytes\r\n");
if (keep_alive) {
rsprintf("Connection: Keep-Alive\r\n");
rsprintf("Keep-Alive: timeout=60, max=10\r\n");
}
rsprintf("Pragma: no-cache\r\n");
rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n");
rsprintf("Content-Type: text/plain\r\n");
rsprintf("Content-disposition: attachment; filename=\"%s\"\r\n", file_name);
if (size)
rsprintf("Content-Length: %d\r\n", size);
rsprintf("\r\n");
}
void show_html_header(LOGBOOK *lbs, BOOL expires, char *title, BOOL close_head, BOOL rss_feed, char *cookie,
int embed_css, int refresh) {
int i, n;
char css[1000], css_base[1000], str[1000], media[1000], file_name[256];
char css_list[MAX_N_LIST][NAME_LENGTH];
show_http_header(lbs, expires, cookie);
/* DOCTYPE */
rsprintf("\n");
/* this code would be for XML files...
rsprintf("\n");
rsprintf("\n");
*/
/* page title */
rsprintf("\n");
rsprintf("\n");
if (refresh)
rsprintf("\n", refresh);
rsprintf("%s\n", title);
/* Cascading Style Sheet */
if (embed_css) {
strlcpy(css, "elog.css", sizeof(css));
if (lbs != NULL && getcfg(lbs->name, "CSS", str, sizeof(str)))
strlcpy(css, str, sizeof(css));
else if (lbs == NULL && getcfg("global", "CSS", str, sizeof(str)))
strlcpy(css, str, sizeof(css));
strlcpy(file_name, resource_dir, sizeof(file_name));
strlcat(file_name, "themes", sizeof(file_name));
strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
strlcat(file_name, theme_name, sizeof(file_name));
strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
strlcat(file_name, css, sizeof(file_name));
FILE *f = fopen(file_name, "rb");
if (f != NULL) {
fseek(f, 0, SEEK_END);
int size = TELL(fileno(f));
fseek(f, 0, SEEK_SET);
char *buf = xmalloc(size + 100);
fread(buf, 1, size, f);
buf[size] = 0;
fclose(f);
rsprintf("\n");
xfree(buf);
}
} else {
rsprintf("\n");
}
if (lbs != NULL && getcfg(lbs->name, "CSS", str, sizeof(str)))
strlcpy(css, str, sizeof(css));
else if (lbs == NULL && getcfg("global", "CSS", str, sizeof(str)))
strlcpy(css, str, sizeof(css));
else
css[0] = 0;
if (css[0]) {
if (strchr(css, ',')) {
n = strbreak(css, css_list, MAX_N_LIST, ",", FALSE);
for (i = 0; i < n; i++) {
strlcpy(str, css_list[i], sizeof(str));
if (strchr(str, '&')) {
strlcpy(media, strchr(str, '&') + 1, sizeof(media));
*strchr(str, '&') = 0;
rsprintf("\n", str, media);
}
}
} else
rsprintf("\n", css_base, css);
}
if (!embed_css) {
rsprintf("\n");
rsprintf("\n");
}
if (rss_feed && !embed_css) {
rsprintf("name);
rsprintf("href=\"elog.rdf\" />\n");
}
if (close_head)
rsprintf("\n");
}
void show_browser(char *br) {
if (stristr(br, "opera"))
rsprintf("var browser = \"Opera\";\n");
else if (stristr(br, "konqueror"))
rsprintf("var browser = \"Konqueror\";\n");
else if (stristr(br, "Safari"))
rsprintf("var browser = \"Safari\";\n");
else if (stristr(br, "MSIE"))
rsprintf("var browser = \"MSIE\";\n");
else if (stristr(br, "Mozilla"))
rsprintf("var browser = \"Mozilla\";\n");
else
rsprintf("var browser = \"Other\";\n");
}
void show_standard_header(LOGBOOK *lbs, BOOL expires, char *title, char *path, BOOL rss_feed, char *cookie,
char *script, int refresh) {
if (script) {
show_html_header(lbs, expires, title, FALSE, rss_feed, cookie, FALSE, refresh);
rsprintf("\n");
rsprintf("\n\n");
rsprintf("\n");
} else
show_html_header(lbs, expires, title, TRUE, rss_feed, cookie, FALSE, refresh);
if (script)
rsprintf("\n", script);
else
rsprintf("\n");
show_top_text(lbs);
if (path && path[0])
rsprintf(" |
|