31324 lines
1.0 MiB
Plaintext
Executable File
31324 lines
1.0 MiB
Plaintext
Executable File
/********************************************************************
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
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
|
|
|
|
const 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 FILETYPE {
|
|
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 LANG_TABLE {
|
|
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(const 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;
|
|
|
|
/* 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 = (char *)pointer;
|
|
assert(*((unsigned int *) (temp - 4)) == 0xdeadc0de);
|
|
assert(*((unsigned int *) (temp + (*((unsigned int *) (temp - 8))))) == 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;
|
|
|
|
if (!pointer)
|
|
return;
|
|
|
|
/* check for magic byte */
|
|
temp = (char *)pointer;
|
|
assert(*((unsigned int *) (temp - 4)) == 0xdeadc0de);
|
|
assert(*((unsigned int *) (temp + (*((unsigned int *) (temp - 8))))) == 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 = (char *)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[2048];
|
|
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, const char *pattern, const 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] = '_';
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
const 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(const 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(const 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(const 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(const 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 = (char *)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, (fd_set *) &readfds, NULL, NULL, (struct timeval *) &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(const 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, const 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 = (char *)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, (const struct sockaddr *) &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] == 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 ".<CR>" 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, (const struct sockaddr *) &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)-1);
|
|
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 = (char *)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, (fd_set *) &readfds, NULL, NULL, (struct timeval *) &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(const char *topgroup) {
|
|
strcpy(_topgroup, topgroup);
|
|
}
|
|
|
|
char *getcfg_topgroup() {
|
|
if (_topgroup[0])
|
|
return _topgroup;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int is_logbook(const 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(const 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(const char *str, const char *param, int conditional_only) {
|
|
int ncl, npl, nand, i, j, k;
|
|
const char *p;
|
|
char 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 i, j, fh, length;
|
|
|
|
str = (char *)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 = (char *)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;
|
|
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 = (LB_CONFIG *)xmalloc(sizeof(LB_CONFIG));
|
|
else
|
|
lb_config = (LB_CONFIG *)xrealloc(lb_config, sizeof(LB_CONFIG) * (n_lb_config + 1));
|
|
lb_config[n_lb_config].section_name = (char *)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 = (CONFIG_PARAM *)xmalloc(sizeof(CONFIG_PARAM));
|
|
else
|
|
lb_config[n_lb_config].config_param = (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 = (char *)xmalloc(strlen(str) + 1);
|
|
lb_config[n_lb_config].config_param[i].uparam = (char *)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 = (char *)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++;
|
|
}
|
|
|
|
/* 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(const char *group, const 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(const char *group, const char *param, char *value, int vsize)
|
|
/*
|
|
Read parameter from configuration file.
|
|
|
|
- if group == [global] and top group exists, read
|
|
from [global <top group>]
|
|
|
|
- if parameter not in [global <top group>], read from [global]
|
|
|
|
- if group is logbook, read from logbook section
|
|
|
|
- if parameter not in [<logbook>], read from [global <top group>]
|
|
or [global]
|
|
*/
|
|
{
|
|
char str[1024];
|
|
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(const char *buf, const char *group, const char *param) {
|
|
char *str, *p, *pstr, *pstart;
|
|
|
|
/* search group */
|
|
str = (char *)xmalloc(10000);
|
|
p = (char *)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(const 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(const 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 = (char *) 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 = (char **)xmalloc(sizeof(char *) * 2);
|
|
_ptrans = (char **)xmalloc(sizeof(char *) * 2);
|
|
} else {
|
|
_porig = (char **)xrealloc(_porig, sizeof(char *) * (n + 2));
|
|
_ptrans = (char **)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 */
|
|
const char *loc(const 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(const 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[1000], email_from_name[1300], 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 <ELog@%s>", host_name);
|
|
sprintf(email_from, "<ELog@%s>", 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, const 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, const 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, const 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, const 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, MAX_PATH_LENGTH);
|
|
*(*plist + (i * MAX_PATH_LENGTH) + strlen(dp->d_name)) = '\0';
|
|
i++;
|
|
seekdir(dir_pointer, telldir(dir_pointer));
|
|
}
|
|
}
|
|
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
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
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 = (char *)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 = (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 = (int *)xmalloc(sizeof(int));
|
|
*lbs->n_el_index = 0;
|
|
lbs->el_index = (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 = (LOGBOOK *)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 = (char *)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 attachments */
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
attachment[i][0] = 0;
|
|
if (i == 0)
|
|
p = strtok(attachment_all, ",");
|
|
else
|
|
p = strtok(nullptr, ",");
|
|
|
|
if (p != nullptr)
|
|
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 (strlen(text) > 0 && text[strlen(text) - 1] == '\n') {
|
|
text[strlen(text) - 1] = 0;
|
|
if (strlen(text) > 0 && 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 + 100],
|
|
*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[2*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, const char *date, char attr_name[MAX_N_ATTR][NAME_LENGTH],
|
|
char attr_value[MAX_N_ATTR][NAME_LENGTH], int n_attr, const char *text, const char *in_reply_to,
|
|
const char *reply_to, const char *encoding, const char afilename[MAX_ATTACHMENTS][256], BOOL mark_original,
|
|
const char *locked_by, const 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 = (char *)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, "<keep>") == 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 = (char *)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, "<keep>"))
|
|
el_decode(message, "Date: ", date1, sizeof(date1));
|
|
else
|
|
strlcpy(date1, date, sizeof(date1));
|
|
if (strieq(locked_by1, "<keep>"))
|
|
el_decode_intlist(message, "Locked by: ", locked_by1, sizeof(locked_by1));
|
|
if (strieq(reply_to1, "<keep>"))
|
|
el_decode_intlist(message, "Reply to: ", reply_to1, sizeof(reply_to1));
|
|
if (strieq(in_reply_to1, "<keep>"))
|
|
el_decode_int(message, "In reply to: ", in_reply_to1, sizeof(in_reply_to1));
|
|
if (strieq(encoding1, "<keep>"))
|
|
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], "<keep>"))
|
|
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 = (char *)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 = (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, "<keep>") && 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:
|
|
<none>
|
|
|
|
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 = (char *)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 = (char *)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 = (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 = (char *)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 = (char *)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);
|
|
}
|
|
*/
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
const char *html_tags[] = {"<A HREF=", "<IMG ", "<B>", "<I>", "<P>", "<BR>", "<HR>", ""};
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
const 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;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
const char *full_html_tags[] = {"<HTML>", "<BODY>", "<HEAD>", ""};
|
|
|
|
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 = (unsigned char *)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 = (unsigned char *)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, "</p>", 4) == 0 || strncmp(str, "<br>", 4) == 0 || strncmp(str, "<br />", 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, "<img ");
|
|
if (p) {
|
|
pn = strstr(p, "name=");
|
|
if (pn) {
|
|
pn += 9;
|
|
index = atoi(pn);
|
|
while (*pn && *pn != '>')
|
|
pn++;
|
|
if (*pn == '>')
|
|
pn++;
|
|
retrieve_domain(domain, sizeof(domain));
|
|
sprintf(p, "<img border=\"0\" src=\"cid:att%d@%s\">", index, domain);
|
|
memmove(p + strlen(p), pn, strlen(pn) + 1);
|
|
|
|
/* now change href to absolute link */
|
|
pa = p - 1;
|
|
while (pa > str && *pa != '<')
|
|
// search '<a href=...>'
|
|
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, "<a href=\"%s%s\">elog:%s</a>", 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, "<a href=\"%s%s/%d%s\">elog:%s</a>", 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, "<a href=\"%s%s\">elog:%s</a>", base_url, link, link_text);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void rsputs(const char *str) {
|
|
while (strlen_retbuf + (int) strlen(str) + 1 >= return_buffer_size) {
|
|
return_buffer = (char *)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);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
const 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 = (char *)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 '</B>' */
|
|
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, "<a href=\"mailto:%s\">%s</a>", link, link_text);
|
|
} else {
|
|
sprintf(return_buffer + j, "<a href=\"%s", key_list[l]);
|
|
j += strlen(return_buffer + j);
|
|
strlen_retbuf = j;
|
|
|
|
/* link can contain special characters */
|
|
rsputs2(lbs, absolute_link, link);
|
|
j = strlen_retbuf;
|
|
|
|
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, "</a>");
|
|
}
|
|
|
|
j += strlen(return_buffer + j);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!key_list[l][0]) {
|
|
if (strncmp(str + i, "<br>", 4) == 0) {
|
|
strcpy(return_buffer + j, "<br>");
|
|
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 {
|
|
const char *pattern;
|
|
const char *subst;
|
|
} PATTERN_LIST;
|
|
|
|
PATTERN_LIST pattern_list[] = {
|
|
|
|
/* smileys */
|
|
{":))", "<img alt=\"Happy\" title=\"Happy\" src=\"%sicons/happy.png\">"},
|
|
{":-))", "<img alt=\"Happy\" title=\"Happy\" src=\"%sicons/happy.png\">"},
|
|
{":)", "<img alt=\"Smile\" title=\"Smile\" src=\"%sicons/smile.png\">"},
|
|
{":-)", "<img alt=\"Smile\" title=\"Smile\" src=\"%sicons/smile.png\">"},
|
|
{":(", "<img alt=\"Frown\" title=\"Frown\" src=\"%sicons/frown.png\">"},
|
|
{":-(", "<img alt=\"Frown\" title=\"Frown\" src=\"%sicons/frown.png\">"},
|
|
{";)", "<img alt=\"Wink\" title=\"Wink\" src=\"%sicons/wink.png\">"},
|
|
{";-)", "<img alt=\"Wink\" title=\"Wink\" src=\"%sicons/wink.png\">"},
|
|
{":d", "<img alt=\"Big grin\" title=\"Big grin\" src=\"%sicons/biggrin.png\">"},
|
|
{"?-)", "<img alt=\"Confused\" title=\"Confused\" src=\"%sicons/confused.png\">"},
|
|
{";(", "<img alt=\"Crying\" title=\"Crying\" src=\"%sicons/crying.png\">"},
|
|
{";-(", "<img alt=\"Crying\" title=\"Crying\" src=\"%sicons/crying.png\">"},
|
|
{":]", "<img alt=\"Pleased\" title=\"Pleased\" src=\"%sicons/pleased.png\">"},
|
|
{":-]", "<img alt=\"Pleased\" title=\"Pleased\" src=\"%sicons/pleased.png\">"},
|
|
{":o", "<img alt=\"Yawn\" title=\"Yawn\" src=\"%sicons/yawn.png\">"},
|
|
{":-o", "<img alt=\"Yawn\" title=\"Yawn\" src=\"%sicons/yawn.png\">"},
|
|
{"8-)", "<img alt=\"Cool\" title=\"Cool\" src=\"%sicons/cool.png\">"},
|
|
{"8o", "<img alt=\"Astonished\" title=\"Astonished\" src=\"%sicons/astonished.png\">"},
|
|
{"x-(", "<img alt=\"Mad\" title=\"Mad\" src=\"%sicons/mad.png\">"},
|
|
{":p", "<img alt=\"Tongue\" title=\"Tongue\" src=\"%sicons/tongue.png\">"},
|
|
{":-p", "<img alt=\"Tongue\" title=\"Tongue\" src=\"%sicons/tongue.png\">"},
|
|
|
|
/* formatting */
|
|
{"[b]", "<b>"},
|
|
{"[/b]", "</b>"},
|
|
{"[u]", "<u>"},
|
|
{"[/u]", "</u>"},
|
|
{"[i]", "<i>"},
|
|
{"[/i]", "</i>"},
|
|
{"[center]", "<center>"},
|
|
{"[/center]", "</center>"},
|
|
{"[color=", "<font color=\"%s\">"},
|
|
{"[/color]", "</font>"},
|
|
{"[size=", "<font size=\"%s\">"},
|
|
{"[/size]", "</font>"},
|
|
{"[font=", "<font face=\"%s\">"},
|
|
{"[/font]", "</font>"},
|
|
{"\r\n[code]", "<pre>"},
|
|
{"[code]", "<pre>"},
|
|
{"[/code]\r\n", "</pre>"},
|
|
{"[/code]", "</pre>"},
|
|
{"\r\n[code1]", "<pre>"},
|
|
{"[code1]", "<pre>"},
|
|
{"[/code1]\r\n", "</pre>"},
|
|
{"[/code1]", "</pre>"},
|
|
|
|
/* lists */
|
|
{"[list]\r", "<ul>"},
|
|
{"[list]", "<ul>"},
|
|
{"[*]", "<li>"},
|
|
{"[/list]\r", "</#>"}, // either </ul> or </ol>
|
|
{"[/list]", "</#>"},
|
|
{"[list=", "<ol type=\"%s\">"},
|
|
|
|
/* headings */
|
|
{"[h1]", "<h1>"},
|
|
{"[/h1]", "</h1>"},
|
|
{"[h2]", "<h2>"},
|
|
{"[/h2]", "</h2>"},
|
|
{"[h3]", "<h3>"},
|
|
{"[/h3]", "</h3>"},
|
|
|
|
/* URLs */
|
|
{"[url=", "<a href=\"%#\">%s</a>"},
|
|
{"[url]", "<a href=\"%#\">%s</a>"},
|
|
{"[/url]", ""},
|
|
{"[email]", "<a href=\"mailto:%#\">%s</a>"},
|
|
{"[/email]", ""},
|
|
{"[img]", "<a href=\"%#\"><img border=0 src=\"%#?thumb=1\"></a>"},
|
|
{"[/img]", ""},
|
|
|
|
/* quote */
|
|
{"[quote=",
|
|
"<br /><table class=\"quotetable\" align=\"center\" cellspacing=\"1\"><tr><td class=\"quotetitle\">%s:</td></tr><tr><td class=\"quote\">"},
|
|
{"[quote]",
|
|
"<br /><table class=\"quotetable\" align=\"center\" cellspacing=\"1\"><tr><td class=\"quotetitle\">%s:</td></tr><tr><td class=\"quote\">"},
|
|
{"[/quote]\r", "</td></tr></table><br />\r\n"},
|
|
{"[/quote]", "</td></tr></table>\r\n"},
|
|
|
|
/* table */
|
|
{"[table]", "<table><tr><td>"},
|
|
{"[table ", "<table %s><tr><td>"},
|
|
{"|-", "</td></tr><tr><td>"},
|
|
{"|", "</td><td>"},
|
|
{"[/table]", "</td></tr></table>"},
|
|
|
|
/* horizontal line */
|
|
{"[line]", "<hr />"},
|
|
|
|
/* anchor */
|
|
{"[anchor]", "<a name=\"%#\">"},
|
|
{"[/anchor]", "</a>"},
|
|
{"", ""}
|
|
};
|
|
|
|
const char
|
|
*email_quote_table =
|
|
"<table width=100%% border=1 align=\"left\" cellpadding=\"3\"><tr><td bgcolor=#486090><font color=white>%s:</font></td></tr><tr><td bgcolor=#FFFFB0>";
|
|
|
|
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[1024], 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 = (char *)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 = (char *)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 '</B>' */
|
|
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, "<a href=\"%s%s\">elog:%s</a>", 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, "<a href=\"%s%s/%d%s\">elog:%s</a>", 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, "<a href=\"%s%s\">elog:%s</a>", base_url, link, link_text);
|
|
}
|
|
} else if (strcmp(key_list[l], "mailto:") == 0) {
|
|
sprintf(return_buffer + j, "<a href=\"mailto:%s\">%s</a>", link, link_text);
|
|
} else {
|
|
sprintf(return_buffer + j, "<a href=\"%s", key_list[l]);
|
|
j += strlen(return_buffer + j);
|
|
strlen_retbuf = j;
|
|
|
|
/* link can contain special characters */
|
|
rsputs2(lbs, email_notify, link);
|
|
j = strlen_retbuf;
|
|
|
|
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, "</a>");
|
|
}
|
|
|
|
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));
|
|
sprintf(hattrib, "cid:att%d@%s", m, 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, "<a", 2) != 0) {
|
|
if (_ssl_flag)
|
|
snprintf(hattrib, sizeof(hattrib), "https://%s", attrib);
|
|
else
|
|
snprintf(hattrib, sizeof(hattrib), "http://%s", attrib);
|
|
}
|
|
|
|
strlcpy(subst, pattern_list[l].subst, sizeof(subst));
|
|
if (email_notify && stristr(subst, "?thumb=1"))
|
|
strlcpy(subst, "<a href=\"%#\"><img border=0 src=\"%#\"></a>", 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, "</ol>");
|
|
else
|
|
strcpy(subst, "</ul>");
|
|
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, "<br>", 4)) {
|
|
strcpy(return_buffer + j, "<br />");
|
|
j += 6;
|
|
i += 3;
|
|
} else
|
|
switch (str[i]) {
|
|
case '\r':
|
|
if (!elcode_disabled && !elcode_disabled1 && !inside_table) {
|
|
strcat(return_buffer, "<br />\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 = (char *)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(const char *param, const 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 <b>%s</b> 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(const 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(const 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(const 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, const 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\n<html>redir</html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void set_redir(LOGBOOK *lbs, const 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, const char *name, const char *value, BOOL global, const 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, const 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, const 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+32], 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;
|
|
|
|
strlcpy(str, "Options ", sizeof(str));
|
|
strlcat(str, attr_list[i], sizeof(str));
|
|
if (getcfg(logbook, str, list, sizeof(list)))
|
|
n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
|
|
|
|
strlcpy(str, "MOptions ", sizeof(str));
|
|
strlcat(str, attr_list[i], sizeof(str));
|
|
if (getcfg(logbook, str, list, sizeof(list))) {
|
|
n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
|
|
attr_flags[i] |= AF_MULTI;
|
|
}
|
|
|
|
strlcpy(str, "ROptions ", sizeof(str));
|
|
strlcat(str, attr_list[i], sizeof(str));
|
|
if (getcfg(logbook, str, list, sizeof(list))) {
|
|
n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
|
|
attr_flags[i] |= AF_RADIO;
|
|
}
|
|
|
|
strlcpy(str, "IOptions ", sizeof(str));
|
|
strlcat(str, attr_list[i], sizeof(str));
|
|
if (getcfg(logbook, str, list, sizeof(list))) {
|
|
n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
|
|
attr_flags[i] |= AF_ICON;
|
|
}
|
|
|
|
strlcpy(str2, "Sort Attribute Options ", sizeof(str2));
|
|
strlcat(str2, attr_list[i], sizeof(str2));
|
|
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++) {
|
|
strlcpy(str, "Type ", sizeof(str));
|
|
strlcat(str, attr_list[i], sizeof(str));
|
|
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, const char *cookie, int code) {
|
|
char str[256];
|
|
|
|
if (code == 401)
|
|
rsprintf("HTTP/1.1 401 Unauthorized\r\n");
|
|
else if (code == 404)
|
|
rsprintf("HTTP/1.1 404 Not Found\r\n");
|
|
else
|
|
rsprintf("HTTP/1.1 200 Document follows\r\n");
|
|
|
|
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, const 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, const char *title, BOOL close_head,
|
|
BOOL rss_feed, const char *cookie, int embed_css, int refresh, int code) {
|
|
int i, n;
|
|
char css[1000], str[1000], media[1000], file_name[256];
|
|
char css_list[MAX_N_LIST][NAME_LENGTH];
|
|
|
|
show_http_header(lbs, expires, cookie, code);
|
|
|
|
/* DOCTYPE */
|
|
rsprintf("<!DOCTYPE html>\n");
|
|
|
|
/* this code would be for XML files...
|
|
rsprintf("<?xml-stylesheet type=\"text/xsl\" href=\"http://www.w3.org/Math/XSL/mathml.xsl\"?>\n");
|
|
rsprintf("<html xmlns=\"http://www.w3.org/1999/xhtml\"><head>\n");
|
|
*/
|
|
|
|
/* page title */
|
|
rsprintf("<html><head>\n");
|
|
rsprintf("<meta name=\"ROBOTS\" content=\"NOINDEX, NOFOLLOW\">\n");
|
|
if (refresh)
|
|
rsprintf("<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh);
|
|
rsprintf("<title>%s</title>\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 = (char *)xmalloc(size + 100);
|
|
fread(buf, 1, size, f);
|
|
buf[size] = 0;
|
|
fclose(f);
|
|
|
|
rsprintf("<style>\n");
|
|
rsputs(buf);
|
|
rsprintf("</style>\n");
|
|
|
|
xfree(buf);
|
|
}
|
|
} else {
|
|
rsprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"elog.css\">\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("<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\" media=\"%s\">\n", str, media);
|
|
}
|
|
}
|
|
} else
|
|
rsprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\">\n", css);
|
|
}
|
|
|
|
if (!embed_css) {
|
|
rsprintf("<link rel=\"shortcut icon\" href=\"favicon.ico\" />\n");
|
|
rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
|
|
}
|
|
|
|
if (rss_feed && !embed_css) {
|
|
rsprintf("<link rel=\"alternate\" type=\"application/rss+xml\" ");
|
|
rsprintf("title=\"ELOG %s\" ", lbs->name);
|
|
rsprintf("href=\"elog.rdf\" />\n");
|
|
}
|
|
|
|
if (close_head)
|
|
rsprintf("</head>\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, const char *title, const char *path, BOOL rss_feed, const char *cookie,
|
|
const char *script, int refresh) {
|
|
if (script) {
|
|
show_html_header(lbs, expires, title, FALSE, rss_feed, cookie, FALSE, refresh, 200);
|
|
|
|
rsprintf("<script type=\"text/javascript\">\n");
|
|
rsprintf("<!--\n");
|
|
show_browser(browser);
|
|
|
|
rsprintf("logbook = \"%s\";\n", lbs->name_enc);
|
|
rsprintf("//-->\n");
|
|
rsprintf("</script>\n");
|
|
rsprintf("<script type=\"text/javascript\" src=\"../elcode.js\"></script>\n\n");
|
|
rsprintf("</head>\n");
|
|
} else
|
|
show_html_header(lbs, expires, title, TRUE, rss_feed, cookie, FALSE, refresh, 200);
|
|
|
|
if (script)
|
|
rsprintf("<body %s>\n", script);
|
|
else
|
|
rsprintf("<body>\n");
|
|
|
|
show_top_text(lbs);
|
|
|
|
if (path && path[0])
|
|
rsprintf("<form name=\"form1\" method=\"GET\" action=\"%s\">\n\n", path);
|
|
else
|
|
rsprintf("<form name=\"form1\" method=\"GET\" action=\".\">\n\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_upgrade_page(LOGBOOK *lbs) {
|
|
char str[1000];
|
|
|
|
show_html_header(lbs, FALSE, "ELOG Upgrade Information", TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
|
|
rsprintf("<body>\n");
|
|
|
|
rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\">\n\n");
|
|
|
|
rsprintf("<tr><td class=\"title2\">ELog Electronic Logbook Upgrade Information</font></td></tr>\n");
|
|
|
|
rsprintf("<tr><td class=\"form1\"><br>\n");
|
|
|
|
rsprintf("You probably use an <b>%s</b> configuration file for a ELOG version\n", CFGFILE);
|
|
rsprintf("1.1.x, since it contains a <b><code>\"Types = ...\"</code></b> entry. From version\n");
|
|
rsprintf("1.2.0 on, the fixed attributes <b>Type</b> and <b>Category</b> have been\n");
|
|
rsprintf("replaced by arbitrary attributes. Please replace these two lines with the\n");
|
|
rsprintf("following entries:<p>\n");
|
|
rsprintf("<pre>\n");
|
|
rsprintf("Attributes = Author, Type, Category, Subject\n");
|
|
rsprintf("Required Attributes = Author\n");
|
|
getcfg(lbs->name, "Types", str, sizeof(str));
|
|
rsprintf("Options Type = %s\n", str);
|
|
getcfg(lbs->name, "Categories", str, sizeof(str));
|
|
rsprintf("Options Category = %s\n", str);
|
|
rsprintf("Page title = $subject\n");
|
|
rsprintf("</pre>\n");
|
|
rsprintf("<p>\n");
|
|
|
|
rsprintf("It is of course possible to change the attributes or add new ones. The new\n");
|
|
rsprintf("options in the configuration file are described under <a href=\"\n");
|
|
rsprintf("https://elog.psi.ch/elog/config.html\">https://elog.psi.ch/elog/config.html\n");
|
|
rsprintf("</a>.\n");
|
|
|
|
rsprintf("</td></tr></table>\n\n");
|
|
|
|
rsprintf("<hr>\n");
|
|
rsprintf("<address>\n");
|
|
rsprintf("<a href=\"https://www.psi.ch/ltp-muon-physics/stefan-ritt\">S. Ritt</a>, 18 October 2001");
|
|
rsprintf("</address>");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
LBLIST *get_subgroup(LBLIST pgrp, char *logbook)
|
|
/* retrieve parent of group member "logbook" (which might be group by itself) */
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < pgrp->n_members; i++) {
|
|
/* check if logbook is current member */
|
|
if (strieq(logbook, pgrp->member[i]->name))
|
|
return &(pgrp->member[i]);
|
|
|
|
/* check if logbook is in subgroup of current member */
|
|
if (pgrp->member[i]->n_members > 0 && get_subgroup(pgrp->member[i], logbook))
|
|
return get_subgroup(pgrp->member[i], logbook);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
LBLIST get_logbook_hierarchy(void) {
|
|
int i, j, n, m, flag;
|
|
char str[1000], grpname[256], grpmembers[1000];
|
|
LBLIST root, *pgrp;
|
|
char grplist[MAX_N_LIST][NAME_LENGTH];
|
|
|
|
/* allocate root node */
|
|
root = (LBNODE *)xmalloc(sizeof(LBNODE));
|
|
memset(root, 0, sizeof(LBNODE));
|
|
|
|
/* enumerate groups */
|
|
for (i = n = 0;; i++) {
|
|
if (!enumcfg("global", grpname, sizeof(grpname), grpmembers, sizeof(grpmembers), i))
|
|
break;
|
|
|
|
/* flag indicates top group (2) or group (1) or other entry (0) */
|
|
flag = 0;
|
|
strlcpy(str, grpname, sizeof(str));
|
|
str[9] = 0;
|
|
if (strieq(str, "top group"))
|
|
flag = 2;
|
|
str[5] = 0;
|
|
if (strieq(str, "group"))
|
|
flag = 1;
|
|
|
|
if (flag) {
|
|
|
|
/* allocate new node, increase member pointer array by one */
|
|
if (n == 0)
|
|
root->member = (LBLIST *)xmalloc(sizeof(void *));
|
|
else
|
|
root->member = (LBLIST *)xrealloc(root->member, (n + 1) * sizeof(void *));
|
|
root->member[n] = (LBNODE *)xmalloc(sizeof(LBNODE));
|
|
|
|
if (strlen(grpname) < 7)
|
|
strlcpy(root->member[n]->name, "Invalid group definition!", 256);
|
|
else if (flag == 1)
|
|
strlcpy(root->member[n]->name, grpname + 6, 256);
|
|
else
|
|
strlcpy(root->member[n]->name, grpname + 10, 256);
|
|
|
|
m = strbreak(grpmembers, grplist, MAX_N_LIST, ",", FALSE);
|
|
root->member[n]->n_members = m;
|
|
|
|
root->member[n]->member = (LBLIST *)xcalloc(sizeof(void *), m);
|
|
root->member[n]->n_members = m;
|
|
for (j = 0; j < m; j++) {
|
|
root->member[n]->member[j] = (LBNODE *)xcalloc(sizeof(LBNODE), 1);
|
|
strlcpy(root->member[n]->member[j]->name, grplist[j], 256);
|
|
}
|
|
|
|
root->member[n]->is_top = (flag == 2);
|
|
|
|
n++;
|
|
}
|
|
}
|
|
|
|
root->n_members = n;
|
|
|
|
/* populate nodes with logbooks or other groups */
|
|
for (i = 0; i < root->n_members; i++)
|
|
if (root->member[i]) {
|
|
|
|
for (j = 0; j < root->n_members; j++) {
|
|
if (i != j && root->member[j] != NULL && (pgrp = get_subgroup(root->member[j],
|
|
root->member[i]->name)) != NULL) {
|
|
|
|
/* node is allocated twice, so free one... */
|
|
xfree(*pgrp);
|
|
|
|
/* ... and reference the other */
|
|
*pgrp = root->member[i];
|
|
|
|
/* mark original pointer invalid */
|
|
root->member[i] = NULL;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* remove empty slots */
|
|
for (i = 0; i < root->n_members; i++)
|
|
if (root->member[i] == NULL) {
|
|
for (j = i + 1; j < root->n_members; j++)
|
|
if (root->member[j])
|
|
break;
|
|
|
|
if (j < root->n_members && root->member[j]) {
|
|
root->member[i] = root->member[j];
|
|
root->member[j] = NULL;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < root->n_members; i++)
|
|
if (root->member[i] == NULL)
|
|
break;
|
|
if (i < root->n_members)
|
|
root->n_members = i;
|
|
|
|
if (n == 0) {
|
|
for (n = 0; lb_list[n].name[0]; n++);
|
|
|
|
/* make simple list with logbooks */
|
|
root->member = (LBLIST *)xcalloc(n, sizeof(void *));
|
|
root->n_members = n;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
root->member[i] = (LBNODE *)xcalloc(1, sizeof(LBNODE));
|
|
strlcpy(root->member[i]->name, lb_list[i].name, 256);
|
|
}
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void free_logbook_hierarchy(LBLIST root) {
|
|
int i;
|
|
|
|
for (i = 0; i < root->n_members; i++) {
|
|
if (root->member[i]) {
|
|
free_logbook_hierarchy(root->member[i]);
|
|
root->member[i] = NULL;
|
|
}
|
|
}
|
|
|
|
xfree(root->member);
|
|
xfree(root);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL is_logbook_in_group(LBLIST pgrp, char *logbook)
|
|
/* test if "logbook" is in group node plb */
|
|
{
|
|
int i;
|
|
|
|
if (strieq(logbook, pgrp->name))
|
|
return TRUE;
|
|
|
|
for (i = 0; i < pgrp->n_members; i++) {
|
|
if (strieq(logbook, pgrp->member[i]->name))
|
|
return TRUE;
|
|
|
|
if (pgrp->member[i]->n_members > 0 && is_logbook_in_group(pgrp->member[i], logbook))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void change_logbook_in_group(LOGBOOK *lbs, const char *new_name) {
|
|
int i, j, n, flag;
|
|
char str[1000], grpname[256], grpmembers[1000];
|
|
char grplist[MAX_N_LIST][NAME_LENGTH];
|
|
|
|
/* enumerate groups */
|
|
for (i = 0;; i++) {
|
|
if (!enumcfg("global", grpname, sizeof(grpname), grpmembers, sizeof(grpmembers), i))
|
|
break;
|
|
|
|
flag = 0;
|
|
strlcpy(str, grpname, sizeof(str));
|
|
str[9] = 0;
|
|
if (strieq(str, "top group"))
|
|
flag = 2;
|
|
str[5] = 0;
|
|
if (strieq(str, "group"))
|
|
flag = 1;
|
|
|
|
if (flag) {
|
|
|
|
n = strbreak(grpmembers, grplist, MAX_N_LIST, ",", FALSE);
|
|
for (j = 0; j < n; j++) {
|
|
if (strieq(lbs->name, grplist[j])) {
|
|
/* rename or remove logbook */
|
|
change_config_line(lbs, grpname, lbs->name, new_name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void add_logbook_to_group(LOGBOOK *lbs, char *new_name) {
|
|
int i, j, n, flag;
|
|
char str[1000], grpname[256], grpmembers[1000];
|
|
char grplist[MAX_N_LIST][NAME_LENGTH];
|
|
|
|
/* enumerate groups */
|
|
for (i = 0;; i++) {
|
|
if (!enumcfg("global", grpname, sizeof(grpname), grpmembers, sizeof(grpmembers), i))
|
|
break;
|
|
|
|
flag = 0;
|
|
strlcpy(str, grpname, sizeof(str));
|
|
str[9] = 0;
|
|
if (strieq(str, "top group"))
|
|
flag = 2;
|
|
str[5] = 0;
|
|
if (strieq(str, "group"))
|
|
flag = 1;
|
|
|
|
if (flag) {
|
|
|
|
n = strbreak(grpmembers, grplist, MAX_N_LIST, ",", FALSE);
|
|
for (j = 0; j < n; j++) {
|
|
if (strieq(lbs->name, grplist[j])) {
|
|
/* rename or remove logbook */
|
|
change_config_line(lbs, grpname, "", new_name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_standard_title(LOGBOOK *lbs, const char *text, int printable) {
|
|
char str[NAME_LENGTH], ref[256], sclass[32], comment[256], full_name[256], url[256], logbook[256];
|
|
int i, j, level;
|
|
LBLIST phier, pnode, pnext, flb;
|
|
char slist[20][NAME_LENGTH], svalue[20][NAME_LENGTH];
|
|
|
|
if (lbs == NULL)
|
|
strlcpy(logbook, "global", sizeof(logbook));
|
|
else
|
|
strlcpy(logbook, lbs->name, sizeof(logbook));
|
|
|
|
if (printable)
|
|
rsprintf
|
|
("<table class=\"pframe\" cellpadding=\"0\" cellspacing=\"0\"><!-- show_standard_title -->\n\n");
|
|
else
|
|
rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\"><!-- show_standard_title -->\n\n");
|
|
|
|
/* scan logbook hierarchy */
|
|
phier = get_logbook_hierarchy();
|
|
|
|
/*---- logbook selection row ----*/
|
|
|
|
pnode = phier; /* start at root of tree */
|
|
pnext = NULL;
|
|
|
|
if (!printable && (!getcfg(logbook, "logbook tabs", str, sizeof(str)) || atoi(str) == 1)) {
|
|
|
|
for (level = 0;; level++) {
|
|
rsprintf("<tr><td class=\"tabs\">\n");
|
|
|
|
if (level == 0 && getcfg("global", "main tab", str, sizeof(str)) && !getcfg_topgroup()) {
|
|
if (getcfg("global", "main tab url", url, sizeof(url)))
|
|
rsprintf("<span class=\"ltab\"><a href=\"%s\">%s</a></span>\n", url, str);
|
|
else
|
|
rsprintf("<span class=\"ltab\"><a href=\"../\">%s</a></span>\n", str);
|
|
}
|
|
|
|
if (level == 1 && getcfg("global", "main tab", str, sizeof(str)) && getcfg_topgroup()) {
|
|
if (getcfg("global", "main tab url", url, sizeof(url)))
|
|
rsprintf("<span class=\"ltab\"><a href=\"%s/\">%s</a></span>\n", url, str);
|
|
else
|
|
rsprintf("<span class=\"ltab\"><a href=\"../%s/\">%s</a></span>\n", getcfg_topgroup(), str);
|
|
}
|
|
|
|
/* iterate through members of this group */
|
|
for (i = 0; i < pnode->n_members; i++) {
|
|
|
|
if (getcfg(pnode->member[i]->name, "Hidden", str, sizeof(str)) && atoi(str) == 1)
|
|
continue;
|
|
|
|
strlcpy(str, pnode->member[i]->name, sizeof(str));
|
|
|
|
/* build reference to first logbook in group */
|
|
comment[0] = 0;
|
|
if (pnode->member[i]->member == NULL) {
|
|
getcfg(pnode->member[i]->name, "Comment", comment, sizeof(comment));
|
|
strlcpy(ref, str, sizeof(ref)); // current node is logbook
|
|
} else {
|
|
flb = pnode->member[i]->member[0]; // current node is group
|
|
while (flb->member)
|
|
// so traverse hierarchy
|
|
flb = flb->member[0];
|
|
strlcpy(ref, flb->name, sizeof(ref));
|
|
}
|
|
url_encode(ref, sizeof(ref));
|
|
|
|
if (is_logbook_in_group(pnode->member[i], logbook)) {
|
|
|
|
/* remember member list of this group for next row */
|
|
pnext = pnode->member[i];
|
|
|
|
if (pnode->member[i]->member == NULL)
|
|
/* selected logbook */
|
|
strcpy(sclass, "sltab");
|
|
else
|
|
/* selected group */
|
|
strcpy(sclass, "sgtab");
|
|
} else {
|
|
if (pnode->member[i]->member == NULL)
|
|
/* unselected logbook */
|
|
strcpy(sclass, "ltab");
|
|
else
|
|
/* unselected group */
|
|
strcpy(sclass, "gtab");
|
|
}
|
|
|
|
if (!pnode->member[i]->is_top) {
|
|
|
|
rsprintf("<span class=\"%s\">", sclass);
|
|
|
|
if (comment[0]) {
|
|
rsprintf("<a href=\"../%s/\" title=\"", ref);
|
|
rsputs3(comment);
|
|
rsprintf("\">");
|
|
} else
|
|
rsprintf("<a href=\"../%s/\">", ref);
|
|
|
|
strlcpy(str, pnode->member[i]->name, sizeof(str));
|
|
|
|
for (j = 0; j < (int) strlen(str); j++)
|
|
if (str[j] == ' ')
|
|
rsprintf(" ");
|
|
else
|
|
rsprintf("%c", str[j]);
|
|
|
|
rsprintf("</a></span>\n");
|
|
}
|
|
}
|
|
|
|
rsprintf("</td></tr>\n\n");
|
|
|
|
pnode = pnext;
|
|
pnext = NULL;
|
|
|
|
if (pnode == NULL || pnode->n_members == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
free_logbook_hierarchy(phier);
|
|
|
|
/*---- title row ----*/
|
|
|
|
rsprintf("<tr><td><table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
|
|
|
|
/* left cell */
|
|
rsprintf("<tr><td class=\"title1\">");
|
|
|
|
/* use comment as title if available, else logbook name */
|
|
if (!getcfg(logbook, "Comment", str, sizeof(str)))
|
|
strcpy(str, logbook);
|
|
|
|
rsprintf(" ");
|
|
|
|
if (is_html(str))
|
|
rsputs(str);
|
|
else
|
|
rsputs3(str);
|
|
|
|
rsputs3(text);
|
|
rsprintf(" </td>\n");
|
|
|
|
/* middle cell */
|
|
if (isparam("unm")) {
|
|
get_user_line(lbs, getparam("unm"), NULL, full_name, NULL, NULL, NULL, NULL);
|
|
rsprintf("<td class=\"title2\">%s \"%s\"</td>\n", loc("Logged in as"), full_name);
|
|
} else if (getcfg(lbs->name, "Guest menu commands", str, sizeof(str)))
|
|
rsprintf("<td class=\"title2\" align=center>%s</td>\n", loc("Not logged in"));
|
|
|
|
/* right cell */
|
|
rsprintf("<td class=\"title3\">");
|
|
|
|
if (getcfg(logbook, "Title image URL", str, sizeof(str)))
|
|
rsprintf("<a href=\"%s\">\n", str);
|
|
|
|
if (getcfg(logbook, "Title image", str, sizeof(str))) {
|
|
// allow $short_name for example to link to personal pictures
|
|
i = build_subst_list(lbs, slist, svalue, NULL, TRUE);
|
|
strsubst_list(str, sizeof(str), slist, svalue, i);
|
|
|
|
rsprintf("%s", str);
|
|
} else
|
|
rsprintf("<img border=0 src=\"elog.png\" alt=\"ELOG logo\" title=\"ELOG logo\">");
|
|
|
|
if (getcfg(logbook, "Title image URL", str, sizeof(str)))
|
|
rsprintf("</a>\n");
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
rsprintf("</tr></table></td></tr>\n\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_top_text(LOGBOOK *lbs) {
|
|
char str[NAME_LENGTH];
|
|
int size;
|
|
|
|
if (getcfg(lbs->name, "top text", str, sizeof(str)) && str[0]) {
|
|
FILE *f;
|
|
char file_name[256], *buf;
|
|
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
|
|
f = fopen(file_name, "rb");
|
|
if (f != NULL) {
|
|
fseek(f, 0, SEEK_END);
|
|
size = TELL(fileno(f));
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
buf = (char *)xmalloc(size + 1);
|
|
fread(buf, 1, size, f);
|
|
buf[size] = 0;
|
|
fclose(f);
|
|
|
|
rsputs(buf);
|
|
} else
|
|
rsputs(str);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_bottom_text(LOGBOOK *lbs) {
|
|
char str[NAME_LENGTH], slist[20][NAME_LENGTH], svalue[20][NAME_LENGTH];
|
|
int i, size;
|
|
|
|
if (lbs == NULL)
|
|
return;
|
|
if (getcfg(lbs->name, "bottom text", str, sizeof(str))) {
|
|
FILE *f;
|
|
char file_name[256], *buf;
|
|
|
|
if (str[0]) {
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
|
|
f = fopen(file_name, "rb");
|
|
if (f != NULL) {
|
|
fseek(f, 0, SEEK_END);
|
|
size = TELL(fileno(f));
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
buf = (char *)xmalloc(size + 100);
|
|
fread(buf, 1, size, f);
|
|
buf[size] = 0;
|
|
fclose(f);
|
|
|
|
i = build_subst_list(lbs, slist, svalue, NULL, TRUE);
|
|
strsubst_list(buf, size + 100, slist, svalue, i);
|
|
|
|
rsputs(buf);
|
|
xfree(buf);
|
|
} else {
|
|
i = build_subst_list(lbs, slist, svalue, NULL, TRUE);
|
|
strsubst_list(str, sizeof(str), slist, svalue, i);
|
|
|
|
rsputs(str);
|
|
}
|
|
}
|
|
} else
|
|
/* add little logo */
|
|
rsprintf
|
|
("<center><a class=\"bottomlink\" title=\"%s\" href=\"https://elog.psi.ch/elog/\">ELOG V%s-%s</a></center>",
|
|
loc("Goto ELOG home page"), VERSION, git_revision());
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_bottom_text_login(LOGBOOK *lbs) {
|
|
char str[NAME_LENGTH], slist[20][NAME_LENGTH], svalue[20][NAME_LENGTH];
|
|
int i, size;
|
|
|
|
if (getcfg(lbs->name, "bottom text login", str, sizeof(str))) {
|
|
FILE *f;
|
|
char file_name[256], *buf;
|
|
|
|
if (str[0]) {
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
|
|
f = fopen(file_name, "rb");
|
|
if (f != NULL) {
|
|
fseek(f, 0, SEEK_END);
|
|
size = TELL(fileno(f));
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
buf = (char *)xmalloc(size + 100);
|
|
fread(buf, 1, size, f);
|
|
buf[size] = 0;
|
|
fclose(f);
|
|
|
|
if (lbs != NULL) {
|
|
i = build_subst_list(lbs, slist, svalue, NULL, TRUE);
|
|
strsubst_list(buf, size + 100, slist, svalue, i);
|
|
}
|
|
|
|
rsputs(buf);
|
|
xfree(buf);
|
|
} else {
|
|
if (lbs != NULL) {
|
|
i = build_subst_list(lbs, slist, svalue, NULL, TRUE);
|
|
strsubst_list(str, sizeof(str), slist, svalue, i);
|
|
}
|
|
|
|
rsputs(str);
|
|
}
|
|
}
|
|
} else
|
|
/* add little logo */
|
|
rsprintf
|
|
("<center><a class=\"bottomlink\" title=\"%s\" href=\"https://elog.psi.ch/elog/\">ELOG V%s-%s</a></center>",
|
|
loc("Goto ELOG home page"), VERSION, git_revision());
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_error(const char *error) {
|
|
char str[256];
|
|
|
|
/* header */
|
|
rsprintf("HTTP/1.1 404 Not Found\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);
|
|
rsprintf("\r\n");
|
|
|
|
rsprintf("<!DOCTYPE html>\n");
|
|
rsprintf("<html><head>\n");
|
|
rsprintf("<META NAME=\"ROBOTS\" CONTENT=\"NOINDEX, NOFOLLOW\">\n");
|
|
rsprintf("<title>%s</title>\n", loc("ELOG error"));
|
|
rsprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"elog.css\">\n");
|
|
rsprintf("</head>\n");
|
|
|
|
rsprintf("<body><center>\n");
|
|
rsprintf("<table class=\"dlgframe\" width=\"50%%\" cellpadding=\"1\" cellspacing=\"0\"");
|
|
rsprintf("<tr><td class=\"errormsg\">%s</td></tr>\n", error);
|
|
|
|
rsprintf("<tr><td class=\"errormsg\">");
|
|
|
|
rsprintf("<script language=\"javascript\" type=\"text/javascript\">\n");
|
|
rsprintf("document.write(\"<button type=button onClick=history.back()>%s</button>\"); \n", loc("Back"));
|
|
rsprintf("</script>\n");
|
|
|
|
rsprintf("<noscript>\n");
|
|
rsprintf("%s\n", loc("Please use your browser's back button to go back"));
|
|
rsprintf("</noscript>\n");
|
|
|
|
rsprintf("</td></tr>\n</table>\n");
|
|
rsprintf("</center></body></html>\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_query(LOGBOOK *lbs, const char *title, const char *query_string, const char *button1, const char *button1_url,
|
|
const char *button2, const char *button2_url) {
|
|
show_standard_header(lbs, TRUE, "ELog query", title, FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
|
|
rsprintf("<tr><td class=\"dlgtitle\">\n");
|
|
rsprintf("%s</td></tr>\n", title);
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">");
|
|
rsprintf("%s", query_string);
|
|
rsprintf("</td></tr>\n\n");
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">");
|
|
rsprintf("<input type=button value=\"%s\" onClick=\"window.location.href='%s';\">\n", button1,
|
|
button1_url);
|
|
rsprintf("<input type=button value=\"%s\" onClick=\"window.location.href='%s';\">\n", button2,
|
|
button2_url);
|
|
rsprintf("</td></tr>\n\n");
|
|
|
|
rsprintf("</table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void set_sid_cookie(LOGBOOK *lbs, const char *sid, const char *full_name) {
|
|
char str[256], lb_name[256], exp[80];
|
|
BOOL global;
|
|
|
|
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");
|
|
}
|
|
|
|
if (lbs)
|
|
strcpy(lb_name, lbs->name);
|
|
else
|
|
strcpy(lb_name, "global");
|
|
|
|
/* get optional expriation from configuration file */
|
|
if (getcfg(lb_name, "Login expiration", str, sizeof(str)) || atof(str) > 0)
|
|
strcpy(exp, str);
|
|
else if (isparam("remember")) {
|
|
strcpy(exp, "744"); /* one month by default = 31*24 */
|
|
} else
|
|
exp[0] = 0;
|
|
|
|
/* check if cookies should be global */
|
|
global = getcfg("global", "Password file", str, sizeof(str));
|
|
|
|
/* set the session ID cookie */
|
|
set_cookie(lbs, "sid", sid, global, exp);
|
|
|
|
/* set the use full name cookie */
|
|
set_cookie(lbs, "ufnm", full_name, global, exp);
|
|
|
|
/* set "remember me" cookie on login */
|
|
if (isparam("remember"))
|
|
set_cookie(lbs, "urem", "1", global, "8760"); /* one year = 24*365 */
|
|
else
|
|
set_cookie(lbs, "urem", "0", global, "8760");
|
|
|
|
set_redir(lbs, isparam("redir") ? getparam("redir") : "");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int exist_file(char *file_name) {
|
|
int fh;
|
|
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
close(fh);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void send_file_direct(char *file_name) {
|
|
int fh, i, length, delta;
|
|
char str[MAX_PATH_LENGTH], dir[MAX_PATH_LENGTH], charset[80];
|
|
|
|
getcwd(dir, sizeof(dir));
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
lseek(fh, 0, SEEK_END);
|
|
length = TELL(fh);
|
|
lseek(fh, 0, SEEK_SET);
|
|
|
|
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");
|
|
|
|
/* set expiration time to one day if no thumbnail */
|
|
if (isparam("thumb")) {
|
|
rsprintf("Pragma: no-cache\r\n");
|
|
rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n");
|
|
} else {
|
|
rsprintf("Cache-control: public, max-age=86400\r\n");
|
|
}
|
|
|
|
if (keep_alive) {
|
|
rsprintf("Connection: Keep-Alive\r\n");
|
|
rsprintf("Keep-Alive: timeout=60, max=10\r\n");
|
|
}
|
|
|
|
/* return proper header for file type */
|
|
for (i = 0; i < (int) strlen(file_name); i++)
|
|
str[i] = toupper(file_name[i]);
|
|
str[i] = 0;
|
|
|
|
for (i = 0; filetype[i].ext[0]; i++)
|
|
if (chkext(str, filetype[i].ext))
|
|
break;
|
|
|
|
if (!getcfg("global", "charset", charset, sizeof(charset)))
|
|
strcpy(charset, DEFAULT_HTTP_CHARSET);
|
|
|
|
if (filetype[i].ext[0]) {
|
|
if (strncmp(filetype[i].type, "text", 4) == 0)
|
|
rsprintf("Content-Type: %s;charset=%s\r\n", filetype[i].type, charset);
|
|
else if (strcmp(filetype[i].ext, ".SVG") == 0) {
|
|
rsprintf("Content-Type: %s\r\n", filetype[i].type);
|
|
if (strrchr(file_name, '/'))
|
|
strlcpy(str, strrchr(file_name, '/')+1, sizeof(str));
|
|
else
|
|
strlcpy(str, file_name, sizeof(str));
|
|
if (str[6] == '_' && str[13] == '_')
|
|
rsprintf("Content-Disposition: attachment; filename=\"%s\"\r\n", str+14);
|
|
else
|
|
rsprintf("Content-Disposition: attachment; filename=\"%s\"\r\n", str);
|
|
} else
|
|
rsprintf("Content-Type: %s\r\n", filetype[i].type);
|
|
} else if (is_ascii(file_name))
|
|
rsprintf("Content-Type: text/plain;charset=%s\r\n", charset);
|
|
else
|
|
rsprintf("Content-Type: application/octet-stream;charset=%s\r\n", charset);
|
|
|
|
rsprintf("Content-Length: %d\r\n\r\n", length);
|
|
|
|
/* increase return buffer size if file too big */
|
|
while (length > return_buffer_size - (int) strlen(return_buffer)) {
|
|
delta = length - (return_buffer_size - strlen(return_buffer)) + 1000;
|
|
|
|
return_buffer = (char *)xrealloc(return_buffer, return_buffer_size + delta);
|
|
memset(return_buffer + return_buffer_size, 0, delta);
|
|
return_buffer_size += delta;
|
|
}
|
|
|
|
return_length = strlen(return_buffer) + length;
|
|
read(fh, return_buffer + strlen(return_buffer), length);
|
|
|
|
close(fh);
|
|
} else {
|
|
char encodedname[256], str[256];
|
|
show_html_header(NULL, FALSE, "404 Not Found", TRUE, FALSE, NULL, FALSE, 0, 404);
|
|
|
|
rsprintf("<body><h1> 404 Not Found</h1>\r\n");
|
|
rsprintf(" The requested file <b>");
|
|
strencode2(encodedname, file_name, sizeof(encodedname));
|
|
if (strrchr(encodedname, DIR_SEPARATOR))
|
|
rsprintf("%s", strrchr(encodedname, DIR_SEPARATOR)+1, sizeof(str));
|
|
else
|
|
rsprintf("%s", encodedname);
|
|
rsprintf("</b> was not found on this server<p>\r\n");
|
|
return_length = strlen_retbuf;
|
|
keep_alive = FALSE;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void strencode(char *text) {
|
|
int i;
|
|
|
|
for (i = 0; i < (int) strlen(text); i++) {
|
|
switch (text[i]) {
|
|
case '\n':
|
|
rsprintf("<br>\n");
|
|
break;
|
|
case '<':
|
|
rsprintf("<");
|
|
break;
|
|
case '>':
|
|
rsprintf(">");
|
|
break;
|
|
case '&':
|
|
rsprintf("&");
|
|
break;
|
|
case '\"':
|
|
rsprintf(""");
|
|
break;
|
|
case ' ':
|
|
rsprintf(" ");
|
|
break;
|
|
|
|
/* the translation for the search highliting */
|
|
|
|
case '\001':
|
|
rsprintf("<");
|
|
break;
|
|
case '\002':
|
|
rsprintf(">");
|
|
break;
|
|
case '\003':
|
|
rsprintf("\"");
|
|
break;
|
|
case '\004':
|
|
rsprintf(" ");
|
|
break;
|
|
|
|
default:
|
|
rsprintf("%c", text[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void strencode_nouml(char *text) {
|
|
int i;
|
|
|
|
for (i = 0; i < (int) strlen(text); i++) {
|
|
switch (text[i]) {
|
|
case '\n':
|
|
rsprintf("<br>\n");
|
|
break;
|
|
case '<':
|
|
rsprintf("<");
|
|
break;
|
|
case '>':
|
|
rsprintf(">");
|
|
break;
|
|
case '\"':
|
|
rsprintf(""");
|
|
break;
|
|
case ' ':
|
|
rsprintf(" ");
|
|
break;
|
|
|
|
/* the translation for the search highliting */
|
|
|
|
case '\001':
|
|
rsprintf("<");
|
|
break;
|
|
case '\002':
|
|
rsprintf(">");
|
|
break;
|
|
case '\003':
|
|
rsprintf("\"");
|
|
break;
|
|
case '\004':
|
|
rsprintf(" ");
|
|
break;
|
|
|
|
default:
|
|
rsprintf("%c", text[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void xmlencode(char *text) {
|
|
int i;
|
|
|
|
for (i = 0; i < (int) strlen(text); i++) {
|
|
switch (text[i]) {
|
|
case '<':
|
|
rsprintf("<");
|
|
break;
|
|
case '>':
|
|
rsprintf(">");
|
|
break;
|
|
case '&':
|
|
rsprintf("&");
|
|
break;
|
|
case '\"':
|
|
rsprintf(""");
|
|
break;
|
|
|
|
default:
|
|
rsprintf("%c", text[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void strencode2(char *b, const char *text, int size) {
|
|
int i;
|
|
|
|
*b = 0;
|
|
for (i = 0; i < (int) strlen(text); i++) {
|
|
switch (text[i]) {
|
|
case '\n':
|
|
if (strlen(b) + 5 >= (unsigned int) size)
|
|
return;
|
|
strcat(b, "<br>\n");
|
|
break;
|
|
case '<':
|
|
if (strlen(b) + 4 >= (unsigned int) size)
|
|
return;
|
|
strcat(b, "<");
|
|
break;
|
|
case '>':
|
|
if (strlen(b) + 4 >= (unsigned int) size)
|
|
return;
|
|
strcat(b, ">");
|
|
break;
|
|
case '\"':
|
|
if (strlen(b) + 6 >= (unsigned int) size)
|
|
return;
|
|
strcat(b, """);
|
|
break;
|
|
default:
|
|
if (strlen(b) + 1 >= (unsigned int) size)
|
|
return;
|
|
sprintf(b + strlen(b), "%c", text[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int build_subst_list(LOGBOOK *lbs, char list[][NAME_LENGTH], char value[][NAME_LENGTH],
|
|
char attrib[][NAME_LENGTH], BOOL format_date) {
|
|
int i;
|
|
char str[NAME_LENGTH+100], format[256], full_name[256], user_email[256];
|
|
time_t t;
|
|
struct tm *ts;
|
|
|
|
/* copy attribute list */
|
|
i = 0;
|
|
if (attrib != NULL)
|
|
for (; i < lbs->n_attr; i++) {
|
|
strcpy(list[i], attr_list[i]);
|
|
if (attrib) {
|
|
if ((attr_flags[i] & AF_DATE) && format_date) {
|
|
|
|
t = (time_t) atoi(attrib[i]);
|
|
ts = localtime(&t);
|
|
assert(ts);
|
|
|
|
sprintf(str, "Date format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
|
|
my_strftime(value[i], NAME_LENGTH, format, ts);
|
|
|
|
} else if ((attr_flags[i] & AF_DATETIME) && format_date) {
|
|
|
|
t = (time_t) atoi(attrib[i]);
|
|
ts = localtime(&t);
|
|
assert(ts);
|
|
|
|
sprintf(str, "Time format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
my_strftime(value[i], NAME_LENGTH, format, ts);
|
|
|
|
} else
|
|
strcpy(value[i], attrib[i]);
|
|
} else
|
|
strlcpy(value[i], isparam(attr_list[i]) ? getparam(attr_list[i]) : "", NAME_LENGTH);
|
|
}
|
|
|
|
/* add remote host */
|
|
strcpy(list[i], "remote_host");
|
|
strlcpy(value[i++], rem_host, NAME_LENGTH);
|
|
|
|
/* add local host */
|
|
strcpy(list[i], "host");
|
|
strlcpy(value[i++], host_name, NAME_LENGTH);
|
|
|
|
/* add forwarded user */
|
|
strcpy(list[i], "http_user");
|
|
strlcpy(value[i++], http_user, NAME_LENGTH);
|
|
|
|
/* add user names */
|
|
strcpy(list[i], "short_name");
|
|
if (isparam("unm")) {
|
|
strlcpy(value[i++], getparam("unm"), NAME_LENGTH);
|
|
get_user_line(lbs, getparam("unm"), NULL, full_name, user_email, NULL, NULL, NULL);
|
|
} else {
|
|
strlcpy(value[i++], loc("Anonymous"), NAME_LENGTH);
|
|
strcpy(full_name, loc("Anonymous"));
|
|
user_email[0] = 0;
|
|
}
|
|
|
|
strcpy(list[i], "long_name");
|
|
strlcpy(value[i++], full_name, NAME_LENGTH);
|
|
|
|
/* add email */
|
|
if (user_email[0]) {
|
|
strcpy(list[i], "user_email");
|
|
strcpy(value[i], "mailto:");
|
|
strlcat(value[i++], user_email, NAME_LENGTH);
|
|
}
|
|
|
|
/* add logbook */
|
|
if (lbs) {
|
|
strcpy(list[i], "logbook");
|
|
strlcpy(value[i++], lbs->name, NAME_LENGTH);
|
|
|
|
/* add logbook */
|
|
strcpy(list[i], "elogbook");
|
|
strlcpy(value[i++], lbs->name_enc, NAME_LENGTH);
|
|
}
|
|
|
|
/* add date */
|
|
strcpy(list[i], "date");
|
|
time(&t);
|
|
if (format_date) {
|
|
ts = localtime(&t);
|
|
assert(ts);
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
my_strftime(str, sizeof(str), format, ts);
|
|
} else
|
|
sprintf(str, "%d", (int) t);
|
|
strcpy(value[i++], str);
|
|
|
|
/* add UTC date */
|
|
strcpy(list[i], "utcdate");
|
|
time(&t);
|
|
if (format_date) {
|
|
ts = gmtime(&t);
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
my_strftime(str, sizeof(str), format, ts);
|
|
} else
|
|
sprintf(str, "%d", (int) t);
|
|
strcpy(value[i++], str);
|
|
|
|
/* add ELOG version and revision */
|
|
strcpy(list[i], "version");
|
|
strcpy(value[i++], VERSION);
|
|
|
|
strcpy(list[i], "revision");
|
|
sprintf(value[i++], "%s", git_revision());
|
|
|
|
return i;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void add_subst_list(char list[][NAME_LENGTH], char value[][NAME_LENGTH], const char *item, const char *str, int *i) {
|
|
strlcpy(list[*i], item, NAME_LENGTH);
|
|
strlcpy(value[(*i)++], str, NAME_LENGTH);
|
|
}
|
|
|
|
void add_subst_time(LOGBOOK *lbs, char list[][NAME_LENGTH], char value[][NAME_LENGTH], const char *item,
|
|
const char *date, int *i, int flags) {
|
|
char format[80], str[256];
|
|
time_t ltime;
|
|
struct tm *pts;
|
|
|
|
if (flags & (AF_DATE | AF_DATETIME)) {
|
|
ltime = date_to_ltime(date);
|
|
sprintf(str, "%d", (int) ltime);
|
|
add_subst_list(list, value, item, str, i);
|
|
} else {
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
ltime = date_to_ltime(date);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
add_subst_list(list, value, item, str, i);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL get_password_file(LOGBOOK *lbs, char *file_name, int size) {
|
|
char str[256];
|
|
|
|
getcfg(lbs->name, "Password file", str, sizeof(str));
|
|
|
|
if (!str[0])
|
|
return FALSE;
|
|
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strlcpy(file_name, str, size);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, size);
|
|
strlcat(file_name, str, size);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_change_pwd_page(LOGBOOK *lbs) {
|
|
char str[1000], config[256], old_pwd[256], new_pwd[256], new_pwd2[256], user[256], auth[32], error_str[256];
|
|
int wrong_pwd;
|
|
/* otherwise calls with null lbs which make this procedure crash */
|
|
if (lbs == NULL)
|
|
lbs = get_first_lbs_with_global_passwd();
|
|
|
|
if (lbs == NULL)
|
|
return;
|
|
getcfg(lbs->name, "Authentication", auth, sizeof(auth));
|
|
|
|
old_pwd[0] = new_pwd[0] = new_pwd2[0] = 0;
|
|
|
|
if (isparam("oldpwd"))
|
|
strlcpy(old_pwd, getparam("oldpwd"), sizeof(old_pwd));
|
|
if (isparam("newpwd"))
|
|
strlcpy(new_pwd, getparam("newpwd"), sizeof(new_pwd));
|
|
if (isparam("newpwd2"))
|
|
strlcpy(new_pwd2, getparam("newpwd2"), sizeof(new_pwd2));
|
|
|
|
if (isparam("unm"))
|
|
strlcpy(user, getparam("unm"), sizeof(user));
|
|
else
|
|
user[0] = 0;
|
|
|
|
if (isparam("config")) {
|
|
strlcpy(str, getparam("config"), sizeof(str));
|
|
strencode2(user, str, sizeof(user));
|
|
}
|
|
|
|
wrong_pwd = FALSE;
|
|
error_str[0] = 0;
|
|
|
|
if (isparam("fail")) {
|
|
wrong_pwd = atoi(getparam("fail"));
|
|
if (!wrong_pwd) {
|
|
wrong_pwd = 3;
|
|
strlcpy(error_str, getparam("fail"), sizeof(error_str));
|
|
}
|
|
}
|
|
|
|
if (old_pwd[0] || new_pwd[0]) {
|
|
if (user[0]) {
|
|
|
|
if (stristr(auth, "Webserver") || stristr(auth, "PAM")) {
|
|
if (strcmp(new_pwd, new_pwd2) != 0)
|
|
wrong_pwd = 2;
|
|
} else {
|
|
/* administrator does not have to supply old password if changing other user's password */
|
|
if (isparam("unm") && is_admin_user(lbs, getparam("unm"))
|
|
&& stricmp(getparam("unm"), user) != 0)
|
|
wrong_pwd = 0;
|
|
else {
|
|
if (!auth_verify_password(lbs, user, old_pwd, str, sizeof(str)))
|
|
wrong_pwd = 1;
|
|
}
|
|
|
|
if (strcmp(new_pwd, new_pwd2) != 0)
|
|
wrong_pwd = 2;
|
|
}
|
|
}
|
|
|
|
if (new_pwd[0]) {
|
|
/* replace password */
|
|
error_str[0] = 0;
|
|
if (!wrong_pwd)
|
|
wrong_pwd =
|
|
(auth_change_password(lbs, user, old_pwd, new_pwd, error_str, sizeof(error_str)) == 0);
|
|
|
|
if (!wrong_pwd && isparam("unm") && strcmp(user, getparam("unm")) == 0) {
|
|
redirect(lbs, "");
|
|
return;
|
|
}
|
|
|
|
if (!wrong_pwd) {
|
|
/* redirect back to configuration page */
|
|
if (isparam("config")) {
|
|
strlcpy(config, getparam("config"), sizeof(config));
|
|
sprintf(str, "?cmd=%s&cfg_user=%s", loc("Config"), config);
|
|
} else
|
|
sprintf(str, "?cmd=%s", loc("Config"));
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/* go though one redirection to avoid passwords to be shown in URL */
|
|
if (wrong_pwd) {
|
|
if (error_str[0])
|
|
sprintf(str, "?cmd=%s&config=%s&fail=%s", loc("Change password"), getparam("unm"), error_str);
|
|
else
|
|
sprintf(str, "?cmd=%s&config=%s&fail=%d", loc("Change password"), getparam("unm"), wrong_pwd);
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
show_standard_header(lbs, TRUE, loc("ELOG change password"), NULL, FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
|
|
if (wrong_pwd == 1) {
|
|
if (error_str[0]) {
|
|
strencode2(str, error_str, sizeof(str));
|
|
rsprintf("<tr><td colspan=2 class=\"dlgerror\">%s!</td></tr>\n", str);
|
|
} else
|
|
rsprintf("<tr><td colspan=2 class=\"dlgerror\">%s!</td></tr>\n", loc("Wrong password"));
|
|
}
|
|
|
|
if (wrong_pwd == 2)
|
|
rsprintf("<tr><td colspan=2 class=\"dlgerror\">%s!</td></tr>\n",
|
|
loc("New passwords do not match, please retype"));
|
|
|
|
if (wrong_pwd == 3) {
|
|
strencode2(str, error_str, sizeof(str));
|
|
rsprintf("<tr><td colspan=2 class=\"dlgerror\">%s!</td></tr>\n", str);
|
|
}
|
|
|
|
rsprintf("<tr><td colspan=2 class=\"dlgtitle\">\n");
|
|
|
|
rsprintf("<input type=hidden name=config value=\"%s\">", user);
|
|
|
|
rsprintf("%s \"%s\"</td></tr>\n", loc("Change password for user"), user);
|
|
|
|
/* do not ask for old pwasword if admin changes other user's password */
|
|
if (isparam("unm")) {
|
|
if (!is_admin_user(lbs, getparam("unm")) || stricmp(getparam("unm"), user) == 0) {
|
|
if (isparam("oldpwd") && !(wrong_pwd == 1)) // hidden password for password recovery
|
|
rsprintf("<input type=hidden name=oldpwd value=\"%s\"", getparam("oldpwd"));
|
|
else {
|
|
rsprintf("<tr><td align=right class=\"dlgform\">%s:\n", loc("Old password"));
|
|
rsprintf("<td align=left class=\"dlgform\"><input type=password name=oldpwd>\n");
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
rsprintf("<tr><td align=right class=\"dlgform\">%s:</td>\n", loc("New password"));
|
|
rsprintf("<td align=left class=\"dlgform\"><input type=password name=newpwd></td></tr>\n");
|
|
|
|
rsprintf("<tr><td align=right class=\"dlgform\">%s:</td>\n", loc("Retype new password"));
|
|
rsprintf("<td align=left class=\"dlgform\"><input type=password name=newpwd2></td></tr>\n");
|
|
|
|
rsprintf("<tr><td align=center colspan=2 class=\"dlgform\"><input type=submit value=\"%s\"></td></tr>",
|
|
loc("Submit"));
|
|
|
|
rsprintf("</table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void get_auto_index(LOGBOOK *lbs, int index, char *format, char *retstr, int size)
|
|
/* return value of specific attribute of last entry, can be used to
|
|
auto-increment tags */
|
|
{
|
|
int i, message_id, loc, len, old_index;
|
|
char *p, attrib[MAX_N_ATTR][NAME_LENGTH], att[MAX_ATTACHMENTS][256], draft[256];
|
|
time_t now;
|
|
|
|
if (strchr(format, '%') == NULL && strchr(format, '#') == NULL) {
|
|
strlcpy(retstr, format, size);
|
|
return;
|
|
}
|
|
|
|
time(&now);
|
|
my_strftime(retstr, size, format, localtime(&now));
|
|
|
|
p = strchr(retstr, '#');
|
|
if (p == NULL)
|
|
return;
|
|
|
|
if (p > retstr && *(p - 1) == '\\') { // escape
|
|
memmove(p - 1, p, strlen(p) + 1);
|
|
return;
|
|
}
|
|
|
|
/* record location and length of ###'s */
|
|
for (i = loc = 0, len = 1; i < (int) strlen(retstr); i++) {
|
|
if (retstr[i] == '#') {
|
|
if (loc == 0)
|
|
loc = i;
|
|
if (i > 0 && retstr[i - 1] == '#')
|
|
len++;
|
|
}
|
|
}
|
|
|
|
/* get attribute from last entry */
|
|
message_id = el_search_message(lbs, EL_LAST, 0, FALSE);
|
|
|
|
if (!message_id) {
|
|
/* start with 1 */
|
|
sprintf(retstr + loc, "%0*d", len, 1);
|
|
return;
|
|
}
|
|
|
|
/* search all entries to find largest index */
|
|
old_index = 0;
|
|
do {
|
|
el_retrieve(lbs, message_id, NULL, attr_list, attrib, lbs->n_attr, NULL, 0, NULL, NULL, att, NULL,
|
|
NULL, draft);
|
|
|
|
/* if same date found, obtain largest index */
|
|
if (strlen(attrib[index]) > 0 && strncmp(attrib[index], retstr, loc) == 0)
|
|
if (atoi(attrib[index] + loc) > old_index)
|
|
old_index = atoi(attrib[index] + loc);
|
|
|
|
message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);
|
|
|
|
} while (message_id);
|
|
|
|
/* increment index */
|
|
sprintf(retstr + loc, "%0*d", len, old_index + 1);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL is_author(LOGBOOK *lbs, char attrib[MAX_N_ATTR][NAME_LENGTH], char *owner) {
|
|
char str[NAME_LENGTH+100], preset[NAME_LENGTH], full_name[NAME_LENGTH], attr[NAME_LENGTH];
|
|
int i;
|
|
|
|
/* check if current user is admin */
|
|
if (is_admin_user(lbs, getparam("unm")))
|
|
return TRUE;
|
|
|
|
/* search attribute which contains short_name of author */
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Preset %s", attr);
|
|
if (getcfg(lbs->name, str, preset, sizeof(preset))) {
|
|
if (strstr(preset, "$short_name")) {
|
|
if (!isparam("unm") || strstr(attrib[i], getparam("unm")) == NULL) {
|
|
strcpy(owner, attrib[i]);
|
|
return FALSE;
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i == lbs->n_attr) {
|
|
/* if not found, search attribute which contains full_name of author */
|
|
if (isparam("unm")) {
|
|
get_full_name(lbs, getparam("unm"), full_name);
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Preset %s", attr);
|
|
if (getcfg(lbs->name, str, preset, sizeof(preset))) {
|
|
if (strstr(preset, "$long_name")) {
|
|
if (strstr(attrib[i], full_name) == NULL) {
|
|
strcpy(owner, attrib[i]);
|
|
return FALSE;
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL get_author(LOGBOOK *lbs, char attrib[MAX_N_ATTR][NAME_LENGTH], char *author) {
|
|
char attr[NAME_LENGTH], str[NAME_LENGTH+100], preset[NAME_LENGTH];
|
|
int i;
|
|
|
|
/* search attribute which contains full_name of author */
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Preset %s", attr);
|
|
if (getcfg(lbs->name, str, preset, sizeof(preset))) {
|
|
if (stristr(preset, "$long_name")) {
|
|
strcpy(author, attrib[i]);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if not found, search attribute which contains short_name of author */
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Preset %s", attr);
|
|
if (getcfg(lbs->name, str, preset, sizeof(preset))) {
|
|
if (stristr(preset, "$short_name")) {
|
|
strcpy(author, attrib[i]);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL is_cond_attr(int index) {
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++)
|
|
if (strchr(attr_options[index][i], '{') && strchr(attr_options[index][i], '}'))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_date_selector(int day, int month, int year, const char *index) {
|
|
int i;
|
|
|
|
rsprintf("<select name=\"m%s\">\n", index);
|
|
|
|
if (isparam("nsel"))
|
|
rsprintf("<option value=\"<keep>\">- %s -\n", loc("keep original values"));
|
|
else
|
|
rsprintf("<option value=\"\">\n");
|
|
for (i = 0; i < 12; i++)
|
|
if (i + 1 == month)
|
|
rsprintf("<option selected value=\"%d\">%s\n", i + 1, month_name(i));
|
|
else
|
|
rsprintf("<option value=\"%d\">%s\n", i + 1, month_name(i));
|
|
rsprintf("</select>\n");
|
|
|
|
rsprintf("<select name=\"d%s\">", index);
|
|
|
|
rsprintf("<option value=\"\">\n");
|
|
for (i = 0; i < 31; i++)
|
|
if (i + 1 == day)
|
|
rsprintf("<option selected value=%d>%d\n", i + 1, i + 1);
|
|
else
|
|
rsprintf("<option value=%d>%d\n", i + 1, i + 1);
|
|
rsprintf("</select>\n");
|
|
|
|
rsprintf(" %s: ", loc("Year"));
|
|
if (year)
|
|
rsprintf("<input type=\"text\" size=5 maxlength=5 name=\"y%s\" value=\"%d\">", index, year);
|
|
else
|
|
rsprintf("<input type=\"text\" size=5 maxlength=5 name=\"y%s\">", index);
|
|
|
|
rsprintf("\n<script language=\"javascript\" type=\"text/javascript\">\n");
|
|
rsprintf("<!--\n");
|
|
rsprintf("function opencal(i)\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" window.open(\"cal.html?i=\"+i, \"\",\n");
|
|
rsprintf(" \"top=280,left=350,width=300,height=195,dependent=yes,menubar=no,status=no,");
|
|
rsprintf("scrollbars=no,location=no,resizable=yes\");\n");
|
|
rsprintf("}\n\n");
|
|
|
|
rsprintf(" document.write(\" \");\n");
|
|
rsprintf(" document.write(\"<a href=\\\"javascript:opencal('%s')\\\">\");\n", index);
|
|
rsprintf(" document.writeln(\"<img src=\\\"cal.png\\\" align=\\\"middle\\\" border=\\\"0\\\"");
|
|
rsprintf("alt=\\\"%s\\\"></a>\");\n", loc("Pick a date"));
|
|
|
|
rsprintf("//-->\n");
|
|
rsprintf("</script> \n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_time_selector(int hour, int min, int sec, const char *index) {
|
|
int i;
|
|
|
|
rsprintf("<select name=\"h%s\">\n", index);
|
|
|
|
if (isparam("nsel"))
|
|
rsprintf("<option value=\"<keep>\">- %s -\n", loc("keep original values"));
|
|
else
|
|
rsprintf("<option value=\"\">\n");
|
|
for (i = 0; i < 24; i++)
|
|
if (i == hour)
|
|
rsprintf("<option selected value=\"%d\">%02d\n", i, i);
|
|
else
|
|
rsprintf("<option value=\"%d\">%02d\n", i, i);
|
|
rsprintf("</select> <b>:</b> \n");
|
|
|
|
rsprintf("<select name=\"n%s\">", index);
|
|
rsprintf("<option value=\"\">\n");
|
|
for (i = 0; i < 60; i++)
|
|
if (i == min)
|
|
rsprintf("<option selected value=%d>%02d\n", i, i);
|
|
else
|
|
rsprintf("<option value=%d>%02d\n", i, i);
|
|
rsprintf("</select> <b>:</b> \n");
|
|
|
|
rsprintf("<select name=\"c%s\">", index);
|
|
rsprintf("<option value=\"\">\n");
|
|
for (i = 0; i < 60; i++)
|
|
if (i == sec)
|
|
rsprintf("<option selected value=%d>%02d\n", i, i);
|
|
else
|
|
rsprintf("<option value=%d>%02d\n", i, i);
|
|
rsprintf("</select>\n");
|
|
|
|
rsprintf("\n<script language=\"javascript\" type=\"text/javascript\">\n");
|
|
rsprintf("<!--\n");
|
|
rsprintf("function settime_%s()\n", index);
|
|
rsprintf("{\n");
|
|
rsprintf(" var d = new Date();\n");
|
|
rsprintf(" document.form1.d%s.value = d.getDate();\n", index);
|
|
rsprintf(" document.form1.m%s.value = d.getMonth()+1;\n", index);
|
|
rsprintf(" year = d.getYear();\n");
|
|
rsprintf(" if (year < 1900)\n");
|
|
rsprintf(" year += 1900;\n");
|
|
rsprintf(" document.form1.y%s.value = year;\n", index);
|
|
rsprintf(" document.form1.h%s.value = d.getHours();\n", index);
|
|
rsprintf(" document.form1.n%s.value = d.getMinutes();\n", index);
|
|
rsprintf(" document.form1.c%s.value = d.getSeconds();\n", index);
|
|
rsprintf("}\n\n");
|
|
|
|
rsprintf(" document.write(\" \");\n");
|
|
rsprintf(" document.write(\"<a href=\\\"javascript:settime_%s()\\\">\");\n", index);
|
|
rsprintf(" document.writeln(\"<img src=\\\"clock.png\\\" align=\\\"middle\\\" border=\\\"0\\\" ");
|
|
rsprintf("alt=\\\"%s\\\"></a>\");\n", loc("Insert current time"));
|
|
|
|
rsprintf("//-->\n");
|
|
rsprintf("</script>\n");
|
|
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void attrib_from_param(int n_attr, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
|
|
int i, j, first, year, month, day, hour, min, sec;
|
|
char str[NAME_LENGTH+100], ua[NAME_LENGTH];
|
|
time_t ltime;
|
|
struct tm ts;
|
|
|
|
for (i = 0; i < n_attr; i++) {
|
|
strcpy(ua, attr_list[i]);
|
|
stou(ua);
|
|
if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) {
|
|
attrib[i][0] = 0;
|
|
first = 1;
|
|
for (j = 0; j < MAX_N_LIST; j++) {
|
|
sprintf(str, "%s_%d", ua, j);
|
|
if (isparam(str)) {
|
|
if (first)
|
|
first = 0;
|
|
else
|
|
strlcat(attrib[i], " | ", NAME_LENGTH);
|
|
if (strlen(attrib[i]) + strlen(getparam(str)) < NAME_LENGTH - 2)
|
|
strlcat(attrib[i], getparam(str), NAME_LENGTH);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
} else if (attr_flags[i] & AF_DATE) {
|
|
|
|
if (isparam(ua))
|
|
strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
|
|
else {
|
|
sprintf(str, "y%d", i);
|
|
year = atoi(isparam(str) ? getparam(str) : "");
|
|
if (year < 100)
|
|
year += 2000;
|
|
|
|
sprintf(str, "m%d", i);
|
|
month = atoi(isparam(str) ? getparam(str) : "");
|
|
|
|
sprintf(str, "d%d", i);
|
|
day = atoi(isparam(str) ? getparam(str) : "");
|
|
|
|
memset(&ts, 0, sizeof(struct tm));
|
|
ts.tm_year = year - 1900;
|
|
ts.tm_mon = month - 1;
|
|
ts.tm_mday = day;
|
|
ts.tm_hour = 12;
|
|
|
|
if (month && day) {
|
|
ltime = mktime(&ts);
|
|
sprintf(attrib[i], "%d", (int) ltime);
|
|
} else
|
|
strcpy(attrib[i], "");
|
|
}
|
|
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
|
|
if (isparam(ua))
|
|
strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
|
|
else {
|
|
sprintf(str, "y%d", i);
|
|
year = atoi(isparam(str) ? getparam(str) : "");
|
|
if (year < 100)
|
|
year += 2000;
|
|
|
|
sprintf(str, "m%d", i);
|
|
month = atoi(isparam(str) ? getparam(str) : "");
|
|
|
|
sprintf(str, "d%d", i);
|
|
day = atoi(isparam(str) ? getparam(str) : "");
|
|
|
|
sprintf(str, "h%d", i);
|
|
hour = atoi(isparam(str) ? getparam(str) : "");
|
|
|
|
sprintf(str, "n%d", i);
|
|
min = atoi(isparam(str) ? getparam(str) : "");
|
|
|
|
sprintf(str, "c%d", i);
|
|
sec = atoi(isparam(str) ? getparam(str) : "");
|
|
|
|
memset(&ts, 0, sizeof(struct tm));
|
|
ts.tm_year = year - 1900;
|
|
ts.tm_mon = month - 1;
|
|
ts.tm_mday = day;
|
|
ts.tm_hour = hour;
|
|
ts.tm_min = min;
|
|
ts.tm_sec = sec;
|
|
ts.tm_isdst = -1;
|
|
|
|
if (month && day) {
|
|
ltime = mktime(&ts);
|
|
sprintf(attrib[i], "%d", (int) ltime);
|
|
} else
|
|
strcpy(attrib[i], "");
|
|
}
|
|
|
|
} else {
|
|
if (isparam(attr_list[i]))
|
|
strlcpy(attrib[i], getparam(attr_list[i]), NAME_LENGTH);
|
|
else if (isparam(ua))
|
|
strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
|
|
else
|
|
attrib[i][0] = 0;
|
|
if (attrib[i][0] == '^' && attrib[i][strlen(attrib[i])-1] == '$') {
|
|
memmove(&attrib[i][0], &attrib[i][1], strlen(attrib[i]));
|
|
attrib[i][strlen(attrib[i])-1] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void ricon(const char *name, const char *comment, const char *onclick) {
|
|
rsprintf
|
|
("<img align=\"middle\" name=\"%s\" src=\"icons/elc_%s.png\" alt=\"%s\" title=\"%s\" border=\"0\"\n",
|
|
name, name, comment, comment);
|
|
rsprintf(" onclick=\"%s\"", onclick);
|
|
rsprintf(" onmousedown=\"document.images.%s.src='icons/eld_%s.png'\"\n", name, name);
|
|
rsprintf(" onmouseup=\"document.images.%s.src='icons/elc_%s.png'\"\n", name, name);
|
|
rsprintf(" onmouseover=\"this.style.cursor='pointer';\" />\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void rsicon(const char *name, const char *comment, const char *elcode) {
|
|
rsprintf("<img align=\"middle\" name=\"%s\" src=\"icons/elc_%s.png\" alt=\"%s\" title=\"%s\" border=\"0\"",
|
|
name, name, comment, comment);
|
|
rsprintf(" onclick=\"elcode(document.form1.Text, '','%s')\"", elcode);
|
|
rsprintf(" onmousedown=\"document.images.%s.src='icons/eld_%s.png'\"", name, name);
|
|
rsprintf(" onmouseup=\"document.images.%s.src='icons/elc_%s.png'\"", name, name);
|
|
rsprintf(" onmouseover=\"this.style.cursor='pointer';\" />");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void compare_attributes(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], int *n) {
|
|
int status, i, n_reply;
|
|
char reply_to[MAX_REPLY_TO * 10], *attr, *list;
|
|
|
|
attr = (char *)xmalloc(MAX_N_ATTR * NAME_LENGTH);
|
|
assert(attr);
|
|
status = el_retrieve(lbs, message_id, NULL, attr_list, (char (*)[NAME_LENGTH]) attr, lbs->n_attr,
|
|
NULL, NULL, NULL, reply_to, NULL, NULL, NULL, NULL);
|
|
if (status != EL_SUCCESS) {
|
|
xfree(attr);
|
|
return;
|
|
}
|
|
|
|
if (*n == 0)
|
|
memcpy(attrib, attr, sizeof(MAX_N_ATTR * NAME_LENGTH));
|
|
else {
|
|
for (i = 0; i < lbs->n_attr; i++)
|
|
if (!strieq(attrib[i], attr + i * NAME_LENGTH))
|
|
sprintf(attrib[i], "- %s -", loc("keep original values"));
|
|
}
|
|
(*n)++;
|
|
if (isparam("elmode") && strieq(getparam("elmode"), "threaded")) {
|
|
|
|
list = (char *)xmalloc(MAX_N_ATTR * NAME_LENGTH);
|
|
assert(list);
|
|
|
|
// go through all replies in threaded mode
|
|
n_reply = strbreak(reply_to, (char (*)[NAME_LENGTH]) list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n_reply; i++) {
|
|
compare_attributes(lbs, atoi(list + i * NAME_LENGTH), attrib, n);
|
|
|
|
xfree(list);
|
|
}
|
|
}
|
|
|
|
xfree(attr);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int check_drafts(LOGBOOK *lbs) {
|
|
time_t now;
|
|
char str[1000], draft[256], title[256], datetime[256], attrib[MAX_N_ATTR][NAME_LENGTH];
|
|
int i, n_draft, *draft_id = NULL;
|
|
|
|
/* if we got here already and user clicked "Create new entry", ignore is set and we skip this */
|
|
if (isparam("ignore"))
|
|
return 0;
|
|
|
|
time(&now);
|
|
/* check if any recent draft */
|
|
for (i = n_draft = 0; i < *(lbs->n_el_index); i++)
|
|
if (lbs->el_index[i].file_time > now - 3600 * 24 * 7) { // only one week to reduce seek time
|
|
el_retrieve(lbs, lbs->el_index[i].message_id, NULL, attr_list, attrib, lbs->n_attr,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, draft);
|
|
if (draft[0] && is_author(lbs, attrib, draft)) {
|
|
if (n_draft == 0)
|
|
draft_id = (int *) xmalloc(sizeof(int));
|
|
else
|
|
draft_id = (int *) xrealloc(draft_id, sizeof(int) * (n_draft + 1));
|
|
draft_id[n_draft] = lbs->el_index[i].message_id;
|
|
n_draft++;
|
|
}
|
|
}
|
|
|
|
if (n_draft == 0)
|
|
return 0;
|
|
|
|
if (n_draft == 1)
|
|
sprintf(title, "%s", loc("Pending draft available"));
|
|
else
|
|
sprintf(title, loc("%d pending drafts available"), n_draft);
|
|
|
|
show_standard_header(lbs, TRUE, "Draft query", NULL, FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>\n");
|
|
|
|
rsprintf("<tr><td colspan=\"2\" class=\"dlgtitle\">");
|
|
rsprintf("%s</td></tr>\n", title);
|
|
|
|
for (i = 0; i < n_draft; i++) {
|
|
el_retrieve(lbs, draft_id[i], datetime, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, draft);
|
|
|
|
rsprintf("<tr><td class=\"draftsel\" align=\"center\">");
|
|
sprintf(str, loc("Draft entry created on %s by %s"), datetime, draft);
|
|
rsprintf("%s</td>\n", str);
|
|
rsprintf("<td class=\"draftsel\">");
|
|
rsprintf("<input type=button value=\"%s\" onClick=\"window.location.href='%d?cmd=%s';\">",
|
|
loc("Edit"), draft_id[i], loc("Edit"));
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
rsprintf("<tr><td colspan=\"2\" align=center class=\"dlgform\">");
|
|
rsprintf("<input type=button value=\"%s\" onClick=\"window.location.href='?cmd=%s&ignore=1';\">\n",
|
|
loc("Create new entry"),
|
|
loc("New"));
|
|
rsprintf("</td></tr>\n\n");
|
|
|
|
rsprintf("</table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\r\n");
|
|
|
|
xfree(draft_id);
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int check_edit_time(LOGBOOK *lbs, int message_id) {
|
|
char str[256];
|
|
int i;
|
|
|
|
if (is_admin_user(lbs, getparam("unm"))) {
|
|
if (getcfg(lbs->name, "Admin Restrict edit time", str, sizeof(str))) {
|
|
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 && time(NULL) > lbs->el_index[i].file_time + atof(str) * 3600 && atof(str) > 0) {
|
|
sprintf(str, loc("Entry can only be edited %1.2lg hours after creation"), atof(str));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
if (getcfg(lbs->name, "Restrict edit time", str, sizeof(str))) {
|
|
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 && time(NULL) > lbs->el_index[i].file_time + atof(str) * 3600) {
|
|
sprintf(str, loc("Entry can only be edited %1.2lg hours after creation"), atof(str));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_edit_form(LOGBOOK *lbs, int message_id, BOOL breply, BOOL bedit, BOOL bupload, BOOL breedit,
|
|
BOOL bduplicate, BOOL bpreview) {
|
|
int i, j, n, index, aindex, size, width, height, fh, length, input_size, input_maxlen,
|
|
format_flags[MAX_N_ATTR], year, month, day, hour, min, sec, n_attr, n_disp_attr, n_lines,
|
|
attr_index[MAX_N_ATTR], enc_selected, show_text, n_moptions, display_inline,
|
|
allowed_encoding, thumb_status, max_n_lines, fixed_text, autosave, new_entry, status;
|
|
char str[4000], str2[NAME_LENGTH], preset[2 * NAME_LENGTH], *p, *pend, star[80],
|
|
comment[1000], reply_string[256], list[MAX_N_ATTR][NAME_LENGTH], file_name[256], *buffer,
|
|
format[256], date[80], script_onload[256], script_onfocus[256], script_onunload[256],
|
|
attrib[MAX_N_ATTR][NAME_LENGTH], *text, orig_tag[80], reply_tag[MAX_REPLY_TO * 10],
|
|
att[MAX_ATTACHMENTS][256], encoding[80], slist[MAX_N_ATTR + 10][NAME_LENGTH],
|
|
svalue[MAX_N_ATTR + 10][NAME_LENGTH], owner[256], locked_by[256], class_value[80], class_name[80],
|
|
ua[NAME_LENGTH], mid[80], title[10100], login_name[256], full_name[256],
|
|
orig_author[256], attr_moptions[MAX_N_LIST][NAME_LENGTH], ref[4400], file_enc[256], tooltip[1100],
|
|
enc_attr[NAME_LENGTH], user_email[256], cmd[1000], thumb_name[256], thumb_ref[256], **user_list, fid[20],
|
|
upwd[80], subdir[256], draft[256], page_title[300], attr[NAME_LENGTH];
|
|
time_t now, ltime;
|
|
char fl[8][NAME_LENGTH];
|
|
struct tm *pts;
|
|
FILE *f;
|
|
BOOL preset_text, subtable;
|
|
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
att[i][0] = 0;
|
|
|
|
for (i = 0; i < lbs->n_attr; i++)
|
|
attrib[i][0] = 0;
|
|
|
|
text = (char *)xmalloc(TEXT_SIZE);
|
|
text[0] = 0;
|
|
orig_author[0] = 0;
|
|
orig_tag[0] = 0;
|
|
encoding[0] = 0;
|
|
date[0] = 0;
|
|
locked_by[0] = 0;
|
|
new_entry = 0;
|
|
|
|
if (!message_id || breply)
|
|
new_entry = 1;
|
|
if (isparam("new_entry"))
|
|
new_entry = 1;
|
|
|
|
/* check for custom form for new entries */
|
|
if (!bedit && getcfg(lbs->name, "Custom new form", str, sizeof(str))) {
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
send_file_direct(str);
|
|
return;
|
|
}
|
|
|
|
/* check for custom form for editing an entry */
|
|
if (bedit && getcfg(lbs->name, "Custom edit form", str, sizeof(str))) {
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
send_file_direct(str);
|
|
return;
|
|
}
|
|
|
|
/* check for file attachment (mhttpd) */
|
|
if (isparam("fa")) {
|
|
strlcpy(att[0], getparam("fa"), 256);
|
|
|
|
/* remove any leading directory, to accept only files in the logbook directory ! */
|
|
if (strchr(att[0], DIR_SEPARATOR)) {
|
|
strlcpy(str, att[0], sizeof(str));
|
|
strlcpy(att[0], strrchr(str, DIR_SEPARATOR) + 1, 256);
|
|
}
|
|
}
|
|
|
|
if (breedit || bupload) {
|
|
/* get date from parameter */
|
|
if (isparam("entry_date"))
|
|
strlcpy(date, getparam("entry_date"), sizeof(date));
|
|
|
|
/* get attributes from parameters */
|
|
attrib_from_param(lbs->n_attr, attrib);
|
|
|
|
strlcpy(text, getparam("text"), TEXT_SIZE);
|
|
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
sprintf(str, "attachment%d", i);
|
|
if (isparam(str))
|
|
strlcpy(att[i], getparam(str), 256);
|
|
}
|
|
|
|
if (isparam("inlineatt")) {
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
sprintf(str, "attachment%d", i);
|
|
if (!isparam(str) && isparam("inlineatt")) {
|
|
strlcpy(att[i], getparam("inlineatt"), 256);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* get encoding */
|
|
strlcpy(encoding, isparam("encoding") ? getparam("encoding") : "", sizeof(encoding));
|
|
if (!strieq(encoding, "plain") && !strieq(encoding, "ELCode") && !strieq(encoding, "HTML"))
|
|
strcpy(encoding, "plain");
|
|
} else {
|
|
if (message_id) {
|
|
/* get message for reply/edit */
|
|
|
|
size = TEXT_SIZE;
|
|
el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, orig_tag, reply_tag,
|
|
att, encoding, locked_by, draft);
|
|
|
|
get_author(lbs, attrib, orig_author);
|
|
|
|
/* strip attachments on duplicate */
|
|
if (bduplicate)
|
|
memset(att, 0, sizeof(att));
|
|
} else if (isparam("nsel")) {
|
|
|
|
/* multi edit: get all entries and check if attributes are the same */
|
|
memset(attrib, 0, sizeof(attrib));
|
|
for (i = n = 0; i < atoi(getparam("nsel")); i++) {
|
|
sprintf(str, "s%d", i);
|
|
if (isparam(str))
|
|
compare_attributes(lbs, atoi(getparam(str)), attrib, &n);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (message_id && getcfg(lbs->name, "Use Lock", str, sizeof(str)) && atoi(str) == 1 && locked_by[0]
|
|
&& !isparam("steal")) {
|
|
sprintf(str, "%d", message_id);
|
|
sprintf(text, "%s %s", loc("Entry is currently edited by"), locked_by);
|
|
sprintf(cmd, "?cmd=%s&steal=1", loc("Edit"));
|
|
show_query(lbs, loc("Entry is locked"), text, loc("Edit anyhow"), cmd, loc("Cancel"), str);
|
|
return;
|
|
}
|
|
|
|
/* Determine encoding */
|
|
if (getcfg(lbs->name, "Allowed encoding", str, sizeof(str)))
|
|
allowed_encoding = atoi(str);
|
|
else
|
|
allowed_encoding = 7;
|
|
|
|
enc_selected = 2; /* Default is HTML */
|
|
|
|
if (allowed_encoding == 2) /* select ELCode if the only one allowed */
|
|
enc_selected = 0;
|
|
else if (allowed_encoding == 1) /* select plain if the only one allowed */
|
|
enc_selected = 1;
|
|
else if (allowed_encoding == 3) /* select ELCode if only plain and ELCode allowed */
|
|
enc_selected = 0;
|
|
|
|
/* Overwrite from config file */
|
|
if (getcfg(lbs->name, "Default Encoding", str, sizeof(str)))
|
|
enc_selected = atoi(str);
|
|
|
|
/* Overwrite from current entry */
|
|
if (encoding[0]) {
|
|
if (encoding[0] == 'E')
|
|
enc_selected = 0;
|
|
else if (encoding[0] == 'p')
|
|
enc_selected = 1;
|
|
else if (encoding[0] == 'H')
|
|
enc_selected = 2;
|
|
}
|
|
|
|
show_text = !getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1;
|
|
|
|
/* check for preset attributes without any condition */
|
|
set_condition("");
|
|
for (index = 0; index < lbs->n_attr; index++) {
|
|
|
|
/* check for preset string */
|
|
sprintf(str, "Preset %s", attr_list[index]);
|
|
if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0) {
|
|
|
|
if ((!bedit && !breply && !bduplicate) || /* don't subst on edit or reply */
|
|
(breedit && i == 2)) { /* subst on reedit only if preset is under condition */
|
|
|
|
/* check for index substitution */
|
|
if (!bedit && strchr(preset, '#')) {
|
|
/* get index */
|
|
get_auto_index(lbs, index, preset, str, sizeof(str));
|
|
strcpy(preset, str);
|
|
}
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
strsubst_list(preset, sizeof(preset), slist, svalue, i);
|
|
strcpy(attrib[index], preset);
|
|
}
|
|
}
|
|
|
|
sprintf(str, "Preset on first reply %s", attr_list[index]);
|
|
if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && breply) {
|
|
if (orig_tag[0] == 0) {
|
|
if (!breedit || (breedit && i == 2)) { /* subst on reedit only if preset is under condition */
|
|
|
|
/* check for index substitution */
|
|
if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
|
|
/* get index */
|
|
get_auto_index(lbs, index, preset, str, sizeof(str));
|
|
strcpy(preset, str);
|
|
}
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
strsubst_list(preset, sizeof(preset), slist, svalue, i);
|
|
strcpy(attrib[index], preset);
|
|
}
|
|
}
|
|
}
|
|
|
|
sprintf(str, "Preset on reply %s", attr_list[index]);
|
|
if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && breply) {
|
|
|
|
if (!breedit || (breedit && i == 2)) { /* subst on reedit only if preset is under condition */
|
|
|
|
/* check for index substitution */
|
|
if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
|
|
/* get index */
|
|
get_auto_index(lbs, index, preset, str, sizeof(str));
|
|
strcpy(preset, str);
|
|
}
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
strsubst_list(preset, sizeof(preset), slist, svalue, i);
|
|
strcpy(attrib[index], preset);
|
|
}
|
|
}
|
|
|
|
sprintf(str, "Preset on edit %s", attr_list[index]);
|
|
if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && bedit) {
|
|
|
|
if (!breedit || (breedit && i == 2)) { /* subst on reedit only if preset is under condition */
|
|
|
|
/* check for index substitution */
|
|
if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
|
|
/* get index */
|
|
get_auto_index(lbs, index, preset, str, sizeof(str));
|
|
strcpy(preset, str);
|
|
}
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
strsubst_list(preset, sizeof(preset), slist, svalue, i);
|
|
strcpy(attrib[index], preset);
|
|
}
|
|
}
|
|
|
|
sprintf(str, "Preset on duplicate %s", attr_list[index]);
|
|
if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && bduplicate) {
|
|
|
|
if (!breedit || (breedit && i == 2)) { /* subst on reedit only if preset is under condition */
|
|
|
|
/* check for index substitution */
|
|
if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
|
|
/* get index */
|
|
get_auto_index(lbs, index, preset, str, sizeof(str));
|
|
strcpy(preset, str);
|
|
}
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
strsubst_list(preset, sizeof(preset), slist, svalue, i);
|
|
strcpy(attrib[index], preset);
|
|
}
|
|
}
|
|
|
|
/* check for p<attribute> */
|
|
sprintf(str, "p%s", attr_list[index]);
|
|
if (isparam(str))
|
|
strlcpy(attrib[index], getparam(str), NAME_LENGTH);
|
|
}
|
|
|
|
/* evaluate conditional attributes */
|
|
evaluate_conditions(lbs, attrib);
|
|
|
|
/* rescan attributes if condition set */
|
|
if (_condition[0]) {
|
|
n_attr = scan_attributes(lbs->name);
|
|
if (breedit || bupload)
|
|
attrib_from_param(n_attr, attrib);
|
|
|
|
/* now check again for conditional preset */
|
|
for (index = 0; index < lbs->n_attr; index++) {
|
|
|
|
/* check for preset string */
|
|
sprintf(str, "Preset %s", attr_list[index]);
|
|
if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0) {
|
|
|
|
if ((!bedit && !breply && !bduplicate) || /* don't subst on edit or reply */
|
|
(breedit && i == 2)) { /* subst on reedit only if preset is under condition */
|
|
|
|
/* check for index substitution */
|
|
if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
|
|
/* get index */
|
|
get_auto_index(lbs, index, preset, str, sizeof(str));
|
|
strcpy(preset, str);
|
|
}
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
strsubst_list(preset, sizeof(preset), slist, svalue, i);
|
|
strcpy(attrib[index], preset);
|
|
}
|
|
}
|
|
|
|
sprintf(str, "Preset on reply %s", attr_list[index]);
|
|
if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && breply) {
|
|
|
|
if (!breedit || (breedit && i == 2)) { /* subst on reedit only if preset is under condition */
|
|
|
|
/* check for index substitution */
|
|
if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
|
|
/* get index */
|
|
get_auto_index(lbs, index, preset, str, sizeof(str));
|
|
strcpy(preset, str);
|
|
}
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
strsubst_list(preset, sizeof(preset), slist, svalue, i);
|
|
strcpy(attrib[index], preset);
|
|
}
|
|
}
|
|
|
|
sprintf(str, "Preset on duplicate %s", attr_list[index]);
|
|
if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && bduplicate) {
|
|
|
|
if (!breedit || (breedit && i == 2)) { /* subst on reedit only if preset is under condition */
|
|
|
|
/* check for index substitution */
|
|
if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
|
|
/* get index */
|
|
get_auto_index(lbs, index, preset, str, sizeof(str));
|
|
strcpy(preset, str);
|
|
}
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
strsubst_list(preset, sizeof(preset), slist, svalue, i);
|
|
strcpy(attrib[index], preset);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else
|
|
// if (_condition[0])
|
|
n_attr = lbs->n_attr;
|
|
|
|
/* check for maximum number of replies */
|
|
if (breply) {
|
|
i = 0;
|
|
p = strtok(reply_tag, ",");
|
|
while (p) {
|
|
i++;
|
|
p = strtok(NULL, ",");
|
|
}
|
|
|
|
if (i >= MAX_REPLY_TO) {
|
|
sprintf(str, loc("Maximum number of replies (%d) exceeded"), MAX_REPLY_TO);
|
|
show_error(str);
|
|
xfree(text);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* check for non-allowed branching */
|
|
if (breply && getcfg(lbs->name, "Allow branching", str, sizeof(str)) && atoi(str) == 0) {
|
|
if (reply_tag[0]) {
|
|
show_error("Branches are not allowed in this logbook");
|
|
xfree(text);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* check for author */
|
|
if (bedit && getcfg(lbs->name, "Restrict edit", str, sizeof(str)) && atoi(str) == 1) {
|
|
if (!is_author(lbs, attrib, owner)) {
|
|
strencode2(str2, owner, sizeof(str2));
|
|
sprintf(str, loc("Only user <b>%s</b> can edit this entry"), str2);
|
|
show_error(str);
|
|
xfree(text);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (bedit) {
|
|
if (isparam("nsel")) {
|
|
for (i = n = 0; i < atoi(getparam("nsel")); i++) {
|
|
sprintf(str, "s%d", i);
|
|
if (isparam(str)) {
|
|
status = check_edit_time(lbs, atoi(getparam(str)));
|
|
if (!status) {
|
|
xfree(text);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} else if (message_id) {
|
|
status = check_edit_time(lbs, message_id);
|
|
if (!status) {
|
|
xfree(text);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for locking */
|
|
if (message_id && bedit && !breedit && !bupload) {
|
|
if (getcfg(lbs->name, "Use Lock", str, sizeof(str)) && atoi(str) == 1) {
|
|
if (isparam("unm"))
|
|
get_full_name(lbs, getparam("unm"), str);
|
|
else
|
|
strlcpy(str, loc("user"), sizeof(str));
|
|
|
|
strcat(str, " ");
|
|
strcat(str, loc("on"));
|
|
strcat(str, " ");
|
|
strcat(str, rem_host);
|
|
|
|
el_lock_message(lbs, message_id, str, TRUE);
|
|
}
|
|
}
|
|
|
|
/* remove attributes for replies */
|
|
if (breply) {
|
|
getcfg(lbs->name, "Remove on reply", str, sizeof(str));
|
|
n = strbreak(str, list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
for (j = 0; j < n_attr; j++) {
|
|
if (strieq(attr_list[j], list[i]))
|
|
attrib[j][0] = 0;
|
|
}
|
|
}
|
|
|
|
/* header */
|
|
|
|
if (getcfg(lbs->name, "Edit Page Title", str, sizeof(str))) {
|
|
i = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, NULL, TRUE);
|
|
strsubst_list(page_title, sizeof(page_title), (char (*)[NAME_LENGTH]) slist,
|
|
(char (*)[NAME_LENGTH]) svalue, i);
|
|
strip_html(page_title);
|
|
} else
|
|
sprintf(page_title, "ELOG %s", lbs->name);
|
|
|
|
show_html_header(lbs, FALSE, page_title, FALSE, FALSE, NULL, FALSE, 0, 200);
|
|
|
|
/* java script for checking required attributes and to check for cancelled edits */
|
|
rsprintf("<script type=\"text/javascript\">\n");
|
|
rsprintf("<!--\n\n");
|
|
rsprintf("var submitted = false;\n");
|
|
|
|
if (breedit) {
|
|
if (isparam("entry_modified") && atoi(getparam("entry_modified")) == 1) {
|
|
rsprintf("var entry_modified = true;\n");
|
|
rsprintf("window.status = \"%s\";\n", loc("Entry has been modified"));
|
|
} else
|
|
rsprintf("var entry_modified = false;\n");
|
|
} else
|
|
rsprintf("var entry_modified = false;\n");
|
|
rsprintf("var draft_modified = false;\n");
|
|
rsprintf("var last_key = 0;\n\n");
|
|
rsprintf("var in_asend = false;\n\n");
|
|
|
|
rsprintf("function chkform(button)\n");
|
|
rsprintf("{\n");
|
|
|
|
rsprintf(" if (last_key == 13) {\n");
|
|
rsprintf(" var ret = confirm('%s');\n", loc("Really submit this entry?"));
|
|
rsprintf(" if (!ret) {\n");
|
|
rsprintf(" last_key = 0;\n");
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" }\n\n");
|
|
|
|
for (i = 0; i < n_attr; i++) {
|
|
if ((attr_flags[i] & AF_REQUIRED) && !(attr_flags[i] & AF_LOCKED)) {
|
|
|
|
/* convert blanks etc. to underscores */
|
|
strcpy(ua, attr_list[i]);
|
|
stou(ua);
|
|
|
|
rsprintf(" try {\n"); // try-catch block in case attributes are not there (conditional)
|
|
|
|
if (attr_flags[i] & AF_MULTI) {
|
|
rsprintf(" if (\n");
|
|
for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
|
|
sprintf(str, "%s_%d", ua, j);
|
|
rsprintf(" !document.form1.%s.checked", str);
|
|
if (attr_options[i][j + 1][0])
|
|
rsprintf(" &&\n");
|
|
}
|
|
rsprintf(") {\n");
|
|
sprintf(str, loc("Please select at least one '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.%s_0.focus();\n", ua);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
|
|
} else if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) {
|
|
rsprintf(" if (\n");
|
|
for (j = 0;; j++) {
|
|
if (!enum_user_line(lbs, j, login_name, sizeof(login_name)))
|
|
break;
|
|
get_user_line(lbs, login_name, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
sprintf(str, "%s_%d", ua, j);
|
|
rsprintf(" !document.form1.%s.checked", str);
|
|
if (enum_user_line(lbs, j + 1, login_name, sizeof(login_name)))
|
|
rsprintf(" &&\n");
|
|
}
|
|
rsprintf(") {\n");
|
|
sprintf(str, loc("Please select at least one '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.%s_0.focus();\n", ua);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
|
|
} else if (attr_flags[i] & AF_RADIO) {
|
|
rsprintf(" for (var i=0 ; i<document.form1.%s.length ; i++)\n", ua);
|
|
rsprintf(" if (document.form1.%s[i].checked) { break }\n", ua);
|
|
rsprintf(" if (i == document.form1.%s.length) {\n", ua);
|
|
sprintf(str, loc("Please select a '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.%s[0].focus();\n", ua);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
|
|
} else if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {
|
|
rsprintf(" if (document.form1.m%d.value == \"\") {\n", i);
|
|
sprintf(str, loc("Please enter month for attribute '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.m%d.focus();\n", i);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" if (document.form1.d%d.value == \"\") {\n", i);
|
|
sprintf(str, loc("Please enter day for attribute '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.d%d.focus();\n", i);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" if (document.form1.y%d.value == \"\") {\n", i);
|
|
sprintf(str, loc("Please enter year for attribute '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.y%d.focus();\n", i);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
|
|
if (attr_flags[i] & AF_DATETIME) {
|
|
rsprintf(" if (document.form1.h%d.value == \"\") {\n", i);
|
|
sprintf(str, loc("Please enter hour for attribute '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.h%d.focus();\n", i);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" if (document.form1.n%d.value == \"\") {\n", i);
|
|
sprintf(str, loc("Please enter minute for attribute '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.n%d.focus();\n", i);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" if (document.form1.c%d.value == \"\") {\n", i);
|
|
sprintf(str, loc("Please enter second for attribute '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.c%d.focus();\n", i);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
}
|
|
|
|
} else {
|
|
rsprintf(" if (document.form1.%s.value == \"\") {\n", ua);
|
|
sprintf(str, loc("Please enter attribute '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.%s.focus();\n", ua);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
}
|
|
|
|
rsprintf(" }\n");
|
|
rsprintf(" catch(err) {\n");
|
|
rsprintf(" }\n\n");
|
|
}
|
|
|
|
if ((attr_flags[i] & AF_NUMERIC) && !(attr_flags[i] & AF_LOCKED)) {
|
|
/* convert blanks etc. to underscores */
|
|
strcpy(ua, attr_list[i]);
|
|
stou(ua);
|
|
|
|
rsprintf(" try {\n"); // try-catch block in case attributes are not there (conditional)
|
|
rsprintf(" if (document.form1.%s.value != \"- %s -\") {\n", ua, loc("keep original values"));
|
|
rsprintf(" for (var i=0 ; i<document.form1.%s.value.length ; i++)\n", ua);
|
|
rsprintf(" if (document.form1.%s.value.charAt(i) != \",\" &&\n", ua);
|
|
rsprintf(" document.form1.%s.value.charAt(i) != \".\" &&\n", ua);
|
|
rsprintf(" document.form1.%s.value.charAt(i) != \"-\" &&\n", ua);
|
|
rsprintf(" (document.form1.%s.value.charAt(i) < \"0\" ||\n", ua);
|
|
rsprintf(" document.form1.%s.value.charAt(i) > \"9\")) { break }\n", ua);
|
|
rsprintf(" if (i<document.form1.%s.value.length) {\n", ua);
|
|
sprintf(str, loc("Please enter numeric value for '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.%s.focus();\n", ua);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" catch(err) {\n");
|
|
rsprintf(" }\n\n");
|
|
}
|
|
|
|
if ((attr_flags[i] & (AF_DATE | AF_DATETIME)) && !(attr_flags[i] & AF_LOCKED)) {
|
|
rsprintf(" try {\n"); // try-catch block in case attributes are not there (conditional)
|
|
rsprintf(" for (var i=0 ; i<document.form1.y%d.value.length ; i++)\n", i);
|
|
rsprintf(" if ((document.form1.y%d.value.charAt(i) < \"0\" ||\n", i);
|
|
rsprintf(" document.form1.y%d.value.charAt(i) > \"9\")) { break }\n", i);
|
|
rsprintf(" if (i<document.form1.y%d.value.length) {\n", i);
|
|
sprintf(str, loc("Please enter numeric value for year of attribute '%s'"), attr_list[i]);
|
|
rsprintf(" alert(\"%s\");\n", str);
|
|
rsprintf(" document.form1.y%d.focus();\n", i);
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" catch(err) {\n");
|
|
rsprintf(" }\n\n");
|
|
}
|
|
}
|
|
|
|
rsprintf(" if (autoSaveTimer != null)\n");
|
|
rsprintf(" clearTimeout(autoSaveTimer);\n");
|
|
rsprintf(" button.disabled = true;\n");
|
|
rsprintf(" button.value = \"%s...\";\n", loc("Please wait"));
|
|
rsprintf(" submitted = true;\n");
|
|
rsprintf(" document.getElementById(\"form1\").elements['cmd'][0].value = \"%s\";\n", loc("Submit"));
|
|
rsprintf(" document.getElementById(\"form1\").submit();\n");
|
|
rsprintf(" return true;\n");
|
|
rsprintf("}\n\n");
|
|
|
|
/* mark_submitted() gets called via "Back" and "Preview" buttons */
|
|
rsprintf("function mark_submitted()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" if (autoSaveTimer != null)\n");
|
|
rsprintf(" clearTimeout(autoSaveTimer);\n");
|
|
rsprintf(" submitted = true;\n"); // don't ask to leave that page
|
|
rsprintf(" return true;\n");
|
|
rsprintf("}\n\n");
|
|
|
|
/* check_delte() gets called via "Delete" button */
|
|
rsprintf("function check_delete()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" var ret = confirm('%s');\n", loc("Really delete this entry?"));
|
|
rsprintf(" if (ret) {\n");
|
|
rsprintf(" mark_submitted()\n");
|
|
rsprintf(" document.form1.jcmd.value='XDelete';\n");
|
|
rsprintf(" return true;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" return false;\n");
|
|
rsprintf("}\n\n");
|
|
|
|
/* chkupload() gets called via "Upload" button */
|
|
rsprintf("function chkupload()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" if (document.form1.attfile.value == \"\") {\n");
|
|
rsprintf(" alert(\"%s\");\n", loc("No attachment file specified"));
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" mark_submitted();\n");
|
|
rsprintf(" return true;\n");
|
|
rsprintf("}\n\n");
|
|
|
|
/* cond_submit() gets called via selection of new conditional attribute */
|
|
rsprintf("function cond_submit()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" submitted = true;\n");
|
|
rsprintf(" document.form1.submit();\n");
|
|
rsprintf("}\n\n");
|
|
|
|
/* save_draft() gets called via the "Save" button */
|
|
rsprintf("function save_draft()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" if (autoSaveTimer != null)\n");
|
|
rsprintf(" clearTimeout(autoSaveTimer);\n");
|
|
rsprintf(" asend();\n");
|
|
rsprintf(" draft_modified = false;\n");
|
|
rsprintf(" return false;\n");
|
|
rsprintf("}\n\n");
|
|
|
|
/* beforeunload() gets called "onBeforeUnload" */
|
|
rsprintf("function beforeunload(e)\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" if (!submitted)\n");
|
|
rsprintf(" e.returnValue = \"%s\";\n", loc("If you leave this page you will lose your unsaved changes"));
|
|
rsprintf("}\n\n");
|
|
|
|
/* unload() gets called "onUnload", issues a "Unlock" command to remove a possible lock */
|
|
rsprintf("function unload()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" if (!submitted) {\n");
|
|
rsprintf(" r = XMLHttpRequestGeneric();\n");
|
|
rsprintf(" r.open('GET', '?jcmd=Unlock&edit_id=%d', true);\n", message_id);
|
|
rsprintf(" r.send();\n");
|
|
rsprintf(" }\n");
|
|
rsprintf("}\n\n");
|
|
|
|
/* mod() gets called via "onchange" event */
|
|
rsprintf("var autoSaveTimer;\n");
|
|
rsprintf("var checkTextTimer;\n");
|
|
rsprintf("var oldText;\n\n");
|
|
rsprintf("var initialText;\n\n");
|
|
|
|
if (getcfg(lbs->name, "Autosave", str, sizeof(str)))
|
|
autosave = atoi(str);
|
|
else
|
|
autosave = 10;
|
|
|
|
if (getcfg(lbs->name, "Save drafts", str, sizeof(str)) && atoi(str) == 0)
|
|
autosave = 0;
|
|
|
|
rsprintf("function mod(e)\n");
|
|
rsprintf("{\n");
|
|
if (autosave) {
|
|
rsprintf(" if (!draft_modified)\n");
|
|
rsprintf(" autoSaveTimer = setTimeout(save_draft, %d);\n", autosave * 1000);
|
|
}
|
|
rsprintf(" entry_modified = true;\n");
|
|
rsprintf(" draft_modified = true;\n");
|
|
rsprintf(" window.status = \"%s\";\n", loc("Entry has been modified"));
|
|
rsprintf(" document.form1.entry_modified.value = \"1\";\n");
|
|
rsprintf(" document.title = '%s - %s';\n", page_title, loc("Edited"));
|
|
rsprintf(" if (document.getElementById('restore') != undefined)\n");
|
|
rsprintf(" document.getElementById('restore').disabled = false;\n");
|
|
rsprintf("}\n\n");
|
|
|
|
rsprintf("function checkText()\n");
|
|
rsprintf("{\n");
|
|
if (autosave) {
|
|
// CKEDITOR cannot call mod(), so manually check if text has changed
|
|
rsprintf(" if (checkTextTimer == null) {\n");
|
|
rsprintf(" checkTextTimer = setTimeout(checkText, 1000);\n");
|
|
rsprintf(" return;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" clearTimeout(checkTextTimer);\n");
|
|
rsprintf(" if (typeof(CKEDITOR) != 'undefined')\n");
|
|
rsprintf(" t = CKEDITOR.instances.Text.getData();\n");
|
|
rsprintf(" else\n");
|
|
rsprintf(" t = document.form1.Text.value;\n");
|
|
rsprintf(" if (oldText == null)\n");
|
|
rsprintf(" oldText = t;\n");
|
|
rsprintf(" if (initialText == null)\n");
|
|
rsprintf(" initialText = t;\n");
|
|
rsprintf(" if (oldText != t)\n");
|
|
rsprintf(" mod();\n");
|
|
rsprintf(" oldText = t;\n");
|
|
rsprintf(" checkTextTimer = setTimeout(checkText, 1000);\n");
|
|
}
|
|
rsprintf("}\n\n");
|
|
|
|
rsprintf("function restoreText()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" if (initialText != null) {\n");
|
|
rsprintf(" if (confirm('%s'+'\\n'+'%s')) {\n", loc("Do you want to restore your original text?"),
|
|
loc("This will overwrite your current modifications."));
|
|
rsprintf(" if (typeof(CKEDITOR) != 'undefined')\n");
|
|
rsprintf(" CKEDITOR.instances.Text.setData(initialText);\n");
|
|
rsprintf(" else\n");
|
|
rsprintf(" document.form1.Text.value = initialText;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" }\n");
|
|
rsprintf("}\n\n");
|
|
|
|
rsprintf("function kp(e)\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" last_key = (e.which) ? e.which : event.keyCode;\n");
|
|
rsprintf(" mod();\n");
|
|
rsprintf("}\n\n");
|
|
|
|
/* switch_smileys turn on/off the smiley bar */
|
|
rsprintf("function switch_smileys()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" s = document.getElementById('smileyRow');\n");
|
|
rsprintf(" i = document.getElementById('smileyIcon');\n");
|
|
rsprintf(" if (s.style.display == 'none') {\n");
|
|
rsprintf(" s.style.display = 'table-row';\n");
|
|
rsprintf(" i.src = 'icons/eld_smile.png';\n");
|
|
rsprintf(" } else {\n");
|
|
rsprintf(" s.style.display = 'none';\n");
|
|
rsprintf(" i.src = 'icons/elc_smile.png';\n");
|
|
rsprintf(" }\n");
|
|
rsprintf("}\n\n");
|
|
|
|
if (/*enc_selected != 2 && */ !getcfg(lbs->name, "Message height", str, sizeof(str)) &&
|
|
!getcfg(lbs->name, "Message width", str, sizeof(str))) {
|
|
/* javascript for resizing edit box */
|
|
rsprintf("function init_resize()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" window.onresize = resize_textarea;\n");
|
|
rsprintf(" resize_textarea();\n");
|
|
rsprintf("}\n\n");
|
|
rsprintf("function resize_textarea()\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" p = $id('TextParent');\n");
|
|
rsprintf(" t = p.getBoundingClientRect().top;\n");
|
|
rsprintf(" if (!!document.documentMode) // IE\n");
|
|
rsprintf(" height = window.innerHeight - t - 210;\n");
|
|
rsprintf(" else\n");
|
|
rsprintf(" height = window.innerHeight - t - 205;\n");
|
|
rsprintf(" if (height < 300)\n");
|
|
rsprintf(" height = 300;\n");
|
|
rsprintf(" width = window.innerWidth;\n");
|
|
rsprintf(" if (width < 300)\n");
|
|
rsprintf(" width = 300;\n");
|
|
rsprintf(" width = width - 8;\n");
|
|
rsprintf(" if (typeof(CKEDITOR) != 'undefined')\n");
|
|
rsprintf(" CKEDITOR.instances.Text.resize(width, height);\n");
|
|
rsprintf(" else {\n");
|
|
rsprintf(" document.form1.Text.style.height = height+6+\"px\";\n");
|
|
rsprintf(" document.form1.Text.style.width = width-6+\"px\";\n");
|
|
rsprintf(" }\n");
|
|
rsprintf("}\n\n");
|
|
}
|
|
|
|
/* ToggleAll() to toggle MOptions buttons */
|
|
rsprintf("function ToggleAll(attrib)\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" for (var i = 0; i < document.form1.elements.length; i++) {\n");
|
|
rsprintf(
|
|
" if (document.form1.elements[i].type == 'checkbox' && document.form1.elements[i].disabled == false) {\n");
|
|
rsprintf(" a = document.form1.elements[i].name;\n");
|
|
rsprintf(" a = a.substring(0, attrib.length);\n");
|
|
rsprintf(" if (a == attrib)\n");
|
|
rsprintf(" document.form1.elements[i].checked = !(document.form1.elements[i].checked);\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" }\n");
|
|
rsprintf("}\n\n");
|
|
|
|
/* language for CKEDITOR */
|
|
if (getcfg("global", "language", str, sizeof(str))) {
|
|
for (i = 0; lang_table[i].language[0]; i++) {
|
|
strlcpy(str2, str, sizeof(str2));
|
|
str2[strlen(lang_table[i].language)] = 0;
|
|
if (stricmp(str2, lang_table[i].language) == 0)
|
|
break;
|
|
}
|
|
if (lang_table[i].language[0])
|
|
rsprintf("var CKEditorLang = '%s';\n", lang_table[i].abbrev);
|
|
else
|
|
rsprintf("var CKEditorLang = 'en';\n");
|
|
} else
|
|
rsprintf("var CKEditorLang = 'en';\n");
|
|
|
|
/* strings for elcode.js */
|
|
if (enc_selected == 0) {
|
|
rsprintf("var linkText_prompt = \"%s\";\n", loc("Enter name of hyperlink"));
|
|
rsprintf("var linkURL_prompt = \"%s\";\n", loc("Enter URL of hyperlink"));
|
|
rsprintf("var linkHeading_prompt = \"%s\";\n", loc("Enter heading level (1, 2 or 3)"));
|
|
}
|
|
|
|
show_browser(browser);
|
|
|
|
rsprintf("var logbook = \"%s\";\n", lbs->name_enc);
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (!att[i][0]) {
|
|
/* put first free attachment for uploader */
|
|
rsprintf("var next_attachment = %d;\n", i + 1);
|
|
break;
|
|
}
|
|
rsprintf("var page_title = '%s';\n", page_title);
|
|
|
|
rsprintf("\n");
|
|
rsprintf("window.onbeforeunload = beforeunload;\n");
|
|
rsprintf("\n");
|
|
|
|
rsprintf("//-->\n");
|
|
rsprintf("</script>\n");
|
|
|
|
/* optionally load ImageMagic JavaScript code */
|
|
if (image_magick_exist)
|
|
rsprintf("<script type=\"text/javascript\" src=\"../im.js\"></script>\n\n");
|
|
|
|
/* optionally load ELCode JavaScript code */
|
|
if (enc_selected == 0)
|
|
rsprintf("<script type=\"text/javascript\" src=\"../elcode.js\"></script>\n\n");
|
|
|
|
show_text = !getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1;
|
|
fixed_text = getcfg(lbs->name, "Fix text", str, sizeof(str)) && atoi(str) == 1 && bedit && message_id;
|
|
|
|
if (enc_selected == 2 && ckedit_exist && show_text && !fixed_text) {
|
|
rsprintf("<script type=\"text/javascript\" src=\"../ckeditor/ckeditor.js\"></script>\n");
|
|
}
|
|
|
|
rsprintf("<script type=\"text/javascript\" src=\"../jquery-1.11.1.min.js\"></script>\n");
|
|
rsprintf("<script type=\"text/javascript\" src=\"../progress/progress.min.js\"></script>\n");
|
|
rsprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"../progress/progressjs.min.css\">\n");
|
|
|
|
/* drag-and-drip script */
|
|
rsprintf("<script type=\"text/javascript\" src=\"../dnd.js\"></script>\n");
|
|
|
|
/* CKEDITOR */
|
|
if (enc_selected == 2 && ckedit_exist && show_text && !fixed_text)
|
|
rsprintf("<script type=\"text/javascript\" src=\"../load-ckeditor.js\"></script>\n");
|
|
|
|
/* external script if requested */
|
|
if (isparam("js")) {
|
|
rsprintf("<script src=\"%s\" type=\"text/javascript\">\n", getparam("js"));
|
|
rsprintf("</script>\n\n");
|
|
}
|
|
|
|
script_onload[0] = 0;
|
|
script_onfocus[0] = 0;
|
|
if ((isparam("inlineatt") && *getparam("inlineatt")) || bpreview)
|
|
strcpy(script_onload, "document.form1.Text.focus();");
|
|
else
|
|
strcpy(script_onload, "i=document.getElementById('fid');if(i)i.focus();");
|
|
|
|
if (enc_selected == 0) {
|
|
if (!getcfg(lbs->name, "Message height", str, sizeof(str)) &&
|
|
!getcfg(lbs->name, "Message width", str, sizeof(str))) {
|
|
strcat(script_onload, "elKeyInit();init_resize();");
|
|
strcat(script_onfocus, "elKeyInit();");
|
|
} else
|
|
strcat(script_onload, "elKeyInit();");
|
|
strcat(script_onfocus, "elKeyInit();");
|
|
} else if (enc_selected == 1) {
|
|
if (!getcfg(lbs->name, "Message height", str, sizeof(str)) &&
|
|
!getcfg(lbs->name, "Message width", str, sizeof(str)))
|
|
strcat(script_onload, "init_resize();");
|
|
}
|
|
strcat(script_onload, "checkText();dndInit();");
|
|
|
|
script_onunload[0] = 0;
|
|
if (getcfg(lbs->name, "Use Lock", str, sizeof(str)) && atoi(str) == 1)
|
|
strcat(script_onunload, "unload();");
|
|
|
|
rsprintf("</head>\n\n");
|
|
rsprintf("<body");
|
|
|
|
if (script_onload[0])
|
|
rsprintf(" OnLoad=\"%s\"", script_onload);
|
|
if (script_onfocus[0])
|
|
rsprintf(" OnFocus=\"%s\"", script_onfocus);
|
|
if (script_onunload[0])
|
|
rsprintf(" OnUnload=\"%s\"", script_onunload);
|
|
rsprintf(">\n");
|
|
|
|
show_top_text(lbs);
|
|
rsprintf("<form name=\"form1\" id=\"form1\" method=\"POST\" action=\"./\" ");
|
|
rsprintf("enctype=\"multipart/form-data\">\n");
|
|
|
|
/*---- add password in case cookie expires during edit ----*/
|
|
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str)) && isparam("unm")) {
|
|
strencode2(str, getparam("unm"), sizeof(str));
|
|
rsprintf("<input type=hidden name=\"unm\" value=\"%s\">\n", str);
|
|
if (isparam("upwd"))
|
|
strlcpy(upwd, getparam("upwd"), sizeof(upwd));
|
|
else
|
|
get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL);
|
|
rsprintf("<input type=hidden name=\"upwd\" value=\"%s\">\n", upwd);
|
|
}
|
|
|
|
rsprintf("<input type=hidden name=\"jcmd\">\n");
|
|
rsprintf("<input type=hidden name=\"smcmd\">\n");
|
|
rsprintf("<input type=hidden name=\"inlineatt\">\n");
|
|
|
|
if (new_entry)
|
|
rsprintf("<input type=hidden name=\"new_entry\" value=\"1\">\n");
|
|
|
|
if (isparam("entry_modified") && atoi(getparam("entry_modified")) == 1)
|
|
rsprintf("<input type=hidden name=\"entry_modified\" value=\"1\">\n");
|
|
else
|
|
rsprintf("<input type=hidden name=\"entry_modified\" value=\"0\">\n");
|
|
|
|
/*---- title row ----*/
|
|
|
|
show_standard_title(lbs, "", 0);
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
/* default cmd */
|
|
rsprintf("<input type=hidden name=cmd value=\"%s\">\n", loc("Update"));
|
|
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return chkform(this);\">\n",
|
|
loc("Submit"));
|
|
|
|
if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1)
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return save_draft();\">\n",
|
|
loc("Save"));
|
|
|
|
if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1)
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
|
|
loc("Preview"));
|
|
|
|
if (message_id && (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1))
|
|
rsprintf(
|
|
"<input type=\"submit\" name=\"cmd\" id=\"restore\" value=\"%s\" disabled onClick=\"restoreText();return false;\">\n",
|
|
loc("Restore"));
|
|
|
|
if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1)
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return check_delete();\">\n",
|
|
loc("Delete"));
|
|
else
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
|
|
loc("Back"));
|
|
|
|
rsprintf(
|
|
" <span id=\"saved1\" style=\"font-size:10px;font-style:italic;display:none\">%s 00:00:00</span>",
|
|
loc("Draft saved at"));
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
|
|
/*---- entry form ----*/
|
|
|
|
/* table for two-column items */
|
|
rsprintf("<tr><td><table class=\"listframe\" width=\"100%%\" cellspacing=\"0\" cellpadding=\"0\">");
|
|
|
|
/* print required message if one of the attributes has it set */
|
|
for (i = 0; i < n_attr; i++) {
|
|
if (attr_flags[i] & AF_REQUIRED) {
|
|
rsprintf("<tr><td colspan=2 class=\"attribvalue\">%s <font color=red>*</font> %s</td></tr>\n",
|
|
loc("Fields marked with"), loc("are required"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isparam("nsel")) {
|
|
time(&now);
|
|
if (bedit && date[0]) {
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = date_to_ltime(date);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
} else {
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
my_strftime(str, sizeof(str), format, localtime(&now));
|
|
strcpy(date, ctime(&now));
|
|
date[24] = 0;
|
|
}
|
|
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", loc("Entry time"));
|
|
rsprintf("<td class=\"attribvalue\">%s\n", str);
|
|
|
|
strencode2(str, date, sizeof(str));
|
|
rsprintf("<input type=hidden name=entry_date value=\"%s\"></td></tr>\n", str);
|
|
}
|
|
|
|
if (_condition[0])
|
|
rsprintf("<input type=hidden name=condition value=\"%s\"></td></tr>\n", _condition);
|
|
|
|
/* retrieve attribute flags */
|
|
for (i = 0; i < n_attr; i++) {
|
|
format_flags[i] = 0;
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Format %s", attr);
|
|
if (getcfg(lbs->name, str, format, sizeof(format))) {
|
|
n = strbreak(format, fl, 8, ",", FALSE);
|
|
if (n > 0)
|
|
format_flags[i] = atoi(fl[0]);
|
|
}
|
|
}
|
|
|
|
subtable = 0;
|
|
|
|
/* generate list of attributes to show */
|
|
if (getcfg(lbs->name, "Show attributes edit", str, sizeof(str))) {
|
|
n_disp_attr = strbreak(str, list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n_disp_attr; i++) {
|
|
for (j = 0; j < n_attr; j++)
|
|
if (strieq(attr_list[j], list[i]))
|
|
break;
|
|
if (!strieq(attr_list[j], list[i]))
|
|
/* attribute not found */
|
|
j = 0;
|
|
attr_index[i] = j;
|
|
}
|
|
} else {
|
|
for (i = 0; i < n_attr; i++)
|
|
attr_index[i] = i;
|
|
n_disp_attr = n_attr;
|
|
}
|
|
|
|
strcpy(fid, "id=\"fid\" ");
|
|
|
|
/* display attributes */
|
|
for (aindex = 0; aindex < n_disp_attr; aindex++) {
|
|
|
|
index = attr_index[aindex];
|
|
|
|
strcpy(class_name, "attribname");
|
|
strcpy(class_value, "attribvalue");
|
|
input_size = 80;
|
|
input_maxlen = NAME_LENGTH;
|
|
strcpy(ua, attr_list[index]);
|
|
stou(ua);
|
|
|
|
sprintf(str, "Format %s", attr_list[index]);
|
|
if (getcfg(lbs->name, str, format, sizeof(format))) {
|
|
n = strbreak(format, fl, 8, ",", FALSE);
|
|
if (n > 1)
|
|
strlcpy(class_name, fl[1], sizeof(class_name));
|
|
if (n > 2)
|
|
strlcpy(class_value, fl[2], sizeof(class_value));
|
|
if (n > 3 && atoi(fl[3]) > 0)
|
|
input_size = atoi(fl[3]);
|
|
if (n > 4 && atoi(fl[4]) > 0)
|
|
input_maxlen = atoi(fl[4]);
|
|
}
|
|
|
|
if (format_flags[index] & AFF_SAME_LINE)
|
|
/* if attribute on same line, do nothing */
|
|
rsprintf("");
|
|
else if (aindex < n_disp_attr - 1 && (format_flags[attr_index[aindex + 1]] & AFF_SAME_LINE)) {
|
|
/* if next attribute on same line, start a new subtable */
|
|
rsprintf("<tr><td colspan=2><table width=\"100%%\" cellpadding=\"0\" cellspacing=\"0\"><tr>");
|
|
subtable = 1;
|
|
} else
|
|
/* for normal attribute, start new row */
|
|
rsprintf("<tr>");
|
|
|
|
strcpy(star, (attr_flags[index] & AF_REQUIRED) ? "<font color=red>*</font>" : "");
|
|
|
|
/* display text box with optional tooltip */
|
|
sprintf(str, "Tooltip %s", attr_list[index]);
|
|
title[0] = 0;
|
|
if (getcfg(lbs->name, str, comment, sizeof(comment)))
|
|
sprintf(title, " title=\"%s\"", comment);
|
|
|
|
rsprintf("<td%s nowrap class=\"%s\">", title, class_name);
|
|
|
|
/* display attribute name */
|
|
rsprintf("%s%s:", attr_list[index], star);
|
|
|
|
/* show optional comment */
|
|
sprintf(str, "Comment %s", attr_list[index]);
|
|
if (getcfg(lbs->name, str, comment, sizeof(comment)))
|
|
rsprintf("<br><span class=\"selcomment\"><b>%s</b></span>\n", comment);
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
/* if attribute cannot be changed, just display text */
|
|
if ((attr_flags[index] & AF_LOCKED) ||
|
|
(message_id && bedit && (attr_flags[index] & AF_FIXED_EDIT)) ||
|
|
(message_id && !bedit && (attr_flags[index] & AF_FIXED_REPLY))) {
|
|
|
|
if (attr_flags[index] & AF_DATE) {
|
|
|
|
sprintf(str, "Date format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
|
|
ltime = atoi(attrib[index]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
} else if (attr_flags[index] & AF_DATETIME) {
|
|
|
|
sprintf(str, "Time format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = atoi(attrib[index]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
} else
|
|
strlcpy(str, attrib[index], sizeof(str));
|
|
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
rsputs2(lbs, FALSE, str);
|
|
rsprintf(" ");
|
|
|
|
if (attr_flags[index] & AF_MULTI) {
|
|
for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
|
|
sprintf(str, "%s_%d", ua, i);
|
|
|
|
if (strstr(attrib[index], attr_options[index][i]))
|
|
rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", str, attr_options[index][i]);
|
|
}
|
|
} else if (attr_flags[index] & AF_MUSERLIST) {
|
|
|
|
for (i = 0;; i++) {
|
|
if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
|
|
break;
|
|
get_user_line(lbs, login_name, NULL, full_name, NULL, NULL, NULL, NULL);
|
|
|
|
sprintf(str, "%s_%d", ua, i);
|
|
|
|
if (strstr(attrib[index], full_name))
|
|
rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", str, full_name);
|
|
}
|
|
} else if (attr_flags[index] & AF_MUSEREMAIL) {
|
|
|
|
for (i = 0;; i++) {
|
|
if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
|
|
break;
|
|
get_user_line(lbs, login_name, NULL, NULL, user_email, NULL, NULL, NULL);
|
|
|
|
sprintf(str, "%s_%d", ua, i);
|
|
|
|
if (strstr(attrib[index], user_email))
|
|
rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", str, user_email);
|
|
}
|
|
} else if (attr_flags[index] & AF_ICON) {
|
|
for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
|
|
sprintf(str, "%s_%d", ua, i);
|
|
|
|
if (strstr(attrib[index], attr_options[index][i]))
|
|
rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", str, attr_options[index][i]);
|
|
}
|
|
} else {
|
|
strencode2(str, attrib[index], sizeof(str));
|
|
rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\"></td>\n", ua, str);
|
|
}
|
|
} else {
|
|
if (attr_options[index][0][0] == 0) {
|
|
|
|
if (attr_flags[index] & AF_DATE) {
|
|
|
|
year = month = day = 0;
|
|
if (attrib[index][0]) {
|
|
ltime = atoi(attrib[index]);
|
|
if (ltime > 0) {
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
year = pts->tm_year + 1900;
|
|
month = pts->tm_mon + 1;
|
|
day = pts->tm_mday;
|
|
}
|
|
}
|
|
|
|
rsprintf("<td%s class=\"attribvalue\">", title);
|
|
sprintf(str, "%d", index);
|
|
show_date_selector(day, month, year, str);
|
|
rsprintf("</td>\n");
|
|
|
|
} else if (attr_flags[index] & AF_DATETIME) {
|
|
|
|
year = month = day = 0;
|
|
hour = min = sec = -1;
|
|
if (attrib[index][0]) {
|
|
ltime = atoi(attrib[index]);
|
|
if (ltime > 0) {
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
year = pts->tm_year + 1900;
|
|
month = pts->tm_mon + 1;
|
|
day = pts->tm_mday;
|
|
hour = pts->tm_hour;
|
|
min = pts->tm_min;
|
|
sec = pts->tm_sec;
|
|
}
|
|
}
|
|
|
|
rsprintf("<td%s class=\"%s\">", title, class_value);
|
|
sprintf(str, "%d", index);
|
|
show_date_selector(day, month, year, str);
|
|
rsprintf(" ");
|
|
show_time_selector(hour, min, sec, str);
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
} else if (attr_flags[index] & AF_USERLIST) {
|
|
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
|
|
/* display drop-down box with list of users */
|
|
rsprintf("<select name=\"%s\"", ua);
|
|
rsprintf(" onChange=\"mod();\">\n");
|
|
|
|
/* display emtpy option */
|
|
sprintf(str, "- %s -", loc("keep original values"));
|
|
if (strcmp(str, attrib[index]) == 0 && isparam("nsel"))
|
|
rsprintf("<option value=\"<keep>\">%s\n", str);
|
|
else
|
|
rsprintf("<option value=\"\">- %s -\n", loc("please select"));
|
|
|
|
for (i = 0;; i++) {
|
|
if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
|
|
break;
|
|
get_user_line(lbs, login_name, NULL, str, NULL, NULL, NULL, NULL);
|
|
|
|
if (strieq(str, attrib[index]))
|
|
rsprintf("<option selected value=\"%s\">%s\n", str, str);
|
|
else
|
|
rsprintf("<option value=\"%s\">%s\n", str, str);
|
|
}
|
|
|
|
rsprintf("</select>\n");
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
} else if (attr_flags[index] & AF_MUSERLIST) {
|
|
|
|
/* display multiple check boxes with user names */
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
|
|
n_moptions = strbreak(attrib[index], attr_moptions, MAX_N_LIST, "|", FALSE);
|
|
|
|
/* allocate list of users and populate it */
|
|
for (n = 0;; n++) {
|
|
if (!enum_user_line(lbs, n, login_name, sizeof(login_name)))
|
|
break;
|
|
}
|
|
|
|
user_list = (char **)xcalloc(sizeof(char *), n);
|
|
for (i = 0; i < n; i++)
|
|
user_list[i] = (char *)xcalloc(NAME_LENGTH, 1);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
enum_user_line(lbs, i, str, NAME_LENGTH);
|
|
get_user_line(lbs, str, NULL, user_list[i], NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
/* sort list */
|
|
qsort(user_list, n, sizeof(char *), ascii_compare);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
sprintf(str, "%s_%d", ua, i);
|
|
|
|
rsprintf("<span style=\"white-space:nowrap;\">\n");
|
|
|
|
for (j = 0; j < n_moptions; j++)
|
|
if (strcmp(attr_moptions[j], user_list[i]) == 0)
|
|
break;
|
|
|
|
if (j < n_moptions)
|
|
rsprintf
|
|
("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" checked onChange=\"mod();\">\n",
|
|
str, str, user_list[i]);
|
|
else
|
|
rsprintf
|
|
("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
|
|
str, str, user_list[i]);
|
|
|
|
rsprintf("<label for=\"%s\">%s</label>\n", str, user_list[i]);
|
|
rsprintf("</span>\n");
|
|
|
|
if (format_flags[index] & AFF_MULTI_LINE)
|
|
rsprintf("<br>");
|
|
}
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
for (i = 0; i < n; i++)
|
|
xfree(user_list[i]);
|
|
xfree(user_list);
|
|
|
|
} else if (attr_flags[index] & AF_MUSEREMAIL) {
|
|
|
|
/* display multiple check boxes with user emails */
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
|
|
n_moptions = strbreak(attrib[index], attr_moptions, MAX_N_LIST, "|", FALSE);
|
|
|
|
/* allocate list of users and populate it */
|
|
for (n = 0;; n++) {
|
|
if (!enum_user_line(lbs, n, login_name, sizeof(login_name)))
|
|
break;
|
|
}
|
|
|
|
user_list = (char **)xcalloc(sizeof(char *), n);
|
|
for (i = 0; i < n; i++)
|
|
user_list[i] = (char *)xcalloc(NAME_LENGTH, 1);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
enum_user_line(lbs, i, str, NAME_LENGTH);
|
|
get_user_line(lbs, str, NULL, NULL, user_list[i], NULL, NULL, NULL);
|
|
}
|
|
|
|
/* sort list */
|
|
qsort(user_list, n, sizeof(char *), ascii_compare);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
sprintf(str, "%s_%d", ua, i);
|
|
|
|
rsprintf("<span style=\"white-space:nowrap;\">\n");
|
|
|
|
for (j = 0; j < n_moptions; j++)
|
|
if (strcmp(attr_moptions[j], user_list[i]) == 0 ||
|
|
strcmp(attr_moptions[j] + 7, user_list[i]) == 0)
|
|
break;
|
|
|
|
if (j < n_moptions)
|
|
rsprintf
|
|
("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" checked onChange=\"mod();\">\n",
|
|
str, str, user_list[i]);
|
|
else
|
|
rsprintf
|
|
("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
|
|
str, str, user_list[i]);
|
|
|
|
rsprintf("<label for=\"%s\">%s</label>\n", str, user_list[i]);
|
|
rsprintf("</span>\n");
|
|
|
|
if (format_flags[index] & AFF_MULTI_LINE)
|
|
rsprintf("<br>");
|
|
}
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
for (i = 0; i < n; i++)
|
|
xfree(user_list[i]);
|
|
xfree(user_list);
|
|
|
|
} else if (attr_flags[index] & AF_USEREMAIL) {
|
|
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
|
|
/* display drop-down box with list of users */
|
|
rsprintf("<select name=\"%s\"", ua);
|
|
rsprintf(" onChange=\"mod();\">\n");
|
|
|
|
/* display emtpy option */
|
|
sprintf(str, "- %s -", loc("keep original values"));
|
|
if (strcmp(str, attrib[index]) == 0 && isparam("nsel"))
|
|
rsprintf("<option value=\"<keep>\">%s\n", str);
|
|
else
|
|
rsprintf("<option value=\"\">- %s -\n", loc("please select"));
|
|
|
|
for (i = 0;; i++) {
|
|
if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
|
|
break;
|
|
get_user_line(lbs, login_name, NULL, NULL, str, NULL, NULL, NULL);
|
|
|
|
if (strieq(str, attrib[index]) || strieq(str, attrib[index] + 7))
|
|
rsprintf("<option selected value=\"%s\">%s\n", str, str);
|
|
else
|
|
rsprintf("<option value=\"%s\">%s\n", str, str);
|
|
}
|
|
|
|
rsprintf("</select>\n");
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
} else {
|
|
|
|
/* show normal edit field */
|
|
rsprintf("<td%s class=\"%s\">", title, class_value);
|
|
|
|
strencode2(str, attrib[index], sizeof(str));
|
|
rsprintf
|
|
("<input type=\"text\" %ssize=%d maxlength=%d name=\"%s\" value=\"%s\" onKeyPress=\"kp(event)\" onChange=\"mod()\">\n",
|
|
fid, input_size, input_maxlen, ua, str);
|
|
fid[0] = 0;
|
|
|
|
rsprintf("</td>\n");
|
|
}
|
|
|
|
} else {
|
|
if (strieq(attr_options[index][0], "boolean")) {
|
|
/* display three radio buttons instead of three-state check box for multi-edit */
|
|
sprintf(str, "- %s -", loc("keep original values"));
|
|
if (isparam("nsel") && strieq(attrib[index], str)) {
|
|
rsprintf("<td%s class=\"%s\">", title, class_value);
|
|
sprintf(str, "%s_0", ua);
|
|
rsprintf("<span style=\"white-space:nowrap;\">\n");
|
|
rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"0\" onChange=\"mod();\">\n", str,
|
|
ua);
|
|
rsprintf("<label for=\"%s\">0</label>\n", str);
|
|
rsprintf("</span>\n");
|
|
|
|
sprintf(str, "%s_1", ua);
|
|
rsprintf("<span style=\"white-space:nowrap;\">\n");
|
|
rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"1\" onChange=\"mod();\">\n", str,
|
|
ua);
|
|
rsprintf("<label for=\"%s\">1</label>\n", str);
|
|
rsprintf("</span>\n");
|
|
|
|
sprintf(str, "%s_2", ua);
|
|
rsprintf("<span style=\"white-space:nowrap;\">\n");
|
|
rsprintf
|
|
("<input type=radio id=\"%s\" name=\"%s\" value=\"<keep>\" checked onChange=\"mod();\">\n",
|
|
str, ua);
|
|
rsprintf("<label for=\"%s\">%s</label>\n", str, loc("keep original values"));
|
|
rsprintf("</span>\n");
|
|
}
|
|
|
|
/* display checkbox */
|
|
else if (atoi(attrib[index]) == 1)
|
|
rsprintf
|
|
("<td%s class=\"%s\"><input type=checkbox checked name=\"%s\" value=1 onChange=\"mod();\">\n",
|
|
title, class_value, ua);
|
|
else
|
|
rsprintf
|
|
("<td%s class=\"%s\"><input type=checkbox name=\"%s\" value=1 onChange=\"mod();\">\n",
|
|
title, class_value, ua);
|
|
} else {
|
|
|
|
sprintf(str, "extend_%d", index);
|
|
if (isparam(str)) {
|
|
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
rsprintf("<i>");
|
|
rsprintf(loc("Add new option here"), attr_list[index]);
|
|
rsprintf(" : </i>\n");
|
|
|
|
if (attr_flags[index] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL))
|
|
rsprintf
|
|
("<input type=\"text\" size=20 maxlength=%d name=\"%s_0\" value=\"%s\" onChange=\"mod();\">\n",
|
|
input_maxlen, ua, attrib[index]);
|
|
else
|
|
rsprintf
|
|
("<input type=\"text\" size=20 maxlength=%d name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
|
|
input_maxlen, ua, attrib[index]);
|
|
|
|
rsprintf("<input type=\"hidden\" name=\"extend_%d\" value=\"1\">\n", index);
|
|
rsprintf("</td>\n");
|
|
|
|
} else if (attr_flags[index] & AF_MULTI) {
|
|
|
|
/* display multiple check boxes */
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
|
|
sprintf(str, "- %s -", loc("keep original values"));
|
|
if (isparam("nsel") && strieq(attrib[index], str)) {
|
|
rsprintf("<span style=\"white-space:nowrap;\">\n");
|
|
sprintf(str, "%s_keep", ua);
|
|
rsprintf
|
|
("<input type=checkbox id=\"%s\" name=\"%s\" value=\"<keep>\" checked onChange=\"mod();\">\n",
|
|
str, ua);
|
|
|
|
rsprintf("<label for=\"%s\">%s</label>\n", str, loc("keep original values"));
|
|
rsprintf("</span>\n");
|
|
}
|
|
|
|
n_moptions = strbreak(attrib[index], attr_moptions, MAX_N_LIST, "|", FALSE);
|
|
for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
|
|
|
|
/* display check box with optional tooltip */
|
|
sprintf(str, "Tooltip %s", attr_options[index][i]);
|
|
tooltip[0] = 0;
|
|
if (getcfg(lbs->name, str, comment, sizeof(comment)))
|
|
sprintf(tooltip, " title=\"%s\"", comment);
|
|
|
|
sprintf(str, "Tooltip %s %s", attr_list[index], attr_options[index][i]);
|
|
tooltip[0] = 0;
|
|
if (getcfg(lbs->name, str, comment, sizeof(comment)))
|
|
sprintf(tooltip, " title=\"%s\"", comment);
|
|
|
|
sprintf(str, "%s_%d", ua, i);
|
|
rsprintf("<span%s style=\"white-space:nowrap;\">\n", tooltip);
|
|
|
|
for (j = 0; j < n_moptions; j++)
|
|
if (strcmp(attr_moptions[j], attr_options[index][i]) == 0)
|
|
break;
|
|
|
|
if (j < n_moptions)
|
|
rsprintf
|
|
("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" checked onChange=\"mod();\">\n",
|
|
str, str, attr_options[index][i]);
|
|
else
|
|
rsprintf
|
|
("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
|
|
str, str, attr_options[index][i]);
|
|
|
|
rsprintf("<label for=\"%s\">%s</label>\n", str, attr_options[index][i]);
|
|
rsprintf("</span>\n");
|
|
|
|
if (format_flags[index] & AFF_MULTI_LINE)
|
|
rsprintf("<br>");
|
|
}
|
|
|
|
rsprintf("<input type=button value=\"%s\" onClick=\"ToggleAll('%s');\">\n",
|
|
loc("Toggle all"), ua);
|
|
|
|
if (attr_flags[index] & AF_EXTENDABLE) {
|
|
sprintf(str, loc("Add %s"), attr_list[index]);
|
|
rsprintf
|
|
("<input type=submit name=\"extend_%d\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
|
|
index, str);
|
|
}
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
} else if (attr_flags[index] & AF_RADIO) {
|
|
/* display radio buttons */
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
|
|
for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
|
|
/* display check box with optional tooltip */
|
|
sprintf(str, "Tooltip %s", attr_options[index][i]);
|
|
tooltip[0] = 0;
|
|
if (getcfg(lbs->name, str, comment, sizeof(comment)))
|
|
sprintf(tooltip, " title=\"%s\"", comment);
|
|
|
|
rsprintf("<span%s style=\"white-space:nowrap;\">\n", tooltip);
|
|
|
|
strencode2(str, attr_options[index][i], sizeof(str));
|
|
if (strchr(str, '{'))
|
|
*strchr(str, '{') = 0;
|
|
strencode2(enc_attr, attrib[index], sizeof(enc_attr));
|
|
|
|
if (strieq(attrib[index], attr_options[index][i]) || strieq(str, enc_attr))
|
|
rsprintf
|
|
("<input type=radio id=\"%s\" name=\"%s\" value=\"%s\" checked onChange=\"mod();\">\n",
|
|
str, ua, str);
|
|
else
|
|
rsprintf
|
|
("<input type=radio id=\"%s\" name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
|
|
str, ua, str);
|
|
|
|
rsprintf("<label for=\"%s\">%s</label>\n", str, str);
|
|
|
|
rsprintf("</span>\n");
|
|
|
|
if (format_flags[index] & AFF_MULTI_LINE)
|
|
rsprintf("<br>");
|
|
}
|
|
|
|
if (attr_flags[index] & AF_EXTENDABLE) {
|
|
sprintf(str, loc("Add %s"), attr_list[index]);
|
|
rsprintf
|
|
("<input type=submit name=\"extend_%d\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
|
|
index, str);
|
|
}
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
} else if (attr_flags[index] & AF_ICON) {
|
|
/* display icons */
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
rsprintf("<table cellpadding=\"0\" cellspacing=\"0\"><tr>\n");
|
|
|
|
for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
|
|
if (strstr(attrib[index], attr_options[index][i]))
|
|
rsprintf
|
|
("<td><input type=radio checked name=\"%s\" value=\"%s\" onChange=\"mod();\">",
|
|
ua, attr_options[index][i]);
|
|
else
|
|
rsprintf("<td><input type=radio name=\"%s\" value=\"%s\" onChange=\"mod();\">", ua,
|
|
attr_options[index][i]);
|
|
|
|
sprintf(str, "Icon comment %s", attr_options[index][i]);
|
|
getcfg(lbs->name, str, comment, sizeof(comment));
|
|
|
|
if (comment[0])
|
|
rsprintf("<img src=\"icons/%s\" alt=\"%s\" title=\"%s\">\n", attr_options[index][i],
|
|
comment, comment);
|
|
else
|
|
rsprintf("<img src=\"icons/%s\" alt=\"%s\" title=\"%s\">\n", attr_options[index][i],
|
|
attr_options[index][i], attr_options[index][i]);
|
|
|
|
rsprintf("</td>\n");
|
|
if ((format_flags[index] & AFF_MULTI_LINE) && attr_options[index][i + 1][0]) {
|
|
rsprintf("</tr><tr>\n");
|
|
}
|
|
}
|
|
|
|
rsprintf("</tr></table></td>\n");
|
|
|
|
} else {
|
|
|
|
rsprintf("<td%s class=\"%s\">\n", title, class_value);
|
|
|
|
/* display drop-down box */
|
|
rsprintf("<select name=\"%s\"", ua);
|
|
|
|
if (is_cond_attr(index))
|
|
rsprintf(" onChange=\"cond_submit()\">\n");
|
|
else
|
|
rsprintf(" onChange=\"mod();\">\n");
|
|
|
|
/* display emtpy option */
|
|
sprintf(str, "- %s -", loc("keep original values"));
|
|
if (strcmp(str, attrib[index]) == 0 && isparam("nsel"))
|
|
rsprintf("<option value=\"<keep>\">%s\n", str);
|
|
else
|
|
rsprintf("<option value=\"\">- %s -\n", loc("please select"));
|
|
|
|
for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
|
|
strencode2(str, attr_options[index][i], sizeof(str));
|
|
if (strchr(str, '{'))
|
|
*strchr(str, '{') = 0;
|
|
strencode2(enc_attr, attrib[index], sizeof(enc_attr));
|
|
|
|
if (strieq(attr_options[index][i], attrib[index]) || strieq(str, enc_attr))
|
|
rsprintf("<option selected value=\"%s\">%s\n", str, str);
|
|
else
|
|
rsprintf("<option value=\"%s\">%s\n", str, str);
|
|
}
|
|
|
|
rsprintf("</select>\n");
|
|
|
|
if (is_cond_attr(index)) {
|
|
/* show "update" button only of javascript is not enabled */
|
|
rsprintf("<noscript>\n");
|
|
rsprintf("<input type=submit value=\"%s\">\n", loc("Update"));
|
|
rsprintf("</noscript>\n");
|
|
}
|
|
|
|
if (attr_flags[index] & AF_EXTENDABLE) {
|
|
sprintf(str, loc("Add %s"), attr_list[index]);
|
|
rsprintf
|
|
("<input type=submit name=\"extend_%d\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
|
|
index, str);
|
|
}
|
|
|
|
rsprintf("</td>\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (aindex < n_disp_attr - 1 && (format_flags[attr_index[aindex + 1]] & AFF_SAME_LINE) == 0) {
|
|
/* if next attribute not on same line, close row or subtable */
|
|
if (subtable) {
|
|
rsprintf("</table></td></tr>\n");
|
|
subtable = 0;
|
|
} else
|
|
rsprintf("</tr>");
|
|
}
|
|
|
|
/* if last attribute, close row or subtable */
|
|
if (aindex == n_disp_attr - 1) {
|
|
if (subtable) {
|
|
rsprintf("</table></td></tr>\n");
|
|
subtable = 0;
|
|
} else
|
|
rsprintf("</tr>");
|
|
}
|
|
}
|
|
|
|
if (bpreview && !fixed_text) {
|
|
_current_message_id = message_id;
|
|
rsprintf("<tr><td colspan=2 class=\"messageframe\">\n");
|
|
|
|
if (strieq(encoding, "plain")) {
|
|
rsputs("<pre class=\"messagepre\">");
|
|
rsputs2(lbs, FALSE, text);
|
|
rsputs("</pre>");
|
|
} else if (strieq(encoding, "ELCode"))
|
|
rsputs_elcode(lbs, FALSE, text);
|
|
else
|
|
rsputs(text);
|
|
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
if (enc_selected == 0 && show_text && !fixed_text) {
|
|
rsprintf("<tr><td colspan=2 class=\"toolframe\">\n");
|
|
|
|
ricon("bold", loc("Bold text CTRL+B"), "elcode(document.form1.Text, 'B','')");
|
|
ricon("italic", loc("Italics text CTRL+I"), "elcode(document.form1.Text, 'I','')");
|
|
ricon("underline", loc("Underlined text CTRL+U"), "elcode(document.form1.Text, 'U','')");
|
|
|
|
rsprintf(" ");
|
|
ricon("center", loc("Centered text"), "elcode(document.form1.Text, 'CENTER','')");
|
|
|
|
rsprintf(" ");
|
|
ricon("url", loc("Insert hyperlink"), "queryURL(document.form1.Text)");
|
|
ricon("email", loc("Insert email"), "elcode(document.form1.Text, 'EMAIL','')");
|
|
|
|
sprintf(str, "window.open('upload.html', '',");
|
|
strlcat(str, "'top=280,left=350,width=500,height=120,dependent=yes,", sizeof(str));
|
|
strlcat(str, "menubar=no,status=no,scrollbars=no,location=no,resizable=yes')", sizeof(str));
|
|
ricon("image", loc("Insert image CTRL+M"), str);
|
|
|
|
rsprintf(" ");
|
|
ricon("quote", loc("Insert quote"), "elcode(document.form1.Text, 'QUOTE','')");
|
|
ricon("list", loc("Insert list CTRL+L"), "elcode(document.form1.Text, 'LIST','')");
|
|
ricon("table", loc("Insert table"), "elcode(document.form1.Text, 'TABLE','')");
|
|
ricon("heading", loc("Insert heading CTRL+H"), "queryHeading(document.form1.Text)");
|
|
ricon("line", loc("Insert horizontal line"), "elcode(document.form1.Text, 'LINE','')");
|
|
ricon("anchor", loc("Insert anchor point"), "elcode(document.form1.Text, 'ANCHOR','')");
|
|
|
|
rsprintf(" ");
|
|
ricon("code", loc("Insert code CTRL+O"), "elcode(document.form1.Text, 'CODE','')");
|
|
|
|
rsprintf(
|
|
" <img align=\"middle\" id=\"smileyIcon\" src=\"icons/elc_smile.png\" alt=\"%s\" title=\"%s\" border=\"0\"",
|
|
loc("Show the smiley bar"), loc("Show the smiley bar"));
|
|
rsprintf(" onclick=\"switch_smileys()\"");
|
|
rsprintf(" onmouseover=\"this.style.cursor='pointer';\" />\n");
|
|
|
|
rsprintf(" <select name=\"_font_name\" ");
|
|
rsprintf("onchange=\"elcode(document.form1.Text,'FONT',this.options[this.selectedIndex].value);");
|
|
rsprintf("this.selectedIndex=0;\">\n");
|
|
rsprintf("<option value=\"0\">%s</option>\n", loc("FONT"));
|
|
|
|
if (!getcfg(lbs->name, "Fonts", str, sizeof(str)))
|
|
strcpy(str, "Arial, Comic Sans MS, Courier New, Tahoma, Times New Roman, Verdana");
|
|
|
|
n = strbreak(str, list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
rsprintf("<option value=\"%s\">%s</option>\n", list[i], list[i]);
|
|
|
|
rsprintf("</select>\n");
|
|
|
|
rsprintf(" <select name=\"_font_size\" ");
|
|
rsprintf("onchange=\"elcode(document.form1.Text,'SIZE',this.options[this.selectedIndex].value);");
|
|
rsprintf("this.selectedIndex=0;\">\n");
|
|
rsprintf("<option value=\"0\">%s</option>\n", loc("SIZE"));
|
|
rsprintf("<option value=\"1\">1</option>\n");
|
|
rsprintf("<option value=\"2\">2</option>\n");
|
|
rsprintf("<option value=\"3\">3</option>\n");
|
|
rsprintf("<option value=\"4\">4</option>\n");
|
|
rsprintf("<option value=\"5\">5</option>\n");
|
|
rsprintf("<option value=\"6\">6</option>\n");
|
|
rsprintf("</select>\n");
|
|
|
|
rsprintf(" <select name=\"_font_color\" ");
|
|
rsprintf("onchange=\"elcode(document.form1.Text,'COLOR',this.options[this.selectedIndex].value);");
|
|
rsprintf("this.selectedIndex=0;\">\n");
|
|
rsprintf("<option value=\"0\">%s</option>\n", loc("COLOR"));
|
|
rsprintf("<option value=\"blue\" style=\"color:blue\">blue</option>\n");
|
|
rsprintf("<option value=\"darkblue\" style=\"color:darkblue\">dark-blue</option>\n");
|
|
rsprintf("<option value=\"orange\" style=\"color:orange\">orange</option>\n");
|
|
rsprintf("<option value=\"red\" style=\"color:red\">red</option>\n");
|
|
rsprintf("<option value=\"darkred\" style=\"color:darkred\">dark red</option>\n");
|
|
rsprintf("<option value=\"green\" style=\"color:green\">green</option>\n");
|
|
rsprintf("<option value=\"darkgreen\" style=\"color:darkgreen\">dark-green</option>\n");
|
|
rsprintf("<option value=\"pink\" style=\"color:deeppink\">pink</option>\n");
|
|
rsprintf("<option value=\"purple\" style=\"color:purple\">purple</option>\n");
|
|
rsprintf("<option value=\"chocolate\" style=\"color:chocolate\">chocolate</option>\n");
|
|
rsprintf("</select>");
|
|
|
|
rsprintf(" ");
|
|
ricon("clock", loc("Insert current time/date"), "insertTime(document.form1.Text)");
|
|
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
if (enc_selected == 0) {
|
|
|
|
rsprintf("<tr id=\"smileyRow\" style=\"display:none\"><td colspan=2 class=\"toolframe\">\n");
|
|
rsicon("smile", loc("smiling"), ":)");
|
|
rsicon("happy", loc("happy"), ":))");
|
|
rsicon("wink", loc("winking"), ";)");
|
|
rsicon("biggrin", loc("big grin"), ":D");
|
|
rsicon("crying", loc("crying"), ";(");
|
|
rsicon("cool", loc("cool"), "8-)");
|
|
rsicon("frown", loc("frowning"), ":(");
|
|
rsicon("confused", loc("confused"), "?-)");
|
|
rsicon("astonished", loc("astonished"), "8o");
|
|
rsicon("mad", loc("mad"), "X-(");
|
|
rsicon("pleased", loc("pleased"), ":]");
|
|
rsicon("tongue", loc("tongue"), ":P");
|
|
rsicon("yawn", loc("yawn"), ":O");
|
|
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
if (getcfg(lbs->name, "Message comment", comment, sizeof(comment)) && !message_id) {
|
|
rsprintf("<tr><td colspan=2 class=\"attribvalue\">%s</td></tr>\n", comment);
|
|
}
|
|
|
|
if (getcfg(lbs->name, "Reply comment", comment, sizeof(comment)) && breply) {
|
|
rsprintf("<tr><td colspan=2 class=\"attribvalue\">%s</td></tr>\n", comment);
|
|
}
|
|
|
|
rsprintf(
|
|
"<tr><td colspan=2 width=\"100%%\" class=\"attribvalue\" id=\"TextParent\" style=\"padding:0\">\n");
|
|
|
|
/* set textarea width */
|
|
width = 112;
|
|
|
|
if (getcfg(lbs->name, "Message width", str, sizeof(str)))
|
|
width = atoi(str);
|
|
|
|
/* increased width according to longest line */
|
|
if (message_id && enc_selected == 1) {
|
|
p = text;
|
|
do {
|
|
pend = strchr(p, '\n');
|
|
if (pend == NULL)
|
|
pend = p + strlen(p);
|
|
|
|
if (pend - p + 1 > width)
|
|
width = pend - p + 1;
|
|
|
|
if (*pend == 0)
|
|
break;
|
|
|
|
p = pend;
|
|
while (*p && (*p == '\r' || *p == '\n'))
|
|
p++;
|
|
} while (1);
|
|
|
|
/* leave space for '> ' */
|
|
if (!bedit && !bduplicate)
|
|
width += 2;
|
|
}
|
|
|
|
/* set textarea height */
|
|
height = 20;
|
|
if (getcfg(lbs->name, "Message height", str, sizeof(str)))
|
|
height = atoi(str);
|
|
|
|
if (breply)
|
|
/* hidden text for original message */
|
|
rsprintf("<input type=\"hidden\" name=\"reply_to\" value=\"%d\">\n", message_id);
|
|
|
|
if (breedit || bupload)
|
|
/* hidden text for original message */
|
|
if (isparam("reply_to"))
|
|
rsprintf("<input type=\"hidden\" name=\"reply_to\" value=\"%s\">\n", getparam("reply_to"));
|
|
|
|
if (bedit && message_id)
|
|
rsprintf("<input type=\"hidden\" id=\"edit_id\" name=\"edit_id\" value=\"%d\">\n", message_id);
|
|
|
|
if (isparam("nsel")) {
|
|
rsprintf("<input type=\"hidden\" name=\"nsel\" value=\"%s\">\n", getparam("nsel"));
|
|
for (i = 0; i < atoi(getparam("nsel")); i++) {
|
|
sprintf(str, "s%d", i);
|
|
if (isparam(str)) {
|
|
rsprintf("<input type=\"hidden\" name=\"s%d\" value=\"%s\">\n", i, getparam(str));
|
|
}
|
|
}
|
|
}
|
|
|
|
preset_text = getcfg(lbs->name, "Preset text", str, sizeof(str));
|
|
if (preset_text) {
|
|
|
|
/* don't use preset text if editing or replying */
|
|
if (bedit || bduplicate || breply)
|
|
preset_text = FALSE;
|
|
|
|
/* user preset on reedit only if preset is under condition */
|
|
if (breedit && !bpreview && !bupload && getcfg(lbs->name, "Preset text", str, sizeof(str)) == 2)
|
|
preset_text = TRUE;
|
|
}
|
|
|
|
if (show_text) {
|
|
|
|
if (fixed_text) {
|
|
|
|
rsprintf("<input type=hidden name=\"text\" value=\"<keep>\">\n");
|
|
rsprintf("<input type=hidden name=\"encoding\" value=\"%s\">\n", encoding);
|
|
|
|
_current_message_id = message_id;
|
|
rsprintf("<tr><td colspan=2 class=\"messageframe\">\n");
|
|
|
|
if (strieq(text, "<keep>") && message_id) {
|
|
size = TEXT_SIZE;
|
|
el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, text, &size, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
if (strieq(encoding, "plain")) {
|
|
rsputs("<pre class=\"messagepre\">");
|
|
rsputs2(lbs, FALSE, text);
|
|
rsputs("</pre>");
|
|
} else if (strieq(encoding, "ELCode"))
|
|
rsputs_elcode(lbs, FALSE, text);
|
|
else
|
|
rsputs(text);
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("<tr><td colspan=2 width=\"100%%\" class=\"attribvalue\">\n");
|
|
|
|
} else {
|
|
|
|
if (enc_selected == 1) {
|
|
/* use hard wrapping only for plain text if configured */
|
|
int hard_wrap = 1;
|
|
if (getcfg(lbs->name, "Hard wrap", str, sizeof(str)))
|
|
hard_wrap = atoi(str);
|
|
|
|
if (hard_wrap)
|
|
rsprintf("<textarea rows=%d cols=%d wrap=hard name=\"Text\">\n", height, width);
|
|
else
|
|
rsprintf("<textarea rows=%d cols=%d name=\"Text\">\n", height, width);
|
|
} else
|
|
rsprintf
|
|
("<textarea rows=%d cols=%d name=\"Text\" style=\"width:100%%;\">\n", height, width);
|
|
|
|
if (isparam("nsel")) {
|
|
rsprintf("--- %s ---\n", loc("keep original text"));
|
|
} else if (bedit) {
|
|
if (!preset_text) {
|
|
|
|
j = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
sprintf(mid, "%d", message_id);
|
|
add_subst_list(slist, svalue, "message id", mid, &j);
|
|
add_subst_time(lbs, slist, svalue, "entry time", date, &j, 0);
|
|
|
|
if (!bupload)
|
|
if (getcfg(lbs->name, "Prepend on edit", str, sizeof(str))) {
|
|
strsubst_list(str, sizeof(str), slist, svalue, j);
|
|
while (strstr(str, "\\n"))
|
|
memcpy(strstr(str, "\\n"), "\r\n", 2);
|
|
rsputs3(str);
|
|
}
|
|
|
|
/* use rsputs3 which just converts "<", ">", "&", "\"" to > etc. */
|
|
/* otherwise some HTML statments would break the page syntax */
|
|
rsputs3(text);
|
|
|
|
if (!bupload)
|
|
if (getcfg(lbs->name, "Append on edit", str, sizeof(str))) {
|
|
strsubst_list(str, sizeof(str), slist, svalue, j);
|
|
while (strstr(str, "\\n"))
|
|
memcpy(strstr(str, "\\n"), "\r\n", 2);
|
|
rsputs3(str);
|
|
}
|
|
}
|
|
} else if (bduplicate) {
|
|
|
|
rsputs3(text);
|
|
|
|
} else if (breply) {
|
|
if (!getcfg(lbs->name, "Quote on reply", str, sizeof(str)) || atoi(str) > 0) {
|
|
if (getcfg(lbs->name, "Prepend on reply", str, sizeof(str))) {
|
|
j = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
sprintf(mid, "%d", message_id);
|
|
add_subst_list(slist, svalue, "message id", mid, &j);
|
|
add_subst_time(lbs, slist, svalue, "entry time", date, &j, 0);
|
|
|
|
strsubst_list(str, sizeof(str), slist, svalue, j);
|
|
while (strstr(str, "\\n"))
|
|
memcpy(strstr(str, "\\n"), "\r\n", 2);
|
|
rsputs3(str);
|
|
}
|
|
|
|
p = text;
|
|
|
|
if (text[0]) {
|
|
if (!getcfg(lbs->name, "Reply string", reply_string, sizeof(reply_string)))
|
|
strcpy(reply_string, "> ");
|
|
|
|
if (enc_selected == 0) {
|
|
|
|
/* check for author */
|
|
if (orig_author[0])
|
|
rsprintf("[quote=\"%s\"]", orig_author);
|
|
else
|
|
rsprintf("[quote]");
|
|
rsputs3(text);
|
|
rsprintf("[/quote]\r\n");
|
|
|
|
} else if (enc_selected == 2) {
|
|
|
|
rsprintf("<p>\n");
|
|
rsprintf
|
|
("<table width=\"98%%\" align=\"center\" cellspacing=\"1\" style=\"border:1px solid #486090;\">\n");
|
|
rsprintf("<tbody>\n");
|
|
rsprintf("<tr>\n");
|
|
rsprintf
|
|
("<td cellpadding=\"3px\" style=\"background-color:#486090; font-weidht:bold; color:white;\">");
|
|
|
|
/* check for author */
|
|
if (orig_author[0]) {
|
|
rsprintf(loc("%s wrote"), orig_author);
|
|
} else {
|
|
rsprintf(loc("Quote"));
|
|
}
|
|
rsprintf(":</td></tr>\n");
|
|
rsprintf("<tr>\n");
|
|
rsprintf("<td cellpadding=\"10px\" style=\"background-color:#FFFFB0;\">");
|
|
rsputs3(text);
|
|
rsprintf("</td>\n");
|
|
rsprintf("</tr>\n");
|
|
rsprintf("</tbody>\n");
|
|
rsprintf("</table>\n");
|
|
rsprintf("</p><p> </p>\n");
|
|
|
|
} else {
|
|
do {
|
|
if (strchr(p, '\n')) {
|
|
*strchr(p, '\n') = 0;
|
|
|
|
if (encoding[0] == 'H') {
|
|
rsputs3(reply_string);
|
|
rsprintf("%s<br>\n", p);
|
|
} else {
|
|
rsputs(reply_string);
|
|
rsputs3(p);
|
|
rsprintf("\n");
|
|
}
|
|
|
|
p += strlen(p) + 1;
|
|
if (*p == '\n')
|
|
p++;
|
|
} else {
|
|
if (encoding[0] == 'H') {
|
|
rsputs3(reply_string);
|
|
rsprintf("%s<p>\n", p);
|
|
} else {
|
|
rsputs(reply_string);
|
|
rsputs3(p);
|
|
rsprintf("\n\n");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
} while (TRUE);
|
|
}
|
|
}
|
|
|
|
if (getcfg(lbs->name, "Append on reply", str, sizeof(str))) {
|
|
|
|
j = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
sprintf(mid, "%d", message_id);
|
|
add_subst_list(slist, svalue, "message id", mid, &j);
|
|
add_subst_time(lbs, slist, svalue, "entry time", date, &j, 0);
|
|
strsubst_list(str, sizeof(str), slist, svalue, j);
|
|
while (strstr(str, "\\n"))
|
|
memcpy(strstr(str, "\\n"), "\r\n", 2);
|
|
rsputs3(str);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (preset_text && !isparam("nsel")) {
|
|
getcfg(lbs->name, "Preset text", str, sizeof(str));
|
|
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
|
|
/* check if file exists */
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buffer = (char *)xmalloc(length + 1);
|
|
read(fh, buffer, length);
|
|
buffer[length] = 0;
|
|
close(fh);
|
|
rsputs3(buffer);
|
|
xfree(buffer);
|
|
} else {
|
|
j = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
strsubst_list(str, sizeof(str), slist, svalue, j);
|
|
while (strstr(str, "\\n"))
|
|
memcpy(strstr(str, "\\n"), "\r\n", 2);
|
|
|
|
if (strchr(str, ' ')) {
|
|
/* name contains blanks -> must be text */
|
|
rsputs3(str);
|
|
} else {
|
|
/* name is probably a file -> show error */
|
|
rsputs("File <i>");
|
|
rsputs3(str);
|
|
rsputs("</i> cannot be found");
|
|
}
|
|
}
|
|
}
|
|
|
|
rsprintf("</textarea><br>\n");
|
|
|
|
/* Encoding radio buttons */
|
|
|
|
if (allowed_encoding < 1 || allowed_encoding > 7) {
|
|
rsprintf
|
|
("<h1>Invalid \"Allowed encoding\" in configuration file, value must be between 1 and 7</h1>\n");
|
|
rsprintf("</table><!-- show_standard_title -->\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
return;
|
|
}
|
|
|
|
if (allowed_encoding == 1)
|
|
rsprintf("<input type=\"hidden\" name=\"encoding\" value=\"plain\">\n");
|
|
else if (allowed_encoding == 2)
|
|
rsprintf("<input type=\"hidden\" name=\"encoding\" value=\"ELCode\">\n");
|
|
else if (allowed_encoding == 4)
|
|
rsprintf("<input type=\"hidden\" name=\"encoding\" value=\"HTML\">\n");
|
|
else {
|
|
if (allowed_encoding == 4)
|
|
rsprintf("<input type=hidden name=\"encoding\" value=\"HTML\">\n");
|
|
else if (allowed_encoding == 2)
|
|
rsprintf("<input type=hidden name=\"encoding\" value=\"ELCode\">\n");
|
|
else if (allowed_encoding == 1)
|
|
rsprintf("<input type=hidden name=\"encoding\" value=\"plain\">\n");
|
|
else {
|
|
rsprintf("<b>%s</b>: ", loc("Encoding"));
|
|
|
|
if (allowed_encoding & 4) {
|
|
if (enc_selected == 2)
|
|
rsprintf
|
|
("<input type=radio id=\"HTML\" name=\"encoding\" value=\"HTML\" checked=\"checked\">");
|
|
else
|
|
rsprintf
|
|
("<input type=radio id=\"HTML\" name=\"encoding\" value=\"HTML\" onclick=\"cond_submit()\">");
|
|
rsprintf("<label for=\"HTML\">HTML </label>\n");
|
|
}
|
|
|
|
if (allowed_encoding & 2) {
|
|
if (enc_selected == 0)
|
|
rsprintf("<input type=radio id=\"ELCode\" name=\"encoding\" value=\"ELCode\" checked>");
|
|
else
|
|
rsprintf
|
|
("<input type=radio id=\"ELCode\" name=\"encoding\" value=\"ELCode\" onclick=\"cond_submit()\">");
|
|
rsprintf
|
|
("<label for=\"ELCode\"><a target=\"_blank\" href=\"?cmd=HelpELCode\">ELCode</a> </label>\n");
|
|
}
|
|
|
|
if (allowed_encoding & 1) {
|
|
if (enc_selected == 1)
|
|
rsprintf("<input type=radio id=\"plain\" name=\"encoding\" value=\"plain\" checked>");
|
|
else
|
|
rsprintf
|
|
("<input type=radio id=\"plain\" name=\"encoding\" value=\"plain\" onclick=\"cond_submit()\">");
|
|
rsprintf("<label for=\"plain\">plain </label>\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
rsprintf("<br>\n");
|
|
}
|
|
}
|
|
|
|
/* Suppress email check box */
|
|
if (message_id && bedit)
|
|
getcfg(lbs->name, "Suppress Email on edit", str, sizeof(str));
|
|
else
|
|
getcfg(lbs->name, "Suppress default", str, sizeof(str));
|
|
|
|
if (atoi(str) == 0) {
|
|
rsprintf("<input type=\"checkbox\" name=\"suppress\" id=\"suppress\" value=\"1\">");
|
|
rsprintf("<label for=\"suppress\">%s</label>\n", loc("Suppress Email notification"));
|
|
} else if (atoi(str) == 1) {
|
|
rsprintf("<input type=\"checkbox\" checked name=\"suppress\" id=\"suppress\" value=\"1\">");
|
|
rsprintf("<label for=\"suppress\">%s</label>\n", loc("Suppress Email notification"));
|
|
} else if (atoi(str) == 2) {
|
|
rsprintf("<input type=\"hidden\" name=\"suppress\" id=\"suppress\" value=\"2\">");
|
|
} else if (atoi(str) == 3) {
|
|
rsprintf("<input type=\"hidden\" name=\"suppress\" id=\"suppress\" value=\"3\">");
|
|
}
|
|
|
|
/* Suppress execute shell check box */
|
|
if (!bedit && getcfg(lbs->name, "Execute new", str, sizeof(str))) {
|
|
if (getcfg(lbs->name, "Suppress execute default", str, sizeof(str))) {
|
|
if (atoi(str) == 0) {
|
|
rsprintf(" \n");
|
|
rsprintf("<input type=\"checkbox\" name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
|
|
rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
|
|
} else if (atoi(str) == 1) {
|
|
rsprintf(" \n");
|
|
rsprintf
|
|
("<input type=\"checkbox\" checked name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
|
|
rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
|
|
}
|
|
} else {
|
|
rsprintf(" \n");
|
|
rsprintf("<input type=\"checkbox\" name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
|
|
rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
|
|
}
|
|
}
|
|
|
|
if (bedit && getcfg(lbs->name, "Execute edit", str, sizeof(str))) {
|
|
if (getcfg(lbs->name, "Suppress execute default", str, sizeof(str))) {
|
|
if (atoi(str) == 0) {
|
|
rsprintf(" \n");
|
|
rsprintf("<input type=\"checkbox\" name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
|
|
rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
|
|
} else if (atoi(str) == 1) {
|
|
rsprintf(" \n");
|
|
rsprintf
|
|
("<input type=\"checkbox\" checked name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
|
|
rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
|
|
}
|
|
} else {
|
|
rsprintf(" \n");
|
|
rsprintf("<input type=\"checkbox\" name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
|
|
rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
|
|
}
|
|
}
|
|
|
|
/* Resubmit check box */
|
|
if (bedit && message_id) {
|
|
if (getcfg(lbs->name, "Resubmit default", str, sizeof(str))) {
|
|
if (atoi(str) == 0) {
|
|
rsprintf(" \n");
|
|
rsprintf("<input type=\"checkbox\" name=\"resubmit\" id=\"resubmit\" value=1>");
|
|
rsprintf("<label for=\"resubmit\">%s</label>\n", loc("Resubmit as new entry"));
|
|
} else if (atoi(str) == 1) {
|
|
rsprintf(" \n");
|
|
rsprintf("<input type=\"checkbox\" checked name=\"resubmit\" id=\"resubmit\" value=1>");
|
|
rsprintf("<label for=\"resubmit\">%s</label>\n", loc("Resubmit as new entry"));
|
|
}
|
|
} else {
|
|
rsprintf(" \n");
|
|
rsprintf("<input type=\"checkbox\" name=\"resubmit\" id=\"resubmit\" value=1>");
|
|
rsprintf("<label for=\"resubmit\">%s</label>\n", loc("Resubmit as new entry"));
|
|
}
|
|
}
|
|
|
|
rsprintf("</tr>\n");
|
|
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (!att[i][0]) {
|
|
/* put first free attachment for show_uploader_finished() */
|
|
rsprintf("<tr><td><input type=hidden name=\"next_attachment\" value=\"%d\"></td></tr>\n", i + 1);
|
|
break;
|
|
}
|
|
|
|
index = 0;
|
|
if (!getcfg(lbs->name, "Enable attachments", str, sizeof(str)) || atoi(str) > 0) {
|
|
if (bedit || bduplicate || isparam("fa")) {
|
|
/* show existing attachments */
|
|
for (index = 0; index < MAX_ATTACHMENTS; index++)
|
|
if (att[index][0]) {
|
|
rsprintf("<tr class=\"attachment\"><td nowrap class=\"attribname\">%s %d:</td>\n",
|
|
loc("Attachment"), index + 1);
|
|
sprintf(str, "attachment%d", index);
|
|
rsprintf("<td class=\"attribvalue\">\n");
|
|
thumb_ref[0] = 0;
|
|
|
|
if (strlen(att[index]) < 14 || att[index][6] != '_' || att[index][13] != '_') {
|
|
strencode2(str, att[index], sizeof(str));
|
|
rsprintf("<b>Error: Invalid attachment \"%s\"</b><br>", str);
|
|
} else {
|
|
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(att[index], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, att[index], sizeof(file_name));
|
|
|
|
display_inline = is_image(file_name) || is_ascii(file_name);
|
|
if (chkext(file_name, ".ps") || chkext(file_name, ".pdf") || chkext(file_name, ".eps")
|
|
|| chkext(file_name, ".svg"))
|
|
display_inline = 0;
|
|
if ((chkext(file_name, ".htm") || chkext(file_name, ".html")) && is_full_html(file_name))
|
|
display_inline = 0;
|
|
|
|
thumb_status = create_thumbnail(lbs, file_name);
|
|
if (thumb_status)
|
|
display_inline = 1;
|
|
|
|
if (getcfg(lbs->name, "Preview attachments", str, sizeof(str)) && atoi(str) == 0)
|
|
display_inline = 0;
|
|
|
|
if (thumb_status && display_inline) {
|
|
get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0);
|
|
if (strrchr(thumb_name, DIR_SEPARATOR))
|
|
strlcpy(str, strrchr(thumb_name, DIR_SEPARATOR) + 1, sizeof(str));
|
|
else
|
|
strlcpy(str, thumb_name, sizeof(str));
|
|
strlcpy(thumb_name, str, sizeof(thumb_name));
|
|
if (thumb_status == 2)
|
|
strsubst(thumb_name, sizeof(thumb_name), "-0.png", "");
|
|
|
|
rsprintf("<table><tr><td class=\"toolframe\">\n");
|
|
char atti[256];
|
|
strlcpy(atti, att[index], sizeof(atti));
|
|
sprintf(str, "im('att'+'%d','%s','%s','smaller');", index, thumb_name, atti);
|
|
ricon("smaller", loc("Make smaller"), str);
|
|
sprintf(str, "im('att'+'%d','%s','%s','original');", index, thumb_name, atti);
|
|
ricon("original", loc("Original size"), str);
|
|
sprintf(str, "im('att'+'%d','%s','%s','larger');", index, thumb_name, atti);
|
|
ricon("larger", loc("Make larger"), str);
|
|
rsprintf(" \n");
|
|
sprintf(str, "im('att'+'%d','%s','%s','rotleft');", index, thumb_name, atti);
|
|
ricon("rotleft", loc("Rotate left"), str);
|
|
sprintf(str, "im('att'+'%d','%s','%s','rotright');", index, thumb_name, atti);
|
|
ricon("rotright", loc("Rotate right"), str);
|
|
rsprintf(" \n");
|
|
sprintf(str, "deleteAtt('%d','%s')", index,
|
|
loc("Are you sure to delete the attachment?"));
|
|
ricon("delatt", loc("Delete attachment"), str);
|
|
rsprintf(" \n");
|
|
|
|
/* ImageMagick available, so get image size */
|
|
strencode2(str, att[index], sizeof(str));
|
|
rsprintf("<b>%s</b> \n", str + 14);
|
|
if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
|
|
sprintf(cmd, "%s -format '%%wx%%h' '%s[0]'", _identify_cmd, file_name);
|
|
else
|
|
sprintf(cmd, "%s -format '%%wx%%h' '%s'", _identify_cmd, file_name);
|
|
#ifdef OS_WINNT
|
|
for (i = 0; i < (int) strlen(cmd); i++)
|
|
if (cmd[i] == '\'')
|
|
cmd[i] = '\"';
|
|
#endif
|
|
my_shell(cmd, str, sizeof(str));
|
|
if (atoi(str) > 0)
|
|
rsprintf("<span class=\"bytes\">%s: %s</span>\n", loc("Original size"), str);
|
|
|
|
rsprintf("</td></tr>\n");
|
|
rsprintf("<tr><td align=center>");
|
|
} else {
|
|
rsprintf("%s\n", att[index] + 14);
|
|
rsprintf(" <input type=\"submit\" name=\"delatt%d\" value=\"%s\" ", index,
|
|
loc("Delete"));
|
|
rsprintf("onClick=\"return mark_submitted();\">");
|
|
rsprintf("<br>\n");
|
|
}
|
|
|
|
if (display_inline) {
|
|
|
|
if (is_image(att[index]) || thumb_status) {
|
|
if (thumb_status == 1) {
|
|
get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0);
|
|
strlcpy(str, att[index], sizeof(str));
|
|
str[13] = 0;
|
|
if (strrchr(thumb_name, DIR_SEPARATOR))
|
|
strlcpy(file_enc, strrchr(thumb_name, DIR_SEPARATOR) + 1 + 14,
|
|
sizeof(file_enc));
|
|
else
|
|
strlcpy(file_enc, thumb_name + 14, sizeof(file_enc));
|
|
url_encode(file_enc,
|
|
sizeof(file_enc)); /* for file names with special characters like "+" */
|
|
sprintf(ref, "%s/%s?thumb=1", str, file_enc);
|
|
|
|
rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\" name=\"att%d\">\n", ref,
|
|
att[index] + 14, att[index] + 14, index);
|
|
strlcpy(thumb_ref, ref, sizeof(thumb_ref));
|
|
} else if (thumb_status == 2) {
|
|
for (i = 0;; i++) {
|
|
get_thumb_name(file_name, thumb_name, sizeof(thumb_name), i);
|
|
if (thumb_name[0]) {
|
|
strlcpy(str, att[index], sizeof(str));
|
|
str[13] = 0;
|
|
if (strrchr(thumb_name, DIR_SEPARATOR))
|
|
strlcpy(file_enc, strrchr(thumb_name, DIR_SEPARATOR) + 1 + 14,
|
|
sizeof(file_enc));
|
|
else
|
|
strlcpy(file_enc, thumb_name + 14, sizeof(file_enc));
|
|
url_encode(file_enc,
|
|
sizeof(file_enc)); /* for file names with special characters like "+" */
|
|
sprintf(ref, "%s/%s?thumb=1", str, file_enc);
|
|
|
|
rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\" name=\"att%d_%d\">\n",
|
|
ref, att[index] + 14, att[index] + 14, index, i);
|
|
strlcpy(thumb_ref, ref, sizeof(thumb_ref));
|
|
} else
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
strlcpy(str, att[index], sizeof(str));
|
|
str[13] = 0;
|
|
strcpy(file_enc, att[index] + 14);
|
|
url_encode(file_enc,
|
|
sizeof(file_enc)); /* for file names with special characters like "+" */
|
|
sprintf(ref, "%s/%s", str, file_enc);
|
|
|
|
rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\" name=\"att%d\">\n", ref,
|
|
att[index] + 14, att[index] + 14, index);
|
|
strlcpy(thumb_ref, ref, sizeof(thumb_ref));
|
|
}
|
|
} else {
|
|
if (is_ascii(file_name)) {
|
|
if (!chkext(att[index], ".HTML"))
|
|
rsprintf("<pre class=\"messagepre\">");
|
|
|
|
f = fopen(file_name, "rt");
|
|
n_lines = 0;
|
|
if (getcfg(lbs->name, "Attachment lines", str, sizeof(str)))
|
|
max_n_lines = atoi(str);
|
|
else
|
|
max_n_lines = 300;
|
|
|
|
if (f != NULL) {
|
|
while (!feof(f)) {
|
|
str[0] = 0;
|
|
fgets(str, sizeof(str), f);
|
|
|
|
if (n_lines < max_n_lines) {
|
|
if (!chkext(att[index], ".HTML"))
|
|
rsputs2(lbs, FALSE, str);
|
|
else
|
|
rsputs(str);
|
|
}
|
|
n_lines++;
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
if (!chkext(att[index], ".HTML"))
|
|
rsprintf("</pre>");
|
|
rsprintf("\n");
|
|
if (max_n_lines == 0)
|
|
rsprintf("<i><b>%d lines</b></i>\n", n_lines);
|
|
else if (n_lines > max_n_lines)
|
|
rsprintf("<i><b>... %d more lines ...</b></i>\n", n_lines - max_n_lines);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (thumb_status && display_inline)
|
|
rsprintf("</td></tr></table>\n");
|
|
}
|
|
|
|
strencode2(str, att[index], sizeof(str));
|
|
if (thumb_ref[0])
|
|
rsprintf("<input type=hidden name=\"attachment%d\" alt=\"%s\" value=\"%s\">\n", index,
|
|
thumb_ref, str);
|
|
else
|
|
rsprintf("<input type=hidden name=\"attachment%d\" value=\"%s\">\n", index, str);
|
|
|
|
rsprintf("</td></tr>\n");
|
|
} else
|
|
break;
|
|
}
|
|
|
|
/* optional attachment comment */
|
|
if (getcfg(lbs->name, "Attachment comment", comment, sizeof(comment))) {
|
|
rsprintf("<tr><td colspan=2 class=\"attribvalue\">\n");
|
|
rsputs(comment);
|
|
rsputs("</td></tr>\n");
|
|
}
|
|
|
|
/* field for add attachment */
|
|
if (att[MAX_ATTACHMENTS - 1][0]) {
|
|
rsprintf("<tr><td colspan=2 class=\"attribname\">%s</td>\n",
|
|
loc("Maximum number of attachments reached"));
|
|
rsprintf("</td></tr>\n");
|
|
} else {
|
|
rsprintf("<tr id=\"attachment_upload\"><td nowrap class=\"attribname\">%s %d:</td>\n",
|
|
loc("Attachment"), index + 1);
|
|
rsprintf(
|
|
"<td class=\"attribvalue\"><input type=\"file\" size=\"60\" maxlength=\"200\" name=\"attfile\">\n");
|
|
rsprintf(
|
|
" <input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return chkupload();\">\n",
|
|
loc("Upload"));
|
|
rsprintf("</td></tr>\n");
|
|
|
|
// print the holder for dropping attachments
|
|
rsprintf("<tr>\n");
|
|
rsprintf("<td style=\"background: white;\" colspan=2>\n");
|
|
rsprintf("<div id=\"holder\" class=\"holder\">%s</div>", loc("Drop attachments here..."));
|
|
rsprintf("</td></tr>");
|
|
}
|
|
}
|
|
|
|
rsprintf("</table><!-- listframe -->\n");
|
|
|
|
/*---- menu buttons again ----*/
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return chkform(this);\">\n",
|
|
loc("Submit"));
|
|
|
|
if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1)
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return save_draft();\">\n",
|
|
loc("Save"));
|
|
|
|
if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1)
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
|
|
loc("Preview"));
|
|
|
|
if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1)
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return check_delete();\">\n",
|
|
loc("Delete"));
|
|
else
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
|
|
loc("Back"));
|
|
|
|
rsprintf(
|
|
" <span id=\"saved2\" style=\"font-size:10px;font-style:italic;display:none\">%s 00:00:00</span>",
|
|
loc("Draft saved at"));
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
|
|
rsprintf("</table><!-- show_standard_title -->\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
|
|
/* rescan unconditional attributes */
|
|
if (_condition[0])
|
|
scan_attributes(lbs->name);
|
|
|
|
xfree(text);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_find_form(LOGBOOK *lbs) {
|
|
int i, j, year, month, day, flag;
|
|
char str[NAME_LENGTH+100], mode[NAME_LENGTH], comment[NAME_LENGTH], option[NAME_LENGTH], login_name[256],
|
|
full_name[256], user_email[256], enc_attr[NAME_LENGTH], whole_attr[2000],
|
|
attrib[MAX_N_ATTR][NAME_LENGTH], attr[NAME_LENGTH];
|
|
|
|
/*---- header ----*/
|
|
|
|
show_standard_header(lbs, FALSE, loc("ELOG find"), NULL, FALSE, NULL, NULL, 0);
|
|
|
|
/*---- title ----*/
|
|
|
|
show_standard_title(lbs, "", 0);
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
rsprintf("<input type=submit value=\"%s\">\n", loc("Search"));
|
|
rsprintf("<input type=reset value=\"%s\">\n", loc("Reset Form"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Back"));
|
|
rsprintf("<input type=hidden name=jcmd value=\"\">\n");
|
|
rsprintf("</span></td></tr>\n\n");
|
|
|
|
/*---- evaluate conditional attributes ----*/
|
|
|
|
for (i = 0; i < lbs->n_attr; i++)
|
|
attrib[i][0] = 0;
|
|
|
|
/* get attributes from parameters */
|
|
attrib_from_param(lbs->n_attr, attrib);
|
|
|
|
evaluate_conditions(lbs, attrib);
|
|
|
|
/*---- entry form ----*/
|
|
|
|
rsprintf("<tr><td class=\"form1\"><table><tr><td valign=\"top\" class=\"form1\">\n");
|
|
|
|
rsprintf("<b>%s:</b><br>", loc("Mode"));
|
|
|
|
if (!getcfg(lbs->name, "Display mode", mode, sizeof(mode)))
|
|
strcpy(mode, "Full");
|
|
|
|
if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) {
|
|
if (strieq(mode, "Full"))
|
|
rsprintf("<input type=radio id=\"full\" name=\"mode\" value=\"full\" checked>");
|
|
else
|
|
rsprintf("<input type=radio id=\"full\" name=\"mode\" value=\"full\">");
|
|
rsprintf("<label for=\"full\">%s </label><br>\n", loc("Display full entries"));
|
|
|
|
if (strieq(mode, "Summary"))
|
|
rsprintf("<input type=radio id=\"summary\" name=\"mode\" value=\"summary\" checked>");
|
|
else
|
|
rsprintf("<input type=radio id=\"summary\" name=\"mode\" value=\"summary\">");
|
|
rsprintf("<label for=\"summary\">%s </label><br>\n", loc("Summary only"));
|
|
|
|
} else {
|
|
if (strieq(mode, "Full") || strieq(mode, "Summary"))
|
|
rsprintf("<input type=radio id=\"summary\" name=\"mode\" value=\"summary\" checked>");
|
|
else
|
|
rsprintf("<input type=radio id=\"summary\" name=\"mode\" value=\"summary\">");
|
|
rsprintf("<label for=\"summary\">%s </label><br>\n", loc("Summary"));
|
|
}
|
|
|
|
if (strieq(mode, "Threaded"))
|
|
rsprintf("<input type=radio id=\"threaded\" name=\"mode\" value=\"threaded\" checked>");
|
|
else
|
|
rsprintf("<input type=radio id=\"threaded\" name=\"mode\" value=\"threaded\">");
|
|
rsprintf("<label for=\"threaded\">%s </label>\n", loc("Display threads"));
|
|
|
|
rsprintf("</td><td valign=\"top\" class=\"form1\"><b>%s:</b><br>", loc("Export to"));
|
|
|
|
if (strieq(mode, "CSV1"))
|
|
rsprintf("<input type=radio id=\"CSV1\" name=\"mode\" value=\"CSV1\" checked>");
|
|
else
|
|
rsprintf("<input type=radio id=\"CSV1\" name=\"mode\" value=\"CSV1\">");
|
|
rsprintf("<label for=\"CSV1\">%s </label><br>\n", loc("CSV (\",\" separated)"));
|
|
|
|
if (strieq(mode, "CSV2"))
|
|
rsprintf("<input type=radio id=\"CSV2\" name=\"mode\" value=\"CSV2\" checked>");
|
|
else
|
|
rsprintf("<input type=radio id=\"CSV2\" name=\"mode\" value=\"CSV2\">");
|
|
rsprintf("<label for=\"CSV2\">%s </label><br>\n", loc("CSV (\";\" separated)"));
|
|
|
|
if (strieq(mode, "CSV3"))
|
|
rsprintf("<input type=radio id=\"CSV3\" name=\"mode\" value=\"CSV3\" checked>");
|
|
else
|
|
rsprintf("<input type=radio id=\"CSV3\" name=\"mode\" value=\"CSV3\">");
|
|
rsprintf("<label for=\"CSV3\">%s </label><br>\n", loc("CSV (\";\" separated) + Text"));
|
|
|
|
if (strieq(mode, "XML"))
|
|
rsprintf("<input type=radio id=\"XML\" name=\"mode\" value=\"XML\" checked>");
|
|
else
|
|
rsprintf("<input type=radio id=\"XML\" name=\"mode\" value=\"XML\">");
|
|
rsprintf("<label for=\"XML\">XML </label><br>\n");
|
|
|
|
if (strieq(mode, "Raw"))
|
|
rsprintf("<input type=radio id=\"Raw\" name=\"mode\" value=\"Raw\" checked>");
|
|
else
|
|
rsprintf("<input type=radio id=\"Raw\" name=\"mode\" value=\"Raw\">");
|
|
rsprintf("<label for=\"Raw\">Raw </label><br>\n");
|
|
|
|
rsprintf("</td>\n");
|
|
|
|
rsprintf("<td valign=\"top\" class=\"form2\"><b>%s:</b><br>", loc("Options"));
|
|
|
|
rsprintf("<input type=checkbox id=\"attach\" name=\"attach\" value=1>");
|
|
rsprintf("<label for=\"attach\">%s<br></label>\n", loc("Show attachments"));
|
|
|
|
rsprintf("<input type=checkbox id=\"printable\" name=\"printable\" value=1>");
|
|
rsprintf("<label for=\"printable\">%s<br></label>\n", loc("Printable output"));
|
|
|
|
/* put hidden reverse=0, which gets used if the reverse checkbox is unchecked and "reverse sort=1" is in elogd.cfg */
|
|
rsprintf("<input type=hidden name=\"reverse\" value=0>\n");
|
|
if (getcfg(lbs->name, "Reverse sort", str, sizeof(str)) && atoi(str) == 1)
|
|
rsprintf("<input type=checkbox id=\"reverse\" name=\"reverse\" value=1 checked>");
|
|
else
|
|
rsprintf("<input type=checkbox id=\"reverse\" name=\"reverse\" value=1>");
|
|
rsprintf("<label for=\"reverse\">%s<br></label>\n", loc("Sort in reverse order"));
|
|
|
|
/* count logbooks */
|
|
for (i = 0;; i++) {
|
|
if (!enumgrp(i, str))
|
|
break;
|
|
|
|
if (is_logbook(str))
|
|
continue;
|
|
}
|
|
|
|
if (i > 2) {
|
|
if (!getcfg(lbs->name, "Search all logbooks", str, sizeof(str)) || atoi(str) == 1 || atoi(str) == 2) {
|
|
|
|
if (atoi(str) == 2)
|
|
rsprintf("<input type=checkbox checked id=all name=all value=1>");
|
|
else
|
|
rsprintf("<input type=checkbox id=all name=all value=1>");
|
|
rsprintf("<label for=\"all\">%s</label><br>\n", loc("Search all logbooks"));
|
|
}
|
|
}
|
|
|
|
rsprintf(loc("Display"));
|
|
if (!getcfg(lbs->name, "Entries per page", str, sizeof(str)))
|
|
strcpy(str, "20");
|
|
rsprintf(" <input type=text name=npp size=3 value=%s> ", str);
|
|
rsprintf(loc("entries per page"));
|
|
|
|
rsprintf("</td></tr></table></td></tr>\n");
|
|
|
|
rsprintf("<tr><td class=\"form2\"><b>%s:</b>", loc("Filters"));
|
|
sprintf(str, "<a href=\"http://dmoz.org/Computers/Programming/Languages/Regular_Expressions/\">");
|
|
strcat(str, loc("regular expressions"));
|
|
strcat(str, "</a>");
|
|
rsprintf(" <span class=\"selcomment\">(");
|
|
rsprintf(loc("Text fields are treated as %s"), str);
|
|
rsprintf(")</span><br>");
|
|
|
|
/* table for two-column items */
|
|
rsprintf("<table width=\"100%%\" cellspacing=0>\n");
|
|
|
|
rsprintf("<tr><td class=\"attribname\" nowrap width=\"10%%\">%s:</td>", loc("Entry date"));
|
|
rsprintf("<td class=\"attribvalue\"><table width=\"100%%\" cellspacing=0 border=0>\n");
|
|
rsprintf("<tr><td width=\"1%%\">%s:<td>", loc("Start"));
|
|
|
|
year = month = day = 0;
|
|
sprintf(str, "ya");
|
|
if (isparam(str))
|
|
year = atoi(getparam(str));
|
|
sprintf(str, "ma");
|
|
if (isparam(str))
|
|
month = atoi(getparam(str));
|
|
sprintf(str, "da");
|
|
if (isparam(str))
|
|
day = atoi(getparam(str));
|
|
show_date_selector(day, month, year, "a");
|
|
rsprintf(" ");
|
|
show_time_selector(-1, -1, -1, "a");
|
|
|
|
rsprintf(" / %s: ", loc("Show last"));
|
|
|
|
if (getcfg(lbs->name, "Show last default", str, sizeof(str)))
|
|
i = atoi(str);
|
|
else
|
|
i = 0;
|
|
|
|
rsprintf("<select name=last>\n");
|
|
if (i > 0)
|
|
rsprintf("<option value=\"\">%s\n", loc("All time"));
|
|
else
|
|
rsprintf("<option value=\"\">\n");
|
|
rsprintf("<option %svalue=1>%s\n", i == 1 ? "selected " : "", loc("Day"));
|
|
rsprintf("<option %svalue=3>%s\n", i == 3 ? "selected " : "", loc("3 Days"));
|
|
rsprintf("<option %svalue=7>%s\n", i == 7 ? "selected " : "", loc("Week"));
|
|
rsprintf("<option %svalue=31>%s\n", i == 31 ? "selected " : "", loc("Month"));
|
|
rsprintf("<option %svalue=92>%s\n", i == 92 ? "selected " : "", loc("3 Months"));
|
|
rsprintf("<option %svalue=182>%s\n", i == 182 ? "selected " : "", loc("6 Months"));
|
|
rsprintf("<option %svalue=364>%s\n", i == 364 ? "selected " : "", loc("Year"));
|
|
rsprintf("</select> \n");
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("<tr><td width=\"1%%\">%s:<td>", loc("End"));
|
|
|
|
year = month = day = 0;
|
|
sprintf(str, "yb");
|
|
if (isparam(str))
|
|
year = atoi(getparam(str));
|
|
sprintf(str, "mb");
|
|
if (isparam(str))
|
|
month = atoi(getparam(str));
|
|
sprintf(str, "db");
|
|
if (isparam(str))
|
|
day = atoi(getparam(str));
|
|
show_date_selector(day, month, year, "b");
|
|
rsprintf(" ");
|
|
show_time_selector(-1, -1, -1, "b");
|
|
|
|
rsprintf("</td></tr></table></td></tr>\n");
|
|
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
rsprintf("<tr><td class=\"attribname\" nowrap>%s:</td>", attr_list[i]);
|
|
rsprintf("<td class=\"attribvalue\">");
|
|
if (attr_options[i][0][0] == 0) {
|
|
|
|
if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {
|
|
|
|
rsprintf("<table width=\"100%%\" cellspacing=0 border=0>\n");
|
|
rsprintf("<tr><td width=\"1%%\">%s:<td>", loc("Start"));
|
|
|
|
year = month = day = 0;
|
|
sprintf(str, "y%da", i);
|
|
if (isparam(str))
|
|
year = atoi(getparam(str));
|
|
sprintf(str, "m%da", i);
|
|
if (isparam(str))
|
|
month = atoi(getparam(str));
|
|
sprintf(str, "d%da", i);
|
|
if (isparam(str))
|
|
day = atoi(getparam(str));
|
|
|
|
sprintf(str, "%da", i);
|
|
show_date_selector(day, month, year, str);
|
|
if (attr_flags[i] & AF_DATETIME) {
|
|
rsprintf(" ");
|
|
show_time_selector(-1, -1, -1, str);
|
|
}
|
|
|
|
rsprintf("</td></tr>\n");
|
|
rsprintf("<tr><td width=\"1%%\">%s:<td>", loc("End"));
|
|
|
|
year = month = day = 0;
|
|
sprintf(str, "y%db", i);
|
|
if (isparam(str))
|
|
year = atoi(getparam(str));
|
|
sprintf(str, "m%db", i);
|
|
if (isparam(str))
|
|
month = atoi(getparam(str));
|
|
sprintf(str, "d%db", i);
|
|
if (isparam(str))
|
|
day = atoi(getparam(str));
|
|
|
|
sprintf(str, "%db", i);
|
|
show_date_selector(day, month, year, str);
|
|
if (attr_flags[i] & AF_DATETIME) {
|
|
rsprintf(" ");
|
|
show_time_selector(-1, -1, -1, str);
|
|
}
|
|
|
|
rsprintf("</td></tr></table>\n");
|
|
|
|
} else if (attr_flags[i] & AF_MUSERLIST) {
|
|
|
|
for (j = 0;; j++) {
|
|
if (!enum_user_line(lbs, j, login_name, sizeof(login_name)))
|
|
break;
|
|
get_user_line(lbs, login_name, NULL, full_name, NULL, NULL, NULL, NULL);
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "%s_%d", attr, j);
|
|
|
|
rsprintf("<nobr><input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\"\">\n", str, str,
|
|
full_name);
|
|
|
|
rsprintf("<label for=\"%s\">%s</label></nobr>\n", str, full_name);
|
|
}
|
|
|
|
} else if (attr_flags[i] & AF_MUSEREMAIL) {
|
|
|
|
for (j = 0;; j++) {
|
|
if (!enum_user_line(lbs, j, login_name, sizeof(login_name)))
|
|
break;
|
|
get_user_line(lbs, login_name, NULL, NULL, user_email, NULL, NULL, NULL);
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "%s_%d", attr, j);
|
|
|
|
rsprintf("<nobr><input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\"\">\n", str, str,
|
|
user_email);
|
|
|
|
rsprintf("<label for=\"%s\">%s</label></nobr>\n", str, user_email);
|
|
}
|
|
|
|
} else {
|
|
rsprintf("<input type=\"text\" size=\"30\" maxlength=\"80\" name=\"%s\" value=\"%s\">\n",
|
|
attr_list[i], attrib[i]);
|
|
}
|
|
|
|
} else {
|
|
if (strieq(attr_options[i][0], "boolean")) {
|
|
|
|
if (isparam(attr_list[i]) && *getparam(attr_list[i]))
|
|
flag = atoi(getparam(attr_list[i]));
|
|
else
|
|
flag = -1;
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "%s_0", attr);
|
|
rsprintf("<span style=\"white-space:nowrap;\">\n");
|
|
if (flag == 0)
|
|
rsprintf("<input type=radio checked id=\"%s\" name=\"%s\" value=\"0\">\n", str, attr_list[i]);
|
|
else
|
|
rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"0\">\n", str, attr_list[i]);
|
|
rsprintf("<label for=\"%s\">0</label>\n", str);
|
|
rsprintf("</span>\n");
|
|
|
|
sprintf(str, "%s_1", attr);
|
|
rsprintf("<span style=\"white-space:nowrap;\">\n");
|
|
if (flag == 1)
|
|
rsprintf("<input type=radio checked id=\"%s\" name=\"%s\" value=\"1\">\n", str, attr_list[i]);
|
|
else
|
|
rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"1\">\n", str, attr_list[i]);
|
|
rsprintf("<label for=\"%s\">1</label>\n", str);
|
|
rsprintf("</span>\n");
|
|
|
|
sprintf(str, "%s_2", attr);
|
|
rsprintf("<span style=\"white-space:nowrap;\">\n");
|
|
if (flag == -1)
|
|
rsprintf("<input type=radio checked id=\"%s\" name=\"%s\" value=\"\">\n", str, attr_list[i]);
|
|
else
|
|
rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"\">\n", str, attr_list[i]);
|
|
rsprintf("<label for=\"%s\">%s</label>\n", str, loc("unspecified"));
|
|
rsprintf("</span>\n");
|
|
}
|
|
|
|
/* display image for icon */
|
|
else if (attr_flags[i] & AF_ICON) {
|
|
for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
|
|
strcpy(option, attr_options[i][j]);
|
|
if (strchr(option, '{'))
|
|
*strchr(option, '{') = 0;
|
|
|
|
sprintf(str, "Icon comment %s", option);
|
|
getcfg(lbs->name, str, comment, sizeof(comment));
|
|
|
|
if (comment[0] == 0)
|
|
strcpy(comment, option);
|
|
|
|
rsprintf("<nobr><input type=radio name=\"%s\" value=\"%s\">", attr_list[i], comment);
|
|
|
|
rsprintf("<img src=\"icons/%s\" alt=\"%s\" title=\"%s\"></nobr>\n", option, comment, comment);
|
|
}
|
|
}
|
|
|
|
/* display check boxes (or'ed) */
|
|
else if (attr_flags[i] & AF_MULTI) {
|
|
for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "%s_%d", attr, j);
|
|
|
|
if (isparam(str))
|
|
rsprintf("<nobr><input type=checkbox checked id=\"%s\" name=\"%s\" value=\"%s\"\">\n",
|
|
str, str, attr_options[i][j]);
|
|
else
|
|
rsprintf("<nobr><input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\"\">\n",
|
|
str, str, attr_options[i][j]);
|
|
|
|
rsprintf("<label for=\"%s\">%s</label></nobr>\n", str, attr_options[i][j]);
|
|
}
|
|
} else {
|
|
rsprintf("<select name=\"%s\"", attr_list[i]);
|
|
|
|
if (is_cond_attr(i))
|
|
rsprintf(" onChange=\"document.form1.jcmd.value='Find';document.form1.submit();\"");
|
|
rsprintf(">\n");
|
|
|
|
rsprintf("<option value=\"\">\n");
|
|
for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
|
|
strencode2(str, attr_options[i][j], sizeof(str));
|
|
if (strchr(str, '{'))
|
|
*strchr(str, '{') = 0;
|
|
strencode2(enc_attr, attrib[i], sizeof(enc_attr));
|
|
|
|
sprintf(whole_attr, "^%s$", str);
|
|
if (strieq(attr_options[i][j], attrib[i]) || strieq(str, enc_attr)
|
|
|| strieq(whole_attr, enc_attr))
|
|
rsprintf("<option selected value=\"^%s$\">%s\n", str, str);
|
|
else
|
|
rsprintf("<option value=\"^%s$\">%s\n", str, str);
|
|
}
|
|
rsprintf("</select>\n");
|
|
}
|
|
}
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
rsprintf("<tr><td class=\"attribname\">%s:</td>", loc("Text"));
|
|
rsprintf("<td class=\"attribvalue\">\n");
|
|
if (isparam("subtext"))
|
|
strlcpy(str, getparam("subtext"), sizeof(str));
|
|
else
|
|
str[0] = 0;
|
|
rsprintf("<input type=\"text\" size=\"30\" maxlength=\"80\" name=\"subtext\" value=\"%s\">\n", str);
|
|
|
|
rsprintf("<input type=checkbox id=\"sall\" name=\"sall\" value=1>\n");
|
|
rsprintf("<label for=\"sall\">%s</label>\n", loc("Search text also in attributes"));
|
|
|
|
rsprintf("<tr><td><td class=\"attribvalue\">\n");
|
|
if (getcfg(lbs->name, "Case sensitive search", str, sizeof(str)) && atoi(str))
|
|
rsprintf("<input type=checkbox id=\"sall\" name=\"casesensitive\" value=1 checked>\n");
|
|
else
|
|
rsprintf("<input type=checkbox id=\"sall\" name=\"casesensitive\" value=1>\n");
|
|
rsprintf("<label for=\"casesensitive\">%s</label>\n", loc("Case sensitive"));
|
|
|
|
rsprintf("</td></tr></table></td></tr></table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
const char *find_section(const char *buf, const char *name) {
|
|
const char *pstart;
|
|
char *pstr, str[80];
|
|
|
|
do {
|
|
if (*buf == '[') {
|
|
pstart = buf;
|
|
buf++;
|
|
pstr = str;
|
|
while (*buf && *buf != ']' && *buf != '\n' && *buf != '\r')
|
|
*pstr++ = *buf++;
|
|
*pstr = 0;
|
|
if (strieq(str, name))
|
|
return pstart;
|
|
}
|
|
|
|
if (buf)
|
|
buf = strchr(buf, '\n');
|
|
if (buf)
|
|
buf++;
|
|
if (buf && *buf == '\r')
|
|
buf++;
|
|
|
|
} while (buf);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
const char *find_next_section(const char *buf) {
|
|
do {
|
|
if (*buf == '[')
|
|
return buf;
|
|
|
|
if (buf)
|
|
buf = strchr(buf, '\n');
|
|
if (buf)
|
|
buf++;
|
|
} while (buf);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void load_config_section(char *section, char **buffer, char *error) {
|
|
int fh, length;
|
|
char *p;
|
|
|
|
error[0] = 0;
|
|
*buffer = NULL;
|
|
fh = open(config_file, O_RDONLY | O_BINARY);
|
|
if (fh < 0) {
|
|
sprintf(error, "Cannot read configuration file \"%s\": %s", config_file, strerror(errno));
|
|
return;
|
|
}
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
*buffer = (char *)xmalloc(length + 1);
|
|
read(fh, *buffer, length);
|
|
(*buffer)[length] = 0;
|
|
close(fh);
|
|
|
|
if (section == NULL)
|
|
return;
|
|
|
|
if ((p = (char *) find_section(*buffer, section)) != NULL) {
|
|
if (strchr(p, ']')) {
|
|
p = strchr(p, ']') + 1;
|
|
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
|
|
p++;
|
|
}
|
|
int n = length;
|
|
if ((int)strlen(p)+1 < n)
|
|
n = strlen(p)+1;
|
|
memmove(*buffer, p, n);
|
|
if ((p = (char *) find_next_section(*buffer + 1)) != NULL) {
|
|
*p = 0;
|
|
|
|
/* strip trailing newlines */
|
|
if (p) {
|
|
p--;
|
|
|
|
while (p > *buffer && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'))
|
|
*p-- = 0;
|
|
}
|
|
} else {
|
|
p = *buffer + strlen(*buffer) - 1;
|
|
|
|
while (p > *buffer && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'))
|
|
*p-- = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_admin_page(LOGBOOK *lbs, const char *top_group) {
|
|
int rows, cols;
|
|
char *buffer, error_str[256];
|
|
char section[NAME_LENGTH], str[NAME_LENGTH], grp[NAME_LENGTH];
|
|
|
|
/*---- header ----*/
|
|
|
|
sprintf(str, "ELOG %s", loc("Admin"));
|
|
show_html_header(lbs, FALSE, str, TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
|
|
rsprintf("<body><form method=\"POST\" action=\"./\" enctype=\"multipart/form-data\">\n");
|
|
|
|
/*---- title ----*/
|
|
|
|
show_standard_title(lbs, "", 0);
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Save"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
|
|
rsprintf("<input type=hidden name=cfgpage value=\"1\">\n");
|
|
|
|
if (lbs->top_group[0] && (!top_group || strieq(top_group, "global"))) {
|
|
if (is_admin_user(NULL, getparam("unm"))) {
|
|
if (lbs->top_group[0]) {
|
|
|
|
sprintf(str, "global %s", lbs->top_group);
|
|
|
|
if (is_group(str)) {
|
|
sprintf(grp, "[global %s]", lbs->top_group);
|
|
sprintf(str, loc("Change %s"), grp);
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_group("global") && !strieq(top_group, "global")) {
|
|
if (is_admin_user_global(getparam("unm"))) {
|
|
sprintf(str, loc("Change %s"), "[global]");
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", str);
|
|
}
|
|
}
|
|
|
|
if (top_group) {
|
|
if (strieq(top_group, "global")) {
|
|
rsprintf("<input type=hidden name=global value=\"global\">\n");
|
|
strcpy(str, "[global]");
|
|
} else {
|
|
rsprintf("<input type=hidden name=global value=\"%s\">\n", top_group);
|
|
sprintf(str, "[global %s]", top_group);
|
|
}
|
|
rsprintf("<br><center><b>%s</b></center>", str);
|
|
}
|
|
|
|
if (is_group("global") && !strieq(top_group, "global")) {
|
|
if (is_admin_user(NULL, getparam("unm"))) {
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Delete this logbook"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Rename this logbook"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Create new logbook"));
|
|
}
|
|
}
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
|
|
/*---- entry form ----*/
|
|
|
|
rsprintf("<tr><td class=\"form1\">\n");
|
|
|
|
/* extract section of current logbook */
|
|
if (top_group) {
|
|
if (strieq(top_group, "global"))
|
|
strcpy(section, "global");
|
|
else
|
|
sprintf(section, "global %s", top_group);
|
|
} else
|
|
strcpy(section, lbs->name);
|
|
|
|
load_config_section(section, &buffer, error_str);
|
|
|
|
if (error_str[0]) {
|
|
rsprintf("<h2>%s</h2>\n", error_str);
|
|
rsprintf("</table></td></tr></table>\n");
|
|
rsprintf("</body></html>\r\n");
|
|
return;
|
|
}
|
|
|
|
if (getcfg(section, "Admin textarea", str, sizeof(str)) && strchr(str, ',') != NULL) {
|
|
cols = atoi(str);
|
|
rows = atoi(strchr(str, ',') + 1);
|
|
} else {
|
|
cols = 120;
|
|
rows = 30;
|
|
}
|
|
|
|
rsprintf("<textarea cols=%d rows=%d wrap=virtual name=Text>", cols, rows);
|
|
|
|
rsputs3(buffer);
|
|
xfree(buffer);
|
|
|
|
rsprintf("</textarea>\n");
|
|
|
|
/* put link for config page */
|
|
rsprintf("<br><a target=\"_blank\" href=\"https://elog.psi.ch/elog/config.html\">Syntax Help</a>");
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Save"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
|
|
rsprintf("</span></td></tr>\n\n");
|
|
|
|
rsprintf("</table>\n\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void remove_crlf(char *buffer) {
|
|
char *p;
|
|
|
|
/* convert \r\n -> \n */
|
|
p = buffer;
|
|
while ((p = strstr(p, "\r\n")) != NULL) {
|
|
memmove(p, p + 1, strlen(p + 1) + 1);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void adjust_crlf(char *buffer, int bufsize) {
|
|
char *p;
|
|
UNUSED(bufsize);
|
|
|
|
#ifdef OS_UNIX
|
|
|
|
/* convert \r\n -> \n */
|
|
p = buffer;
|
|
while ((p = strstr(p, "\r\n")) != NULL) {
|
|
memmove(p, p + 1, strlen(p + 1) + 1); // strcpy() gives error under Ubuntu
|
|
}
|
|
#else
|
|
|
|
char *tmpbuf;
|
|
|
|
assert(bufsize);
|
|
tmpbuf = xmalloc(bufsize);
|
|
|
|
/* convert \n -> \r\n */
|
|
p = buffer;
|
|
while ((p = strstr(p, "\n")) != NULL) {
|
|
|
|
if (p > buffer && *(p - 1) == '\r') {
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
if ((int) strlen(buffer) + 2 >= bufsize) {
|
|
xfree(tmpbuf);
|
|
return;
|
|
}
|
|
|
|
strlcpy(tmpbuf, p, bufsize);
|
|
*(p++) = '\r';
|
|
strlcpy(p, tmpbuf, bufsize - (p - buffer));
|
|
p++;
|
|
}
|
|
|
|
xfree(tmpbuf);
|
|
#endif
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int save_admin_config(char *section, char *buffer, char *error) {
|
|
int fh, i, length;
|
|
char *buf, *buf2, *p1, *p2;
|
|
|
|
error[0] = 0;
|
|
|
|
fh = open(config_file, O_RDWR | O_BINARY, 644);
|
|
if (fh < 0) {
|
|
sprintf(error, loc("Cannot open file <b>%s</b>"), config_file);
|
|
strcat(error, ": ");
|
|
strcat(error, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
/* read previous contents */
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buf = (char *)xmalloc(length + strlen(buffer) + 10);
|
|
read(fh, buf, length);
|
|
buf[length] = 0;
|
|
|
|
/* find previous logbook config */
|
|
p1 = (char *) find_section(buf, section);
|
|
p2 = (char *) find_next_section(p1 + 1);
|
|
|
|
/* save tail */
|
|
buf2 = NULL;
|
|
|
|
if (p2)
|
|
buf2 = xstrdup(p2);
|
|
|
|
/* combine old and new config */
|
|
sprintf(p1, "[%s]\r\n", section);
|
|
strcat(p1, buffer);
|
|
strcat(p1, "\r\n\r\n");
|
|
|
|
if (p2) {
|
|
strlcat(p1, buf2, length + strlen(buffer) + 1);
|
|
xfree(buf2);
|
|
}
|
|
|
|
adjust_crlf(buf, length + strlen(buffer) + 10);
|
|
|
|
lseek(fh, 0, SEEK_SET);
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(error, loc("Cannot write to <b>%s</b>"), config_file);
|
|
strcat(error, ": ");
|
|
strcat(error, strerror(errno));
|
|
close(fh);
|
|
xfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
xfree(buf);
|
|
|
|
/* force re-read of config file */
|
|
check_config_file(TRUE);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int change_config_line(LOGBOOK *lbs, char *option, const char *old_value, const char *new_value) {
|
|
int fh, i, j, n, length, bufsize;
|
|
char str[NAME_LENGTH], *buf, *buf2, *p1, *p2, *p3;
|
|
char list[MAX_N_LIST][NAME_LENGTH], line[NAME_LENGTH];
|
|
|
|
fh = open(config_file, O_RDWR | O_BINARY, 644);
|
|
if (fh < 0) {
|
|
sprintf(str, loc("Cannot open file <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
|
|
/* read previous contents */
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
bufsize = 2 * (length + strlen(new_value) + 10);
|
|
buf = (char *)xmalloc(bufsize);
|
|
read(fh, buf, length);
|
|
buf[length] = 0;
|
|
|
|
/* find location of option */
|
|
p1 = (char *) find_param(buf, lbs->name, option);
|
|
if (p1 == NULL)
|
|
return 0;
|
|
|
|
p2 = strchr(p1, '=');
|
|
if (p2 == 0)
|
|
return 0;
|
|
|
|
p2++;
|
|
while (*p2 == ' ' || *p2 == '\t')
|
|
p2++;
|
|
|
|
strlcpy(line, p2, sizeof(line));
|
|
if (strchr(line, '\r'))
|
|
*strchr(line, '\r') = 0;
|
|
if (strchr(line, '\n'))
|
|
*strchr(line, '\n') = 0;
|
|
n = strbreak(line, list, MAX_N_LIST, ",", FALSE);
|
|
|
|
/* save tail */
|
|
p3 = strchr(p2, '\n');
|
|
if (p3 && *(p3 - 1) == '\r')
|
|
p3--;
|
|
|
|
buf2 = NULL;
|
|
if (p3)
|
|
buf2 = xstrdup(p3);
|
|
|
|
if (old_value[0]) {
|
|
for (i = 0; i < n; i++) {
|
|
if (strieq(old_value, list[i])) {
|
|
if (new_value[0]) {
|
|
/* rename value */
|
|
strcpy(list[i], new_value);
|
|
} else {
|
|
/* delete value */
|
|
for (j = i; j < n - 1; j++)
|
|
strcpy(list[j], list[j + 1]);
|
|
n--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (n < MAX_N_LIST)
|
|
strcpy(list[n++], new_value);
|
|
}
|
|
|
|
/* write new option list */
|
|
for (i = 0; i < n; i++) {
|
|
strcpy(p2, list[i]);
|
|
if (i < n - 1)
|
|
strcat(p2, ", ");
|
|
p2 += strlen(p2);
|
|
}
|
|
|
|
/* append tail */
|
|
if (buf2) {
|
|
strlcat(p2, buf2, length + strlen(new_value) + 10);
|
|
xfree(buf2);
|
|
}
|
|
|
|
adjust_crlf(buf, bufsize);
|
|
|
|
lseek(fh, 0, SEEK_SET);
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(str, loc("Cannot write to <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
close(fh);
|
|
xfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
xfree(buf);
|
|
|
|
/* force re-read of config file */
|
|
check_config_file(TRUE);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int delete_logbook(LOGBOOK *lbs, char *error) {
|
|
int fh, i, length;
|
|
char *buf, *p1, *p2;
|
|
|
|
error[0] = 0;
|
|
|
|
fh = open(config_file, O_RDWR | O_BINARY, 644);
|
|
if (fh < 0) {
|
|
sprintf(error, loc("Cannot open file <b>%s</b>"), config_file);
|
|
strcat(error, ": ");
|
|
strcat(error, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
/* remove logbook name in groups */
|
|
change_logbook_in_group(lbs, "");
|
|
|
|
/* read previous contents */
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buf = (char *)xmalloc(length + 1);
|
|
read(fh, buf, length);
|
|
buf[length] = 0;
|
|
|
|
/* find logbook config */
|
|
p1 = (char *) find_section(buf, lbs->name);
|
|
p2 = (char *) find_next_section(p1 + 1);
|
|
|
|
if (p2) {
|
|
i = strlen(p2) + 1;
|
|
strlcpy(p1, p2, i);
|
|
} else
|
|
*p1 = 0;
|
|
|
|
lseek(fh, 0, SEEK_SET);
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(error, loc("Cannot write to <b>%s</b>"), config_file);
|
|
strcat(error, ": ");
|
|
strcat(error, strerror(errno));
|
|
close(fh);
|
|
xfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
xfree(buf);
|
|
|
|
/* force re-read of config file */
|
|
check_config_file(TRUE);
|
|
el_index_logbooks();
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int rename_logbook(LOGBOOK *lbs, char *new_name) {
|
|
int fh, i, length, bufsize;
|
|
char *buf, *buf2, *p1, *p2;
|
|
char str[256], lb_dir[256], old_dir[1000], new_dir[1000];
|
|
|
|
fh = open(config_file, O_RDWR | O_BINARY, 644);
|
|
if (fh < 0) {
|
|
sprintf(str, loc("Cannot open file <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
|
|
/* rename logbook file */
|
|
if (!getcfg(lbs->name, "Subdir", str, sizeof(str))) {
|
|
strlcpy(lb_dir, logbook_dir, sizeof(lb_dir));
|
|
if (lb_dir[strlen(lb_dir) - 1] != DIR_SEPARATOR)
|
|
strlcat(lb_dir, DIR_SEPARATOR_STR, sizeof(lb_dir));
|
|
|
|
sprintf(old_dir, "%s%s", lb_dir, lbs->name);
|
|
sprintf(new_dir, "%s%s", lb_dir, new_name);
|
|
rename(old_dir, new_dir);
|
|
}
|
|
|
|
/* change logbook name in groups */
|
|
change_logbook_in_group(lbs, new_name);
|
|
|
|
/* read previous contents */
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
bufsize = 2 * (length + strlen(new_name) + 10);
|
|
buf = (char *)xmalloc(bufsize);
|
|
read(fh, buf, length);
|
|
buf[length] = 0;
|
|
|
|
/* find logbook config */
|
|
p1 = (char *) find_section(buf, lbs->name);
|
|
p2 = strchr(p1, ']');
|
|
if (p2 == NULL) {
|
|
close(fh);
|
|
xfree(buf);
|
|
show_error(loc("Syntax error in config file"));
|
|
return 0;
|
|
}
|
|
p2++;
|
|
|
|
/* save tail */
|
|
buf2 = xstrdup(p2);
|
|
|
|
/* replace logbook name */
|
|
sprintf(p1, "[%s]", new_name);
|
|
|
|
strlcat(p1, buf2, length + strlen(new_name) + 1);
|
|
xfree(buf2);
|
|
|
|
adjust_crlf(buf, bufsize);
|
|
|
|
lseek(fh, 0, SEEK_SET);
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(str, loc("Cannot write to <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
close(fh);
|
|
xfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
xfree(buf);
|
|
|
|
/* force re-read of config file */
|
|
check_config_file(TRUE);
|
|
el_index_logbooks();
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int create_logbook(LOGBOOK *oldlbs, char *logbook, char *templ) {
|
|
int fh, i, length, bufsize, templ_length;
|
|
char *buf, *p1, *p2, str[256];
|
|
|
|
fh = open(config_file, O_RDWR | O_BINARY, 644);
|
|
if (fh < 0) {
|
|
sprintf(str, loc("Cannot open file <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
|
|
/* add logbook to current group */
|
|
add_logbook_to_group(oldlbs, logbook);
|
|
|
|
/* read previous contents */
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
bufsize = 2 * (2 * length + 1);
|
|
buf = (char *) xmalloc(bufsize);
|
|
read(fh, buf, length);
|
|
buf[length] = 0;
|
|
templ_length = 0;
|
|
p2 = NULL;
|
|
|
|
/* find template logbook */
|
|
if (templ[0]) {
|
|
p1 = (char *) find_section(buf, templ);
|
|
p2 = (char *) find_next_section(p1 + 1);
|
|
} else
|
|
p1 = NULL;
|
|
|
|
if (p1) {
|
|
p1 = strchr(p1, ']');
|
|
if (p1)
|
|
while (*p1 == ']' || *p1 == '\r' || *p1 == '\n')
|
|
p1++;
|
|
|
|
if (p2)
|
|
templ_length = p2 - p1;
|
|
else
|
|
templ_length = strlen(p1);
|
|
}
|
|
|
|
/* insert single blank line after last logbook */
|
|
p2 = buf + strlen(buf) - 1;
|
|
|
|
while (p2 > buf && (*p2 == '\r' || *p2 == '\n' || *p2 == ' ' || *p2 == '\t')) {
|
|
*p2 = 0;
|
|
p2--;
|
|
}
|
|
if (p2 > buf)
|
|
p2++;
|
|
|
|
strcat(p2, "\r\n\r\n[");
|
|
strcat(p2, logbook);
|
|
strcat(p2, "]\r\n");
|
|
if (p1) {
|
|
p2 = buf + strlen(buf);
|
|
strncpy(p2, p1, templ_length);
|
|
p2[templ_length] = 0;
|
|
}
|
|
|
|
adjust_crlf(buf, bufsize);
|
|
|
|
lseek(fh, 0, SEEK_SET);
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(str, loc("Cannot write to <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
close(fh);
|
|
xfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
xfree(buf);
|
|
|
|
/* force re-read of config file */
|
|
check_config_file(TRUE);
|
|
el_index_logbooks();
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int save_config(char *buffer, char *error) {
|
|
int fh, i;
|
|
char *buf;
|
|
|
|
error[0] = 0;
|
|
|
|
fh = open(config_file, O_RDWR | O_BINARY | O_CREAT, 0644);
|
|
if (fh < 0) {
|
|
sprintf(error, loc("Cannot open file <b>%s</b>"), config_file);
|
|
strcat(error, ": ");
|
|
strcat(error, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
buf = (char *) xmalloc(strlen(buffer) * 2);
|
|
strlcpy(buf, buffer, strlen(buffer) * 2);
|
|
adjust_crlf(buf, strlen(buffer) * 2);
|
|
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(error, loc("Cannot write to <b>%s</b>"), config_file);
|
|
strcat(error, ": ");
|
|
strcat(error, strerror(errno));
|
|
close(fh);
|
|
return 0;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
|
|
/* force re-read of config file */
|
|
check_config_file(TRUE);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int save_user_config(LOGBOOK *lbs, const char *user, BOOL new_user) {
|
|
char file_name[256], str[1000], *pl, user_enc[256], new_pwd[80], new_pwd2[80], smtp_host[256],
|
|
email_addr[256], mail_from[256], mail_from_name[256], subject[256], mail_text[2000], str2[256],
|
|
admin_user[80], url[1000], error[2000], sid[32];
|
|
int i, self_register, code, first_user;
|
|
PMXML_NODE node, subnode, npwd;
|
|
|
|
/* if we outsourced the authentication, use external username */
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (!is_admin_user(lbs, http_user) && stristr(str, "Webserver")) {
|
|
/* do not allow HTML in user name */
|
|
strencode2(user_enc, http_user, sizeof(user_enc));
|
|
} else {
|
|
strencode2(user_enc, user, sizeof(user_enc));
|
|
}
|
|
|
|
/* check for user name */
|
|
if (!isparam("new_user_name") || *getparam("new_user_name") == 0) {
|
|
sprintf(str, loc("Please enter \"%s\""), loc("Login name"));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
|
|
/* check for full name */
|
|
if (!isparam("new_full_name") || *getparam("new_full_name") == 0) {
|
|
sprintf(str, loc("Please enter \"%s\""), loc("Full name"));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
|
|
/* check for valid email address */
|
|
if (!isparam("new_user_email") || *getparam("new_user_email") == 0) {
|
|
show_error(loc("Please specify a valid email address"));
|
|
return 0;
|
|
}
|
|
strlcpy(str, getparam("new_user_email"), sizeof(str));
|
|
if (strchr(str, '@') == NULL || strchr(str, '.') == NULL) {
|
|
show_error(loc("Please specify a valid email address"));
|
|
return 0;
|
|
}
|
|
|
|
/* check for blank character in user name */
|
|
strlcpy(str, getparam("new_user_name"), sizeof(str));
|
|
if (strchr(str, ' ')) {
|
|
show_error(loc("User name may not contain blanks"));
|
|
return 0;
|
|
}
|
|
|
|
/* check for string lengths */
|
|
if (strlen(getparam("new_user_name")) > 40) {
|
|
sprintf(str, "%s %s", loc("Login name"), loc("is too long"));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
if (strlen(getparam("new_full_name")) > 80) {
|
|
sprintf(str, "%s %s", loc("Full name"), loc("is too long"));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
if (strlen(getparam("new_user_email")) > 80) {
|
|
sprintf(str, "%s %s", loc("Email address"), loc("is too long"));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* check for blank password if not external authentication */
|
|
if (isparam("newpwd")) {
|
|
strlcpy(str, getparam("newpwd"), sizeof(str));
|
|
if (str[0] == 0) {
|
|
show_error(loc("Empty password not allowed"));
|
|
return 0;
|
|
}
|
|
if (strchr(str, ' ')) {
|
|
show_error(loc("Password may not contain blanks"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* check self register flag */
|
|
self_register = 0;
|
|
if (getcfg(lbs->name, "Self register", str, sizeof(str)))
|
|
self_register = atoi(str);
|
|
|
|
/* check if passwords match */
|
|
new_pwd[0] = 0;
|
|
if (isparam("newpwd") && isparam("newpwd2")) {
|
|
strlcpy(new_pwd, getparam("newpwd"), sizeof(new_pwd));
|
|
strlcpy(new_pwd2, getparam("newpwd2"), sizeof(new_pwd2));
|
|
|
|
if (strcmp(new_pwd, new_pwd2) != 0) {
|
|
show_error(loc("New passwords do not match, please retype"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* check if first user */
|
|
first_user = !enum_user_line(lbs, 0, str, sizeof(str));
|
|
|
|
/* check if user exists */
|
|
if (new_user) {
|
|
if (get_user_line(lbs, user_enc, NULL, NULL, NULL, NULL, NULL, NULL) == 1) {
|
|
sprintf(str, "%s \"%s\" %s", loc("Login name"), user_enc, loc("exists already"));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* if register through selection page, use first logbook with same password file */
|
|
if (lbs == NULL) {
|
|
getcfg(NULL, "password file", file_name, sizeof(file_name));
|
|
for (i = 0; lb_list[i].name[0]; i++) {
|
|
getcfg(lb_list[i].name, "password file", str, sizeof(str));
|
|
if (strieq(file_name, str))
|
|
break;
|
|
}
|
|
if (lb_list[i].name[0] == 0)
|
|
lbs = &lb_list[0];
|
|
else
|
|
lbs = &lb_list[i];
|
|
}
|
|
|
|
getcfg(lbs->name, "Password file", str, sizeof(str));
|
|
|
|
if (lbs->pwd_xml_tree == NULL)
|
|
return 0;
|
|
|
|
sprintf(str, "/list/user[name=%s]", user_enc);
|
|
node = mxml_find_node(lbs->pwd_xml_tree, str);
|
|
|
|
code = 0;
|
|
|
|
if (new_user) {
|
|
node = mxml_find_node(lbs->pwd_xml_tree, "/list");
|
|
if (!node) {
|
|
show_error(loc("Error accessing password file"));
|
|
return 0;
|
|
}
|
|
node = mxml_add_node(node, "user", NULL);
|
|
|
|
if (isparam("new_user_name")) {
|
|
strencode2(str, getparam("new_user_name"), sizeof(str));
|
|
mxml_add_node(node, "name", str);
|
|
}
|
|
#ifdef HAVE_PAM
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (!stristr(str, "PAM")) {
|
|
#endif /* HAVE_PAM */
|
|
do_crypt(new_pwd, str, sizeof(str));
|
|
npwd = mxml_add_node(node, "password", str);
|
|
if (npwd)
|
|
mxml_add_attribute(npwd, "encoding", "SHA256");
|
|
#ifdef HAVE_PAM
|
|
}
|
|
#endif /* HAVE_PAM */
|
|
|
|
if (isparam("new_full_name")) {
|
|
strencode2(str, getparam("new_full_name"), sizeof(str));
|
|
mxml_add_node(node, "full_name", str);
|
|
}
|
|
mxml_add_node(node, "last_logout", "0");
|
|
mxml_add_node(node, "last_activity", "0");
|
|
if (isparam("new_user_email"))
|
|
mxml_add_node(node, "email", getparam("new_user_email"));
|
|
|
|
if (!first_user && (self_register == 3 || self_register == 4)) {
|
|
code = rand() + (rand() << 16);
|
|
sprintf(str, "%d", code);
|
|
mxml_add_node(node, "inactive", str);
|
|
} else
|
|
mxml_add_node(node, "inactive", "0");
|
|
|
|
} else {
|
|
/* replace record */
|
|
if (isparam("new_user_name")) {
|
|
strencode2(str, getparam("new_user_name"), sizeof(str));
|
|
mxml_replace_subvalue(node, "name", str);
|
|
}
|
|
if (new_pwd[0])
|
|
mxml_replace_subvalue(node, "password", new_pwd);
|
|
if (isparam("new_full_name")) {
|
|
strencode2(str, getparam("new_full_name"), sizeof(str));
|
|
mxml_replace_subvalue(node, "full_name", str);
|
|
}
|
|
if (isparam("new_user_email"))
|
|
mxml_replace_subvalue(node, "email", getparam("new_user_email"));
|
|
if (isparam("active"))
|
|
mxml_replace_subvalue(node, "inactive", "0");
|
|
else
|
|
mxml_replace_subvalue(node, "inactive", "1");
|
|
}
|
|
|
|
subnode = mxml_find_node(node, "email_notify");
|
|
if (subnode)
|
|
mxml_delete_node(subnode);
|
|
mxml_add_node(node, "email_notify", NULL);
|
|
subnode = mxml_find_node(node, "email_notify");
|
|
for (i = 0; lb_list[i].name[0]; i++) {
|
|
sprintf(str, "sub_lb%d", i);
|
|
if (isparam(str) && getparam(str) && atoi(getparam(str)))
|
|
mxml_add_node(subnode, "logbook", lb_list[i].name);
|
|
}
|
|
|
|
if (get_password_file(lbs, file_name, sizeof(file_name)))
|
|
mxml_write_tree(file_name, lbs->pwd_xml_tree);
|
|
|
|
/* if requested, send notification email to user or admin user */
|
|
if (new_user && !first_user && (self_register == 2 || self_register == 3 || self_register == 4)
|
|
&& !isparam("admin")) {
|
|
if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) {
|
|
show_error(loc("No SMTP host defined in [global] section of configuration file"));
|
|
return 0;
|
|
}
|
|
|
|
/* try to get URL from referer */
|
|
if (!getcfg("global", "URL", url, sizeof(url))) {
|
|
if (referer[0])
|
|
strcpy(url, referer);
|
|
else {
|
|
if (elog_tcp_port == 80) {
|
|
if (_ssl_flag)
|
|
sprintf(url, "https://%s/", http_host);
|
|
else
|
|
sprintf(url, "http://%s/", http_host);
|
|
} else {
|
|
if (_ssl_flag)
|
|
sprintf(url, "https://%s:%d/", http_host, elog_tcp_port);
|
|
else
|
|
sprintf(url, "http://%s:%d/", http_host, elog_tcp_port);
|
|
}
|
|
if (lbs) {
|
|
strlcat(url, lbs->name_enc, sizeof(url));
|
|
strlcat(url, "/", sizeof(url));
|
|
}
|
|
}
|
|
} else {
|
|
if (url[strlen(url) - 1] != '/')
|
|
strlcat(url, "/", sizeof(url));
|
|
if (lbs) {
|
|
strlcat(url, lbs->name_enc, sizeof(url));
|
|
strlcat(url, "/", sizeof(url));
|
|
}
|
|
}
|
|
|
|
retrieve_email_from(lbs, mail_from, mail_from_name, NULL);
|
|
|
|
/* send email to new user */
|
|
if (self_register == 4) {
|
|
if (lbs)
|
|
sprintf(subject, loc("Account activation for ELOG logbook \"%s\""), lbs->name);
|
|
else
|
|
sprintf(subject, loc("Account activation for ELOG on host \"%s\""), host_name);
|
|
|
|
if (!isparam("new_user_email")) {
|
|
show_error("A valid email address necessary for this ELOG account");
|
|
return 0;
|
|
}
|
|
|
|
strlcpy(email_addr, getparam("new_user_email"), sizeof(email_addr));
|
|
|
|
mail_text[0] = 0;
|
|
compose_email_header(lbs, subject, mail_from_name, email_addr,
|
|
NULL, mail_text, sizeof(mail_text), 1, 0, NULL, 0, 0);
|
|
sprintf(mail_text + strlen(mail_text), "\r\n%s:\r\n\r\n",
|
|
loc("Please click the URL below to activate following ELOG account"));
|
|
|
|
if (lbs)
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Logbook"), lbs->name);
|
|
else
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Host"), host_name);
|
|
|
|
if (isparam("new_user_name"))
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Login name"),
|
|
getparam("new_user_name"));
|
|
if (isparam("new_full_name"))
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Full name"),
|
|
getparam("new_full_name"));
|
|
if (isparam("new_user_email"))
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Email"),
|
|
getparam("new_user_email"));
|
|
|
|
sprintf(mail_text + strlen(mail_text), "\r\n%s:\r\n", loc("Activation URL"));
|
|
|
|
sprintf(mail_text + strlen(mail_text), "\r\nURL : %s", url);
|
|
|
|
if (isparam("new_user_name"))
|
|
sprintf(mail_text + strlen(mail_text), "?cmd=%s", loc("Activate"));
|
|
|
|
sprintf(mail_text + strlen(mail_text), "&code=%d&unm=%s\r\n", code, getparam("new_user_name"));
|
|
|
|
if (sendmail(lbs, smtp_host, mail_from, email_addr, mail_text, error, sizeof(error)) == -1) {
|
|
sprintf(str, loc("Cannot send email notification to \"%s\""), getparam("new_user_email"));
|
|
strlcat(str, " : ", sizeof(str));
|
|
strlcat(str, error, sizeof(str));
|
|
strencode2(str2, str, sizeof(str2));
|
|
show_error(str2);
|
|
return 0;
|
|
};
|
|
|
|
show_standard_header(lbs, FALSE, loc("ELOG registration"), "", FALSE, NULL, NULL, 0);
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
rsprintf("<tr><td colspan=2 class=\"dlgtitle\">\n");
|
|
rsprintf(loc("An email has been sent to <%s>"), getparam("new_user_email"));
|
|
rsprintf(".<br>\n");
|
|
rsprintf(loc("Use that email to activate your account"));
|
|
rsprintf(".</td></tr></table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\n");
|
|
return 0;
|
|
}
|
|
|
|
/* send email to admin user(s) */
|
|
if (self_register == 2 || self_register == 3) {
|
|
if (getcfg(lbs->name, "Admin user", admin_user, sizeof(admin_user))) {
|
|
pl = strtok(admin_user, " ,");
|
|
while (pl) {
|
|
get_user_line(lbs, pl, NULL, NULL, email_addr, NULL, NULL, NULL);
|
|
if (email_addr[0]) {
|
|
|
|
/* compose subject */
|
|
if (self_register == 3) {
|
|
if (lbs)
|
|
sprintf(subject, loc("Registration request for ELOG logbook \"%s\""), lbs->name);
|
|
else
|
|
sprintf(subject, loc("Registration request for ELOG on host \"%s\""), host_name);
|
|
sprintf(str, loc("A new ELOG user wants to register on \"%s\""), host_name);
|
|
} else {
|
|
if (isparam("new_user_name")) {
|
|
if (lbs)
|
|
sprintf(subject, loc("User \"%s\" registered on logbook \"%s\""),
|
|
getparam("new_user_name"), lbs->name);
|
|
else
|
|
sprintf(subject, loc("User \"%s\" registered on host \"%s\""),
|
|
getparam("new_user_name"), host_name);
|
|
}
|
|
|
|
sprintf(str, loc("A new ELOG user has been registered on %s"), host_name);
|
|
}
|
|
|
|
mail_text[0] = 0;
|
|
compose_email_header(lbs, subject, mail_from_name, email_addr,
|
|
NULL, mail_text, sizeof(mail_text), 1, 0, NULL, 0, 0);
|
|
sprintf(mail_text + strlen(mail_text), "\r\n%s:\r\n\r\n", str);
|
|
|
|
if (lbs)
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Logbook"),
|
|
lbs->name);
|
|
else
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Host"),
|
|
host_name);
|
|
|
|
if (isparam("new_user_name"))
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Login name"),
|
|
getparam("new_user_name"));
|
|
if (isparam("new_full_name"))
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Full name"),
|
|
getparam("new_full_name"));
|
|
if (isparam("new_user_email"))
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Email"),
|
|
getparam("new_user_email"));
|
|
|
|
if (self_register == 3) {
|
|
sprintf(mail_text + strlen(mail_text), "\r\n%s:\r\n",
|
|
loc("Hit following URL to activate that account"));
|
|
|
|
sprintf(mail_text + strlen(mail_text), "\r\nURL : %s", url);
|
|
|
|
if (isparam("new_user_name"))
|
|
sprintf(mail_text + strlen(mail_text), "?cmd=%s&new_user_name=%s",
|
|
loc("Activate"), getparam("new_user_name"));
|
|
|
|
sprintf(mail_text + strlen(mail_text), "&code=%d&unm=%s\r\n", code, pl);
|
|
} else {
|
|
if (isparam("new_user_name"))
|
|
sprintf(mail_text + strlen(mail_text),
|
|
"\r\n%s URL : %s?cmd=Config&cfg_user=%s&unm=%s\r\n", loc("Logbook"),
|
|
url, getparam("new_user_name"), pl);
|
|
}
|
|
|
|
if (sendmail(lbs, smtp_host, mail_from, email_addr, mail_text, error, sizeof(error)) == -1) {
|
|
sprintf(str, loc("Cannot send email notification to \"%s\""),
|
|
getparam("new_user_email"));
|
|
strlcat(str, " : ", sizeof(str));
|
|
strlcat(str, error, sizeof(str));
|
|
strencode2(str2, str, sizeof(str2));
|
|
show_error(str2);
|
|
return 0;
|
|
};
|
|
}
|
|
|
|
pl = strtok(NULL, " ,");
|
|
}
|
|
} else {
|
|
show_error(loc("No admin user has been defined in configuration file"));
|
|
return 0;
|
|
}
|
|
|
|
if (self_register == 3) {
|
|
sprintf(str, "?cmd=%s", loc("Requested"));
|
|
redirect(lbs, str);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* log in user automatically */
|
|
if (new_user && (self_register == 1 || self_register == 2)) {
|
|
|
|
if (isparam("new_user_name")) {
|
|
/* get a new session ID */
|
|
sid_new(lbs, getparam("new_user_name"), (char *) inet_ntoa(rem_addr), sid);
|
|
|
|
if (lbs)
|
|
snprintf(str, sizeof(str), "../%s/", lbs->name_enc);
|
|
else
|
|
sprintf(str, ".");
|
|
if (isparam("new_user_name")) {
|
|
sprintf(str + strlen(str), "?cmd=%s&cfg_user=", loc("Config"));
|
|
strlcat(str, getparam("new_user_name"), sizeof(str));
|
|
} else if (isparam("cfg_user")) {
|
|
sprintf(str + strlen(str), "?cmd=%s&cfg_user=", loc("Config"));
|
|
strlcat(str, getparam("cfg_user"), sizeof(str));
|
|
} else if (getcfg(lbs->name, "password file", str2, sizeof(str2)))
|
|
sprintf(str + strlen(str), "?cmd=%s", loc("Config"));
|
|
setparam("redir", str);
|
|
|
|
/* set SID cookie */
|
|
set_sid_cookie(lbs, sid, getparam("new_user_name"));
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int remove_user(LOGBOOK *lbs, char *user) {
|
|
char file_name[256], str[1000], str2[1000];
|
|
PMXML_NODE node;
|
|
|
|
if (lbs->pwd_xml_tree == NULL) {
|
|
show_error("No password file loaded");
|
|
return FALSE;
|
|
}
|
|
|
|
sprintf(str, "/list/user[name=%s]", user);
|
|
node = mxml_find_node(lbs->pwd_xml_tree, str);
|
|
if (node == NULL) {
|
|
sprintf(str, loc("User \"%s\" not found in password file"), user);
|
|
strencode2(str2, str, sizeof(str2));
|
|
show_error(str2);
|
|
return FALSE;
|
|
}
|
|
|
|
mxml_delete_node(node);
|
|
|
|
if (get_password_file(lbs, file_name, sizeof(file_name))) {
|
|
if (!mxml_write_tree(file_name, lbs->pwd_xml_tree)) {
|
|
sprintf(str, loc("Cannot write to file <b>%s</b>"), file_name);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int ascii_compare(const void *s1, const void *s2) {
|
|
return stricmp(*(char **) s1, *(char **) s2);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int ascii_compare2(const void *s1, const void *s2) {
|
|
return stricmp((char *) s1, (char *) s2);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_config_page(LOGBOOK *lbs) {
|
|
char str[256], user[80], password[80], full_name[256], user_email[256], logbook[256], auth[32], **user_list;
|
|
int i, j, n, cols, inactive;
|
|
BOOL email_notify[1000], sort_email;
|
|
|
|
if (lbs)
|
|
strcpy(logbook, lbs->name);
|
|
else
|
|
strcpy(logbook, "global");
|
|
|
|
/* get user */
|
|
strcpy(user, isparam("unm") ? getparam("unm") : "");
|
|
if (isparam("cfg_user"))
|
|
strcpy(user, getparam("cfg_user"));
|
|
|
|
/* get sort_email flag */
|
|
sort_email = FALSE;
|
|
if (isparam("sort_email") && atoi(getparam("sort_email")) > 0)
|
|
sort_email = TRUE;
|
|
|
|
/*---- header ----*/
|
|
|
|
show_standard_header(lbs, TRUE, loc("ELOG user config"), ".", FALSE, NULL, NULL, 0);
|
|
|
|
/*---- javascript to warn removal of user and of deactivation ----*/
|
|
rsprintf("<script type=\"text/javascript\">\n");
|
|
rsprintf("<!--\n\n");
|
|
rsprintf("function chkrem()\n");
|
|
rsprintf("{\n");
|
|
strencode2(str, user, sizeof(str));
|
|
strlcpy(user, str, sizeof(user));
|
|
sprintf(str, loc("Really remove user \\\"%s\\\"?"), user);
|
|
rsprintf(" var subm = confirm(\"%s\");\n", str);
|
|
rsprintf(" return subm;\n");
|
|
rsprintf("}\n\n");
|
|
rsprintf("function chkdeact(c)\n");
|
|
rsprintf("{\n");
|
|
strlcpy(str, loc("Are you sure you want to deactivate your own account?"), sizeof(str));
|
|
rsprintf(" if (c.checked == false)\n");
|
|
rsprintf(" return confirm(\"%s\");\n", str);
|
|
rsprintf(" return true;\n");
|
|
rsprintf("}\n\n");
|
|
rsprintf("//-->\n");
|
|
rsprintf("</script>\n\n");
|
|
|
|
/*---- title ----*/
|
|
|
|
show_standard_title(lbs, "", 0);
|
|
|
|
/*---- activation notice ----*/
|
|
|
|
if (isparam("notice")) {
|
|
rsprintf("<tr><td class=\"notifymsg\" colspan=2>\n");
|
|
rsprintf(getparam("notice"));
|
|
rsprintf("</tr>\n");
|
|
}
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
rsprintf("<input type=hidden name=cmd value=\"%s\">\n", loc("Config")); // for select javascript
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Save"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Back"));
|
|
strencode2(str, user, sizeof(str));
|
|
rsprintf("<input type=hidden name=config value=\"%s\">\n", str);
|
|
rsprintf("<input type=hidden name=cfgpage value=\"1\">\n"); // needed for "Save" command
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
|
|
/* table for two-column items */
|
|
rsprintf("<tr><td class=\"form2\">");
|
|
rsprintf("<table width=\"100%%\" cellspacing=0>\n");
|
|
|
|
/*---- if admin user, show user list ----*/
|
|
|
|
if (is_admin_user(lbs, getparam("unm"))) {
|
|
rsprintf("<input type=hidden name=admin value=1>\n");
|
|
rsprintf("<tr><td nowrap width=\"10%%\">%s:</td>\n", loc("Select user"));
|
|
rsprintf("<td><select name=cfg_user onChange=\"document.form1.submit()\">\n");
|
|
|
|
/* count user list */
|
|
for (n = 0;; n++) {
|
|
if (!enum_user_line(lbs, n, str, sizeof(str)))
|
|
break;
|
|
}
|
|
|
|
/* allocate list of users and populate it */
|
|
user_list = (char **) xcalloc(sizeof(char *), n);
|
|
for (i = 0; i < n; i++)
|
|
user_list[i] = (char *) xcalloc(NAME_LENGTH, 1);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
enum_user_line(lbs, i, user_list[i], NAME_LENGTH);
|
|
get_user_line(lbs, user_list[i], NULL, full_name, user_email, NULL, NULL, NULL);
|
|
if (sort_email)
|
|
strlcpy(user_list[i], user_email, NAME_LENGTH);
|
|
}
|
|
|
|
/* sort list */
|
|
qsort(user_list, n, sizeof(char *), ascii_compare);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
if (sort_email) {
|
|
strlcpy(user_email, user_list[i], sizeof(user_email));
|
|
user_list[i][0] = 0;
|
|
get_user_line(lbs, user_list[i], NULL, full_name, user_email, NULL, NULL, NULL);
|
|
} else
|
|
get_user_line(lbs, user_list[i], NULL, full_name, user_email, NULL, NULL, NULL);
|
|
if (strcmp(user_list[i], user) == 0) {
|
|
strencode2(str, user_list[i], sizeof(str));
|
|
if (sort_email)
|
|
rsprintf("<option selected value=\"%s\"><%s> %s\n", str, user_email, str);
|
|
else
|
|
rsprintf("<option selected value=\"%s\">%s <%s>\n", str, str, user_email);
|
|
} else {
|
|
strencode2(str, user_list[i], sizeof(str));
|
|
if (sort_email)
|
|
rsprintf("<option value=\"%s\"><%s> %s\n", str, user_email, str);
|
|
else
|
|
rsprintf("<option value=\"%s\">%s <%s>\n", str, str, user_email);
|
|
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
xfree(user_list[i]);
|
|
xfree(user_list);
|
|
|
|
rsprintf("</select>\n");
|
|
|
|
/* show "update" button only of javascript is not enabled */
|
|
rsprintf("<noscript>\n");
|
|
rsprintf("<input type=submit value=\"%s\">\n", loc("Update"));
|
|
rsprintf("</noscript>\n");
|
|
|
|
if (sort_email)
|
|
rsprintf(
|
|
"<input type=\"checkbox\" checked name=\"sort_email\" value=\"1\" onChange=\"document.form1.submit()\">Sort by email");
|
|
else
|
|
rsprintf(
|
|
"<input type=\"checkbox\" name=\"sort_email\" value=\"1\" onChange=\"document.form1.submit()\">Sort by email");
|
|
}
|
|
|
|
/*---- entry form ----*/
|
|
|
|
if (get_user_line(lbs, user, password, full_name, user_email, email_notify, NULL, &inactive) != 1)
|
|
sprintf(str, loc("User [%s] has been deleted"), user);
|
|
else
|
|
strlcpy(str, user, sizeof(str));
|
|
|
|
if (is_admin_user(lbs, getparam("unm"))) {
|
|
rsprintf("<tr><td nowrap width=\"15%%\">%s:</td>\n", loc("Active"));
|
|
if (stricmp(user, getparam("unm")) == 0)
|
|
rsprintf
|
|
("<td><input type=checkbox name=active value=1 %s onClick=\"return chkdeact(this);\"></td></tr>\n",
|
|
inactive ? "" : "checked");
|
|
else
|
|
rsprintf("<td><input type=checkbox name=active value=1 %s></td></tr>\n", inactive ? "" : "checked");
|
|
} else {
|
|
rsprintf("<tr><td nowrap width=\"10%%\">%s:</td>\n", loc("Active"));
|
|
rsprintf("<td><input type=checkbox name=active value=1 readonly %s></td></tr>\n",
|
|
inactive ? "" : "checked");
|
|
}
|
|
|
|
rsprintf("<tr><td nowrap width=\"15%%\">%s:</td>\n", loc("Login name"));
|
|
|
|
getcfg(lbs->name, "Authentication", auth, sizeof(auth));
|
|
|
|
strencode2(str, user, sizeof(str));
|
|
if (stristr(auth, "Webserver") || stristr(auth, "PAM"))
|
|
rsprintf("<td><input type=text size=40 name=new_user_name value=\"%s\" readonly></td></tr>\n", str);
|
|
else
|
|
rsprintf("<td><input type=text size=40 name=new_user_name value=\"%s\"></td></tr>\n", str);
|
|
|
|
rsprintf("<tr><td nowrap width=\"15%%\">%s:</td>\n", loc("Full name"));
|
|
rsprintf("<td><input type=text size=40 name=new_full_name value=\"%s\"></tr>\n", full_name);
|
|
|
|
rsprintf("<tr><td nowrap width=\"15%%\">Email:</td>\n");
|
|
rsprintf("<td><input type=text size=40 name=new_user_email value=\"%s\"></td></tr>\n", user_email);
|
|
|
|
for (i = n = 0; lb_list[i].name[0]; i++) {
|
|
|
|
if (!getcfg_topgroup() || strieq(getcfg_topgroup(), lb_list[i].top_group)) {
|
|
|
|
/* check if user has access */
|
|
if (!isparam("unm") || check_login_user(&lb_list[i], getparam("unm"))) {
|
|
|
|
/* check if emails are enabled for this logbook */
|
|
if (!getcfg(lb_list[i].name, "Suppress email to users", str, sizeof(str)) || atoi(str) == 0)
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (n > 0) {
|
|
for (i = 0; lb_list[i].name[0]; i++) {}
|
|
j = (int) (i / 16) + 1;
|
|
cols = ((j > 5) ? 5 : j);
|
|
rsprintf("<tr><td colspan=\"2\"><br />%s:\n", loc("Subscribe to logbooks"));
|
|
|
|
rsprintf("<br><span class=\"selcomment\"><b>(%s)</b></span>\n",
|
|
loc("enable automatic email notifications"));
|
|
|
|
rsprintf("<td></tr>\n");
|
|
|
|
rsprintf("<tr><td colspan=\"2\"><table><tr>\n");
|
|
|
|
for (j = i = 0; lb_list[i].name[0]; i++) {
|
|
|
|
if (!getcfg_topgroup() || strieq(getcfg_topgroup(), lb_list[i].top_group)) {
|
|
|
|
/* check if user has access */
|
|
if (!isparam("unm") || check_login_user(&lb_list[i], getparam("unm"))) {
|
|
|
|
/* check if emails are enabled for this logbook */
|
|
if (!getcfg(lb_list[i].name, "Suppress email to users", str, sizeof(str)) || atoi(str) == 0) {
|
|
if (email_notify[i])
|
|
rsprintf("<td><input type=checkbox checked id=\"lb%d\" name=\"sub_lb%d\" value=\"1\">\n",
|
|
i,
|
|
i);
|
|
else
|
|
rsprintf("<td><input type=checkbox id=\"lb%d\" name=\"sub_lb%d\" value=\"1\">\n", i, i);
|
|
rsprintf("<label for=\"lb%d\">%s</label></td>\n", i, lb_list[i].name);
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
if (j > 0 && (j % cols) == 0)
|
|
rsprintf("</tr>\n<tr>");
|
|
}
|
|
rsprintf("</tr></table><br />\n");
|
|
}
|
|
|
|
if (n > 2) {
|
|
rsprintf("<script language=\"JavaScript\" type=\"text/javascript\">\n");
|
|
rsprintf("<!--\n");
|
|
rsprintf("function SetNone()\n");
|
|
rsprintf(" {\n");
|
|
rsprintf(" for (var i=0,els=document.querySelectorAll('[name^=\"sub_lb\"]') ; i<els.length ; i++)\n");
|
|
rsprintf(" els[i].checked = false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf("function SetAll()\n");
|
|
rsprintf(" {\n");
|
|
rsprintf(" for (var i=0,els=document.querySelectorAll('[name^=\"sub_lb\"]') ; i<els.length ; i++)\n");
|
|
rsprintf(" els[i].checked = true;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf("//-->\n");
|
|
rsprintf("</script>\n");
|
|
|
|
rsprintf("<input type=button value=\"%s\" onClick=\"SetAll();\">\n", loc("Set all"));
|
|
rsprintf("<input type=button value=\"%s\" onClick=\"SetNone();\">\n", loc("Set none"));
|
|
}
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("</table></td></tr>\n");
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
if (is_admin_user(lbs, getparam("unm")) || !getcfg(logbook, "allow password change", str, sizeof(str))
|
|
|| atoi(str) == 1)
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Change password"));
|
|
|
|
rsprintf("<input type=submit name=cmd value=\"%s\" onClick=\"return chkrem();\">\n", loc("Remove user"));
|
|
|
|
if (is_admin_user(lbs, getparam("unm"))) {
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("New user"));
|
|
strlcpy(str, loc("Change config file"), sizeof(str));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", str);
|
|
}
|
|
|
|
rsprintf("</span></td></tr></table>\n\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int activate_user(LOGBOOK *lbs, char *user_name, int code) {
|
|
int inactive, self_register;
|
|
char str[256], str2[256], smtp_host[256], url[1000], mail_text[2000],
|
|
error[256], mail_from_name[256], mail_from[256], user_email[256], logbook[256];
|
|
|
|
if (lbs == NULL)
|
|
strlcpy(logbook, "global", sizeof(logbook));
|
|
else
|
|
strlcpy(logbook, lbs->name, sizeof(logbook));
|
|
|
|
get_user_line(lbs, user_name, NULL, NULL, user_email, NULL, NULL, &inactive);
|
|
if (code != inactive) {
|
|
show_error(loc("Invalid activation code"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!set_user_inactive(lbs, user_name, 0)) {
|
|
show_error(loc("Error activating user"));
|
|
return FALSE;
|
|
}
|
|
|
|
self_register = 0;
|
|
if (getcfg(logbook, "Self register", str, sizeof(str)))
|
|
self_register = atoi(str);
|
|
|
|
if (self_register == 3) {
|
|
|
|
if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) {
|
|
show_error(loc("No SMTP host defined in [global] section of configuration file"));
|
|
return FALSE;
|
|
}
|
|
|
|
/* try to get URL from referer */
|
|
if (!getcfg("global", "URL", url, sizeof(url))) {
|
|
if (referer[0])
|
|
strcpy(url, referer);
|
|
else {
|
|
if (elog_tcp_port == 80) {
|
|
if (_ssl_flag)
|
|
sprintf(url, "https://%s/", http_host);
|
|
else
|
|
sprintf(url, "http://%s/", http_host);
|
|
} else {
|
|
if (_ssl_flag)
|
|
sprintf(url, "https://%s:%d/", http_host, elog_tcp_port);
|
|
else
|
|
sprintf(url, "http://%s:%d/", http_host, elog_tcp_port);
|
|
}
|
|
if (lbs) {
|
|
strlcat(url, lbs->name_enc, sizeof(url));
|
|
strlcat(url, "/", sizeof(url));
|
|
}
|
|
}
|
|
} else {
|
|
if (url[strlen(url) - 1] != '/')
|
|
strlcat(url, "/", sizeof(url));
|
|
if (lbs) {
|
|
strlcat(url, lbs->name_enc, sizeof(url));
|
|
strlcat(url, "/", sizeof(url));
|
|
}
|
|
}
|
|
|
|
retrieve_email_from(lbs, mail_from, mail_from_name, NULL);
|
|
|
|
mail_text[0] = 0;
|
|
compose_email_header(lbs, loc("Your ELOG account has been activated"), mail_from_name,
|
|
user_email, NULL, mail_text, sizeof(mail_text), 1, 0, NULL, 0, 0);
|
|
|
|
strlcat(mail_text, "\r\n", sizeof(mail_text));
|
|
strlcat(mail_text, loc("Your ELOG account has been activated on host"), sizeof(mail_text));
|
|
sprintf(mail_text + strlen(mail_text), " %s", http_host);
|
|
sprintf(mail_text + strlen(mail_text), ".\r\n\r\n");
|
|
sprintf(url + strlen(url), "?unm=%s", user_name);
|
|
sprintf(mail_text + strlen(mail_text), "%s %s.\r\n\r\n", loc("You can access it at"), url);
|
|
sprintf(mail_text + strlen(mail_text), "%s.\r\n",
|
|
loc("To subscribe to any logbook, click on 'Config' in that logbook"));
|
|
|
|
if (sendmail(lbs, smtp_host, mail_from, user_email, mail_text, error, sizeof(error)) == -1) {
|
|
sprintf(str, loc("Cannot send email notification to \"%s\""), user_email);
|
|
strlcat(str, " : ", sizeof(str));
|
|
strlcat(str, error, sizeof(str));
|
|
strencode2(str2, str, sizeof(str2));
|
|
show_error(str2);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_forgot_pwd_page(LOGBOOK *lbs) {
|
|
int i;
|
|
char str[2000], str2[1000], login_name[256], full_name[256], user_email[256], name[256], pwd[256],
|
|
redir[1000], smtp_host[256], mail_from[256], mail_from_name[256], subject[256],
|
|
mail_text[1000], url[1000], error[1000];
|
|
|
|
if (isparam("login_name")) {
|
|
/* seach in pwd file */
|
|
|
|
strcpy(name, getparam("login_name"));
|
|
|
|
for (i = 0;; i++) {
|
|
if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
|
|
break;
|
|
|
|
get_user_line(lbs, login_name, NULL, full_name, user_email, NULL, NULL, NULL);
|
|
|
|
if (strieq(name, login_name) || strieq(name, full_name) || strieq(name, user_email)) {
|
|
if (user_email[0] == 0) {
|
|
show_error(loc("A password recovery email has been sent if the user exists in the database"));
|
|
return;
|
|
}
|
|
|
|
/* create random password */
|
|
for (i = 0; i < 16; i++)
|
|
pwd[i] = 'A' + (rand() % 25);
|
|
pwd[i] = 0;
|
|
|
|
/* send email with new password */
|
|
if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) {
|
|
show_error(loc("No SMTP host defined in [global] section of configuration file"));
|
|
return;
|
|
}
|
|
|
|
/* try to get URL from referer */
|
|
if (!getcfg("global", "URL", url, sizeof(url))) {
|
|
if (referer[0])
|
|
strcpy(url, referer);
|
|
else {
|
|
if (elog_tcp_port == 80) {
|
|
if (_ssl_flag)
|
|
sprintf(url, "https://%s/", http_host);
|
|
else
|
|
sprintf(url, "http://%s/", http_host);
|
|
} else {
|
|
if (_ssl_flag)
|
|
sprintf(url, "https://%s:%d/", http_host, elog_tcp_port);
|
|
else
|
|
sprintf(url, "http://%s:%d/", http_host, elog_tcp_port);
|
|
}
|
|
}
|
|
} else {
|
|
if (url[strlen(url) - 1] != '/')
|
|
strlcat(url, "/", sizeof(url));
|
|
if (lbs) {
|
|
strlcat(url, lbs->name_enc, sizeof(url));
|
|
strlcat(url, "/", sizeof(url));
|
|
}
|
|
}
|
|
|
|
url_slash_encode(pwd, sizeof(pwd));
|
|
sprintf(redir, "?cmd=%s&oldpwd=%s", loc("Change password"), pwd);
|
|
url_encode(redir, sizeof(redir));
|
|
|
|
strencode2(str2, redir, sizeof(str2));
|
|
sprintf(str, "?redir=%s&uname=%s&upassword=%s", str2, login_name, pwd);
|
|
strlcat(url, str, sizeof(url));
|
|
|
|
retrieve_email_from(lbs, mail_from, mail_from_name, NULL);
|
|
|
|
if (lbs)
|
|
sprintf(subject, loc("Password recovery for ELOG %s"), lbs->name);
|
|
else
|
|
sprintf(subject, loc("Password recovery for ELOG %s"), http_host);
|
|
|
|
mail_text[0] = 0;
|
|
compose_email_header(lbs, subject, mail_from_name, user_email, NULL, mail_text,
|
|
sizeof(mail_text), 1, 0, NULL, 0, 0);
|
|
|
|
strlcat(mail_text, "\r\n", sizeof(mail_text));
|
|
sprintf(mail_text + strlen(mail_text),
|
|
loc("This is an automatically generated account recovery email for host %s"), http_host);
|
|
strlcat(mail_text, ".\r\n", sizeof(mail_text));
|
|
strlcat(mail_text, loc("Please click on following link to recover your account"),
|
|
sizeof(mail_text));
|
|
strlcat(mail_text, ":\r\n\r\n", sizeof(mail_text));
|
|
strlcat(mail_text, url, sizeof(mail_text));
|
|
strlcat(mail_text, "\r\n\r\n", sizeof(mail_text));
|
|
sprintf(mail_text + strlen(mail_text), "ELOG Version %s\r\n", VERSION);
|
|
|
|
if (sendmail(lbs, smtp_host, mail_from, user_email, mail_text, error, sizeof(error)) != -1) {
|
|
/* save new password */
|
|
auth_change_password(lbs, login_name, NULL, pwd, str, sizeof(str));
|
|
|
|
/* show notification web page */
|
|
show_standard_header(lbs, FALSE, loc("ELOG password recovery"), "", FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
rsprintf("<tr><td class=\"dlgtitle\">\n");
|
|
rsprintf(loc("Email notification"));
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">\n");
|
|
rsprintf(loc("A password recovery email for user <i>\"%s\"</i> has been sent to %s"),
|
|
full_name, user_email);
|
|
rsprintf("</td></tr></table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\n");
|
|
return;
|
|
} else {
|
|
sprintf(str, loc("Error sending Email via \"%s\""), smtp_host);
|
|
strlcat(str, ": ", sizeof(str));
|
|
strlcat(str, error, sizeof(str));
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
show_error(loc("A password recovery email has been sent if the user exists in the database"));
|
|
return;
|
|
} else {
|
|
/*---- header ----*/
|
|
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (stristr(str, "Webserver") || stristr(str, "PAM")) {
|
|
show_error
|
|
("This installation of ELOG uses site authentication\nwhere password recovery is not possible");
|
|
return;
|
|
}
|
|
|
|
show_standard_header(lbs, TRUE, loc("ELOG password recovery"), NULL, FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
|
|
/*---- entry form ----*/
|
|
|
|
rsprintf("<tr><td class=\"dlgtitle\">%s</td></tr>\n", loc("Enter your user name or email address"));
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">\n");
|
|
rsprintf("<input type=hidden name=cmd value=%s>\n", loc("Forgot"));
|
|
rsprintf("<input type=text size=40 name=login_name></td></tr>\n");
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\"><input type=submit value=\"%s\">\n", loc("Submit"));
|
|
|
|
rsprintf("</td></tr></table>\n\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_new_user_page(LOGBOOK *lbs, char *user) {
|
|
char str[256];
|
|
|
|
/*---- header ----*/
|
|
|
|
show_html_header(lbs, TRUE, loc("ELOG new user"), TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
rsprintf("<body><center><br><br>\n");
|
|
show_top_text(lbs);
|
|
rsprintf("<form name=\"form1\" id=\"form1\" method=\"GET\" action=\".\">\n\n");
|
|
|
|
/*---- title ----*/
|
|
|
|
if (lbs)
|
|
show_standard_title(lbs, "", 1);
|
|
else
|
|
show_standard_title(NULL, "", 1);
|
|
|
|
/* table for two-column items */
|
|
rsprintf("<tr><td class=\"form2\">");
|
|
rsprintf("<table width=\"100%%\" cellspacing=0>\n");
|
|
|
|
/*---- entry form ----*/
|
|
|
|
rsprintf("<tr><td nowrap>%s:</td>\n", loc("Login name"));
|
|
if (user && user[0]) {
|
|
strencode2(str, user, sizeof(str));
|
|
rsprintf("<td><input type=text size=40 name=new_user_name value=\"%s\" readonly></td>\n", str);
|
|
rsprintf("<td> </td>\n");
|
|
} else {
|
|
rsprintf("<td><input type=text size=40 name=new_user_name></td>\n");
|
|
rsprintf("<td nowrap align=left><font size=2><i>(%s)</i></font></td></tr>\n",
|
|
loc("name may not contain blanks"));
|
|
}
|
|
|
|
rsprintf("<tr><td nowrap>%s:</td>\n", loc("Full name"));
|
|
rsprintf("<td colspan=2><input type=text size=40 name=new_full_name></tr>\n");
|
|
|
|
rsprintf("<tr><td nowrap>Email:</td>\n");
|
|
rsprintf("<td colspan=2><input type=text size=40 name=new_user_email></tr>\n");
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (!stristr(str, "Webserver") && !stristr(str, "PAM")) {
|
|
rsprintf("<tr><td nowrap>%s:</td>\n", loc("Password"));
|
|
rsprintf("<td colspan=2><input type=password size=40 name=newpwd>\n");
|
|
|
|
rsprintf("<tr><td nowrap>%s:</td>\n", loc("Retype password"));
|
|
rsprintf("<td colspan=2><input type=password size=40 name=newpwd2>\n");
|
|
}
|
|
rsprintf("</td></tr></table>\n");
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
rsprintf("<tr><td class=\"menucenter\">\n");
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Save"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
|
|
rsprintf("</td></tr>\n\n");
|
|
|
|
rsprintf("</td></tr></table>\n\n");
|
|
rsprintf("</form></center></body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_elog_delete(LOGBOOK *lbs, int message_id) {
|
|
int i, status, reply = 0, next, nsel;
|
|
char str[256], str2[256], in_reply_to[80], reply_to[MAX_REPLY_TO * 10], owner[256];
|
|
char attrib[MAX_N_ATTR][NAME_LENGTH], mode[80];
|
|
|
|
/* check for editing interval */
|
|
if (isparam("nsel")) {
|
|
for (i = 0; i < atoi(getparam("nsel")); i++) {
|
|
sprintf(str, "s%d", i);
|
|
if (isparam(str)) {
|
|
status = check_edit_time(lbs, atoi(getparam(str)));
|
|
if (!status) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} else if (message_id) {
|
|
status = check_edit_time(lbs, message_id);
|
|
if (!status) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* redirect if confirm = NO */
|
|
if (isparam("confirm") && strcmp(getparam("confirm"), loc("No")) == 0) {
|
|
if (message_id) {
|
|
sprintf(str, "%d", message_id);
|
|
redirect(lbs, str);
|
|
} else {
|
|
strlcpy(str, isparam("lastcmd") ? getparam("lastcmd") : "", sizeof(str));
|
|
url_decode(str);
|
|
redirect(lbs, str);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (isparam("elmode"))
|
|
strlcpy(mode, getparam("elmode"), sizeof(mode));
|
|
|
|
if (isparam("confirm")) {
|
|
if (strcmp(getparam("confirm"), loc("Yes")) == 0) {
|
|
if (message_id) {
|
|
/* delete message */
|
|
status = el_delete_message(lbs, message_id, TRUE, NULL, TRUE, TRUE);
|
|
if (status != EL_SUCCESS) {
|
|
sprintf(str, "%s = %d", loc("Error deleting message: status"), status);
|
|
show_error(str);
|
|
return;
|
|
} else {
|
|
strlcpy(str, isparam("nextmsg") ? getparam("nextmsg") : "", sizeof(str));
|
|
if (atoi(str) == 0)
|
|
sprintf(str, "%d", el_search_message(lbs, EL_LAST, 0, TRUE));
|
|
if (atoi(str) == 0)
|
|
redirect(lbs, "");
|
|
else
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
} else {
|
|
if (isparam("nsel")) {
|
|
for (i = 0; i < atoi(getparam("nsel")); i++) {
|
|
sprintf(str, "s%d", i);
|
|
if (isparam(str)) {
|
|
if (strieq(mode, "threaded"))
|
|
status = el_delete_message(lbs, atoi(getparam(str)), TRUE, NULL, TRUE, TRUE);
|
|
else
|
|
status = el_delete_message(lbs, atoi(getparam(str)), TRUE, NULL, TRUE, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
redirect(lbs, isparam("lastcmd") ? getparam("lastcmd") : "");
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
/* check if at least one message is selected */
|
|
if (!message_id) {
|
|
nsel = isparam("nsel") ? atoi(getparam("nsel")) : 0;
|
|
for (i = 0; i < nsel; i++) {
|
|
sprintf(str, "s%d", i);
|
|
if (isparam(str))
|
|
break;
|
|
}
|
|
if (i == nsel) {
|
|
show_error(loc("No entry selected for deletion"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* check for author */
|
|
if (getcfg(lbs->name, "Restrict edit", str, sizeof(str)) && atoi(str) == 1) {
|
|
/* get message for reply/edit */
|
|
|
|
el_retrieve(lbs, message_id, NULL, attr_list, attrib, lbs->n_attr, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
if (!is_author(lbs, attrib, owner)) {
|
|
strencode2(str2, owner, sizeof(str2));
|
|
sprintf(str, loc("Only user <b>%s</b> can delete this entry"), str2);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* header */
|
|
if (message_id)
|
|
sprintf(str, "%d", message_id);
|
|
else
|
|
str[0] = 0;
|
|
show_standard_header(lbs, TRUE, "Delete ELog entry", str, FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
rsprintf("<tr><td class=\"dlgtitle\">\n");
|
|
|
|
/* define hidden field for command */
|
|
rsprintf("<input type=hidden name=cmd value=\"%s\">\n", loc("Delete"));
|
|
|
|
if (!message_id) {
|
|
rsprintf("%s</td></tr>\n", loc("Are you sure to delete these messages?"));
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">\n");
|
|
if (isparam("nsel"))
|
|
rsprintf("<input type=hidden name=nsel value=%s>\n", getparam("nsel"));
|
|
|
|
if (isparam("lastcmd")) {
|
|
strlcpy(str, getparam("lastcmd"), sizeof(str));
|
|
rsprintf("<input type=hidden name=lastcmd value=\"%s\">\n", str);
|
|
}
|
|
|
|
if (isparam("nsel")) {
|
|
reply = FALSE;
|
|
for (i = 0; i < atoi(getparam("nsel")); i++) {
|
|
sprintf(str, "s%d", i);
|
|
if (isparam(str)) {
|
|
rsprintf("#%s ", getparam(str));
|
|
rsprintf("<input type=hidden name=%s value=%s>\n", str, getparam(str));
|
|
}
|
|
|
|
if (!reply) {
|
|
el_retrieve(lbs, isparam(str) ? atoi(getparam(str)) : 0,
|
|
NULL, attr_list, NULL, 0, NULL, NULL, in_reply_to, reply_to, NULL, NULL, NULL,
|
|
NULL);
|
|
if (reply_to[0])
|
|
reply = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
if (reply && strieq(mode, "threaded"))
|
|
rsprintf("<tr><td align=center class=\"dlgform\">%s</td></tr>\n", loc("and all their replies"));
|
|
|
|
} else {
|
|
rsprintf("%s</td></tr>\n", loc("Are you sure to delete this entry?"));
|
|
|
|
/* check for replies */
|
|
|
|
/* retrieve original message */
|
|
el_retrieve(lbs, message_id, NULL, attr_list, NULL, 0, NULL, NULL, in_reply_to, reply_to, NULL,
|
|
NULL, NULL, NULL);
|
|
|
|
if (reply_to[0])
|
|
rsprintf("<tr><td align=center class=\"dlgform\">#%d<br>%s</td></tr>\n", message_id,
|
|
loc("and all its replies"));
|
|
else
|
|
rsprintf("<tr><td align=center class=\"dlgform\">#%d</td></tr>\n", message_id);
|
|
|
|
/* put link to next message */
|
|
next = el_search_message(lbs, EL_NEXT, message_id, TRUE);
|
|
|
|
rsprintf("<input type=hidden name=nextmsg value=%d>\n", next);
|
|
}
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\"><input type=submit name=confirm value=\"%s\">\n",
|
|
loc("Yes"));
|
|
rsprintf("<input type=submit name=confirm value=\"%s\">\n", loc("No"));
|
|
rsprintf("</td></tr>\n\n");
|
|
}
|
|
|
|
rsprintf("</table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_logbook_delete(LOGBOOK *lbs) {
|
|
char str[256];
|
|
|
|
/* redirect if confirm = NO */
|
|
if (isparam("confirm") && strcmp(getparam("confirm"), loc("No")) == 0) {
|
|
|
|
redirect(lbs, "?cmd=Config");
|
|
return;
|
|
}
|
|
|
|
if (isparam("confirm")) {
|
|
if (strcmp(getparam("confirm"), loc("Yes")) == 0) {
|
|
|
|
/* delete logbook */
|
|
str[0] = 0;
|
|
delete_logbook(lbs, str);
|
|
if (str[0])
|
|
show_error(str);
|
|
else
|
|
redirect(NULL, "../");
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
|
|
strcpy(str, "Delete logbook");
|
|
show_standard_header(lbs, TRUE, str, "", FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
rsprintf("<tr><td class=\"dlgtitle\">\n");
|
|
|
|
/* define hidden field for command */
|
|
rsprintf("<input type=hidden name=cmd value=\"%s\">\n", loc("Delete this logbook"));
|
|
|
|
sprintf(str, loc("Are you sure to delete logbook \"%s\"?"), lbs->name);
|
|
rsprintf("%s</td></tr>\n", str);
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">");
|
|
rsprintf("<input type=submit name=confirm value=\"%s\">\n", loc("Yes"));
|
|
rsprintf("<input type=submit name=confirm value=\"%s\">\n", loc("No"));
|
|
rsprintf("</td></tr>\n\n");
|
|
}
|
|
|
|
rsprintf("</table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_logbook_rename(LOGBOOK *lbs) {
|
|
int i;
|
|
char str[256], lbn[256];
|
|
|
|
if (isparam("lbname")) {
|
|
|
|
/* check if logbook name exists already */
|
|
strcpy(lbn, getparam("lbname"));
|
|
for (i = 0; lb_list[i].name[0]; i++)
|
|
if (strieq(lbn, lb_list[i].name)) {
|
|
sprintf(str, loc("Logbook \"%s\" exists already, please choose different name"), lbn);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
if (!rename_logbook(lbs, getparam("lbname")))
|
|
return;
|
|
|
|
sprintf(str, "../%s/?cmd=Config", getparam("lbname"));
|
|
redirect(NULL, str);
|
|
return;
|
|
|
|
} else {
|
|
|
|
strcpy(str, loc("Rename logbook"));
|
|
show_standard_header(lbs, TRUE, str, "", FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
rsprintf("<tr><td class=\"dlgtitle\">\n");
|
|
|
|
/* define hidden field for command */
|
|
rsprintf("<input type=hidden name=cmd value=\"%s\">\n", loc("Rename this logbook"));
|
|
|
|
rsprintf("%s</td></tr>\n", loc("Enter new logbook name"));
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">");
|
|
rsprintf("<input type=text name=\"lbname\"><br><br>\n");
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Rename this logbook"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
|
|
rsprintf("</td></tr>\n\n");
|
|
}
|
|
|
|
rsprintf("</table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_logbook_new(LOGBOOK *lbs) {
|
|
char str[1000], lbn[256];
|
|
int i;
|
|
|
|
if (isparam("lbname")) {
|
|
|
|
/* check if logbook name exists already */
|
|
strcpy(lbn, getparam("lbname"));
|
|
for (i = 0; lb_list[i].name[0]; i++)
|
|
if (strieq(lbn, lb_list[i].name)) {
|
|
sprintf(str, loc("Logbook \"%s\" exists already, please choose different name"), lbn);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
/* create new logbook */
|
|
if (!create_logbook(lbs, getparam("lbname"), getparam("template")))
|
|
return;
|
|
|
|
strcpy(lbn, getparam("lbname"));
|
|
url_encode(lbn, sizeof(lbn));
|
|
sprintf(str, "../%s/?cmd=Config", lbn);
|
|
for (i = 0; lb_list[i].name[0]; i++)
|
|
if (strieq(lbn, lb_list[i].name))
|
|
break;
|
|
if (lb_list[i].name[0])
|
|
redirect(&lb_list[i], str);
|
|
else
|
|
redirect(NULL, str);
|
|
return;
|
|
}
|
|
|
|
show_standard_header(lbs, TRUE, loc("Create new logbook"), "", FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
rsprintf("<tr><td class=\"dlgtitle\">\n");
|
|
|
|
/* define hidden field for command */
|
|
rsprintf("<input type=hidden name=cmd value=\"%s\">\n", loc("Create new logbook"));
|
|
rsprintf("%s</td></tr>\n", loc("Create new logbook"));
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">\n");
|
|
rsprintf("%s : ", loc("Logbook name"));
|
|
rsprintf("<input type=text name=lbname>\n");
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">\n");
|
|
rsprintf("%s : \n", loc("Use existing logbook as template"));
|
|
rsprintf("<select name=template>\n");
|
|
rsprintf("<option value=\"\">- %s -\n", loc("none"));
|
|
for (i = 0; lb_list[i].name[0]; i++)
|
|
rsprintf("<option value=\"%s\">%s\n", lb_list[i].name, lb_list[i].name);
|
|
rsprintf("</select>\n");
|
|
rsprintf("</td></tr>\n\n");
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">\n");
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Create new logbook"));
|
|
rsprintf("<input type=submit name=tmp value=\"%s\">\n", loc("Cancel"));
|
|
rsprintf("</td></tr>\n\n");
|
|
|
|
rsprintf("</table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int show_download_page(LOGBOOK *lbs, char *path) {
|
|
char file_name[1024], error_str[256];
|
|
int index, message_id, fh, i, size, delta;
|
|
char message[TEXT_SIZE + 1000], *p, *buffer;
|
|
|
|
if (stricmp(path, "gbl") == 0) {
|
|
|
|
/* return complete config file */
|
|
load_config_section(NULL, &buffer, error_str);
|
|
if (error_str[0]) {
|
|
rsprintf("<h2>%s</h2>", error_str);
|
|
return EL_FILE_ERROR;
|
|
}
|
|
|
|
size = strlen(buffer);
|
|
strlcpy(message, buffer, sizeof(message));
|
|
xfree(buffer);
|
|
|
|
} else {
|
|
|
|
message_id = atoi(path);
|
|
|
|
if (message_id == 0) {
|
|
|
|
/* return config */
|
|
load_config_section(lbs->name, &buffer, error_str);
|
|
if (error_str[0]) {
|
|
rsprintf("<h2>%s</h2>", error_str);
|
|
return EL_FILE_ERROR;
|
|
}
|
|
|
|
size = strlen(buffer);
|
|
strlcpy(message, buffer, sizeof(message));
|
|
xfree(buffer);
|
|
|
|
} else {
|
|
|
|
/* return entry */
|
|
|
|
if (message_id == -1)
|
|
index = *lbs->n_el_index - 1; // last entry
|
|
else {
|
|
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_RDWR | O_BINARY, 0644);
|
|
if (fh < 0)
|
|
return EL_FILE_ERROR;
|
|
|
|
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);
|
|
|
|
/* decode message size */
|
|
p = strstr(message + 8, "$@MID@$:");
|
|
if (p == NULL)
|
|
size = strlen(message);
|
|
else
|
|
size = p - message;
|
|
|
|
message[size] = 0;
|
|
}
|
|
}
|
|
|
|
show_plain_header(size, "export.txt");
|
|
|
|
/* increase return buffer size if file too big */
|
|
while (size + 1 >= return_buffer_size - (int) strlen(return_buffer)) {
|
|
delta = size - (return_buffer_size - strlen(return_buffer)) + 1000;
|
|
|
|
return_buffer = (char *) xrealloc(return_buffer, return_buffer_size + delta);
|
|
memset(return_buffer + return_buffer_size, 0, delta);
|
|
return_buffer_size += delta;
|
|
}
|
|
|
|
return_length = strlen(return_buffer) + size;
|
|
strlcat(return_buffer, message, return_buffer_size);
|
|
|
|
return EL_SUCCESS;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int download_config() {
|
|
char error_str[256];
|
|
int size, delta;
|
|
char message[TEXT_SIZE + 1000], *buffer;
|
|
|
|
/* return complete config file */
|
|
load_config_section(NULL, &buffer, error_str);
|
|
if (error_str[0]) {
|
|
rsprintf("Error loading configuration file: %s", error_str);
|
|
return EL_FILE_ERROR;
|
|
}
|
|
|
|
size = strlen(buffer);
|
|
strlcpy(message, buffer, sizeof(message));
|
|
xfree(buffer);
|
|
|
|
show_plain_header(size, "export.txt");
|
|
|
|
/* increase return buffer size if file too big */
|
|
while (size + 1 >= return_buffer_size - (int) strlen(return_buffer)) {
|
|
delta = size - (return_buffer_size - strlen(return_buffer)) + 1000;
|
|
|
|
return_buffer = (char *) xrealloc(return_buffer, return_buffer_size + delta);
|
|
memset(return_buffer + return_buffer_size, 0, delta);
|
|
return_buffer_size += delta;
|
|
}
|
|
|
|
return_length = strlen(return_buffer) + size;
|
|
strlcat(return_buffer, message, return_buffer_size);
|
|
|
|
return EL_SUCCESS;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_import_page_csv(LOGBOOK *lbs) {
|
|
char str[256], str2[256];
|
|
|
|
/*---- header ----*/
|
|
|
|
show_html_header(lbs, FALSE, loc("ELOG CSV import"), TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
|
|
rsprintf("<body><form method=\"POST\" action=\"./\" enctype=\"multipart/form-data\">\n");
|
|
|
|
/*---- title ----*/
|
|
|
|
show_standard_title(lbs, "", 0);
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Import"));
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
|
|
/* table for two-column items */
|
|
rsprintf("<tr><td class=\"form2\">");
|
|
rsprintf("<table width=\"100%%\" cellspacing=0>\n");
|
|
|
|
/*---- entry form ----*/
|
|
|
|
rsprintf("<tr><td class=\"attribname\" nowrap width=\"10%%\">%s:</td>\n", loc("Field separator"));
|
|
rsprintf("<td class=\"attribvalue\">");
|
|
|
|
str[0] = 0;
|
|
if (isparam("sep"))
|
|
strlcpy(str, getparam("sep"), sizeof(str));
|
|
|
|
if (str[0] == 0)
|
|
rsprintf("<input type=\"radio\" checked id=\"comma\" name=\"sep\" value=\"auto\">");
|
|
else
|
|
rsprintf("<input type=\"radio\" id=\"comma\" name=\"sep\" value=\"auto\">");
|
|
rsprintf("<label for=\"comma\">%s</label>\n", loc("Auto detect"));
|
|
|
|
if (str[0] == ',')
|
|
rsprintf("<input type=\"radio\" checked id=\"comma\" name=\"sep\" value=\",\">");
|
|
else
|
|
rsprintf("<input type=\"radio\" id=\"comma\" name=\"sep\" value=\",\">");
|
|
rsprintf("<label for=\"comma\">%s (,)</label>\n", loc("Comma"));
|
|
|
|
if (str[0] == ';')
|
|
rsprintf("<input type=\"radio\" checked id=\"semi\" name=\"sep\" value=\";\">");
|
|
else
|
|
rsprintf("<input type=\"radio\" id=\"semi\" name=\"sep\" value=\";\">");
|
|
rsprintf("<label for=\"semi\">%s (;)</label>\n", loc("Semicolon"));
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("<tr><td class=\"attribname\" nowrap width=\"10%%\">%s:</td>\n", loc("Options"));
|
|
rsprintf("<td class=\"attribvalue\">");
|
|
|
|
if (isparam("head"))
|
|
rsprintf("<input type=checkbox checked id=\"head\" name=\"head\" value=\"1\">\n");
|
|
else
|
|
rsprintf("<input type=checkbox id=\"head\" name=\"head\" value=\"1\">\n");
|
|
rsprintf("<label for=\"head\">%s</label><br>\n", loc("Derive attributes from CSV file"));
|
|
|
|
if (isparam("notignore"))
|
|
rsprintf("<input type=checkbox checked id=\"notignore\" name=\"notignore\" value=\"1\">\n");
|
|
else
|
|
rsprintf("<input type=checkbox id=\"notignore\" name=\"notignore\" value=\"1\">\n");
|
|
rsprintf("<label for=\"notignore\">%s</label><br>\n", loc("Do not ignore first line"));
|
|
|
|
rsprintf("<input type=checkbox id=\"preview\" name=\"preview\" value=\"1\">\n");
|
|
rsprintf("<label for=\"preview\">%s</label><br>\n", loc("Preview import"));
|
|
|
|
if (isparam("filltext"))
|
|
rsprintf("<input type=checkbox checked id=\"filltext\" name=\"filltext\" value=\"1\">\n");
|
|
else
|
|
rsprintf("<input type=checkbox id=\"filltext\" name=\"filltext\" value=\"1\">\n");
|
|
strcpy(str, loc("text"));
|
|
sprintf(str2, loc("Column header '%s' must be present in CSV file"), str);
|
|
rsprintf("<label for=\"filltext\">%s (%s)</label><br>\n", loc("Fill text body"), str2);
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("<tr><td class=\"attribname\" nowrap width=\"10%%\">%s:</td>\n", loc("CSV filename"));
|
|
|
|
rsprintf("<td class=\"attribvalue\">");
|
|
|
|
if (isparam("csvfile"))
|
|
rsprintf("<b>%s</b>:<br>\n", loc("Please re-enter filename"));
|
|
|
|
rsprintf("<input type=\"file\" size=\"60\" maxlength=\"200\" name=\"csvfile\" ");
|
|
if (isparam("csvfile"))
|
|
rsprintf(" value=\"%s\" ", getparam("csvfile"));
|
|
rsprintf("></td></tr>\n");
|
|
|
|
rsprintf("</table></td></tr></table>\n\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_import_page_xml(LOGBOOK *lbs) {
|
|
/*---- header ----*/
|
|
|
|
show_html_header(lbs, FALSE, loc("ELOG XML import"), TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
|
|
rsprintf("<body><form method=\"POST\" action=\"./\" enctype=\"multipart/form-data\">\n");
|
|
|
|
/*---- title ----*/
|
|
|
|
show_standard_title(lbs, "", 0);
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Import"));
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
|
|
/* table for two-column items */
|
|
rsprintf("<tr><td class=\"form2\">");
|
|
rsprintf("<table width=\"100%%\" cellspacing=0>\n");
|
|
|
|
/*---- entry form ----*/
|
|
|
|
rsprintf("<tr><td class=\"attribname\" nowrap width=\"10%%\">%s:</td>\n", loc("Options"));
|
|
rsprintf("<td class=\"attribvalue\">");
|
|
|
|
if (isparam("head"))
|
|
rsprintf("<input type=checkbox checked id=\"head\" name=\"head\" value=\"1\">\n");
|
|
else
|
|
rsprintf("<input type=checkbox id=\"head\" name=\"head\" value=\"1\">\n");
|
|
rsprintf("<label for=\"head\">%s</label><br>\n", loc("Derive attributes from XML file"));
|
|
|
|
if (isparam("keep"))
|
|
rsprintf("<input type=checkbox checked id=\"keep\" name=\"keep\" value=\"1\">\n");
|
|
else
|
|
rsprintf("<input type=checkbox id=\"keep\" name=\"keep\" value=\"1\">\n");
|
|
rsprintf("<label for=\"keep\">%s</label><br>\n",
|
|
loc
|
|
("Keep original entry IDs (may overwrite existing entries, but is required if imported entries contain replies)"));
|
|
|
|
rsprintf("<input type=checkbox id=\"preview\" name=\"preview\" value=\"1\">\n");
|
|
rsprintf("<label for=\"preview\">%s</label><br>\n", loc("Preview import"));
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("<tr><td class=\"attribname\" nowrap width=\"10%%\">%s:</td>\n", loc("XML filename"));
|
|
rsprintf("<td class=\"attribvalue\">");
|
|
|
|
if (isparam("xmlfile"))
|
|
rsprintf("<b>%s</b>:<br>\n", loc("Please re-enter filename"));
|
|
|
|
rsprintf("<input type=\"file\" size=\"60\" maxlength=\"200\" name=\"xmlfile\" ");
|
|
if (isparam("xmlfile"))
|
|
rsprintf(" value=\"%s\" ", getparam("xmlfile"));
|
|
rsprintf("></td></tr>\n");
|
|
|
|
rsprintf("</table></td></tr></table>\n\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void csv_import(LOGBOOK *lbs, const char *csv, const char *csvfile) {
|
|
const char *p;
|
|
char *line, *list;
|
|
char str[256], date[80], sep[80];
|
|
int i, j, n, n_attr, iline, n_imported, textcol, datecol, attr_offset;
|
|
BOOL first, in_quotes, filltext;
|
|
time_t ltime;
|
|
|
|
list = (char *) xmalloc((MAX_N_ATTR + 2) * NAME_LENGTH);
|
|
line = (char *) xmalloc(10000);
|
|
|
|
first = TRUE;
|
|
in_quotes = FALSE;
|
|
iline = n_imported = 0;
|
|
filltext = FALSE;
|
|
textcol = -1;
|
|
datecol = -1;
|
|
attr_offset = 0;
|
|
|
|
strcpy(sep, ",");
|
|
if (isparam("sep"))
|
|
strcpy(sep, getparam("sep"));
|
|
if (sep[0] == 0)
|
|
strcpy(sep, ",");
|
|
if (strieq(sep, "auto")) {
|
|
|
|
/* count commas */
|
|
for (i = 0, p = csv; p; i++) {
|
|
p = strchr(p, ',');
|
|
if (p)
|
|
p++;
|
|
}
|
|
n = i;
|
|
|
|
/* count semicolon */
|
|
for (i = 0, p = csv; p; i++) {
|
|
p = strchr(p, ';');
|
|
if (p)
|
|
p++;
|
|
}
|
|
|
|
strcpy(sep, i > n ? ";" : ",");
|
|
}
|
|
|
|
n_attr = lbs->n_attr;
|
|
|
|
if (isparam("preview")) {
|
|
|
|
/* title row */
|
|
sprintf(str, loc("CSV import preview of %s"), csvfile);
|
|
show_standard_header(lbs, TRUE, str, "./", FALSE, NULL, NULL, 0);
|
|
rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\">\n");
|
|
rsprintf("<tr><td class=\"title1\">%s</td></tr>\n", str, str);
|
|
|
|
/* menu buttons */
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("CSV Import"));
|
|
|
|
/* hidden fields */
|
|
rsprintf("<input type=hidden name=sep value=\"%s\">\n", sep);
|
|
if (isparam("head"))
|
|
rsprintf("<input type=hidden name=head value=\"%s\">\n", getparam("head"));
|
|
if (isparam("notignore"))
|
|
rsprintf("<input type=hidden name=notignore value=\"%s\">\n", getparam("notignore"));
|
|
if (isparam("filltext"))
|
|
rsprintf("<input type=hidden name=filltext value=\"%s\">\n", getparam("filltext"));
|
|
rsprintf("<input type=hidden name=csvfile value=\"%s\">\n", csvfile);
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
|
|
rsprintf("<tr><td><table class=\"listframe\" width=\"100%%\" cellspacing=0>");
|
|
}
|
|
|
|
p = csv;
|
|
datecol = -1;
|
|
attr_offset = 0;
|
|
|
|
do {
|
|
for (i = 0; i < 10000 && *p; i++) {
|
|
if (!in_quotes && (*p == '\r' || *p == '\n'))
|
|
break;
|
|
|
|
line[i] = *p++;
|
|
if (line[i] == '"')
|
|
in_quotes = !in_quotes;
|
|
}
|
|
|
|
line[i] = 0;
|
|
while (*p == '\r' || *p == '\n')
|
|
p++;
|
|
if (!*p)
|
|
break;
|
|
|
|
memset(list, 0, MAX_N_ATTR * NAME_LENGTH);
|
|
n = strbreak(line, (char (*)[NAME_LENGTH]) list, MAX_N_ATTR, sep, FALSE);
|
|
|
|
if (n == MAX_N_ATTR) {
|
|
strlcpy(str, loc("Too many attributes in CSV file"), sizeof(str));
|
|
show_error(str);
|
|
}
|
|
|
|
/* check if text column is present */
|
|
if (first && isparam("filltext") && atoi(getparam("filltext"))) {
|
|
for (i = 0; i < n; i++)
|
|
if (strieq(list + i * NAME_LENGTH, loc("text"))) {
|
|
filltext = TRUE;
|
|
textcol = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* interprete date entries correctly */
|
|
if (!(first && isparam("head"))) {
|
|
for (i = attr_offset; i < n; i++) {
|
|
if (attr_flags[i - attr_offset] & AF_DATE) {
|
|
/* convert to seconds in Unix format */
|
|
ltime = convert_date(list + i * NAME_LENGTH);
|
|
if (ltime == 0) {
|
|
show_error(loc("Invalid date format"));
|
|
return;
|
|
}
|
|
sprintf(list + i * NAME_LENGTH, "%d", (int) ltime);
|
|
}
|
|
if (attr_flags[i - attr_offset] & AF_DATETIME) {
|
|
/* convert to seconds in Unix format */
|
|
ltime = convert_datetime(list + i * NAME_LENGTH);
|
|
if (ltime == 0) {
|
|
show_error(loc("Invalid date format"));
|
|
return;
|
|
}
|
|
sprintf(list + i * NAME_LENGTH, "%d", (int) ltime);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (first) {
|
|
/* check for date column */
|
|
for (i = attr_offset = 0; i < n; i++)
|
|
if (strieq(list + i * NAME_LENGTH, "Date"))
|
|
datecol = i;
|
|
|
|
/* skip message ID */
|
|
for (i = attr_offset = 0; i < n; i++)
|
|
if (strieq(list + i * NAME_LENGTH, "Message ID") || strieq(list + i * NAME_LENGTH, "Date"))
|
|
attr_offset++;
|
|
}
|
|
|
|
/* derive attributes from first line */
|
|
if (first && isparam("head")) {
|
|
|
|
if (isparam("preview")) {
|
|
rsprintf("<tr>\n");
|
|
for (i = attr_offset; i < n; i++)
|
|
if (i != textcol)
|
|
rsprintf("<th class=\"listtitle\">%s</th>\n", list + i * NAME_LENGTH);
|
|
|
|
if (filltext)
|
|
rsprintf("<th class=\"listtitle\">%s</th>\n", loc("text"));
|
|
|
|
rsprintf("</tr>\n");
|
|
|
|
if (filltext)
|
|
n_attr = n - 1 - attr_offset;
|
|
else
|
|
n_attr = n - attr_offset;
|
|
|
|
} else {
|
|
for (i = j = attr_offset; i < n; i++)
|
|
if (i != textcol)
|
|
strlcpy(attr_list[j++ - attr_offset], list + i * NAME_LENGTH, NAME_LENGTH);
|
|
|
|
if (filltext) {
|
|
if (!set_attributes(lbs, attr_list, n - 1 - attr_offset))
|
|
return;
|
|
lbs->n_attr = n - 1 - attr_offset;
|
|
} else {
|
|
if (!set_attributes(lbs, attr_list, n - attr_offset))
|
|
return;
|
|
lbs->n_attr = n - attr_offset;
|
|
}
|
|
n_attr = lbs->n_attr;
|
|
}
|
|
|
|
} else {
|
|
|
|
/* ignore first line */
|
|
if (first && !isparam("notignore")) {
|
|
first = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (isparam("preview")) {
|
|
rsprintf("<tr>\n");
|
|
for (i = j = attr_offset; i < n_attr + attr_offset; i++) {
|
|
if (iline % 2 == 0)
|
|
rsputs("<td class=\"list1\">");
|
|
else
|
|
rsputs("<td class=\"list2\">");
|
|
|
|
/* skip text column */
|
|
if (i == textcol)
|
|
j++;
|
|
|
|
if (i >= n || !list[j * NAME_LENGTH])
|
|
rsputs(" ");
|
|
else
|
|
rsputs(list + j * NAME_LENGTH);
|
|
|
|
rsputs("</td>\n");
|
|
j++;
|
|
}
|
|
|
|
if (filltext) {
|
|
rsputs("<td class=\"summary\">");
|
|
if (list[textcol * NAME_LENGTH])
|
|
rsputs(list + textcol * NAME_LENGTH);
|
|
else
|
|
rsputs(" ");
|
|
rsputs("</td>\n");
|
|
}
|
|
|
|
rsputs("</tr>\n");
|
|
iline++;
|
|
|
|
} else {
|
|
|
|
/* get date and check it */
|
|
if (datecol != -1) {
|
|
strlcpy(date, list + datecol * NAME_LENGTH, sizeof(date));
|
|
ltime = date_to_ltime(date);
|
|
if (ltime <= 0) {
|
|
/* try other date formats */
|
|
ltime = convert_datetime(date);
|
|
if (ltime <= 0)
|
|
ltime = convert_date(date);
|
|
if (ltime <= 0) {
|
|
strcpy(str, loc("Invalid date format"));
|
|
strlcat(str, ": \"", sizeof(str));
|
|
strlcat(str, date, sizeof(str));
|
|
strlcat(str, "\"", sizeof(str));
|
|
show_error(str);
|
|
return;
|
|
}
|
|
/* convert back ltime to date */
|
|
get_rfc2822_date(date, sizeof(date), ltime);
|
|
}
|
|
} else
|
|
date[0] = 0;
|
|
|
|
if (!filltext) {
|
|
/* submit entry */
|
|
if (el_submit
|
|
(lbs, 0, FALSE, date, attr_list,
|
|
(char (*)[NAME_LENGTH]) (list + attr_offset * NAME_LENGTH), n_attr, "", "", "",
|
|
"plain",
|
|
NULL, TRUE, NULL, NULL))
|
|
n_imported++;
|
|
} else {
|
|
strlcpy(line, list + textcol * NAME_LENGTH, 10000);
|
|
insert_breaks(line, 78, 10000);
|
|
|
|
for (i = textcol; i < n_attr + attr_offset; i++)
|
|
strlcpy(list + i * NAME_LENGTH, list + (i + 1) * NAME_LENGTH, NAME_LENGTH);
|
|
|
|
/* submit entry */
|
|
if (el_submit
|
|
(lbs, 0, FALSE, date, attr_list,
|
|
(char (*)[NAME_LENGTH]) (list + attr_offset * NAME_LENGTH), n_attr, line, "", "",
|
|
"plain",
|
|
NULL, TRUE, NULL, NULL))
|
|
n_imported++;
|
|
}
|
|
}
|
|
}
|
|
|
|
first = FALSE;
|
|
|
|
} while (*p);
|
|
|
|
xfree(line);
|
|
xfree(list);
|
|
|
|
if (isparam("preview")) {
|
|
rsprintf("</table></td></tr></table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
|
|
return;
|
|
}
|
|
|
|
sprintf(str, loc("%d entries successfully imported"), n_imported);
|
|
show_elog_list(lbs, 0, 0, 0, TRUE, str);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void xml_import(LOGBOOK *lbs, const char *xml, const char *xmlfile) {
|
|
char str[NAME_LENGTH], date[80], error[256], encoding[256], *list, *p, in_reply_to[80],
|
|
reply_to[MAX_REPLY_TO * 10], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], attachment_all[64
|
|
*
|
|
MAX_ATTACHMENTS];
|
|
int i, j, index, n_attr, iline, n_imported, i_line, line_len, message_id, bedit;
|
|
time_t ltime;
|
|
PMXML_NODE root, entry;
|
|
|
|
iline = n_imported = 0;
|
|
|
|
n_attr = lbs->n_attr;
|
|
|
|
root = mxml_parse_buffer(xml, error, sizeof(error), NULL);
|
|
if (root == NULL) {
|
|
strencode2(str, error, sizeof(str));
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
root = mxml_find_node(root, "ELOG_LIST");
|
|
if (root == NULL) {
|
|
sprintf(str, loc("XML file does not contain %s element"), "<ELOG_LIST>");
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
entry = mxml_subnode(root, 0);
|
|
|
|
if (mxml_find_node(entry, "MID") == NULL) {
|
|
sprintf(str, loc("XML file does not contain %s element"), "<MID>");
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
if (mxml_find_node(entry, "DATE") == NULL) {
|
|
sprintf(str, loc("XML file does not contain %s element"), "<DATE>");
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
if (mxml_find_node(entry, "ENCODING") == NULL) {
|
|
sprintf(str, loc("XML file does not contain %s element"), "<ENCODING>");
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
if (isparam("preview")) {
|
|
|
|
/* title row */
|
|
sprintf(str, loc("XML import preview of %s"), xmlfile);
|
|
show_standard_header(lbs, TRUE, str, "./", FALSE, NULL, NULL, 0);
|
|
rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\">\n");
|
|
rsprintf("<tr><td class=\"title1\">%s</td></tr>\n", str, str);
|
|
|
|
/* menu buttons */
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("XML Import"));
|
|
|
|
/* hidden fields */
|
|
if (isparam("head"))
|
|
rsprintf("<input type=hidden name=head value=\"%s\">\n", getparam("head"));
|
|
if (isparam("keep"))
|
|
rsprintf("<input type=hidden name=keep value=\"%s\">\n", getparam("keep"));
|
|
rsprintf("<input type=hidden name=xmlfile value=\"%s\">\n", xmlfile);
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
rsprintf("<tr><td><table class=\"listframe\" width=\"100%%\" cellspacing=0>");
|
|
}
|
|
|
|
list = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH);
|
|
|
|
/* derive attributes from XML file */
|
|
if (isparam("head")) {
|
|
if (isparam("preview")) {
|
|
rsprintf("<tr>\n");
|
|
for (i = 0; i < mxml_get_number_of_children(entry); i++) {
|
|
strlcpy(str, mxml_get_name(mxml_subnode(entry, i)), sizeof(str));
|
|
if (strieq(str, "MID"))
|
|
strcpy(str, "ID");
|
|
if (strieq(str, "DATE"))
|
|
strcpy(str, loc("Date"));
|
|
if (strieq(str, "TEXT"))
|
|
strcpy(str, loc("Text"));
|
|
if (!strieq(str, "ENCODING") && !strieq(str, "IN_REPLY_TO") && !strieq(str, "REPLY_TO")
|
|
&& !strieq(str, "ATTACHMENT"))
|
|
rsprintf("<th class=\"listtitle\">%s</th>\n", str);
|
|
}
|
|
|
|
rsprintf("</tr>\n");
|
|
n_attr = i;
|
|
} else {
|
|
for (i = j = 0; i < mxml_get_number_of_children(entry); i++) {
|
|
strlcpy(str, mxml_get_name(mxml_subnode(entry, i)), NAME_LENGTH);
|
|
if (stricmp(str, "MID") != 0 && stricmp(str, "DATE") != 0 &&
|
|
stricmp(str, "ENCODING") != 0 && stricmp(str, "TEXT") != 0 &&
|
|
stricmp(str, "IN_REPLY_TO") != 0 && stricmp(str, "REPLY_TO") != 0 &&
|
|
stricmp(str, "ATTACHMENT") != 0)
|
|
strlcpy(attr_list[j++], mxml_get_name(mxml_subnode(entry, i)), NAME_LENGTH);
|
|
}
|
|
|
|
if (!set_attributes(lbs, attr_list, j))
|
|
return;
|
|
lbs->n_attr = n_attr = j;
|
|
}
|
|
} else {
|
|
if (isparam("preview")) {
|
|
rsprintf("<tr>\n");
|
|
rsprintf("<th class=\"listtitle\">%s</th>\n", "ID");
|
|
rsprintf("<th class=\"listtitle\">%s</th>\n", loc("Date"));
|
|
for (i = 0; i < n_attr; i++)
|
|
rsprintf("<th class=\"listtitle\">%s</th>\n", attr_list[i]);
|
|
rsprintf("<th class=\"listtitle\">%s</th>\n", loc("Text"));
|
|
rsprintf("</tr>\n");
|
|
}
|
|
}
|
|
|
|
for (index = 0; index < mxml_get_number_of_children(root); index++) {
|
|
|
|
entry = mxml_subnode(root, index);
|
|
|
|
if (isparam("preview")) {
|
|
rsprintf("<tr>\n");
|
|
for (i = 0; i < mxml_get_number_of_children(entry); i++) {
|
|
|
|
strlcpy(str, mxml_get_name(mxml_subnode(entry, i)), NAME_LENGTH);
|
|
if (strieq(str, "ENCODING") || strieq(str, "IN_REPLY_TO") || strieq(str, "REPLY_TO")
|
|
|| strieq(str, "ATTACHMENT"))
|
|
continue;
|
|
if (strieq(str, "TEXT"))
|
|
break;
|
|
|
|
if (iline % 2 == 0)
|
|
rsputs("<td class=\"list1\">");
|
|
else
|
|
rsputs("<td class=\"list2\">");
|
|
|
|
strlcpy(str, mxml_get_value(mxml_subnode(entry, i)), NAME_LENGTH);
|
|
|
|
if (!str[0])
|
|
rsputs(" ");
|
|
else
|
|
rsputs(str);
|
|
|
|
rsputs("</td>\n");
|
|
}
|
|
|
|
rsputs("<td class=\"summary\">");
|
|
if (mxml_find_node(entry, "TEXT")) {
|
|
strlcpy(str, mxml_get_value(mxml_find_node(entry, "TEXT")), sizeof(str));
|
|
if (str[0]) {
|
|
|
|
/* limit output to 3 lines */
|
|
for (i = i_line = line_len = 0; i < (int) sizeof(str) - 1; i++, line_len++) {
|
|
if (str[i] == '\n') {
|
|
i_line++;
|
|
line_len = 0;
|
|
} else
|
|
/* limit line length to 150 characters */
|
|
if (line_len > 150 && str[i] == ' ') {
|
|
str[i] = '\n';
|
|
i_line++;
|
|
line_len = 0;
|
|
}
|
|
|
|
if (i_line == 3)
|
|
break;
|
|
}
|
|
str[i] = 0;
|
|
|
|
strip_html(str);
|
|
if (str[0])
|
|
strencode(str);
|
|
else
|
|
rsputs(" ");
|
|
} else
|
|
rsputs(" ");
|
|
}
|
|
rsputs("</td>\n");
|
|
rsputs("</tr>\n");
|
|
iline++;
|
|
|
|
} else {
|
|
|
|
message_id = 0;
|
|
if (isparam("keep"))
|
|
message_id = atoi(mxml_get_value(mxml_find_node(entry, "MID")));
|
|
|
|
for (i = 0; i < n_attr; i++) {
|
|
strlcpy(str, attr_list[i], sizeof(str));
|
|
while (strchr(str, ' '))
|
|
*strchr(str, ' ') = '_';
|
|
if (mxml_find_node(entry, str) == NULL)
|
|
*(list + (i * NAME_LENGTH)) = 0;
|
|
else
|
|
strlcpy(list + i * NAME_LENGTH, mxml_get_value(mxml_find_node(entry, str)), NAME_LENGTH);
|
|
}
|
|
|
|
/* interprete date entries correctly */
|
|
for (i = 0; i < n_attr; i++) {
|
|
if (attr_flags[i] & AF_DATE) {
|
|
/* convert to seconds in Unix format */
|
|
ltime = convert_date(list + i * NAME_LENGTH);
|
|
if (ltime == 0) {
|
|
show_error(loc("Invalid date format"));
|
|
return;
|
|
}
|
|
sprintf(list + i * NAME_LENGTH, "%d", (int) ltime);
|
|
}
|
|
if (attr_flags[i] & AF_DATETIME) {
|
|
/* convert to seconds in Unix format */
|
|
ltime = convert_datetime(list + i * NAME_LENGTH);
|
|
if (ltime == 0) {
|
|
show_error(loc("Invalid date format"));
|
|
return;
|
|
}
|
|
sprintf(list + i * NAME_LENGTH, "%d", (int) ltime);
|
|
}
|
|
}
|
|
|
|
encoding[0] = 0;
|
|
if (mxml_find_node(entry, "ENCODING"))
|
|
strlcpy(encoding, mxml_get_value(mxml_find_node(entry, "ENCODING")), sizeof(encoding));
|
|
else
|
|
strcpy(encoding, "plain");
|
|
|
|
reply_to[0] = 0;
|
|
if (mxml_find_node(entry, "REPLY_TO"))
|
|
strlcpy(reply_to, mxml_get_value(mxml_find_node(entry, "REPLY_TO")), sizeof(reply_to));
|
|
|
|
in_reply_to[0] = 0;
|
|
if (mxml_find_node(entry, "IN_REPLY_TO"))
|
|
strlcpy(in_reply_to, mxml_get_value(mxml_find_node(entry, "IN_REPLY_TO")), sizeof(in_reply_to));
|
|
|
|
date[0] = 0;
|
|
if (mxml_find_node(entry, "DATE"))
|
|
strlcpy(date, mxml_get_value(mxml_find_node(entry, "DATE")), sizeof(date));
|
|
|
|
attachment_all[0] = 0;
|
|
if (mxml_find_node(entry, "ATTACHMENT"))
|
|
strlcpy(attachment_all, mxml_get_value(mxml_find_node(entry, "ATTACHMENT")),
|
|
sizeof(attachment_all));
|
|
memset(attachment, 0, sizeof(attachment));
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
if (i == 0)
|
|
p = strtok(attachment_all, ",");
|
|
else
|
|
p = strtok(NULL, ",");
|
|
|
|
if (p != NULL)
|
|
strlcpy(attachment[i], p, MAX_PATH_LENGTH);
|
|
else
|
|
break;
|
|
}
|
|
|
|
str[0] = 0;
|
|
if (mxml_find_node(entry, "TEXT"))
|
|
p = mxml_get_value(mxml_find_node(entry, "TEXT"));
|
|
else
|
|
p = str;
|
|
|
|
bedit = FALSE;
|
|
if (isparam("keep")) {
|
|
for (i = 0; i < *lbs->n_el_index; i++)
|
|
if (lbs->el_index[i].message_id == message_id)
|
|
break;
|
|
if (lbs->el_index[i].message_id == message_id)
|
|
bedit = TRUE;
|
|
}
|
|
|
|
/* submit entry */
|
|
if (el_submit
|
|
(lbs, message_id, bedit, date, attr_list, (char (*)[NAME_LENGTH]) list, n_attr, p,
|
|
in_reply_to,
|
|
reply_to, encoding, attachment, FALSE, NULL, NULL))
|
|
n_imported++;
|
|
}
|
|
}
|
|
|
|
xfree(list);
|
|
|
|
if (isparam("preview")) {
|
|
rsprintf("</table></td></tr></table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
|
|
return;
|
|
}
|
|
|
|
sprintf(str, loc("%d entries successfully imported"), n_imported);
|
|
show_elog_list(lbs, 0, 0, 0, TRUE, str);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int show_md5_page(LOGBOOK *lbs) {
|
|
int i, j;
|
|
char *buffer, error_str[256];
|
|
unsigned char digest[16];
|
|
|
|
/* 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");
|
|
rsprintf("Connection: close\r\n");
|
|
rsprintf("Content-Type: text/plain;charset=%s\r\n", DEFAULT_HTTP_CHARSET);
|
|
rsprintf("Pragma: no-cache\r\n");
|
|
rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n\r\n");
|
|
|
|
/* calculate MD5 for logbook section in config file */
|
|
load_config_section(lbs->name, &buffer, error_str);
|
|
if (error_str[0])
|
|
rsprintf("<h2>%s</h2>", error_str);
|
|
else {
|
|
rsprintf("ID: %6d MD5:", 0);
|
|
|
|
remove_crlf(buffer);
|
|
MD5_checksum(buffer, strlen(buffer), digest);
|
|
|
|
for (i = 0; i < 16; i++)
|
|
rsprintf("%02X", digest[i]);
|
|
rsprintf("\n");
|
|
}
|
|
xfree(buffer);
|
|
|
|
/* show MD5's of logbook entries */
|
|
for (i = 0; i < *lbs->n_el_index; i++) {
|
|
rsprintf("ID: %6d MD5:", lbs->el_index[i].message_id);
|
|
for (j = 0; j < 16; j++)
|
|
rsprintf("%02X", lbs->el_index[i].md5_digest[j]);
|
|
rsprintf("\n");
|
|
}
|
|
|
|
keep_alive = FALSE;
|
|
|
|
return EL_SUCCESS;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void combine_url(LOGBOOK *lbs, char *url, const char *param, char *result, int size, int *ssl) {
|
|
if (ssl)
|
|
*ssl = 0;
|
|
if (strstr(url, "http://"))
|
|
strlcpy(result, url + 7, size);
|
|
else if (strstr(url, "https://")) {
|
|
if (ssl)
|
|
*ssl = 1;
|
|
strlcpy(result, url + 8, size);
|
|
} else
|
|
strlcpy(result, url, size);
|
|
|
|
url_encode(result, size);
|
|
|
|
if (result[strlen(result) - 1] != '/')
|
|
strlcat(result, "/", size);
|
|
|
|
if (lbs != NULL) {
|
|
if (!strstr(result, lbs->name_enc)) {
|
|
strlcat(result, lbs->name_enc, size);
|
|
strlcat(result, "/", size);
|
|
}
|
|
}
|
|
|
|
if (param)
|
|
strlcat(result, param, size);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int retrieve_remote_md5(LOGBOOK *lbs, char *host, MD5_INDEX **md5_index, char *error_str) {
|
|
int i, n, id, x, version, ssl;
|
|
char *text, *p, url[256], str[1000];
|
|
|
|
*md5_index = NULL;
|
|
|
|
combine_url(lbs, host, "?cmd=GetMD5", url, sizeof(url), &ssl);
|
|
|
|
text = NULL;
|
|
error_str[0] = 0;
|
|
if (retrieve_url(lbs, url, ssl, &text, TRUE) < 0) {
|
|
sprintf(error_str, loc("Cannot connect to remote server \"%s\""), host);
|
|
return -1;
|
|
}
|
|
p = strstr(text, "ELOG HTTP ");
|
|
if (!p) {
|
|
if (isparam("debug"))
|
|
rsputs(text);
|
|
strlcpy(error_str, loc("Remote server is not an ELOG server"), 256);
|
|
xfree(text);
|
|
return -1;
|
|
}
|
|
version = atoi(p + 10) * 100 + atoi(p + 12) * 10 + atoi(p + 14);
|
|
if (version < 250) {
|
|
if (isparam("debug"))
|
|
rsputs(text);
|
|
memset(str, 0, sizeof(str));
|
|
strncpy(str, p + 10, 5);
|
|
sprintf(error_str, loc("Incorrect remote ELOG server version %s"), str);
|
|
xfree(text);
|
|
return -1;
|
|
}
|
|
|
|
p = strstr(text, "Location: ");
|
|
if (p) {
|
|
if (isparam("debug"))
|
|
rsputs(text);
|
|
|
|
if (strstr(text, "?fail="))
|
|
sprintf(error_str, loc("Invalid user name \"%s\" or password for remote logbook"),
|
|
isparam("unm") ? getparam("unm") : "");
|
|
else {
|
|
strlcpy(str, p + 9, sizeof(str));
|
|
if (strchr(str, '?'))
|
|
*strchr(str, '?') = 0;
|
|
strlcpy(error_str, loc("URL is redirected to:"), 256);
|
|
strlcat(error_str, str, 256);
|
|
}
|
|
|
|
return -3;
|
|
}
|
|
|
|
p = strstr(text, "\r\n\r\n");
|
|
if (!p) {
|
|
if (isparam("debug"))
|
|
rsputs(text);
|
|
strlcpy(error_str, loc("Invalid HTTP header"), 256);
|
|
xfree(text);
|
|
return -1;
|
|
}
|
|
|
|
for (n = 0;; n++) {
|
|
p = strstr(p, "ID:");
|
|
if (!p)
|
|
break;
|
|
p += 3;
|
|
|
|
id = atoi(p);
|
|
|
|
p = strstr(p, "MD5:");
|
|
if (!p)
|
|
break;
|
|
p += 4;
|
|
|
|
if (n == 0)
|
|
*md5_index = (MD5_INDEX *) xmalloc(sizeof(MD5_INDEX));
|
|
else
|
|
*md5_index = (MD5_INDEX *) xrealloc(*md5_index, (n + 1) * sizeof(MD5_INDEX));
|
|
|
|
(*md5_index)[n].message_id = id;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
sscanf(p + 2 * i, "%02X", &x);
|
|
(*md5_index)[n].md5_digest[i] = (unsigned char) x;
|
|
}
|
|
}
|
|
|
|
if (n == 0) {
|
|
if (isparam("debug"))
|
|
rsputs(text);
|
|
if (strstr(text, "Login")) {
|
|
strlcpy(error_str, loc("No user name supplied to access remote logbook"), 256);
|
|
xfree(text);
|
|
return -2;
|
|
} else
|
|
strlcpy(error_str, loc("Error accessing remote logbook"), 256);
|
|
}
|
|
|
|
xfree(text);
|
|
|
|
return n;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int submit_message(LOGBOOK *lbs, char *host, int message_id, char *error_str) {
|
|
int size, i, n, status, fh, port, sock, content_length, header_length, remote_id, n_attr, ssl;
|
|
char str[256], file_name[MAX_PATH_LENGTH], attrib[MAX_N_ATTR][NAME_LENGTH];
|
|
char subdir[256], param[256], remote_host_name[256], url[256], upwd[80];
|
|
char date[80], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
|
|
attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], encoding[80], locked_by[256], draft[256], *buffer;
|
|
char *content, *p, boundary[80], request[10000], response[10000];
|
|
#ifdef HAVE_SSL
|
|
SSL *ssl_con = NULL;
|
|
#else
|
|
void *ssl_con = NULL;
|
|
#endif
|
|
|
|
text = (char *) xmalloc(TEXT_SIZE);
|
|
error_str[0] = 0;
|
|
|
|
/* get message with attribute list devied from database */
|
|
size = TEXT_SIZE;
|
|
status = el_retrieve(lbs, message_id, date, attr_list, attrib, -1, text, &size, in_reply_to, reply_to,
|
|
attachment, encoding, locked_by, draft);
|
|
|
|
if (status != EL_SUCCESS) {
|
|
xfree(text);
|
|
strcpy(error_str, loc("Cannot read entry from local logbook"));
|
|
return -1;
|
|
}
|
|
|
|
/* count attributes */
|
|
for (n_attr = 0; attr_list[n_attr][0]; n_attr++);
|
|
|
|
combine_url(lbs, host, "", url, sizeof(url), &ssl);
|
|
split_url(url, remote_host_name, &port, subdir, param);
|
|
|
|
sock = elog_connect(remote_host_name, port);
|
|
if (sock == -1) {
|
|
sprintf(error_str, loc("Cannot connect to host %s, port %d"), remote_host_name, port);
|
|
return -1;
|
|
}
|
|
#ifdef HAVE_SSL
|
|
if (ssl)
|
|
if (ssl_connect(sock, &ssl_con) < 0) {
|
|
strcpy(error_str, "Error initiating SSL connection\n");
|
|
SSL_free(ssl_con);
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
content_length = 100000;
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment[i][0]) {
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(attachment[i], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, attachment[i], sizeof(file_name));
|
|
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
lseek(fh, 0, SEEK_END);
|
|
size = TELL(fh);
|
|
close(fh);
|
|
} else
|
|
size = 0;
|
|
|
|
content_length += size;
|
|
}
|
|
|
|
content = (char *) xmalloc(content_length);
|
|
|
|
/* compose content */
|
|
sprintf(boundary, "---------------------------%04X%04X%04X", rand(), rand(), rand());
|
|
strcpy(content, boundary);
|
|
strcat(content, "\r\nContent-Disposition: form-data; name=\"cmd\"\r\n\r\nSubmit\r\n");
|
|
|
|
sprintf(content + strlen(content),
|
|
"%s\r\nContent-Disposition: form-data; name=\"mirror_id\"\r\n\r\n%d\r\n", boundary, message_id);
|
|
|
|
if (isparam("unm")) {
|
|
sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"unm\"\r\n\r\n%s\r\n",
|
|
boundary, getparam("unm"));
|
|
|
|
if (isparam("upwd"))
|
|
strlcpy(upwd, getparam("upwd"), sizeof(upwd));
|
|
else
|
|
get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL);
|
|
sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"upwd\"\r\n\r\n%s\r\n",
|
|
boundary, upwd);
|
|
}
|
|
|
|
if (in_reply_to[0])
|
|
sprintf(content + strlen(content),
|
|
"%s\r\nContent-Disposition: form-data; name=\"in_reply_to\"\r\n\r\n%s\r\n", boundary,
|
|
in_reply_to);
|
|
|
|
if (reply_to[0])
|
|
sprintf(content + strlen(content),
|
|
"%s\r\nContent-Disposition: form-data; name=\"reply_to\"\r\n\r\n%s\r\n", boundary, reply_to);
|
|
|
|
for (i = 0; i < n_attr; i++)
|
|
sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n",
|
|
boundary, attr_list[i], attrib[i]);
|
|
|
|
sprintf(content + strlen(content),
|
|
"%s\r\nContent-Disposition: form-data; name=\"entry_date\"\r\n\r\n%s\r\n", boundary, date);
|
|
|
|
sprintf(content + strlen(content),
|
|
"%s\r\nContent-Disposition: form-data; name=\"encoding\"\r\n\r\n%s\r\n", boundary, encoding);
|
|
|
|
sprintf(content + strlen(content),
|
|
"%s\r\nContent-Disposition: form-data; name=\"Text\"\r\n\r\n%s\r\n%s\r\n", boundary, text,
|
|
boundary);
|
|
|
|
content_length = strlen(content);
|
|
p = content + content_length;
|
|
|
|
/* read attachments */
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment[i][0]) {
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(attachment[i], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, attachment[i], sizeof(file_name));
|
|
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
lseek(fh, 0, SEEK_END);
|
|
size = TELL(fh);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buffer = (char *) xmalloc(size);
|
|
read(fh, buffer, size);
|
|
close(fh);
|
|
|
|
/* submit attachment */
|
|
sprintf(p, "Content-Disposition: form-data; name=\"attfile%d\"; filename=\"%s\"\r\n\r\n", i + 1,
|
|
attachment[i]);
|
|
|
|
content_length += strlen(p);
|
|
p += strlen(p);
|
|
|
|
memcpy(p, buffer, size);
|
|
p += size;
|
|
strcpy(p, boundary);
|
|
strcat(p, "\r\n");
|
|
|
|
content_length += size + strlen(p);
|
|
p += strlen(p);
|
|
|
|
xfree(buffer);
|
|
}
|
|
}
|
|
|
|
/* compose request */
|
|
strcpy(request, "POST ");
|
|
if (subdir[0]) {
|
|
if (subdir[0] != '/')
|
|
strcat(request, "/");
|
|
strcat(request, subdir);
|
|
if (request[strlen(request) - 1] != '/')
|
|
strcat(request, "/");
|
|
}
|
|
strcat(request, " HTTP/1.0\r\n");
|
|
|
|
sprintf(request + strlen(request), "Content-Type: multipart/form-data; boundary=%s\r\n", boundary);
|
|
sprintf(request + strlen(request), "Host: %s\r\n", host_name);
|
|
sprintf(request + strlen(request), "User-Agent: ELOGD\r\n");
|
|
sprintf(request + strlen(request), "Content-Length: %d\r\n", content_length);
|
|
|
|
if (isparam("wpwd"))
|
|
sprintf(request + strlen(request), "Cookie: wpwd=%s\r\n", getparam("wpwd"));
|
|
|
|
strcat(request, "\r\n");
|
|
|
|
header_length = strlen(request);
|
|
|
|
send_with_timeout(ssl_con, sock, request, header_length);
|
|
send_with_timeout(ssl_con, sock, content, content_length);
|
|
|
|
#ifdef HAVE_SSL
|
|
if (ssl)
|
|
/* receive response */
|
|
i = SSL_read(ssl_con, response, 10000);
|
|
|
|
else
|
|
#endif
|
|
/* receive response */
|
|
i = recv(sock, response, 10000, 0);
|
|
|
|
if (i < 0) {
|
|
closesocket(sock);
|
|
xfree(text);
|
|
strcpy(error_str, "Cannot receive response");
|
|
#ifdef HAVE_SSL
|
|
if (ssl)
|
|
SSL_free(ssl_con);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* discard remainder of response */
|
|
n = i;
|
|
while (i > 0) {
|
|
i = recv(sock, response + n, 10000, 0);
|
|
if (i > 0)
|
|
n += i;
|
|
}
|
|
response[n] = 0;
|
|
|
|
#ifdef HAVE_SSL
|
|
if (ssl) {
|
|
SSL_shutdown(ssl_con);
|
|
SSL_free(ssl_con);
|
|
}
|
|
#endif
|
|
|
|
closesocket(sock);
|
|
remote_id = -1;
|
|
|
|
/* check response status */
|
|
if (strstr(response, "302 Found")) {
|
|
if (strstr(response, "Location:")) {
|
|
if (strstr(response, "fail"))
|
|
sprintf(error_str, "Invalid user name or password\n");
|
|
|
|
strlcpy(str, strstr(response, "Location:") + 9, sizeof(str));
|
|
if (strchr(str, '\n'))
|
|
*strchr(str, '\n') = 0;
|
|
if (strchr(str, '?'))
|
|
*strchr(str, '?') = 0;
|
|
|
|
if (strrchr(str, '/'))
|
|
remote_id = atoi(strrchr(str, '/') + 1);
|
|
else
|
|
remote_id = atoi(str);
|
|
}
|
|
} else if (strstr(response, "Logbook Selection"))
|
|
sprintf(error_str, "No logbook specified\n");
|
|
else if (strstr(response, "enter password"))
|
|
sprintf(error_str, "Missing or invalid password\n");
|
|
else if (strstr(response, "form name=form1"))
|
|
sprintf(error_str, "Missing or invalid user name/password\n");
|
|
else if (strstr(response, "Error: Attribute")) {
|
|
strncpy(str, strstr(response, "Error: Attribute") + 20, sizeof(str)-1);
|
|
if (strchr(str, '<'))
|
|
*strchr(str, '<') = 0;
|
|
sprintf(error_str, "Missing required attribute \"%s\"\n", str);
|
|
} else
|
|
sprintf(error_str, "Error transmitting message\n");
|
|
|
|
if (error_str[0] && isparam("debug"))
|
|
rsputs(response);
|
|
|
|
xfree(text);
|
|
|
|
if (error_str[0])
|
|
return -1;
|
|
|
|
return remote_id;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int receive_message(LOGBOOK *lbs, char *url, int message_id, char *error_str, BOOL bnew) {
|
|
int i, status, size, n_attr, header_size, ssl;
|
|
char str[NAME_LENGTH], str2[NAME_LENGTH], *p, *p2, *message, date[80], attrib[MAX_N_ATTR][NAME_LENGTH],
|
|
in_reply_to[80], reply_to[MAX_REPLY_TO * 10], encoding[80], locked_by[256],
|
|
attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], attachment_all[64 * MAX_ATTACHMENTS];
|
|
|
|
error_str[0] = 0;
|
|
|
|
combine_url(lbs, url, "", str, sizeof(str), &ssl);
|
|
sprintf(str + strlen(str), "%d?cmd=%s", message_id, loc("Download"));
|
|
|
|
retrieve_url(lbs, str, ssl, &message, TRUE);
|
|
if (message == NULL) {
|
|
sprintf(error_str, loc("Cannot receive \"%s\""), str);
|
|
return -1;
|
|
}
|
|
p = strstr(message, "\r\n\r\n");
|
|
if (p == NULL) {
|
|
if (isparam("debug"))
|
|
rsputs(message);
|
|
xfree(message);
|
|
sprintf(error_str, loc("Cannot receive \"%s\""), str);
|
|
return -1;
|
|
}
|
|
p += 4;
|
|
|
|
/* check for correct ID */
|
|
if (atoi(p + 8) != message_id) {
|
|
if (isparam("debug"))
|
|
rsputs(message);
|
|
sprintf(error_str, loc("Received wrong entry id \"%d\""), atoi(p + 8));
|
|
xfree(message);
|
|
return -1;
|
|
}
|
|
|
|
/* decode entry */
|
|
el_decode(p, "Date: ", date, sizeof(date));
|
|
el_decode_intlist(p, "Reply to: ", reply_to, sizeof(reply_to));
|
|
el_decode_int(p, "In reply to: ", in_reply_to, sizeof(in_reply_to));
|
|
|
|
/* derive attribute names from message */
|
|
for (i = 0;; i++) {
|
|
el_enum_attr(p, i, attr_list[i], attrib[i]);
|
|
if (!attr_list[i][0])
|
|
break;
|
|
}
|
|
n_attr = i;
|
|
|
|
el_decode(p, "Attachment: ", attachment_all, sizeof(attachment_all));
|
|
el_decode(p, "Encoding: ", encoding, sizeof(encoding));
|
|
|
|
/* break apart attachements */
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
attachment[i][0] = 0;
|
|
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
if (i == 0)
|
|
p2 = strtok(attachment_all, ",");
|
|
else
|
|
p2 = strtok(NULL, ",");
|
|
|
|
if (p2 != NULL)
|
|
strcpy(attachment[i], p2);
|
|
else
|
|
break;
|
|
}
|
|
|
|
el_decode(p, "Locked by: ", locked_by, sizeof(locked_by));
|
|
if (locked_by[0]) {
|
|
xfree(message);
|
|
sprintf(error_str, loc("Entry #%d is locked on remote server"), message_id);
|
|
return -1;
|
|
}
|
|
|
|
p = strstr(message, "========================================\n");
|
|
|
|
/* check for \n -> \r conversion (e.g. zipping/unzipping) */
|
|
if (p == NULL)
|
|
p = strstr(message, "========================================\r");
|
|
|
|
if (p != NULL) {
|
|
p += 41;
|
|
|
|
/* remove last CR */
|
|
if (p[strlen(p) - 1] == '\n')
|
|
p[strlen(p) - 1] = 0;
|
|
|
|
status = el_submit(lbs, message_id, !bnew, date, attr_list, attrib, n_attr, p, in_reply_to, reply_to,
|
|
encoding, attachment, FALSE, "", NULL);
|
|
|
|
xfree(message);
|
|
|
|
if (status != message_id) {
|
|
strlcpy(error_str, loc("Cannot save remote entry locally"), 256);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
if (attachment[i][0]) {
|
|
|
|
combine_url(lbs, url, "", str, sizeof(str), &ssl);
|
|
strlcpy(str2, attachment[i], sizeof(str2));
|
|
str2[13] = '/';
|
|
strlcat(str, str2, sizeof(str));
|
|
|
|
size = retrieve_url(lbs, str, ssl, &message, TRUE);
|
|
p = strstr(message, "\r\n\r\n");
|
|
if (p == NULL) {
|
|
xfree(message);
|
|
sprintf(error_str, loc("Cannot receive \"%s\""), str);
|
|
return -1;
|
|
}
|
|
p += 4;
|
|
header_size = p - message;
|
|
|
|
el_submit_attachment(lbs, attachment[i], p, size - header_size, NULL);
|
|
xfree(message);
|
|
}
|
|
}
|
|
} else {
|
|
xfree(message);
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void submit_config(LOGBOOK *lbs, char *server, char *buffer, char *error_str) {
|
|
int i, n, port, sock, content_length, header_length, ssl;
|
|
char str[256], upwd[80];
|
|
char subdir[256], param[256], remote_host_name[256];
|
|
char *content, boundary[80], request[10000], response[10000];
|
|
#ifdef HAVE_SSL
|
|
SSL *ssl_con = NULL;
|
|
#else
|
|
void *ssl_con = NULL;
|
|
#endif
|
|
|
|
error_str[0] = 0;
|
|
|
|
combine_url(lbs, server, "", str, sizeof(str), &ssl);
|
|
split_url(str, remote_host_name, &port, subdir, param);
|
|
|
|
sock = elog_connect(remote_host_name, port);
|
|
if (sock == -1) {
|
|
sprintf(error_str, loc("Cannot connect to host %s, port %d"), remote_host_name, port);
|
|
return;
|
|
}
|
|
#ifdef HAVE_SSL
|
|
if (ssl)
|
|
if (ssl_connect(sock, &ssl_con) < 0) {
|
|
strlcpy(error_str, "Error initiating SSL connection\n", 256);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
content_length = 100000;
|
|
content = (char *) xmalloc(content_length);
|
|
|
|
/* compose content */
|
|
sprintf(boundary, "---------------------------%04X%04X%04X", rand(), rand(), rand());
|
|
strcpy(content, boundary);
|
|
strcat(content, "\r\nContent-Disposition: form-data; name=\"cmd\"\r\n\r\nSave\r\n");
|
|
|
|
if (isparam("unm")) {
|
|
sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"unm\"\r\n\r\n%s\r\n",
|
|
boundary, getparam("unm"));
|
|
|
|
if (isparam("upwd"))
|
|
strlcpy(upwd, getparam("upwd"), sizeof(upwd));
|
|
else
|
|
get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL);
|
|
sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"upwd\"\r\n\r\n%s\r\n",
|
|
boundary, upwd);
|
|
}
|
|
|
|
sprintf(content + strlen(content),
|
|
"%s\r\nContent-Disposition: form-data; name=\"Text\"\r\n\r\n%s\r\n%s\r\n", boundary, buffer,
|
|
boundary);
|
|
|
|
content_length = strlen(content);
|
|
|
|
/* compose request */
|
|
strcpy(request, "POST ");
|
|
if (subdir[0]) {
|
|
if (subdir[0] != '/')
|
|
strcat(request, "/");
|
|
strcat(request, subdir);
|
|
if (request[strlen(request) - 1] != '/')
|
|
strcat(request, "/");
|
|
}
|
|
strcat(request, " HTTP/1.0\r\n");
|
|
|
|
sprintf(request + strlen(request), "Content-Type: multipart/form-data; boundary=%s\r\n", boundary);
|
|
sprintf(request + strlen(request), "Host: %s\r\n", host_name);
|
|
sprintf(request + strlen(request), "User-Agent: ELOGD\r\n");
|
|
sprintf(request + strlen(request), "Content-Length: %d\r\n", content_length);
|
|
|
|
if (isparam("wpwd"))
|
|
sprintf(request + strlen(request), "Cookie: wpwd=%s\r\n", getparam("wpwd"));
|
|
|
|
strcat(request, "\r\n");
|
|
|
|
header_length = strlen(request);
|
|
|
|
send_with_timeout(ssl_con, sock, request, header_length);
|
|
send_with_timeout(ssl_con, sock, content, content_length);
|
|
|
|
|
|
#ifdef HAVE_SSL
|
|
if (ssl)
|
|
/* receive response */
|
|
i = SSL_read(ssl_con, response, 10000);
|
|
else
|
|
#endif
|
|
/* receive response */
|
|
i = recv(sock, response, 10000, 0);
|
|
|
|
if (i < 0) {
|
|
closesocket(sock);
|
|
strlcpy(error_str, "Cannot receive response", 256);
|
|
#ifdef HAVE_SSL
|
|
if (ssl)
|
|
SSL_free(ssl_con);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* discard remainder of response */
|
|
n = i;
|
|
while (i > 0) {
|
|
i = recv(sock, response + n, 10000, 0);
|
|
if (i > 0)
|
|
n += i;
|
|
}
|
|
response[n] = 0;
|
|
|
|
#ifdef HAVE_SSL
|
|
if (ssl) {
|
|
SSL_shutdown(ssl_con);
|
|
SSL_free(ssl_con);
|
|
}
|
|
#endif
|
|
|
|
closesocket(sock);
|
|
|
|
/* check response status */
|
|
if (strstr(response, "302 Found")) {
|
|
if (strstr(response, "Location:")) {
|
|
if (strstr(response, "fail"))
|
|
strlcpy(error_str, "Invalid usr name or password\n", 256);
|
|
}
|
|
} else if (strstr(response, "Logbook Selection"))
|
|
strlcpy(error_str, "No logbook specified\n", 256);
|
|
else if (strstr(response, "enter password"))
|
|
strlcpy(error_str, "Missing or invalid password\n", 256);
|
|
else if (strstr(response, "form name=form1"))
|
|
strlcpy(error_str, "Missing or invalid user name/password\n", 256);
|
|
else if (strstr(response, "Error: Attribute")) {
|
|
strncpy(str, strstr(response, "Error: Attribute") + 20, sizeof(str)-1);
|
|
if (strchr(str, '<'))
|
|
*strchr(str, '<') = 0;
|
|
sprintf(error_str, "Missing required attribute \"%s\"\n", str);
|
|
} else
|
|
strlcpy(error_str, "Error transmitting message\n", 256);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void receive_config(LOGBOOK *lbs, char *server, char *error_str) {
|
|
char str[256], pwd[256], *buffer, *p;
|
|
int status, version, ssl;
|
|
|
|
error_str[0] = 0;
|
|
|
|
do {
|
|
|
|
combine_url(lbs, server, "", str, sizeof(str), &ssl);
|
|
if (lbs == NULL)
|
|
strcat(str, "?cmd=GetConfig"); // request complete config file
|
|
else
|
|
strcat(str, "?cmd=Download"); // request config section of logbook
|
|
|
|
if (retrieve_url(lbs, str, ssl, &buffer, TRUE) < 0) {
|
|
*strchr(str, '?') = 0;
|
|
sprintf(error_str, "Cannot contact elogd server at http://%s", str);
|
|
return;
|
|
}
|
|
|
|
/* check version */
|
|
p = strstr(buffer, "ELOG HTTP ");
|
|
if (!p) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
puts(buffer);
|
|
sprintf(error_str, "Remote server is not an ELOG server");
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
version = atoi(p + 10) * 100 + atoi(p + 12) * 10 + atoi(p + 14);
|
|
if (version < 254) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
puts(buffer);
|
|
|
|
strlcpy(str, p + 10, 10);
|
|
if (strchr(str, '\r'))
|
|
*strchr(str, '\r') = 0;
|
|
|
|
sprintf(error_str, "Incorrect remote ELOG server version %s, must be 2.5.4 or later", str);
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
|
|
/* evaluate status */
|
|
p = strchr(buffer, ' ');
|
|
if (p == NULL) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
puts(buffer);
|
|
xfree(buffer);
|
|
*strchr(str, '?') = 0;
|
|
sprintf(error_str, "Received invalid response from elogd server at http%s://%s", ssl ? "s" : "",
|
|
str);
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
p++;
|
|
status = atoi(p);
|
|
if (status == 401) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
puts(buffer);
|
|
xfree(buffer);
|
|
eprintf("Please enter password to access remote elogd server: ");
|
|
fgets(pwd, sizeof(pwd), stdin);
|
|
while (pwd[strlen(pwd) - 1] == '\n' || pwd[strlen(pwd) - 1] == '\r')
|
|
pwd[strlen(pwd) - 1] = 0;
|
|
} else if (status != 200) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
puts(buffer);
|
|
xfree(buffer);
|
|
*strchr(str, '?') = 0;
|
|
sprintf(error_str, "Received invalid response from elogd server at http%s://%s", ssl ? "s" : "",
|
|
str);
|
|
return;
|
|
}
|
|
|
|
} while (status != 200);
|
|
|
|
p = strstr(buffer, "\r\n\r\n");
|
|
if (p == NULL) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
puts(buffer);
|
|
xfree(buffer);
|
|
sprintf(error_str, loc("Cannot receive \"%s\""), str);
|
|
return;
|
|
}
|
|
p += 4;
|
|
|
|
if (strstr(p, "[global]") == NULL) {
|
|
strlcpy(error_str, p, 256);
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
|
|
if (lbs == NULL) {
|
|
if (!save_config(p, str))
|
|
rsprintf("%s", str);
|
|
} else {
|
|
if (!save_admin_config(lbs->name, p, str))
|
|
rsprintf("%s", str);
|
|
}
|
|
|
|
xfree(buffer);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int adjust_config(char *url) {
|
|
int fh, i, length;
|
|
char *buf, *buf2, *p1, *p2;
|
|
char str[256];
|
|
|
|
fh = open(config_file, O_RDWR | O_BINARY, 0644);
|
|
if (fh < 0) {
|
|
sprintf(str, loc("Cannot open file <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
eputs(str);
|
|
return 0;
|
|
}
|
|
|
|
/* read previous contents */
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buf = (char *) xmalloc(2 * length + 1000);
|
|
read(fh, buf, length);
|
|
buf[length] = 0;
|
|
|
|
/* add mirror server */
|
|
p1 = stristr(buf, "Mirror server =");
|
|
if (p1 != NULL) {
|
|
p2 = strchr(p1, '\n');
|
|
if (p2 && *(p2 - 1) == '\r')
|
|
p2--;
|
|
} else {
|
|
p1 = strstr(buf, "[global]");
|
|
if (p1 == NULL) {
|
|
eputs("Cannot find [global] section in config file.");
|
|
return 0;
|
|
}
|
|
p1 = strchr(p1, '\n');
|
|
while (*p1 == '\n' || *p1 == '\r')
|
|
p1++;
|
|
|
|
p2 = p1;
|
|
}
|
|
|
|
/* save tail */
|
|
buf2 = NULL;
|
|
if (p2)
|
|
buf2 = xstrdup(p2);
|
|
|
|
sprintf(p1, "Mirror server = %s\r\n", url);
|
|
strlcat(p1, buf2, length + 1000);
|
|
xfree(buf2);
|
|
eprintf("Option \"Mirror server = %s\" added to config file.\n", url);
|
|
|
|
/* outcomment "URL = xxx" */
|
|
p1 = strstr(buf, "URL =");
|
|
if (p1 != NULL) {
|
|
|
|
/* save tail */
|
|
buf2 = xstrdup(p1);
|
|
|
|
/* add comment */
|
|
sprintf(p1, "; Following line has been outcommented after cloning\r\n");
|
|
strlcat(p1, "; ", length + 1000);
|
|
strlcat(p1, buf2, length + 1000);
|
|
xfree(buf2);
|
|
|
|
eputs("Option \"URL = xxx\" has been outcommented from config file.");
|
|
}
|
|
|
|
adjust_crlf(buf, 2 * length + 1000);
|
|
|
|
lseek(fh, 0, SEEK_SET);
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(str, loc("Cannot write to <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
eputs(str);
|
|
close(fh);
|
|
xfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
xfree(buf);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void receive_pwdfile(LOGBOOK *lbs, char *server, char *error_str) {
|
|
char str[256], url[256], pwd[256], *buffer, *buf, *p;
|
|
int i, status, version, fh, ssl;
|
|
|
|
error_str[0] = 0;
|
|
|
|
do {
|
|
|
|
combine_url(lbs, server, "", url, sizeof(url), &ssl);
|
|
strlcpy(str, url, sizeof(str));
|
|
strcat(str, "?cmd=GetPwdFile"); // request password file
|
|
|
|
if (retrieve_url(lbs, str, ssl, &buffer, TRUE) < 0) {
|
|
*strchr(str, '?') = 0;
|
|
sprintf(error_str, "Cannot contact elogd server at http://%s", str);
|
|
return;
|
|
}
|
|
|
|
/* check version */
|
|
p = strstr(buffer, "ELOG HTTP ");
|
|
if (!p) {
|
|
sprintf(error_str, "Remote server is not an ELOG server");
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
version = atoi(p + 10) * 100 + atoi(p + 12) * 10 + atoi(p + 14);
|
|
if (version < 254) {
|
|
strlcpy(str, p + 10, 10);
|
|
if (strchr(str, '\r'))
|
|
*strchr(str, '\r') = 0;
|
|
|
|
sprintf(error_str, "Incorrect remote ELOG server version %s, must be 2.5.4 or later", str);
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
|
|
/* evaluate status */
|
|
p = strchr(buffer, ' ');
|
|
if (p == NULL) {
|
|
xfree(buffer);
|
|
*strchr(str, '?') = 0;
|
|
sprintf(error_str, "Received invalid response from elogd server at http://%s", str);
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
p++;
|
|
status = atoi(p);
|
|
if (status != 200 && status != 302 && status != 404) {
|
|
xfree(buffer);
|
|
*strchr(str, '?') = 0;
|
|
sprintf(error_str, "Received invalid response from elogd server at http://%s", str);
|
|
return;
|
|
}
|
|
|
|
p = strstr(buffer, "\r\n\r\n");
|
|
if (p == NULL) {
|
|
xfree(buffer);
|
|
sprintf(error_str, loc("Cannot receive \"%s\""), str);
|
|
return;
|
|
}
|
|
p += 4;
|
|
|
|
/* check for logbook access */
|
|
if (strstr(p, loc("Please login")) || strstr(p, "GetPwdFile") || status == 302) {
|
|
|
|
if (strstr(buffer, "?fail="))
|
|
eprintf("\nInvalid username or password.");
|
|
|
|
if (strstr(p, loc("Please login")) == NULL && strstr(p, "GetPwdFile") && isparam("unm"))
|
|
eprintf("\nUser \"%s\" has no admin rights on remote server.", getparam("unm"));
|
|
|
|
/* ask for username and password */
|
|
eprintf("\nPlease enter admin username to access\n%s: ", url);
|
|
fgets(str, sizeof(str), stdin);
|
|
while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n')
|
|
str[strlen(str) - 1] = 0;
|
|
setparam("unm", str);
|
|
|
|
eprintf("\nPlease enter admin password to access\n%s: ", url);
|
|
read_password(str, sizeof(str));
|
|
eprintf("\n");
|
|
while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n')
|
|
str[strlen(str) - 1] = 0;
|
|
do_crypt(str, pwd, sizeof(pwd));
|
|
setparam("upwd", pwd);
|
|
status = 0;
|
|
}
|
|
|
|
} while (status != 200);
|
|
|
|
get_password_file(lbs, str, sizeof(str));
|
|
fh = open(str, O_CREAT | O_RDWR | O_BINARY, 0644);
|
|
if (fh < 0) {
|
|
sprintf(error_str, loc("Cannot open file <b>%s</b>"), str);
|
|
strcat(error_str, ": ");
|
|
strcat(error_str, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
buf = (char *) xmalloc(2 * strlen(p));
|
|
strlcpy(buf, p, 2 * strlen(p));
|
|
adjust_crlf(buf, 2 * strlen(p));
|
|
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(error_str, loc("Cannot write to <b>%s</b>"), str);
|
|
strcat(error_str, ": ");
|
|
strcat(error_str, strerror(errno));
|
|
close(fh);
|
|
xfree(buf);
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
|
|
xfree(buf);
|
|
xfree(buffer);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int save_md5(LOGBOOK *lbs, char *server, MD5_INDEX *md5_index, int n) {
|
|
char str[256], url[256], file_name[256];
|
|
int i, j;
|
|
FILE *f;
|
|
|
|
combine_url(lbs, server, "", url, sizeof(url), NULL);
|
|
url_decode(url);
|
|
if (strstr(url, "http://"))
|
|
strlcpy(str, url + 7, sizeof(str));
|
|
else if (strstr(url, "https://"))
|
|
strlcpy(str, url + 8, sizeof(str));
|
|
else
|
|
strlcpy(str, url, sizeof(str));
|
|
|
|
for (i = 0; i < (int) strlen(str); i++)
|
|
if (strchr(":/\\ ", str[i]))
|
|
str[i] = '_';
|
|
|
|
while (str[strlen(str) - 1] == '_')
|
|
str[strlen(str) - 1] = 0;
|
|
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
strlcat(file_name, ".md5", sizeof(file_name));
|
|
|
|
f = fopen(file_name, "wt");
|
|
if (f == NULL)
|
|
return -1;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
fprintf(f, "ID%d: ", md5_index[i].message_id);
|
|
for (j = 0; j < 16; j++)
|
|
fprintf(f, "%02X", md5_index[i].md5_digest[j]);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
fclose(f);
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int load_md5(LOGBOOK *lbs, char *server, MD5_INDEX **md5_index) {
|
|
char str[256], url[256], file_name[256], *p;
|
|
int i, j, x;
|
|
FILE *f;
|
|
|
|
*md5_index = NULL;
|
|
|
|
combine_url(lbs, server, "", url, sizeof(url), NULL);
|
|
url_decode(url);
|
|
if (strstr(url, "http://"))
|
|
strlcpy(str, url + 7, sizeof(str));
|
|
else if (strstr(url, "https://"))
|
|
strlcpy(str, url + 8, sizeof(str));
|
|
else
|
|
strlcpy(str, url, sizeof(str));
|
|
|
|
for (i = 0; i < (int) strlen(str); i++)
|
|
if (strchr(":/\\ ", str[i]))
|
|
str[i] = '_';
|
|
|
|
while (str[strlen(str) - 1] == '_')
|
|
str[strlen(str) - 1] = 0;
|
|
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
strlcat(file_name, ".md5", sizeof(file_name));
|
|
|
|
f = fopen(file_name, "rt");
|
|
if (f == NULL)
|
|
return 0;
|
|
|
|
for (i = 0; !feof(f); i++) {
|
|
str[0] = 0;
|
|
fgets(str, sizeof(str), f);
|
|
if (!str[0])
|
|
break;
|
|
|
|
if (i == 0)
|
|
*md5_index = (MD5_INDEX *) xcalloc(sizeof(MD5_INDEX), 1);
|
|
else
|
|
*md5_index = (MD5_INDEX *) xrealloc(*md5_index, sizeof(MD5_INDEX) * (i + 1));
|
|
|
|
p = str + 2;
|
|
|
|
(*md5_index)[i].message_id = atoi(p);
|
|
while (*p && *p != ' ')
|
|
p++;
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
|
|
for (j = 0; j < 16; j++) {
|
|
sscanf(p + j * 2, "%02X", &x);
|
|
(*md5_index)[i].md5_digest[j] = (unsigned char) x;
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
return i;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL equal_md5(unsigned char m1[16], unsigned char m2[16]) {
|
|
int i;
|
|
for (i = 0; i < 16; i++)
|
|
if (m1[i] != m2[i])
|
|
break;
|
|
|
|
return i == 16;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
#define SYNC_HTML 1
|
|
#define SYNC_CRON 2
|
|
#define SYNC_CLONE 3
|
|
|
|
void mprint(LOGBOOK *lbs, int mode, const char *str) {
|
|
char line[3000];
|
|
|
|
if (mode == SYNC_HTML)
|
|
rsprintf("%s\n", str);
|
|
else if (mode == SYNC_CRON) {
|
|
if (_logging_level > 1) {
|
|
sprintf(line, "MIRROR: %s", str);
|
|
write_logfile(lbs, line);
|
|
}
|
|
} else
|
|
eputs(str);
|
|
}
|
|
|
|
void synchronize_logbook(LOGBOOK *lbs, int mode, BOOL sync_all) {
|
|
int index, i, j, i_msg, i_remote, i_cache, n_remote, n_cache, nserver, remote_id, exist_remote,
|
|
exist_cache, message_id, max_id, ssl;
|
|
int all_identical, n_delete;
|
|
char str[2000], url[256], loc_ref[1000], rem_ref[3000], pwd[256], locked_by[256], draft[256];
|
|
MD5_INDEX *md5_remote, *md5_cache;
|
|
char list[MAX_N_LIST][NAME_LENGTH], error_str[256], *buffer;
|
|
unsigned char digest[16];
|
|
|
|
if (!getcfg(lbs->name, "Mirror server", str, sizeof(str))) {
|
|
show_error(loc("No mirror server defined in configuration file"));
|
|
return;
|
|
}
|
|
|
|
nserver = strbreak(str, list, MAX_N_LIST, ",", FALSE);
|
|
|
|
for (index = 0; index < nserver; index++) {
|
|
|
|
if (mode == SYNC_HTML) {
|
|
rsprintf("<table width=\"100%%\" cellpadding=\"1\" cellspacing=\"0\"");
|
|
|
|
if (getcfg_topgroup())
|
|
sprintf(loc_ref, "<a href=\"../%s/\">%s</a>", lbs->name_enc, lbs->name);
|
|
else if (sync_all)
|
|
sprintf(loc_ref, "<a href=\"%s/\">%s</a>", lbs->name_enc, lbs->name);
|
|
else
|
|
sprintf(loc_ref, "<a href=\".\">%s</a>", lbs->name);
|
|
|
|
sprintf(str, loc("Synchronizing logbook %s with server \"%s\""), loc_ref, list[index]);
|
|
rsprintf("<tr><td class=\"title1\">%s</td></tr>\n", str);
|
|
rsprintf("</table><p>\n");
|
|
|
|
rsprintf("<pre>");
|
|
} else if (mode == SYNC_CLONE) {
|
|
if (list[index][strlen(list[index]) - 1] != '/')
|
|
eprintf("\nRetrieving entries from \"%s/%s\"...\n", list[index], lbs->name);
|
|
else
|
|
eprintf("\nRetrieving entries from \"%s%s\"...\n", list[index], lbs->name);
|
|
}
|
|
|
|
/* send partial return buffer */
|
|
flush_return_buffer();
|
|
|
|
do {
|
|
|
|
n_remote = retrieve_remote_md5(lbs, list[index], &md5_remote, error_str);
|
|
if (n_remote <= 0) {
|
|
|
|
if ((n_remote == -2 || n_remote == -3) && mode == SYNC_CLONE) {
|
|
|
|
if (n_remote == -3)
|
|
eprintf("\nInvalid username or password.");
|
|
|
|
combine_url(lbs, list[index], "", url, sizeof(url), NULL);
|
|
/* ask for username and password */
|
|
eprintf("\nPlease enter username to access\n%s: ", url);
|
|
fgets(str, sizeof(str), stdin);
|
|
while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n')
|
|
str[strlen(str) - 1] = 0;
|
|
setparam("unm", str);
|
|
|
|
eprintf("\nPlease enter password to access\n%s: ", url);
|
|
read_password(str, sizeof(str));
|
|
eprintf("\n");
|
|
while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n')
|
|
str[strlen(str) - 1] = 0;
|
|
do_crypt(str, pwd, sizeof(pwd));
|
|
setparam("upwd", pwd);
|
|
|
|
} else {
|
|
|
|
mprint(lbs, mode, error_str);
|
|
|
|
if (md5_remote)
|
|
xfree(md5_remote);
|
|
|
|
if (mode == SYNC_HTML)
|
|
rsprintf("</pre>\n");
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
} while (n_remote <= 0);
|
|
|
|
if (n_remote <= 0)
|
|
continue;
|
|
|
|
/* load local copy of remote MD5s from file */
|
|
n_cache = load_md5(lbs, list[index], &md5_cache);
|
|
|
|
all_identical = TRUE;
|
|
|
|
/*---- check for configuration file ----*/
|
|
|
|
if (getcfg(lbs->name, "Mirror config", str, sizeof(str)) && atoi(str) == 1 && md5_cache && mode
|
|
!=
|
|
SYNC_CLONE) {
|
|
|
|
load_config_section(lbs->name, &buffer, error_str);
|
|
if (error_str[0])
|
|
mprint(lbs, mode, error_str);
|
|
else {
|
|
remove_crlf(buffer);
|
|
MD5_checksum(buffer, strlen(buffer), digest);
|
|
}
|
|
|
|
/* compare MD5s */
|
|
if (get_verbose() >= VERBOSE_INFO) {
|
|
eprintf("CONFIG : ");
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", digest[j]);
|
|
eprintf("\nCache : ");
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", md5_cache[0].md5_digest[j]);
|
|
eprintf("\nRemote : ");
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", md5_remote[0].md5_digest[j]);
|
|
eprintf("\n\n");
|
|
}
|
|
|
|
if (n_remote > 0) {
|
|
/* if config has been changed on this server, but not remotely, send it */
|
|
if (!equal_md5(md5_cache[0].md5_digest, digest) && equal_md5(md5_cache[0].md5_digest,
|
|
md5_remote[0].md5_digest)) {
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (_logging_level > 1)
|
|
write_logfile(lbs, "MIRROR send config");
|
|
|
|
/* submit configuration section */
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
submit_config(lbs, list[index], buffer, error_str);
|
|
|
|
if (error_str[0])
|
|
mprint(lbs, mode, error_str);
|
|
else
|
|
mprint(lbs, mode, "Local config submitted");
|
|
md5_cache[0].message_id = -1;
|
|
} else
|
|
mprint(lbs, mode, "Local config should be submitted");
|
|
|
|
} else
|
|
/* if config has been changed remotely, but not on this server, receive it */
|
|
if (!equal_md5(md5_cache[0].md5_digest, md5_remote[0].md5_digest)
|
|
&& equal_md5(md5_cache[0].md5_digest, digest)) {
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (_logging_level > 1)
|
|
write_logfile(lbs, "MIRROR receive config");
|
|
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
receive_config(lbs, list[index], error_str);
|
|
|
|
if (error_str[0])
|
|
mprint(lbs, mode, error_str);
|
|
else
|
|
mprint(lbs, mode, "Remote config received");
|
|
|
|
md5_cache[0].message_id = -1;
|
|
} else
|
|
mprint(lbs, mode, loc("Remote config should be received"));
|
|
|
|
} else
|
|
/* if config has been changed remotely and on this server, show conflict */
|
|
if (!equal_md5(md5_cache[0].md5_digest, md5_remote[0].md5_digest)
|
|
&& !equal_md5(md5_cache[0].md5_digest, digest)
|
|
&& !equal_md5(md5_remote[0].md5_digest, digest)) {
|
|
|
|
if (_logging_level > 1)
|
|
write_logfile(lbs, "MIRROR config conflict");
|
|
|
|
sprintf(str, "%s. ", loc("Configuration has been changed locally and remotely"));
|
|
strcat(str, loc("Please merge manually to resolve conflict"));
|
|
strcat(str, ".");
|
|
|
|
mprint(lbs, mode, str);
|
|
|
|
} else {
|
|
/* configs are identical */
|
|
md5_cache[0].message_id = -1;
|
|
}
|
|
} else { /* n_remote == 0 */
|
|
|
|
sprintf(str, loc("Logbook \"%s\" does not exist on remote server"), lbs->name);
|
|
|
|
mprint(lbs, mode, str);
|
|
continue;
|
|
|
|
}
|
|
|
|
flush_return_buffer();
|
|
|
|
if (buffer)
|
|
xfree(buffer);
|
|
}
|
|
|
|
/*---- loop through logbook entries ----*/
|
|
|
|
n_delete = 0;
|
|
|
|
for (i_msg = 0; i_msg < *lbs->n_el_index; i_msg++) {
|
|
|
|
message_id = lbs->el_index[i_msg].message_id;
|
|
|
|
/* check if message is locked */
|
|
el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, locked_by, draft);
|
|
if (locked_by[0]) {
|
|
sprintf(str, "ID%d:\t%s", message_id,
|
|
loc("Entry is locked on local server and therefore skipped"));
|
|
mprint(lbs, mode, str);
|
|
all_identical = FALSE;
|
|
continue;
|
|
}
|
|
|
|
/* look for message id in MD5s */
|
|
for (i_remote = 0; i_remote < n_remote; i_remote++)
|
|
if (md5_remote[i_remote].message_id == message_id)
|
|
break;
|
|
exist_remote = i_remote < n_remote;
|
|
|
|
for (i_cache = 0; i_cache < n_cache; i_cache++)
|
|
if (md5_cache[i_cache].message_id == message_id)
|
|
break;
|
|
exist_cache = i_cache < n_cache;
|
|
|
|
/* if message exists in both lists, compare MD5s */
|
|
if (exist_remote && exist_cache) {
|
|
|
|
/* compare MD5s */
|
|
if (get_verbose() >= VERBOSE_INFO) {
|
|
eprintf("ID%-5d: ", message_id);
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", lbs->el_index[i_msg].md5_digest[j]);
|
|
eprintf("\nCache : ");
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", md5_cache[i_cache].md5_digest[j]);
|
|
eprintf("\nRemote : ");
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", md5_remote[i_remote].md5_digest[j]);
|
|
eprintf("\n\n");
|
|
}
|
|
|
|
/* if message has been changed on this server, but not remotely, send it */
|
|
if (!equal_md5(md5_cache[i_cache].md5_digest, lbs->el_index[i_msg].md5_digest)
|
|
&& equal_md5(md5_cache[i_cache].md5_digest, md5_remote[i_remote].md5_digest)) {
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (_logging_level > 1) {
|
|
sprintf(str, "MIRROR send entry #%d", message_id);
|
|
write_logfile(lbs, str);
|
|
}
|
|
|
|
/* submit local message */
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
submit_message(lbs, list[index], message_id, error_str);
|
|
|
|
/* not that submit_message() may have changed attr_list !!! */
|
|
|
|
if (error_str[0])
|
|
sprintf(str, "%s: %s", loc("Error sending local entry"), error_str);
|
|
else
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Local entry submitted"));
|
|
mprint(lbs, mode, str);
|
|
|
|
md5_cache[i_cache].message_id = -1;
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Local entry should be submitted"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
|
|
} else
|
|
/* if message has been changed remotely, but not on this server, receive it */
|
|
if (!equal_md5(md5_cache[i_cache].md5_digest, md5_remote[i_remote].md5_digest)
|
|
&& equal_md5(md5_cache[i_cache].md5_digest, lbs->el_index[i_msg].md5_digest)) {
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (mode == SYNC_CLONE) {
|
|
eprintf("ID%d:\t", message_id);
|
|
} else if (mode == SYNC_HTML) {
|
|
if (getcfg_topgroup())
|
|
rsprintf("<a href=\"../%s/%d\">ID%d:</a>\t", lbs->name_enc, message_id, message_id);
|
|
else if (sync_all)
|
|
rsprintf("<a href=\"%s/%d\">ID%d:</a>\t", lbs->name_enc, message_id, message_id);
|
|
else
|
|
rsprintf("<a href=\"%d\">ID%d:</a>\t", message_id, message_id);
|
|
|
|
flush_return_buffer();
|
|
}
|
|
|
|
if (_logging_level > 1) {
|
|
sprintf(str, "MIRROR receive entry #%d", message_id);
|
|
write_logfile(lbs, str);
|
|
}
|
|
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
receive_message(lbs, list[index], message_id, error_str, FALSE);
|
|
|
|
if (error_str[0]) {
|
|
sprintf(str, "%s: %s", loc("Error receiving message"), error_str);
|
|
mprint(lbs, mode, str);
|
|
} else if (mode == SYNC_HTML) {
|
|
|
|
rsprintf("%s\n", loc("Remote entry received"));
|
|
|
|
} else if (mode == SYNC_CLONE) {
|
|
eprintf("%s\n", loc("Remote entry received"));
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry received"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
|
|
if (!error_str[0])
|
|
md5_cache[i_cache].message_id = -1;
|
|
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry should be received"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
|
|
} else
|
|
/* if message has been changed remotely and on this server, show conflict */
|
|
if (!equal_md5(md5_cache[i_cache].md5_digest, md5_remote[i_remote].md5_digest)
|
|
&& !equal_md5(md5_cache[i_cache].md5_digest, lbs->el_index[i_msg].md5_digest)
|
|
&& !equal_md5(md5_remote[i_remote].md5_digest, lbs->el_index[i_msg].md5_digest)) {
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (mode == SYNC_CLONE) {
|
|
|
|
eprintf("Warning: Entry #%d has been changed locally and remotely, will not be retrieved\n",
|
|
message_id);
|
|
|
|
} else {
|
|
|
|
if (_logging_level > 1) {
|
|
sprintf(str, "MIRROR conflict entry #%d", message_id);
|
|
write_logfile(lbs, str);
|
|
}
|
|
|
|
combine_url(lbs, list[index], "", str, sizeof(str), NULL);
|
|
|
|
if (getcfg_topgroup())
|
|
sprintf(loc_ref, "<a href=\"../%s/%d\">%s</a>", lbs->name_enc, message_id, loc("local"));
|
|
else if (sync_all)
|
|
sprintf(loc_ref, "<a href=\"%s/%d\">%s</a>", lbs->name_enc, message_id, loc("local"));
|
|
else
|
|
sprintf(loc_ref, "<a href=\"%d\">%s</a>", message_id, loc("local"));
|
|
|
|
sprintf(rem_ref, "<a href=\"http://%s%d\">%s</a>", str, message_id, loc("remote"));
|
|
|
|
sprintf(str, "ID%d:\t%s. ", message_id, loc("Entry has been changed locally and remotely"));
|
|
sprintf(str + strlen(str), loc("Please delete %s or %s entry to resolve conflict"),
|
|
loc_ref, rem_ref);
|
|
strcat(str, ".");
|
|
mprint(lbs, mode, str);
|
|
}
|
|
|
|
} else {
|
|
|
|
/* messages are identical */
|
|
md5_cache[i_cache].message_id = -1;
|
|
}
|
|
}
|
|
|
|
if (exist_cache && !exist_remote) {
|
|
|
|
/* if message has been changed locally, send it */
|
|
if (!equal_md5(md5_cache[i_cache].md5_digest, lbs->el_index[i_msg].md5_digest)) {
|
|
|
|
/* compare MD5s */
|
|
if (get_verbose() >= VERBOSE_INFO) {
|
|
eprintf("ID%-5d: ", message_id);
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", lbs->el_index[i_msg].md5_digest[j]);
|
|
eprintf("\nCache : ");
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", md5_cache[i_cache].md5_digest[j]);
|
|
eprintf("\nRemote : none");
|
|
eprintf("\n\n");
|
|
}
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (_logging_level > 1) {
|
|
sprintf(str, "MIRROR send entry #%d", message_id);
|
|
write_logfile(lbs, str);
|
|
}
|
|
|
|
/* submit local message */
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
submit_message(lbs, list[index], message_id, error_str);
|
|
|
|
/* not that submit_message() may have changed attr_list !!! */
|
|
|
|
if (error_str[0])
|
|
sprintf(str, "%s: %s", loc("Error sending local message"), error_str);
|
|
else
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Local entry submitted"));
|
|
mprint(lbs, mode, str);
|
|
|
|
md5_cache[i_cache].message_id = -1;
|
|
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Local entry should be submitted"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
|
|
} else {
|
|
|
|
/* if message exists only in cache, but not remotely,
|
|
it must have been deleted remotely, so remove it locally */
|
|
|
|
if (!isparam("confirm") && mode == SYNC_HTML) {
|
|
|
|
combine_url(lbs, list[index], "", str, sizeof(str), NULL);
|
|
|
|
if (getcfg_topgroup())
|
|
sprintf(loc_ref, "<a href=\"../%s/%d\">%s</a>", lbs->name_enc, message_id, loc("local"));
|
|
else if (sync_all)
|
|
sprintf(loc_ref, "<a href=\"%s/%d\">%s</a>", lbs->name_enc, message_id,
|
|
loc("Local entry"));
|
|
else
|
|
sprintf(loc_ref, "<a href=\"%d\">%s</a>", message_id, loc("Local entry"));
|
|
|
|
sprintf(str, loc("%s should be deleted"), loc_ref);
|
|
rsprintf("ID%d:\t%s\n", message_id, str);
|
|
n_delete++;
|
|
|
|
}
|
|
if (!isparam("confirm") && mode == SYNC_CLONE) {
|
|
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Entry should be deleted locally"));
|
|
mprint(lbs, mode, str);
|
|
|
|
} else {
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (mode == SYNC_CLONE) {
|
|
|
|
el_delete_message(lbs, message_id, TRUE, NULL, TRUE, TRUE);
|
|
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Entry deleted locally"));
|
|
mprint(lbs, mode, str);
|
|
|
|
/* message got deleted from local message list, so redo current index */
|
|
i_msg--;
|
|
|
|
} else {
|
|
|
|
if (_logging_level > 1) {
|
|
sprintf(str, "MIRROR delete local entry #%d", message_id);
|
|
write_logfile(lbs, str);
|
|
}
|
|
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
el_delete_message(lbs, message_id, TRUE, NULL, TRUE, TRUE);
|
|
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Entry deleted locally"));
|
|
mprint(lbs, mode, str);
|
|
|
|
/* message got deleted from local message list, so redo current index */
|
|
i_msg--;
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Entry should be deleted locally"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
}
|
|
|
|
/* mark message non-conflicting */
|
|
md5_cache[i_cache].message_id = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if message does not exist in cache and remotely,
|
|
it must be new, so send it */
|
|
if (!exist_cache && !exist_remote) {
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (_logging_level > 1) {
|
|
sprintf(str, "MIRROR send entry #%d", message_id);
|
|
write_logfile(lbs, str);
|
|
}
|
|
remote_id = 0;
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
remote_id = submit_message(lbs, list[index], message_id, error_str);
|
|
|
|
if (error_str[0])
|
|
sprintf(str, "%s: %s", loc("Error sending local entry"), error_str);
|
|
else if (remote_id != message_id)
|
|
sprintf(str, "Error: Submitting entry #%d resulted in remote entry #%d\n", message_id,
|
|
remote_id);
|
|
else
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Local entry submitted"));
|
|
mprint(lbs, mode, str);
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Local entry should be submitted"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
}
|
|
|
|
/* if message does not exist in cache but remotely,
|
|
messages were added on both sides, so resubmit local one and retrieve remote one
|
|
if messages are different */
|
|
if (!exist_cache && exist_remote && !equal_md5(md5_remote[i_remote].md5_digest,
|
|
lbs->el_index[i_msg].md5_digest)) {
|
|
|
|
/* compare MD5s */
|
|
if (get_verbose() >= VERBOSE_INFO) {
|
|
eprintf("ID%-5d: ", message_id);
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", lbs->el_index[i_msg].md5_digest[j]);
|
|
eprintf("\nCache : none");
|
|
eprintf("\nRemote : ");
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", md5_remote[i_remote].md5_digest[j]);
|
|
eprintf("\n\n");
|
|
}
|
|
|
|
all_identical = FALSE;
|
|
|
|
/* find max id both locally and remotely */
|
|
max_id = 1;
|
|
for (i = 0; i < *lbs->n_el_index; i++)
|
|
if (lbs->el_index[i].message_id > max_id)
|
|
max_id = lbs->el_index[i].message_id;
|
|
|
|
for (i = 0; i < n_remote; i++)
|
|
if (md5_remote[i].message_id > max_id)
|
|
max_id = md5_remote[i].message_id;
|
|
|
|
if (_logging_level > 1) {
|
|
sprintf(str, "MIRROR change entry #%d to #%d", message_id, max_id + 1);
|
|
write_logfile(lbs, str);
|
|
}
|
|
|
|
/* rearrange local message not to conflict with remote message */
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
el_move_message(lbs, message_id, max_id + 1);
|
|
|
|
sprintf(str, "ID%d:\t", message_id);
|
|
sprintf(str + strlen(str), loc("Changed local entry ID to %d"), max_id + 1);
|
|
mprint(lbs, mode, str);
|
|
|
|
/* current message has been changed, so start over */
|
|
i_msg--;
|
|
} else {
|
|
sprintf(str, "ID%d:\t", message_id);
|
|
sprintf(str + strlen(str), loc("Local entry ID should be changed to %d"), max_id + 1);
|
|
mprint(lbs, mode, str);
|
|
}
|
|
}
|
|
|
|
flush_return_buffer();
|
|
}
|
|
|
|
/* go through remote message which do not exist locally */
|
|
for (i_remote = 0; i_remote < n_remote; i_remote++)
|
|
if (md5_remote[i_remote].message_id) {
|
|
|
|
message_id = md5_remote[i_remote].message_id;
|
|
|
|
for (i_msg = 0; i_msg < *lbs->n_el_index; i_msg++)
|
|
if (message_id == lbs->el_index[i_msg].message_id)
|
|
break;
|
|
|
|
if (i_msg == *lbs->n_el_index) {
|
|
|
|
for (i_cache = 0; i_cache < n_cache; i_cache++)
|
|
if (md5_cache[i_cache].message_id == message_id)
|
|
break;
|
|
exist_cache = i_cache < n_cache;
|
|
|
|
if (!exist_cache) {
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (mode == SYNC_HTML) {
|
|
if (getcfg_topgroup())
|
|
rsprintf("<a href=\"../%s/%d\">ID%d:</a>\t", lbs->name_enc, message_id, message_id);
|
|
else if (sync_all)
|
|
rsprintf("<a href=\"%s/%d\">ID%d:</a>\t", lbs->name_enc, message_id, message_id);
|
|
else
|
|
rsprintf("<a href=\"%d\">ID%d:</a>\t", message_id, message_id);
|
|
|
|
flush_return_buffer();
|
|
} else if (mode == SYNC_CLONE) {
|
|
eprintf("ID%d:\t", message_id);
|
|
}
|
|
|
|
/* if message does not exist locally and in cache, it is new, so retrieve it */
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
receive_message(lbs, list[index], message_id, error_str, TRUE);
|
|
|
|
if (error_str[0]) {
|
|
sprintf(str, "Error receiving message: %s", error_str);
|
|
mprint(lbs, mode, str);
|
|
} else if (mode == SYNC_HTML) {
|
|
rsprintf("%s\n", loc("Remote entry received"));
|
|
} else if (mode == SYNC_CLONE) {
|
|
eprintf("%s\n", loc("Remote entry received"));
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry received"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry should be received"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!equal_md5(md5_cache[i_cache].md5_digest, md5_remote[i_remote].md5_digest)) {
|
|
|
|
/* compare MD5s */
|
|
if (get_verbose() >= VERBOSE_INFO) {
|
|
eprintf("ID-%5: none", message_id);
|
|
eprintf("\nCache : ");
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", md5_cache[i_cache].md5_digest[j]);
|
|
eprintf("\nRemote : ");
|
|
for (j = 0; j < 16; j++)
|
|
eprintf("%02X", md5_remote[i_remote].md5_digest[j]);
|
|
eprintf("\n\n");
|
|
}
|
|
|
|
all_identical = FALSE;
|
|
|
|
/* if message has changed remotely, receive it */
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
receive_message(lbs, list[index], message_id, error_str, TRUE);
|
|
|
|
if (error_str[0]) {
|
|
sprintf(str, "Error receiving message: %s", error_str);
|
|
mprint(lbs, mode, str);
|
|
} else if (mode == SYNC_HTML) {
|
|
|
|
if (getcfg_topgroup())
|
|
rsprintf("<a href=\"../%s/%d\">ID%d:</a>\t", lbs->name_enc, message_id,
|
|
message_id);
|
|
else if (sync_all)
|
|
rsprintf("<a href=\"%s/%d\">ID%d:</a>\t", lbs->name_enc, message_id,
|
|
message_id);
|
|
else
|
|
rsprintf("<a href=\"%d\">ID%d:</a>\t", message_id, message_id);
|
|
|
|
rsprintf("%s\n", loc("Remote entry received"));
|
|
}
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Remote entry should be received"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
|
|
} else {
|
|
|
|
/* if message does not exist locally but in cache, delete remote message */
|
|
|
|
all_identical = FALSE;
|
|
|
|
if (!isparam("confirm") && mode == SYNC_HTML) {
|
|
|
|
combine_url(lbs, list[index], "", str, sizeof(str), NULL);
|
|
sprintf(rem_ref, "<a href=\"http://%s%d\">%s</a>", str, message_id,
|
|
loc("Remote entry"));
|
|
|
|
sprintf(str, loc("%s should be deleted"), rem_ref);
|
|
rsprintf("ID%d:\t%s\n", message_id, str);
|
|
n_delete++;
|
|
|
|
} else if (!isparam("confirm") && mode == SYNC_CLONE) {
|
|
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Entry should be deleted remotely"));
|
|
mprint(lbs, mode, str);
|
|
|
|
} else {
|
|
|
|
if (_logging_level > 1) {
|
|
sprintf(str, "MIRROR delete remote entry #%d", message_id);
|
|
write_logfile(lbs, str);
|
|
}
|
|
|
|
sprintf(str, "%d?cmd=%s&confirm=%s", message_id, loc("Delete"), loc("Yes"));
|
|
combine_url(lbs, list[index], str, url, sizeof(url), &ssl);
|
|
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0) {
|
|
retrieve_url(lbs, url, ssl, &buffer, TRUE);
|
|
|
|
if (strstr(buffer, "Location: ")) {
|
|
if (mode == SYNC_HTML)
|
|
rsprintf("ID%d:\t%s\n", message_id, loc("Entry deleted remotely"));
|
|
} else {
|
|
|
|
if (mode == SYNC_HTML && isparam("debug"))
|
|
rsputs(buffer);
|
|
|
|
mprint(lbs, mode, loc("Error deleting remote entry"));
|
|
}
|
|
|
|
md5_cache[i_cache].message_id = -1;
|
|
xfree(buffer);
|
|
} else {
|
|
sprintf(str, "ID%d:\t%s", message_id, loc("Entry should be deleted remotely"));
|
|
mprint(lbs, mode, str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
flush_return_buffer();
|
|
}
|
|
|
|
xfree(md5_remote);
|
|
|
|
/* save remote MD5s in file */
|
|
if (!all_identical) {
|
|
n_remote = retrieve_remote_md5(lbs, list[index], &md5_remote, error_str);
|
|
if (n_remote < 0)
|
|
rsprintf("%s\n", error_str);
|
|
|
|
/* keep conflicting messages in cache */
|
|
for (i = 0; i < n_cache; i++)
|
|
if (md5_cache[i].message_id != -1) {
|
|
|
|
if (i == 0)
|
|
memcpy(md5_remote[0].md5_digest, md5_cache[0].md5_digest, 16);
|
|
else
|
|
for (j = 0; j < n_remote; j++)
|
|
if (md5_remote[j].message_id == md5_cache[i].message_id) {
|
|
memcpy(md5_remote[j].md5_digest, md5_cache[i].md5_digest, 16);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!getcfg(lbs->name, "Mirror simulate", str, sizeof(str)) || atoi(str) == 0)
|
|
save_md5(lbs, list[index], md5_remote, n_remote);
|
|
|
|
if (md5_remote)
|
|
xfree(md5_remote);
|
|
}
|
|
|
|
if (md5_cache)
|
|
xfree(md5_cache);
|
|
|
|
if (mode == SYNC_HTML && n_delete) {
|
|
|
|
if (getcfg_topgroup())
|
|
rsprintf("<br><b><a href=\"../%s/?cmd=Synchronize&confirm=1\">", lbs->name_enc);
|
|
else if (sync_all)
|
|
rsprintf("<br><b><a href=\"%s/?cmd=Synchronize&confirm=1\">", lbs->name_enc);
|
|
else
|
|
rsprintf("<br><b><a href=\"../%s/?cmd=Synchronize&confirm=1\">", lbs->name_enc);
|
|
|
|
if (n_delete > 1)
|
|
rsprintf(loc("Click here to delete %d entries"), n_delete);
|
|
else
|
|
rsprintf(loc("Click here to delete this entry"));
|
|
|
|
rsprintf("</a></b>\n");
|
|
}
|
|
|
|
if (mode == SYNC_HTML && all_identical)
|
|
rsprintf(loc("All entries identical"));
|
|
|
|
if (mode == SYNC_CLONE && all_identical)
|
|
mprint(lbs, mode, loc("All entries identical"));
|
|
|
|
if (mode == SYNC_HTML)
|
|
rsprintf("</pre>\n");
|
|
}
|
|
|
|
flush_return_buffer();
|
|
keep_alive = FALSE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void synchronize(LOGBOOK *lbs, int mode) {
|
|
int i;
|
|
char str[256], pwd[256];
|
|
|
|
if (mode == SYNC_HTML) {
|
|
show_html_header(NULL, FALSE, loc("Synchronization"), TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
rsprintf("<body>\n");
|
|
}
|
|
|
|
if (lbs == NULL) {
|
|
for (i = 0; lb_list[i].name[0]; i++)
|
|
if (getcfg(lb_list[i].name, "mirror server", str, sizeof(str))) {
|
|
|
|
if (exist_top_group() && getcfg_topgroup())
|
|
if (lb_list[i].top_group[0] && !strieq(lb_list[i].top_group, getcfg_topgroup()))
|
|
continue;
|
|
|
|
/* skip if excluded */
|
|
if (getcfg(lb_list[i].name, "Mirror exclude", str, sizeof(str)) && atoi(str) == 1)
|
|
continue;
|
|
|
|
/* if called by cron, set user name and password */
|
|
if (mode == SYNC_CRON && getcfg(lb_list[i].name, "mirror user", str, sizeof(str))) {
|
|
if (get_user_line(&lb_list[i], str, pwd, NULL, NULL, NULL, NULL, NULL) == EL_SUCCESS) {
|
|
setparam("unm", str);
|
|
setparam("upwd", pwd);
|
|
}
|
|
}
|
|
|
|
synchronize_logbook(&lb_list[i], mode, TRUE);
|
|
}
|
|
} else
|
|
synchronize_logbook(lbs, mode, FALSE);
|
|
|
|
if (mode == SYNC_HTML) {
|
|
rsprintf("<table width=\"100%%\" cellpadding=\"1\" cellspacing=\"0\"");
|
|
rsprintf("<tr><td class=\"seltitle\"><a href=\".\">%s</a></td></tr>\n", loc("Back"));
|
|
rsprintf("</table><p>\n");
|
|
|
|
rsprintf("</body></html>\n");
|
|
flush_return_buffer();
|
|
keep_alive = FALSE;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void display_line(LOGBOOK *lbs, int message_id, int number, const char *mode, int expand, int level,
|
|
BOOL printable, int n_line, int show_attachments, int show_att_column,
|
|
char *date, char *in_reply_to, char *reply_to, int n_attr_disp,
|
|
char disp_attr[MAX_N_ATTR + 4][NAME_LENGTH], BOOL disp_attr_link[MAX_N_ATTR + 4],
|
|
char attrib[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, BOOL show_text,
|
|
char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], char *encoding, BOOL select,
|
|
int *n_display, char *locked_by, int highlight, regex_t *re_buf, int highlight_mid,
|
|
int absolute_link, char *draft) {
|
|
char str[NAME_LENGTH+100], ref[2000], rowstyle[80], tdstyle[80], format[256],
|
|
file_name[MAX_PATH_LENGTH], *slist, *svalue, comment[256], param[80], subdir[256], attr[NAME_LENGTH];
|
|
const char *nowrap;
|
|
char display[NAME_LENGTH], attr_icon[80];
|
|
int i, j, n, i_line, index, colspan, line_len, thumb_status, max_line_len, n_lines,
|
|
max_n_lines;
|
|
BOOL skip_comma;
|
|
FILE *f;
|
|
struct tm *pts;
|
|
time_t ltime;
|
|
|
|
slist = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH);
|
|
svalue = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH);
|
|
|
|
_current_message_id = message_id;
|
|
ref[0] = 0;
|
|
if (absolute_link)
|
|
compose_base_url(lbs, ref, sizeof(ref), FALSE);
|
|
sprintf(ref + strlen(ref), "../%s/%d", lbs->name_enc, message_id);
|
|
|
|
if (strieq(mode, "Summary")) {
|
|
if (draft && draft[0])
|
|
strcpy(rowstyle, "listdraft");
|
|
else if (highlight_mid == message_id) {
|
|
if (number % 2 == 1)
|
|
strcpy(rowstyle, "list1h");
|
|
else
|
|
strcpy(rowstyle, "list2h");
|
|
} else {
|
|
if (number % 2 == 1)
|
|
strcpy(rowstyle, "list1");
|
|
else
|
|
strcpy(rowstyle, "list2");
|
|
}
|
|
} else if (strieq(mode, "Full")) {
|
|
if (highlight_mid == message_id)
|
|
strcpy(rowstyle, "list1h");
|
|
else
|
|
strcpy(rowstyle, "list1");
|
|
} else if (strieq(mode, "Threaded")) {
|
|
if (draft && draft[0])
|
|
strcpy(rowstyle, "threaddraft");
|
|
else if (highlight) {
|
|
if (highlight == message_id)
|
|
strcpy(rowstyle, "thread");
|
|
else
|
|
strcpy(rowstyle, "threadreply");
|
|
} else {
|
|
if (highlight_mid == message_id) {
|
|
if (level == 0)
|
|
strcpy(rowstyle, "threadh");
|
|
else
|
|
strcpy(rowstyle, "threadreply");
|
|
} else {
|
|
if (level == 0)
|
|
strcpy(rowstyle, "thread");
|
|
else
|
|
strcpy(rowstyle, "threadreply");
|
|
}
|
|
}
|
|
}
|
|
|
|
rsprintf("<tr>");
|
|
|
|
/* check attributes for row style */
|
|
for (i = 0; i < n_attr; i++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
if (attrib[i][0] == 0)
|
|
snprintf(str, sizeof(str), "Style %s \"\"", attr);
|
|
else
|
|
snprintf(str, sizeof(str), "Style %s %s", attr, attrib[i]);
|
|
if (getcfg(lbs->name, str, display, sizeof(display))) {
|
|
sprintf(str, "%s\" style=\"%s", rowstyle, display);
|
|
strlcpy(rowstyle, str, sizeof(rowstyle));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* only single cell for threaded display */
|
|
if (strieq(mode, "Threaded")) {
|
|
rsprintf("<td align=left class=\"%s\">", rowstyle);
|
|
|
|
if (locked_by && locked_by[0]) {
|
|
sprintf(str, "%s %s", loc("Entry is currently edited by"), locked_by);
|
|
rsprintf("<img src=\"stop.png\" alt=\"%s\" title=\"%s\"> ", str, str);
|
|
}
|
|
|
|
/* show select box */
|
|
if (select && level == 0)
|
|
rsprintf("<input type=checkbox name=\"s%d\" value=%d>\n", (*n_display)++, message_id);
|
|
|
|
for (i = 0; i < level; i++)
|
|
rsprintf(" ");
|
|
|
|
/* display "+" if expandable */
|
|
if (expand == 0 && (reply_to[0] || in_reply_to[0]))
|
|
rsprintf("+ ");
|
|
}
|
|
|
|
nowrap = printable ? "" : "nowrap";
|
|
skip_comma = FALSE;
|
|
|
|
if (getcfg(lbs->name, "List conditions", str, sizeof(str)) && atoi(str) == 1)
|
|
evaluate_conditions(lbs, attrib);
|
|
|
|
if (strieq(mode, "Threaded") && getcfg(lbs->name, "Thread display", display, sizeof(display))) {
|
|
/* check if to use icon from attributes */
|
|
attr_icon[0] = 0;
|
|
if (getcfg(lbs->name, "Thread icon", attr_icon, sizeof(attr_icon))) {
|
|
for (i = 0; i < n_attr; i++)
|
|
if (strieq(attr_list[i], attr_icon))
|
|
break;
|
|
if (i < n_attr && attrib[i][0])
|
|
strcpy(attr_icon, attrib[i]);
|
|
else
|
|
attr_icon[0] = 0;
|
|
}
|
|
|
|
if (highlight != message_id)
|
|
rsprintf("\n<a href=\"%s\">", ref);
|
|
|
|
if (attr_icon[0])
|
|
rsprintf("\n<img border=0 src=\"icons/%s\" alt=\"%s\" title=\"%s\">\n ", attr_icon, attr_icon,
|
|
attr_icon);
|
|
else {
|
|
/* display standard icons */
|
|
if (level == 0)
|
|
rsprintf("\n<img border=0 src=\"entry.png\" alt=\"%s\" title=\"%s\">\n ", loc("Entry"),
|
|
loc("Entry"));
|
|
else
|
|
rsprintf("\n<img border=0 src=\"reply.png\" alt=\"%s\" title=\"%s\">\n ", loc("Reply"),
|
|
loc("Reply"));
|
|
}
|
|
if (highlight != message_id)
|
|
rsprintf("</a>\n");
|
|
|
|
j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE);
|
|
sprintf(str, "%d", message_id);
|
|
add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &j);
|
|
add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date,
|
|
&j, 0);
|
|
|
|
strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
j);
|
|
|
|
if (highlight != message_id)
|
|
rsprintf("\n<a href=\"%s\">\n", ref);
|
|
else
|
|
rsprintf("\n<b>");
|
|
|
|
|
|
if (is_html(display) && !is_script(display) && html_allowed(lbs))
|
|
rsputs(display);
|
|
else
|
|
rsputs2(lbs, absolute_link, display);
|
|
|
|
rsputs(" ");
|
|
for (i = n = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment && attachment[i][0])
|
|
n++;
|
|
if (n > 5) {
|
|
if (highlight != message_id)
|
|
rsprintf("<a href=\"%s\">", ref);
|
|
rsprintf("<b>%dx", n);
|
|
rsprintf("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\"></b></a>");
|
|
} else {
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment && attachment[i][0]) {
|
|
strlcpy(str, attachment[i], sizeof(str));
|
|
str[13] = 0;
|
|
sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[i] + 14);
|
|
url_encode(ref, sizeof(ref)); /* for file names with special characters like "+" */
|
|
|
|
rsprintf("<a href=\"%s\" target=\"_blank\">", ref);
|
|
rsprintf
|
|
("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\" alt=\"%s\" title=\"%s\"></a>",
|
|
attachment[i] + 14, attachment[i] + 14);
|
|
}
|
|
}
|
|
|
|
if (highlight != message_id)
|
|
rsprintf("</a>\n", ref);
|
|
else
|
|
rsprintf("</b>\n");
|
|
} else {
|
|
/* show select box */
|
|
if (select && !strieq(mode, "Threaded")) {
|
|
rsprintf("<td class=\"%s\">", rowstyle);
|
|
rsprintf("<input type=checkbox name=\"s%d\" value=%d>\n", (*n_display)++, message_id);
|
|
rsprintf("</td>\n");
|
|
}
|
|
|
|
if (strieq(mode, "Threaded")) {
|
|
if (highlight != message_id)
|
|
rsprintf("\n<a href=\"%s\">\n", ref);
|
|
else
|
|
rsprintf("\n<b>");
|
|
}
|
|
|
|
skip_comma = TRUE;
|
|
for (index = 0; index < n_attr_disp; index++) {
|
|
if (strieq(disp_attr[index], loc("ID"))) {
|
|
if (strieq(mode, "Threaded")) {
|
|
if (level == 0)
|
|
rsprintf("\n<img border=0 src=\"entry.png\" alt=\"%s\" title=\"%s\"> ", loc("Entry"),
|
|
loc("Entry"));
|
|
else
|
|
rsprintf("\n<img border=0 src=\"reply.png\" alt=\"%s\" title=\"%s\"> ", loc("Reply"),
|
|
loc("Reply"));
|
|
|
|
} else {
|
|
|
|
rsprintf("<td class=\"%s\">", rowstyle);
|
|
|
|
if (locked_by && locked_by[0]) {
|
|
sprintf(str, "%s %s", loc("Entry is currently edited by"), locked_by);
|
|
rsprintf("\n<img src=\"stop.png\" alt=\"%s\" title=\"%s\"> ", str, str);
|
|
}
|
|
|
|
if (draft && draft[0]) {
|
|
strlcpy(display, loc("Draft"), sizeof(display));
|
|
} else {
|
|
if (getcfg(lbs->name, "ID display", display, sizeof(display))) {
|
|
j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
attrib, TRUE);
|
|
sprintf(str, "%d", message_id);
|
|
add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
"message id",
|
|
str, &j);
|
|
add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
"entry time", date, &j, 0);
|
|
|
|
strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist,
|
|
(char (*)[NAME_LENGTH]) svalue, j);
|
|
|
|
} else
|
|
sprintf(display, "%d", message_id);
|
|
}
|
|
|
|
rsprintf("\n<a href=\"%s\"> %s </a>\n", ref, display);
|
|
rsprintf("</td>\n");
|
|
}
|
|
}
|
|
|
|
if (strieq(disp_attr[index], loc("Logbook"))) {
|
|
if (strieq(mode, "Threaded")) {
|
|
if (skip_comma) {
|
|
rsprintf(" %s", lbs->name);
|
|
skip_comma = FALSE;
|
|
} else
|
|
rsprintf(", %s", lbs->name);
|
|
} else {
|
|
if (disp_attr_link == NULL || disp_attr_link[index])
|
|
rsprintf("\n<td class=\"%s\" %s><a href=\"%s\">%s</a></td>\n", rowstyle, nowrap, ref,
|
|
lbs->name);
|
|
else
|
|
rsprintf("\n<td class=\"%s\" %s>%s</td>\n", rowstyle, nowrap, lbs->name);
|
|
}
|
|
}
|
|
|
|
if (strieq(disp_attr[index], loc("Edit"))) {
|
|
if (!strieq(mode, "Threaded")) {
|
|
rsprintf("\n<td class=\"%s\" %s><a href=\"%s?cmd=%s\">", rowstyle, nowrap, ref, loc("Edit"));
|
|
rsprintf("\n<img src=\"edit.png\" border=0 alt=\"%s\" title=\"%s\"></a></td>\n",
|
|
loc("Edit entry"), loc("Edit entry"));
|
|
}
|
|
}
|
|
|
|
if (strieq(disp_attr[index], loc("Delete"))) {
|
|
if (!strieq(mode, "Threaded")) {
|
|
rsprintf("\n<td class=\"%s\" %s><a href=\"%s?cmd=%s\">", rowstyle, nowrap, ref, loc("Delete"));
|
|
rsprintf("\n<img src=\"delete.png\" border=0 alt=\"%s\" title=\"%s\"></a></td>\n",
|
|
loc("Delete entry"), loc("Delete entry"));
|
|
}
|
|
}
|
|
|
|
if (strieq(disp_attr[index], loc("Date"))) {
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = date_to_ltime(date);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
if (strieq(mode, "Threaded")) {
|
|
if (skip_comma) {
|
|
rsprintf(" %s", str);
|
|
skip_comma = FALSE;
|
|
} else
|
|
rsprintf(", %s", str);
|
|
} else {
|
|
if (disp_attr_link == NULL || disp_attr_link[index])
|
|
rsprintf("\n<td class=\"%s\" %s><a href=\"%s\">%s</a></td>\n", rowstyle, nowrap, ref, str);
|
|
else
|
|
rsprintf("\n<td class=\"%s\" %s>%s</td>\n", rowstyle, nowrap, str);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < n_attr; i++)
|
|
if (strieq(disp_attr[index], attr_list[i])) {
|
|
|
|
/* check attributes for cell style */
|
|
strlcpy(tdstyle, rowstyle, sizeof(tdstyle));
|
|
snprintf(str, sizeof(str), "Cell Style %s %s", attr_list[i], attrib[i]);
|
|
if (getcfg(lbs->name, str, display, sizeof(display))) {
|
|
sprintf(str, "%s\" style=\"%s", rowstyle, display);
|
|
strlcpy(tdstyle, str, sizeof(rowstyle));
|
|
}
|
|
|
|
if (strieq(mode, "Threaded")) {
|
|
if (strieq(attr_options[i][0], "boolean")) {
|
|
if (atoi(attrib[i]) == 1) {
|
|
if (skip_comma) {
|
|
rsprintf(" ");
|
|
skip_comma = FALSE;
|
|
} else
|
|
rsprintf(", ");
|
|
|
|
if (is_html(attrib[i]) && !is_script(attrib[i]) && html_allowed(lbs))
|
|
rsputs(attrib[i]);
|
|
else
|
|
rsputs2(lbs, absolute_link, attrib[i]);
|
|
|
|
rsprintf(" ");
|
|
}
|
|
} else if (attr_flags[i] & AF_DATE) {
|
|
if (skip_comma) {
|
|
rsprintf(" ");
|
|
skip_comma = FALSE;
|
|
} else
|
|
rsprintf(", ");
|
|
|
|
sprintf(str, "Date format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
rsputs(str);
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
if (skip_comma) {
|
|
rsprintf(" ");
|
|
skip_comma = FALSE;
|
|
} else
|
|
rsprintf(", ");
|
|
|
|
sprintf(str, "Time format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
rsputs(str);
|
|
} else if (attr_flags[i] & AF_ICON) {
|
|
|
|
sprintf(str, "Icon comment %s", attrib[i]);
|
|
getcfg(lbs->name, str, comment, sizeof(comment));
|
|
if (!comment[0])
|
|
strcpy(comment, attrib[i]);
|
|
|
|
if (attrib[i][0])
|
|
rsprintf(" \n<img border=0 src=\"icons/%s\" alt=\"%s\" title=\"%s\"> ",
|
|
attrib[i], comment, comment);
|
|
} else {
|
|
if (skip_comma) {
|
|
rsprintf(" ");
|
|
skip_comma = FALSE;
|
|
} else
|
|
rsprintf(", ");
|
|
|
|
if (is_html(attrib[i]) && !is_script(attrib[i]) && html_allowed(lbs))
|
|
rsputs(attrib[i]);
|
|
else
|
|
rsputs2(lbs, absolute_link, attrib[i]);
|
|
|
|
}
|
|
} else {
|
|
if (strieq(attr_options[i][0], "boolean")) {
|
|
if (atoi(attrib[i]) == 1)
|
|
rsprintf("\n<td class=\"%s\"><input type=checkbox checked disabled></td>\n", tdstyle);
|
|
else
|
|
rsprintf("\n<td class=\"%s\"><input type=checkbox disabled></td>\n", tdstyle);
|
|
} else if (attr_flags[i] & AF_DATE) {
|
|
|
|
sprintf(str, "Date format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
if (disp_attr_link == NULL || disp_attr_link[index])
|
|
rsprintf("\n<td class=\"%s\" %s><a href=\"%s\">%s</a></td>\n", tdstyle, nowrap, ref,
|
|
str);
|
|
else
|
|
rsprintf("\n<td class=\"%s\" %s>%s</td>\n", tdstyle, nowrap, str);
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
|
|
sprintf(str, "Time format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
if (disp_attr_link == NULL || disp_attr_link[index])
|
|
rsprintf("\n<td class=\"%s\" %s><a href=\"%s\">%s</a></td>\n", tdstyle, nowrap, ref,
|
|
str);
|
|
else
|
|
rsprintf("\n<td class=\"%s\" %s>%s</td>\n", tdstyle, nowrap, str);
|
|
} else if (attr_flags[i] & AF_ICON) {
|
|
rsprintf("<td class=\"%s\">", rowstyle);
|
|
|
|
sprintf(str, "Icon comment %s", attrib[i]);
|
|
getcfg(lbs->name, str, comment, sizeof(comment));
|
|
if (!comment[0])
|
|
strcpy(comment, attrib[i]);
|
|
|
|
if (attrib[i][0])
|
|
rsprintf("\n<img border=0 src=\"icons/%s\" alt=\"%s\" title=\"%s\">", attrib[i],
|
|
comment, comment);
|
|
|
|
rsprintf(" </td>");
|
|
} else {
|
|
rsprintf("<td class=\"%s\">", tdstyle);
|
|
|
|
if (disp_attr_link == NULL || disp_attr_link[index])
|
|
rsprintf("<a href=\"%s\">", ref);
|
|
|
|
sprintf(str, "List Change %s", attr_list[i]);
|
|
if (getcfg(lbs->name, str, display, sizeof(display))) {
|
|
j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist,
|
|
(char (*)[NAME_LENGTH]) svalue, attrib, TRUE);
|
|
sprintf(str, "%d", message_id);
|
|
add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
"message id", str, &j);
|
|
add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
"entry time", date, &j, 0);
|
|
|
|
strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist,
|
|
(char (*)[NAME_LENGTH]) svalue, j);
|
|
|
|
} else
|
|
strcpy(display, attrib[i]);
|
|
|
|
if (is_html(display) && !is_script(display) && html_allowed(lbs))
|
|
rsputs(display);
|
|
else {
|
|
if (isparam(attr_list[i])) {
|
|
highlight_searchtext(re_buf + 1 + i, display, str, TRUE);
|
|
strlcpy(display, str, sizeof(display));
|
|
} else if (isparam("subtext") && isparam("sall") && atoi(getparam("sall"))) {
|
|
highlight_searchtext(re_buf, display, str, TRUE);
|
|
strlcpy(display, str, sizeof(display));
|
|
}
|
|
rsputs2(lbs, absolute_link, display);
|
|
}
|
|
|
|
if (disp_attr_link == NULL || disp_attr_link[index])
|
|
rsprintf("</a>");
|
|
|
|
/* at least one space to produce non-empty table cell */
|
|
if (!display[0])
|
|
rsprintf(" ");
|
|
|
|
rsprintf("</td>");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strieq(mode, "Threaded")) {
|
|
|
|
rsputs(" ");
|
|
|
|
for (i = n = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment && attachment[i][0])
|
|
n++;
|
|
if (n > 5) {
|
|
if (highlight != message_id)
|
|
rsprintf("<a href=\"%s\">", ref);
|
|
rsprintf("<b>%dx", n);
|
|
rsprintf("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\"></b></a>");
|
|
} else {
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment && attachment[i][0]) {
|
|
strlcpy(str, attachment[i], sizeof(str));
|
|
str[13] = 0;
|
|
sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[i] + 14);
|
|
url_encode(ref, sizeof(ref)); /* for file names with special characters like "+" */
|
|
|
|
rsprintf("<a href=\"%s\" target=\"_blank\">", ref);
|
|
rsprintf
|
|
("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\" alt=\"%s\" title=\"%s\"></a>",
|
|
attachment[i] + 14, attachment[i] + 14);
|
|
}
|
|
}
|
|
|
|
if (highlight != message_id)
|
|
rsprintf("</a>\n");
|
|
else
|
|
rsprintf("</b>\n");
|
|
}
|
|
}
|
|
|
|
if (strieq(mode, "Threaded") && expand > 1 && show_text) {
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("<tr><td class=\"summary\">");
|
|
|
|
if (expand == 2) {
|
|
for (i = i_line = line_len = 0; i < (int) sizeof(str) - 1; i++, line_len++) {
|
|
str[i] = text[i];
|
|
if (line_break(text + i, encoding)) {
|
|
i_line++;
|
|
line_len = 0;
|
|
} else
|
|
/* limit line length to 150 characters */
|
|
if (line_len > 150 && text[i] == ' ') {
|
|
str[i] = '\n';
|
|
i_line++;
|
|
line_len = 0;
|
|
}
|
|
|
|
if (i_line == n_line)
|
|
break;
|
|
}
|
|
str[i] = 0;
|
|
|
|
/* only show text, not to rip apart HTML documents,
|
|
e.g. only the start of a table */
|
|
if (strieq(encoding, "HTML"))
|
|
strip_html(str);
|
|
if (str[0])
|
|
strencode_nouml(str);
|
|
else
|
|
rsputs(" ");
|
|
} else {
|
|
if (strieq(encoding, "plain")) {
|
|
rsputs("<pre>");
|
|
if (text[0])
|
|
rsputs2(lbs, absolute_link, text);
|
|
else
|
|
rsputs(" ");
|
|
rsputs("</pre>");
|
|
} else if (strieq(encoding, "ELCode"))
|
|
rsputs_elcode(lbs, FALSE, text);
|
|
else if (text[0])
|
|
rsputs(text);
|
|
else
|
|
rsputs(" ");
|
|
}
|
|
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
if (strieq(mode, "Summary") && n_line > 0 && show_text) {
|
|
rsprintf("<td class=\"summary\">");
|
|
|
|
if (getcfg(lbs->name, "Summary line length", param, sizeof(param)))
|
|
max_line_len = atoi(param);
|
|
else
|
|
max_line_len = n_line >= 10 ? 140 : 40;
|
|
for (i = i_line = line_len = 0; i < (int) sizeof(str) - 1; line_len++, i++) {
|
|
str[i] = text[i];
|
|
if (line_break(text + i, encoding)) {
|
|
i_line++;
|
|
line_len = 0;
|
|
} else
|
|
/* limit line length to max_line_len characters */
|
|
if (line_len > max_line_len && text[i] == ' ') {
|
|
str[i] = '\n';
|
|
i_line++;
|
|
line_len = 0;
|
|
}
|
|
|
|
if (i_line == n_line)
|
|
break;
|
|
}
|
|
str[i] = 0;
|
|
|
|
/* only show text, not to rip apart HTML documents,
|
|
e.g. only the start of a table */
|
|
if (strieq(encoding, "HTML"))
|
|
strip_html(str);
|
|
if (str[0])
|
|
strencode_nouml(str);
|
|
else
|
|
rsputs(" ");
|
|
|
|
rsputs("</td>\n");
|
|
}
|
|
|
|
if (show_att_column) {
|
|
/* show attachment icons */
|
|
rsputs("<td class=\"listatt\"> ");
|
|
|
|
for (i = n = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment && attachment[i][0])
|
|
n++;
|
|
if (n > 5) {
|
|
if (highlight != message_id)
|
|
rsprintf("<a href=\"%s\">", ref);
|
|
rsprintf("<b>%dx", n);
|
|
rsprintf("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\"></b></a>");
|
|
} else {
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment && attachment[i][0]) {
|
|
strlcpy(str, attachment[i], sizeof(str));
|
|
str[13] = 0;
|
|
sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[i] + 14);
|
|
url_encode(ref, sizeof(ref)); /* for file names with special characters like "+" */
|
|
|
|
rsprintf("<a href=\"%s\" target=\"_blank\">", ref);
|
|
rsprintf
|
|
("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\" alt=\"%s\" title=\"%s\"></a>",
|
|
attachment[i] + 14, attachment[i] + 14);
|
|
}
|
|
}
|
|
|
|
rsputs(" </td>");
|
|
}
|
|
|
|
colspan = n_attr_disp;
|
|
|
|
if (select)
|
|
colspan++;
|
|
|
|
if (strieq(mode, "Full") && show_text) {
|
|
if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) {
|
|
rsprintf("<tr><td class=\"messagelist\" colspan=%d>", colspan);
|
|
|
|
if (strieq(encoding, "plain")) {
|
|
rsputs("<pre>");
|
|
rsputs2(lbs, absolute_link, text);
|
|
rsputs("</pre>");
|
|
} else if (strieq(encoding, "ELCode"))
|
|
rsputs_elcode(lbs, FALSE, text);
|
|
else
|
|
rsputs(text);
|
|
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
for (index = 0; index < MAX_ATTACHMENTS; index++) {
|
|
if (show_attachments && attachment[index][0]) {
|
|
|
|
/* check if attachment is inlined */
|
|
if (is_inline_attachment(encoding, message_id, text, index, attachment[index]))
|
|
continue;
|
|
|
|
strlcpy(str, attachment[index], sizeof(str));
|
|
str[13] = 0;
|
|
sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[index] + 14);
|
|
url_encode(ref, sizeof(ref)); /* for file names with special characters like "+" */
|
|
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(attachment[index], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, attachment[index], sizeof(file_name));
|
|
thumb_status = create_thumbnail(lbs, file_name);
|
|
|
|
if (!show_attachments) {
|
|
rsprintf("<a href=\"%s\" target=\"_blank\">%s</a> ", ref,
|
|
attachment[index] + 14);
|
|
} else {
|
|
|
|
if (thumb_status) {
|
|
rsprintf
|
|
("<tr><td colspan=%d class=\"attachment\">%s %d: <a href=\"%s\" target=\"_blank\">%s</a>\n",
|
|
colspan, loc("Attachment"), index + 1, ref, attachment[index] + 14);
|
|
|
|
if (show_attachments) {
|
|
rsprintf("<tr><td colspan=%d class=\"attachmentframe\">\n", colspan);
|
|
if (thumb_status == 2) {
|
|
for (i = 0;; i++) {
|
|
strlcpy(str, file_name, sizeof(str));
|
|
if (chkext(str, ".pdf") || chkext(str, ".ps"))
|
|
if (strrchr(str, '.'))
|
|
*strrchr(str, '.') = 0;
|
|
sprintf(str + strlen(str), "-%d.png", i);
|
|
if (file_exist(str)) {
|
|
strlcpy(str, ref, sizeof(str));
|
|
if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
|
|
if (strrchr(str, '.'))
|
|
*strrchr(str, '.') = 0;
|
|
sprintf(str + strlen(str), "-%d.png", i);
|
|
rsprintf("<a name=\"att%d\" href=\"%s\">\n", index + 1, ref);
|
|
rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\"></a>\n", str,
|
|
attachment[index] + 14, attachment[index] + 14);
|
|
} else
|
|
break;
|
|
}
|
|
} else {
|
|
rsprintf("<a name=\"att%d\" href=\"%s\">\n", index + 1, ref);
|
|
strlcpy(str, ref, sizeof(str));
|
|
if (chkext(str, ".pdf") || chkext(str, ".ps"))
|
|
if (strrchr(str, '.'))
|
|
*strrchr(str, '.') = 0;
|
|
strlcat(str, ".png", sizeof(str));
|
|
rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\"></a>\n", str, attachment[index]
|
|
+ 14,
|
|
attachment[index] + 14);
|
|
}
|
|
rsprintf("</td></tr>\n\n");
|
|
}
|
|
} else {
|
|
if (is_image(attachment[index])) {
|
|
rsprintf
|
|
("<tr><td colspan=%d class=\"attachment\">%s %d: <a href=\"%s\" target=\"_blank\">%s</a>\n",
|
|
colspan, loc("Attachment"), index + 1, ref, attachment[index] + 14);
|
|
if (show_attachments) {
|
|
rsprintf("</td></tr><tr>");
|
|
rsprintf("<td colspan=%d class=\"attachmentframe\">", colspan);
|
|
rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\">", ref, attachment[index] + 14,
|
|
attachment[index] + 14);
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
} else {
|
|
rsprintf
|
|
("<tr><td colspan=%d class=\"attachment\">%s %d: <a href=\"%s\" target=\"_blank\">%s</a>\n",
|
|
colspan, loc("Attachment"), index + 1, ref, attachment[index] + 14);
|
|
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(attachment[index], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, attachment[index], sizeof(file_name));
|
|
|
|
if (is_ascii(file_name) && !chkext(attachment[index], ".PS")
|
|
&& !chkext(attachment[index], ".PDF") && !chkext(attachment[index], ".EPS")
|
|
&& !chkext(attachment[index], ".SVG")
|
|
&& !chkext(attachment[index], ".HTM") && !chkext(attachment[index], ".HTML")
|
|
&& show_attachments) {
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
/* display attachment */
|
|
rsprintf("<tr><td colspan=%d class=\"messageframe\">\n", colspan);
|
|
|
|
/* anchor for references */
|
|
rsprintf("<a name=\"att%d\"></a>\n", index + 1);
|
|
|
|
/* display attachment */
|
|
|
|
if (!chkext(attachment[index], ".HTML"))
|
|
rsprintf("<pre class=\"messagepre\">");
|
|
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(attachment[index], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, attachment[index], sizeof(file_name));
|
|
|
|
f = fopen(file_name, "rt");
|
|
|
|
n_lines = 0;
|
|
if (getcfg(lbs->name, "Attachment lines", str, sizeof(str)))
|
|
max_n_lines = atoi(str);
|
|
else
|
|
max_n_lines = 300;
|
|
|
|
if (f != NULL) {
|
|
while (!feof(f)) {
|
|
str[0] = 0;
|
|
fgets(str, sizeof(str), f);
|
|
|
|
if (n_lines < max_n_lines) {
|
|
if (!chkext(attachment[index], ".HTML"))
|
|
rsputs2(lbs, absolute_link, str);
|
|
else
|
|
rsputs(str);
|
|
}
|
|
n_lines++;
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
if (!chkext(attachment[index], ".HTML"))
|
|
rsprintf("</pre>");
|
|
rsprintf("\n");
|
|
if (max_n_lines == 0)
|
|
rsprintf("<i><b>%d lines</b></i>\n", n_lines);
|
|
else if (n_lines > max_n_lines)
|
|
rsprintf("<i><b>... %d more lines ...</b></i>\n", n_lines - max_n_lines);
|
|
}
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
xfree(slist);
|
|
xfree(svalue);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void display_reply(LOGBOOK *lbs, int message_id, int printable, int expand, int n_line, int n_attr_disp,
|
|
char disp_attr[MAX_N_ATTR + 4][NAME_LENGTH], BOOL show_text, int level, int highlight,
|
|
regex_t *re_buf, int highlight_mid, int absolute_link) {
|
|
char *date, *text, *in_reply_to, *reply_to, *encoding, *attachment, *locked_by, *draft, *attrib, *p;
|
|
int status, size;
|
|
|
|
text = (char *) xmalloc(TEXT_SIZE);
|
|
attachment = (char *) xmalloc(MAX_PATH_LENGTH * MAX_ATTACHMENTS);
|
|
attrib = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH);
|
|
date = (char *) xmalloc(80);
|
|
in_reply_to = (char *) xmalloc(80);
|
|
reply_to = (char *) xmalloc(256);
|
|
encoding = (char *) xmalloc(80);
|
|
locked_by = (char *) xmalloc(256);
|
|
draft = (char *) xmalloc(256);
|
|
|
|
if (draft == NULL)
|
|
return;
|
|
|
|
reply_to[0] = 0;
|
|
size = TEXT_SIZE;
|
|
status = el_retrieve(lbs, message_id, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text, &size,
|
|
in_reply_to, reply_to, (char (*)[256]) attachment, encoding, locked_by, draft);
|
|
|
|
if (status != EL_SUCCESS || draft[0]) {
|
|
xfree(text);
|
|
xfree(attachment);
|
|
xfree(attrib);
|
|
xfree(date);
|
|
xfree(in_reply_to);
|
|
xfree(reply_to);
|
|
xfree(encoding);
|
|
xfree(locked_by);
|
|
xfree(draft);
|
|
return;
|
|
}
|
|
|
|
display_line(lbs, message_id, 0, "threaded", expand, level, printable, n_line, FALSE, FALSE, date,
|
|
in_reply_to, reply_to, n_attr_disp, disp_attr, NULL, (char (*)[1500]) attrib, lbs->n_attr,
|
|
text, show_text, (char (*)[256]) attachment, encoding, 0, NULL, locked_by, highlight,
|
|
&re_buf[0], highlight_mid, absolute_link, draft);
|
|
|
|
if (reply_to[0]) {
|
|
p = reply_to;
|
|
do {
|
|
display_reply(lbs, atoi(p), printable, expand, n_line, n_attr_disp, disp_attr, show_text, level + 1,
|
|
highlight, &re_buf[0], highlight_mid, absolute_link);
|
|
|
|
while (*p && isdigit(*p))
|
|
p++;
|
|
while (*p && (*p == ',' || *p == ' '))
|
|
p++;
|
|
} while (*p);
|
|
}
|
|
|
|
xfree(text);
|
|
xfree(attachment);
|
|
xfree(attrib);
|
|
xfree(date);
|
|
xfree(in_reply_to);
|
|
xfree(reply_to);
|
|
xfree(encoding);
|
|
xfree(locked_by);
|
|
xfree(draft);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int msg_compare(const void *m1, const void *m2) {
|
|
return strcoll(((MSG_LIST *) m1)->string, ((MSG_LIST *) m2)->string);
|
|
}
|
|
|
|
int msg_compare_reverse(const void *m1, const void *m2) {
|
|
return strcoll(((MSG_LIST *) m2)->string, ((MSG_LIST *) m1)->string);
|
|
}
|
|
|
|
int msg_compare_numeric(const void *m1, const void *m2) {
|
|
return ((MSG_LIST *) m1)->number - ((MSG_LIST *) m2)->number;
|
|
}
|
|
|
|
int msg_compare_reverse_numeric(const void *m1, const void *m2) {
|
|
return ((MSG_LIST *) m2)->number - ((MSG_LIST *) m1)->number;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
char *param_in_str(char *str, char *param) {
|
|
char *p;
|
|
|
|
p = str;
|
|
do {
|
|
if (stristr(p, param) == NULL)
|
|
return NULL;
|
|
|
|
p = stristr(p, param);
|
|
|
|
/* if parameter not followed by '=', skip it */
|
|
if (*(p + strlen(param)) != '=') {
|
|
p += strlen(param);
|
|
continue;
|
|
}
|
|
|
|
/* if parameter is value of another parameter, skip it */
|
|
if (p > str + 1 && *(p - 1) == '=') {
|
|
p += strlen(param);
|
|
continue;
|
|
}
|
|
|
|
if (*p == 0)
|
|
return NULL;
|
|
|
|
return p;
|
|
|
|
} while (1);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL subst_param(char *str, int size, const char *param, const char *value) {
|
|
int len;
|
|
char *p1, *p2, *s, param_enc[256], str2[256];
|
|
|
|
strlcpy(param_enc, param, sizeof(param_enc));
|
|
url_slash_encode(param_enc, sizeof(param_enc));
|
|
|
|
if (!value[0]) {
|
|
|
|
/* remove parameter */
|
|
s = param_in_str(str, param_enc);
|
|
|
|
if (s == NULL)
|
|
return FALSE;
|
|
|
|
/* remove parameter */
|
|
p1 = s - 1;
|
|
|
|
for (p2 = p1 + strlen(param_enc) + 1; *p2 && *p2 != '&'; p2++);
|
|
strlcpy(str2, p2, sizeof(str2));
|
|
strlcpy(p1, str2, size - (p1 - str));
|
|
|
|
if (!strchr(str, '?') && strchr(str, '&'))
|
|
*strchr(str, '&') = '?';
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if ((p1 = param_in_str(str, param_enc)) == NULL) {
|
|
if (strchr(str, '?'))
|
|
strlcat(str, "&", size);
|
|
else
|
|
strlcat(str, "?", size);
|
|
|
|
strlcat(str, param_enc, size);
|
|
strlcat(str, "=", size);
|
|
strlcat(str, value, size);
|
|
return FALSE;
|
|
}
|
|
|
|
p1 += strlen(param_enc) + 1;
|
|
for (p2 = p1; *p2 && *p2 != '&'; p2++);
|
|
len = p2 - p1;
|
|
if (len > (int) strlen(value)) {
|
|
/* new value is shorter than old one */
|
|
strlcpy(str2, value, size - (p1 - str));
|
|
strlcpy(str2 + strlen(value), p2, size - (p1 + strlen(value) - str));
|
|
strlcpy(p1, str2, size - (p1 - str));
|
|
} else {
|
|
/* new value is longer than old one */
|
|
s = (char *) xmalloc(size);
|
|
strlcpy(s, p2, size);
|
|
strlcpy(str2, value, size - (p1 - str));
|
|
strlcat(str2, s, size - (p1 + strlen(value) - str));
|
|
strlcpy(p1, str2, size - (p1 - str));
|
|
xfree(s);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL logged_in(LOGBOOK *lbs) {
|
|
if (isparam("unm")) {
|
|
if (check_login_user(lbs, getparam("unm")) && check_login(lbs, getparam("sid")))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL is_user_allowed(LOGBOOK *lbs, char *command) {
|
|
char str[1000], users[2000];
|
|
char list[MAX_N_LIST][NAME_LENGTH];
|
|
int i, n;
|
|
|
|
/* check for user level access */
|
|
if (!getcfg(lbs->name, "Password file", str, sizeof(str)))
|
|
return TRUE;
|
|
|
|
/* check for deny */
|
|
sprintf(str, "Deny %s", command);
|
|
if (getcfg(lbs->name, str, users, sizeof(users))) {
|
|
|
|
if (!isparam("unm"))
|
|
return FALSE;
|
|
|
|
/* check if current user in list */
|
|
n = strbreak(users, list, MAX_N_LIST, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
if (strieq(list[i], getparam("unm")))
|
|
break;
|
|
|
|
if (i < n)
|
|
return FALSE;
|
|
}
|
|
|
|
/* check admin command */
|
|
if (strieq(command, loc("Admin"))) {
|
|
if (getcfg(lbs->name, "Admin user", str, sizeof(str))) {
|
|
return is_admin_user(lbs, getparam("unm"));
|
|
}
|
|
}
|
|
|
|
/* check for allow */
|
|
sprintf(str, "Allow %s", command);
|
|
if (!getcfg(lbs->name, str, users, sizeof(users)))
|
|
return TRUE;
|
|
|
|
/* check if current user in list */
|
|
if (!isparam("unm"))
|
|
return FALSE;
|
|
|
|
n = strbreak(users, list, MAX_N_LIST, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
if (strieq(list[i], getparam("unm")))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int is_draft(LOGBOOK *lbs, int message_id) {
|
|
char draft[256];
|
|
|
|
el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, draft);
|
|
return draft[0];
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL is_command_allowed(LOGBOOK *lbs, char *command, int message_id) {
|
|
char str[1000], menu_str[1000], other_str[1000];
|
|
char menu_item[MAX_N_LIST][NAME_LENGTH];
|
|
int i, n;
|
|
|
|
if (command[0] == 0)
|
|
return TRUE;
|
|
|
|
/* check for guest access */
|
|
if (!getcfg(lbs->name, "Guest Menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs))
|
|
getcfg(lbs->name, "Menu commands", menu_str, sizeof(menu_str));
|
|
|
|
/* default menu commands */
|
|
if (menu_str[0] == 0) {
|
|
strcpy(menu_str, "List, New, Edit, Delete, Reply, Duplicate, Synchronize, Find, ");
|
|
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str))) {
|
|
|
|
if (is_admin_user(lbs, getparam("unm"))) {
|
|
|
|
strcat(menu_str, "Admin, ");
|
|
strcat(menu_str, "Change config file, ");
|
|
strcat(menu_str, "Delete this logbook, ");
|
|
strcat(menu_str, "Rename this logbook, ");
|
|
strcat(menu_str, "Create new logbook, ");
|
|
strcat(menu_str, "GetPwdFile, ");
|
|
|
|
if (is_admin_user(NULL, getparam("unm"))) {
|
|
|
|
if (lbs->top_group[0]) {
|
|
sprintf(str, "Change [global %s]", lbs->top_group);
|
|
strcat(menu_str, str);
|
|
strcat(menu_str, ", ");
|
|
}
|
|
|
|
if (!lbs->top_group[0] || (is_admin_user(NULL, getparam("unm")))) {
|
|
|
|
strcat(menu_str, "Change [global]");
|
|
strcat(menu_str, ", ");
|
|
}
|
|
}
|
|
}
|
|
strcat(menu_str, "Config, Logout, ");
|
|
} else {
|
|
strcat(menu_str, "Config, ");
|
|
strcat(menu_str, "Change [global], ");
|
|
strcat(menu_str, "Delete this logbook, ");
|
|
strcat(menu_str, "Rename this logbook, ");
|
|
strcat(menu_str, "Create new logbook, ");
|
|
}
|
|
|
|
strcat(menu_str, "Help, HelpELCode, ");
|
|
|
|
} else {
|
|
/* check for admin command */
|
|
n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE);
|
|
menu_str[0] = 0;
|
|
for (i = 0; i < n; i++) {
|
|
if (strcmp(menu_item[i], "Admin") == 0) {
|
|
if (!is_admin_user(lbs, getparam("unm")))
|
|
continue;
|
|
}
|
|
strcat(menu_str, menu_item[i]);
|
|
strcat(menu_str, ", ");
|
|
}
|
|
|
|
strcat(menu_str, "HelpELCode, Synchronize, ");
|
|
|
|
if (is_admin_user(lbs, getparam("unm"))) {
|
|
|
|
strcat(menu_str, "Change config file, ");
|
|
strcat(menu_str, "Delete this logbook, ");
|
|
strcat(menu_str, "Rename this logbook, ");
|
|
strcat(menu_str, "Create new logbook, ");
|
|
strcat(menu_str, "GetPwdFile, ");
|
|
|
|
if (is_admin_user(NULL, getparam("unm"))) {
|
|
|
|
if (lbs->top_group[0]) {
|
|
sprintf(str, "Change [global %s]", lbs->top_group);
|
|
strcat(menu_str, str);
|
|
strcat(menu_str, ", ");
|
|
}
|
|
|
|
if (!lbs->top_group[0] || (is_admin_user(NULL, getparam("unm")))) {
|
|
|
|
strcat(menu_str, "Change [global]");
|
|
strcat(menu_str, ", ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check list menu commands */
|
|
str[0] = 0;
|
|
if (!getcfg(lbs->name, "Guest List Menu commands", str, sizeof(str)) || logged_in(lbs))
|
|
getcfg(lbs->name, "list menu commands", str, sizeof(str));
|
|
|
|
if (!str[0]) {
|
|
if (!getcfg(lbs->name, "Guest Find Menu commands", str, sizeof(str)) || logged_in(lbs))
|
|
getcfg(lbs->name, "Find Menu commands", str, sizeof(str));
|
|
}
|
|
|
|
if (str[0])
|
|
strlcat(menu_str, str, sizeof(menu_str));
|
|
else {
|
|
strlcat(menu_str, "New, Find, Select, Last x, Help, HelpELCode, ", sizeof(menu_str));
|
|
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str)))
|
|
strlcat(menu_str, "Admin, Config, Logout, ", sizeof(menu_str));
|
|
else
|
|
strlcat(menu_str, "Config, ", sizeof(menu_str));
|
|
}
|
|
|
|
strlcpy(other_str, "Preview, Back, Search, Download, Import, CSV Import, XML Import, ", sizeof(other_str));
|
|
strlcat(other_str, "Cancel, First, Last, Previous, Next, Requested, Forgot, ", sizeof(other_str));
|
|
|
|
/* only allow Submit & Co if "New" is allowed */
|
|
if (stristr(menu_str, "New,") || stristr(menu_str, "Edit"))
|
|
strlcat(other_str, "Update, Upload, Submit, Save, ", sizeof(other_str));
|
|
|
|
/* add save for new user registration */
|
|
if (isparam("new_user_name"))
|
|
strlcat(other_str, "Save, ", sizeof(other_str));
|
|
|
|
/* admin commands */
|
|
if (is_admin_user(lbs, getparam("unm"))) {
|
|
strlcat(other_str, "Remove user, New user, Activate, ", sizeof(other_str));
|
|
} else if (getcfg(lbs->name, "Self register", str, sizeof(str)) && atoi(str) > 0) {
|
|
strlcat(other_str, "Remove user, New user, ", sizeof(other_str));
|
|
}
|
|
|
|
/* allow always edit of draft messages */
|
|
if (is_draft(lbs, message_id))
|
|
strlcat(other_str, "Edit, ", sizeof(other_str));
|
|
|
|
/* allow change password if "config" possible */
|
|
if (strieq(command, loc("Change password")) && stristr(menu_str, "Config")) {
|
|
return TRUE;
|
|
}
|
|
/* exclude non-localized submit for elog */
|
|
else if (command[0] && strieq(command, "Submit")
|
|
&& (stristr(menu_str, "New,") || stristr(menu_str, "Edit"))) {
|
|
return TRUE;
|
|
}
|
|
/* exclude other non-localized commands */
|
|
else if (command[0] && strieq(command, "GetMD5")) {
|
|
return TRUE;
|
|
} else if (command[0] && strieq(command, "IM")) {
|
|
return TRUE;
|
|
}
|
|
/* check if command is present in the menu list */
|
|
else if (command[0]) {
|
|
n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
if (strieq(command, menu_item[i]) || strieq(command, loc(menu_item[i])))
|
|
break;
|
|
|
|
if (i == n) {
|
|
n = strbreak(other_str, menu_item, MAX_N_LIST, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
if (strieq(command, menu_item[i]) || strieq(command, loc(menu_item[i])))
|
|
break;
|
|
|
|
if (i == n)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void build_ref(char *ref, int size, const char *mode, const char *expand, const char *attach, const char *new_entries) {
|
|
char str[1000], *p;
|
|
|
|
if (strchr(getparam("cmdline"), '?'))
|
|
strlcat(ref, strchr(getparam("cmdline"), '?'), size);
|
|
|
|
/* eliminate old search */
|
|
if (strstr(ref, "cmd=Search&")) {
|
|
strlcpy(str, strstr(ref, "cmd=Search&") + 11, sizeof(str));
|
|
p = strstr(ref, "cmd=Search&");
|
|
strlcpy(p, str, size - (p - ref));
|
|
}
|
|
|
|
/* eliminate id=xxx */
|
|
if (strstr(ref, "id=")) {
|
|
strlcpy(str, ref, sizeof(str));
|
|
p = strstr(str, "id=") + 3;
|
|
while (*p && isdigit(*p))
|
|
p++;
|
|
strlcpy(strstr(ref, "id="), p, size);
|
|
if (strlen(ref) > 0 && ref[strlen(ref) - 1] == '?')
|
|
ref[strlen(ref) - 1] = 0;
|
|
}
|
|
|
|
/* eliminate old mode if new one is present */
|
|
if (mode[0])
|
|
subst_param(ref, size, "mode", mode);
|
|
|
|
/* eliminate old expand if new one is present */
|
|
if (expand[0])
|
|
subst_param(ref, size, "expand", expand);
|
|
|
|
/* eliminate old attach if new one is present */
|
|
if (attach[0])
|
|
subst_param(ref, size, "attach", attach);
|
|
|
|
/* eliminate old new_entries if new one is present */
|
|
if (new_entries[0])
|
|
subst_param(ref, size, "new_entries", new_entries);
|
|
|
|
/* eliminate old last= */
|
|
if (isparam("last"))
|
|
subst_param(ref, size, "last", getparam("last"));
|
|
|
|
/* replace any '&' by '&' */
|
|
strlcpy(str, ref, sizeof(str));
|
|
strencode2(ref, str, size);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_page_filters(LOGBOOK *lbs, int n_msg, int page_n, BOOL mode_commands, char *mode) {
|
|
int cur_exp, n, i, j, i1, i2, index, attr_index, size;
|
|
char ref[256], str[NAME_LENGTH+100], comment[NAME_LENGTH], list[MAX_N_LIST][NAME_LENGTH],
|
|
option[NAME_LENGTH], option_whole[NAME_LENGTH+100], litem[NAME_LENGTH];
|
|
|
|
rsprintf("<tr><td class=\"menuframe\">\n");
|
|
rsprintf("<table width=\"100%%\" border=0 cellpadding=\"0\" cellspacing=\"0\">\n");
|
|
|
|
rsprintf("<tr>\n");
|
|
|
|
if (mode_commands) {
|
|
rsprintf("<td class=\"menu2a\">\n");
|
|
|
|
if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) {
|
|
if (page_n != 1)
|
|
sprintf(ref, "page%d", page_n);
|
|
else
|
|
ref[0] = 0;
|
|
build_ref(ref, sizeof(ref), "full", "", "", "");
|
|
|
|
if (strieq(mode, "full"))
|
|
rsprintf(" %s |", loc("Full"));
|
|
else
|
|
rsprintf(" <a href=\"%s\">%s</a> |", ref, loc("Full"));
|
|
}
|
|
|
|
if (page_n != 1)
|
|
sprintf(ref, "page%d", page_n);
|
|
else
|
|
ref[0] = 0;
|
|
build_ref(ref, sizeof(ref), "summary", "", "", "");
|
|
|
|
if (strieq(mode, "summary"))
|
|
rsprintf(" %s |", loc("Summary"));
|
|
else
|
|
rsprintf(" <a href=\"%s\">%s</a> |", ref, loc("Summary"));
|
|
|
|
if (page_n != 1)
|
|
sprintf(ref, "page%d", page_n);
|
|
else
|
|
ref[0] = 0;
|
|
build_ref(ref, sizeof(ref), "threaded", "", "", "");
|
|
|
|
if (strieq(mode, "threaded"))
|
|
rsprintf(" %s ", loc("Threaded"));
|
|
else
|
|
rsprintf(" <a href=\"%s\">%s</a> ", ref, loc("Threaded"));
|
|
|
|
if (strieq(mode, "full")) {
|
|
if (page_n != 1)
|
|
sprintf(ref, "page%d", page_n);
|
|
else
|
|
ref[0] = 0;
|
|
|
|
cur_exp = 0;
|
|
if (strieq(mode, "full"))
|
|
cur_exp = 1;
|
|
if (isparam("elattach"))
|
|
cur_exp = atoi(getparam("elattach"));
|
|
if (isparam("attach"))
|
|
cur_exp = atoi(getparam("attach"));
|
|
|
|
if (cur_exp) {
|
|
build_ref(ref, sizeof(ref), "", "", "0", "");
|
|
rsprintf("| <a href=\"%s\">%s</a> ", ref, loc("Hide attachments"));
|
|
} else {
|
|
build_ref(ref, sizeof(ref), "", "", "1", "");
|
|
rsprintf("| <a href=\"%s\">%s</a> ", ref, loc("Show attachments"));
|
|
}
|
|
}
|
|
|
|
if (strieq(mode, "threaded")) {
|
|
if (page_n != 1)
|
|
sprintf(ref, "page%d", page_n);
|
|
else
|
|
ref[0] = 0;
|
|
|
|
cur_exp = 1;
|
|
if (getcfg(lbs->name, "Expand default", str, sizeof(str)))
|
|
cur_exp = atoi(str);
|
|
if (isparam("expand"))
|
|
cur_exp = atoi(getparam("expand"));
|
|
|
|
if (cur_exp > 0) {
|
|
sprintf(str, "%d", cur_exp > 0 ? cur_exp - 1 : 0);
|
|
build_ref(ref, sizeof(ref), "", str, "", "");
|
|
rsprintf("| <a href=\"%s\">%s</a> ", ref, loc("Collapse"));
|
|
} else
|
|
rsprintf("| %s ", loc("Collapse"));
|
|
|
|
if (cur_exp < 3) {
|
|
if (page_n != 1)
|
|
sprintf(ref, "page%d", page_n);
|
|
else
|
|
ref[0] = 0;
|
|
sprintf(str, "%d", cur_exp < 3 ? cur_exp + 1 : 3);
|
|
build_ref(ref, sizeof(ref), "", str, "", "");
|
|
rsprintf("| <a href=\"%s\">%s</a> ", ref, loc("Expand"));
|
|
} else
|
|
rsprintf("| %s ", loc("Expand"));
|
|
}
|
|
|
|
rsprintf("</td>\n");
|
|
}
|
|
|
|
rsprintf("<td class=\"menu2b\">\n");
|
|
|
|
/*---- filter menu text ----*/
|
|
|
|
if (getcfg(lbs->name, "filter menu text", str, sizeof(str))) {
|
|
FILE *f;
|
|
char file_name[256], *buf;
|
|
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
|
|
f = fopen(file_name, "rb");
|
|
if (f != NULL) {
|
|
fseek(f, 0, SEEK_END);
|
|
size = TELL(fileno(f));
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
buf = (char *) xmalloc(size + 1);
|
|
fread(buf, 1, size, f);
|
|
buf[size] = 0;
|
|
fclose(f);
|
|
|
|
rsputs(buf);
|
|
|
|
} else
|
|
rsprintf("<center><b>Error: file <i>\"%s\"</i> not found</b></center>", file_name);
|
|
}
|
|
|
|
ref[0] = 0;
|
|
if (!isparam("new_entries") || atoi(getparam("new_entries")) == 0) {
|
|
build_ref(ref, sizeof(ref), "", "", "", "1");
|
|
rsprintf
|
|
("<a href=\"%s\"><img align=\"middle\" border=\"0\" src=\"new_entry.png\" alt=\"%s\" title=\"%s\"></a> ",
|
|
ref, loc("Show only new entries"), loc("Show only new entries"));
|
|
} else {
|
|
build_ref(ref, sizeof(ref), "", "", "", "0");
|
|
rsprintf
|
|
("<a href=\"%s\"><img align=\"middle\" border=\"0\" src=\"all_entry.png\" alt=\"%s\" title=\"%s\"></a> ",
|
|
ref, loc("Show all entries"), loc("Show all entries"));
|
|
}
|
|
|
|
if (getcfg(lbs->name, "Quick filter", str, sizeof(str))) {
|
|
|
|
n = strbreak(str, list, MAX_N_LIST, ",", FALSE);
|
|
|
|
if (getcfg(lbs->name, "Case sensitive search", str, sizeof(str)) && atoi(str))
|
|
rsprintf("<input type=hidden name=\"casesensitive\" value=1>\n");
|
|
|
|
for (index = 0; index < n; index++) {
|
|
/* find according attribute index */
|
|
for (attr_index = 0; attr_index < lbs->n_attr; attr_index++)
|
|
if (strieq(list[index], attr_list[attr_index]))
|
|
break;
|
|
|
|
if (attr_index == lbs->n_attr && !strieq(list[index], "Date") && !strieq(list[index], "Subtext")
|
|
&& !strieq(list[index], "ID")) {
|
|
rsprintf("Error: Attribute \"%s\" for quick filter not found", list[index]);
|
|
attr_index = 0;
|
|
}
|
|
|
|
if (strieq(list[index], "Date")) {
|
|
i = isparam("last") ? atoi(getparam("last")) : 0;
|
|
|
|
if (i == 0 && getcfg(lbs->name, "Last default", str, sizeof(str))) {
|
|
i = atoi(str);
|
|
setparam("last", str);
|
|
}
|
|
|
|
rsprintf("<select title=\"%s\" name=last onChange=\"document.form1.submit()\">\n",
|
|
loc("Select period"));
|
|
|
|
rsprintf("<option value=\"_all_\">-- %s --\n", loc("All entries"));
|
|
|
|
rsprintf("<option %s value=1>%s\n", i == 1 ? "selected" : "", loc("Last day"));
|
|
rsprintf("<option %s value=3>%s\n", i == 3 ? "selected" : "", loc("Last 3 Days"));
|
|
rsprintf("<option %s value=7>%s\n", i == 7 ? "selected" : "", loc("Last week"));
|
|
rsprintf("<option %s value=31>%s\n", i == 31 ? "selected" : "", loc("Last month"));
|
|
rsprintf("<option %s value=92>%s\n", i == 92 ? "selected" : "", loc("Last 3 Months"));
|
|
rsprintf("<option %s value=182>%s\n", i == 182 ? "selected" : "", loc("Last 6 Months"));
|
|
rsprintf("<option %s value=364>%s\n", i == 364 ? "selected" : "", loc("Last Year"));
|
|
|
|
rsprintf("</select>\n");
|
|
} else if (strieq(attr_options[attr_index][0], "boolean")) {
|
|
|
|
sprintf(str, loc("Select %s"), list[index]);
|
|
rsprintf("<select title=\"%s\" name=\"%s\" onChange=\"document.form1.submit()\">\n", str,
|
|
list[index]);
|
|
|
|
rsprintf("<option value=\"_all_\">-- %s --\n", list[index]);
|
|
|
|
if (isparam(attr_list[attr_index]) && strieq("1", getparam(attr_list[attr_index])))
|
|
rsprintf("<option selected value=\"1\">1\n");
|
|
else
|
|
rsprintf("<option value=\"1\">1\n");
|
|
|
|
if (isparam(attr_list[attr_index]) && strieq("0", getparam(attr_list[attr_index])))
|
|
rsprintf("<option selected value=\"0\">0\n");
|
|
else
|
|
rsprintf("<option value=\"0\">0\n");
|
|
|
|
rsprintf("</select>\n");
|
|
} else {
|
|
|
|
/* check if attribute has options */
|
|
if (attr_list[attr_index][0] == 0 || attr_options[attr_index][0][0] == 0) {
|
|
|
|
if (attr_flags[attr_index] & (AF_DATE | AF_DATETIME)) {
|
|
|
|
rsprintf("<select name=\"%s\" onChange=\"document.form1.submit()\">\n", list[index]);
|
|
|
|
i = isparam(list[index]) ? atoi(getparam(list[index])) : 0;
|
|
|
|
rsprintf("<option %s value=-364>%s %s\n", i == -364 ? "selected" : "", loc("Last"),
|
|
loc("Year"));
|
|
rsprintf("<option %s value=-182>%s %s\n", i == -182 ? "selected" : "", loc("Last"),
|
|
loc("6 Months"));
|
|
rsprintf("<option %s value=-92>%s %s\n", i == -92 ? "selected" : "", loc("Last"),
|
|
loc("3 Months"));
|
|
rsprintf("<option %s value=-31>%s %s\n", i == -31 ? "selected" : "", loc("Last"),
|
|
loc("Month"));
|
|
rsprintf("<option %s value=-7>%s %s\n", i == -7 ? "selected" : "", loc("Last"),
|
|
loc("Week"));
|
|
rsprintf("<option %s value=-3>%s %s\n", i == -3 ? "selected" : "", loc("Last"),
|
|
loc("3 Days"));
|
|
rsprintf("<option %s value=-1>%s %s\n", i == -1 ? "selected" : "", loc("Last"), loc("Day"));
|
|
|
|
strlcpy(litem, list[index], sizeof(litem));
|
|
snprintf(str, sizeof(str), "-- %s --", litem);
|
|
rsprintf("<option %s value=\"_all_\">%s\n", i == 0 ? "selected" : "", str);
|
|
|
|
rsprintf("<option %s value=1>%s %s\n", i == 1 ? "selected" : "", loc("Next"), loc("Day"));
|
|
rsprintf("<option %s value=3>%s %s\n", i == 3 ? "selected" : "", loc("Next"),
|
|
loc("3 Days"));
|
|
rsprintf("<option %s value=7>%s %s\n", i == 7 ? "selected" : "", loc("Next"), loc("Week"));
|
|
rsprintf("<option %s value=31>%s %s\n", i == 31 ? "selected" : "", loc("Next"),
|
|
loc("Month"));
|
|
rsprintf("<option %s value=92>%s %s\n", i == 92 ? "selected" : "", loc("Next"),
|
|
loc("3 Months"));
|
|
rsprintf("<option %s value=182>%s %s\n", i == 182 ? "selected" : "", loc("Next"),
|
|
loc("6 Months"));
|
|
rsprintf("<option %s value=364>%s %s\n", i == 364 ? "selected" : "", loc("Next"),
|
|
loc("Year"));
|
|
|
|
rsprintf("</select>\n");
|
|
} else {
|
|
if (strieq(list[index], "Subtext")) {
|
|
rsprintf
|
|
("<input onClick=\"this.value='';\" title=\"%s\" type=text onChange=\"document.form1.submit()\"",
|
|
loc("Enter text"));
|
|
sprintf(str, "-- %s --", loc("Text"));
|
|
if (isparam(list[index]) && *getparam(list[index]))
|
|
strencode2(str, getparam(list[index]), sizeof(str));
|
|
|
|
rsprintf(" name=\"Subtext\" value=\"%s\">\n", str);
|
|
} else {
|
|
sprintf(str, loc("Enter %s"), list[index]);
|
|
rsprintf
|
|
("<input onClick=\"this.value='';\" title=\"%s\" type=text onChange=\"document.form1.submit()\"",
|
|
str);
|
|
strlcpy(litem, list[index], sizeof(litem));
|
|
snprintf(str, sizeof(str), "-- %s --", litem);
|
|
if (isparam(list[index]) && *getparam(list[index]))
|
|
strencode2(str, getparam(list[index]), sizeof(str));
|
|
|
|
rsprintf(" name=\"%s\" value=\"%s\">\n", list[index], str);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
sprintf(str, loc("Select %s"), list[index]);
|
|
rsprintf("<select title=\"%s\" name=\"%s\" onChange=\"document.form1.submit()\">\n", str,
|
|
list[index]);
|
|
|
|
rsprintf("<option value=\"_all_\">-- %s --\n", list[index]);
|
|
|
|
for (j = 0; j < MAX_N_LIST && attr_options[attr_index][j][0]; j++) {
|
|
comment[0] = 0;
|
|
if (attr_flags[attr_index] & AF_ICON) {
|
|
sprintf(str, "Icon comment %s", attr_options[attr_index][j]);
|
|
getcfg(lbs->name, str, comment, sizeof(comment));
|
|
}
|
|
|
|
if (comment[0] == 0)
|
|
strcpy(comment, attr_options[attr_index][j]);
|
|
|
|
for (i1 = i2 = 0; i1 <= (int) strlen(comment); i1++) {
|
|
if (comment[i1] == '(') {
|
|
option[i2++] = '\\';
|
|
option[i2++] = '(';
|
|
} else if (comment[i1] == '{') {
|
|
option[i2] = 0;
|
|
break;
|
|
} else
|
|
option[i2++] = comment[i1];
|
|
}
|
|
|
|
sprintf(option_whole, "^%s$", option);
|
|
if (isparam(attr_list[attr_index]) &&
|
|
(strieq(option, getparam(attr_list[attr_index])) ||
|
|
strieq(option_whole, getparam(attr_list[attr_index]))))
|
|
rsprintf("<option selected value=\"%s\">%s\n", option_whole, option);
|
|
else
|
|
rsprintf("<option value=\"%s\">%s\n", option_whole, option);
|
|
}
|
|
|
|
rsprintf("</select> \n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* show button if JavaScript is switched off */
|
|
rsprintf("<noscript>\n");
|
|
rsprintf("<input type=submit value=\"%s\">\n", loc("Search"));
|
|
rsprintf("</noscript>\n");
|
|
|
|
/* show submit button for IE (otherwise <Return> will not work) */
|
|
if (strstr(browser, "MSIE"))
|
|
rsprintf("<input type=submit value=\"%s\">\n", loc("Search"));
|
|
}
|
|
|
|
rsprintf(" <b>%d %s</b> ", n_msg, loc("Entries"));
|
|
|
|
rsprintf("</td></tr></table></td></tr>\n\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_page_navigation(LOGBOOK *lbs, int n_msg, int page_n, int n_page) {
|
|
int i, num_pages, skip, max_n_msg;
|
|
char ref[256], str[256];
|
|
|
|
if (!page_n || n_msg <= n_page)
|
|
return;
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu3\">\n");
|
|
|
|
rsprintf("%s \n", loc("Goto page"));
|
|
|
|
if (page_n > 1) {
|
|
sprintf(ref, "page%d", page_n - 1);
|
|
build_ref(ref, sizeof(ref), "", "", "", "");
|
|
|
|
rsprintf("<a href=\"%s\">%s</a> ", ref, loc("Previous"));
|
|
}
|
|
|
|
if (page_n && n_msg > n_page) {
|
|
/* number of pages */
|
|
num_pages = (n_msg - 1) / n_page + 1;
|
|
skip = FALSE;
|
|
|
|
for (i = 1; i <= num_pages; i++) {
|
|
sprintf(ref, "page%d", i);
|
|
build_ref(ref, sizeof(ref), "", "", "", "");
|
|
|
|
if (i <= 3 || (i >= page_n - 1 && i <= page_n + 1) || i >= num_pages - 2) {
|
|
if (i > 1 && !skip)
|
|
rsprintf(", \n");
|
|
|
|
skip = FALSE;
|
|
} else {
|
|
if (!skip)
|
|
rsprintf(" ... ");
|
|
|
|
skip = TRUE;
|
|
}
|
|
|
|
if (skip)
|
|
continue;
|
|
|
|
if (page_n == i)
|
|
rsprintf("%d", i);
|
|
else
|
|
rsprintf("<a href=\"%s\">%d</a>", ref, i);
|
|
|
|
/*
|
|
if (i == num_pages )
|
|
rsprintf(" \n");
|
|
else
|
|
rsprintf(", ");
|
|
*/
|
|
}
|
|
|
|
rsprintf(" \n");
|
|
}
|
|
|
|
if (page_n != -1 && n_page < n_msg && page_n * n_page < n_msg) {
|
|
sprintf(ref, "page%d", page_n + 1);
|
|
build_ref(ref, sizeof(ref), "", "", "", "");
|
|
|
|
rsprintf("<a href=\"%s\">%s</a> ", ref, loc("Next"));
|
|
}
|
|
|
|
if (getcfg(lbs->name, "All display limit", str, sizeof(str)))
|
|
max_n_msg = atoi(str);
|
|
else
|
|
max_n_msg = 500;
|
|
|
|
if (page_n != -1 && n_page < n_msg && n_msg < max_n_msg) {
|
|
sprintf(ref, "page");
|
|
build_ref(ref, sizeof(ref), "", "", "", "");
|
|
|
|
rsprintf("<a href=\"%s\">%s</a>\n", ref, loc("All"));
|
|
}
|
|
|
|
rsprintf("</span></td></tr>\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_select_navigation(LOGBOOK *lbs) {
|
|
int i, n_log;
|
|
char str[NAME_LENGTH];
|
|
char lbk_list[MAX_N_LIST][NAME_LENGTH];
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu4\">\n");
|
|
|
|
rsprintf("<script language=\"JavaScript\" type=\"text/javascript\">\n");
|
|
rsprintf("<!--\n");
|
|
rsprintf("function ToggleAll()\n");
|
|
rsprintf(" {\n");
|
|
rsprintf(" for (var i = 0; i < document.form1.elements.length; i++)\n");
|
|
rsprintf(" {\n");
|
|
rsprintf
|
|
(" if (document.form1.elements[i].type == 'checkbox' && document.form1.elements[i].disabled == false)\n");
|
|
rsprintf(" document.form1.elements[i].checked = !(document.form1.elements[i].checked);\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" }\n");
|
|
rsprintf("//-->\n");
|
|
rsprintf("</script>\n");
|
|
|
|
rsprintf("%s: \n", loc("Selected entries"));
|
|
|
|
rsprintf("<input type=button value=\"%s\" onClick=\"ToggleAll();\">\n", loc("Toggle all"));
|
|
|
|
if (!getcfg(lbs->name, "Menu commands", str, sizeof(str)) || stristr(str, "Delete")) {
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Delete"));
|
|
}
|
|
|
|
if (!getcfg(lbs->name, "Menu commands", str, sizeof(str)) || stristr(str, "Edit")) {
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Edit"));
|
|
}
|
|
|
|
if (getcfg(lbs->name, "Menu commands", str, sizeof(str)) && stristr(str, "Copy to")) {
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Copy to"));
|
|
rsprintf("<select name=destc>\n");
|
|
|
|
if (getcfg(lbs->name, "Copy to", str, sizeof(str))) {
|
|
n_log = strbreak(str, lbk_list, MAX_N_LIST, ",", FALSE);
|
|
|
|
for (i = 0; i < n_log; i++)
|
|
rsprintf("<option value=\"%s\">%s\n", lbk_list[i], lbk_list[i]);
|
|
} else {
|
|
for (i = 0;; i++) {
|
|
if (!enumgrp(i, str))
|
|
break;
|
|
|
|
if (!is_logbook(str))
|
|
continue;
|
|
|
|
if (strieq(str, lbs->name))
|
|
continue;
|
|
|
|
rsprintf("<option value=\"%s\">%s\n", str, str);
|
|
}
|
|
}
|
|
rsprintf("</select>\n");
|
|
}
|
|
|
|
if (getcfg(lbs->name, "Menu commands", str, sizeof(str)) && stristr(str, "Move to")) {
|
|
rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Move to"));
|
|
rsprintf("<select name=destm>\n");
|
|
|
|
if (getcfg(lbs->name, "Move to", str, sizeof(str))) {
|
|
n_log = strbreak(str, lbk_list, MAX_N_LIST, ",", FALSE);
|
|
|
|
for (i = 0; i < n_log; i++)
|
|
rsprintf("<option value=\"%s\">%s\n", lbk_list[i], lbk_list[i]);
|
|
} else {
|
|
for (i = 0;; i++) {
|
|
if (!enumgrp(i, str))
|
|
break;
|
|
|
|
if (!is_logbook(str))
|
|
continue;
|
|
|
|
if (strieq(str, lbs->name))
|
|
continue;
|
|
|
|
rsprintf("<option value=\"%s\">%s\n", str, str);
|
|
}
|
|
}
|
|
rsprintf("</select>\n");
|
|
}
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
time_t retrieve_date(const char *index, BOOL bstart) {
|
|
int year, month, day, hour, min, sec, current_year, current_month;
|
|
char pm[10], py[10], pd[10], ph[10], pn[10], ps[10], str[NAME_LENGTH], str2[NAME_LENGTH];
|
|
struct tm tms;
|
|
time_t ltime;
|
|
|
|
sprintf(pm, "m%s", index);
|
|
sprintf(py, "y%s", index);
|
|
sprintf(pd, "d%s", index);
|
|
sprintf(ph, "h%s", index);
|
|
sprintf(pn, "n%s", index);
|
|
sprintf(ps, "c%s", index);
|
|
|
|
time(<ime);
|
|
memcpy(&tms, localtime(<ime), sizeof(tms));
|
|
current_year = tms.tm_year + 1900;
|
|
current_month = tms.tm_mon + 1;
|
|
|
|
if (!isparam(pm) && !isparam(py) && !isparam(pd))
|
|
return 0;
|
|
|
|
/* if year not given, use current year */
|
|
if (!isparam(py))
|
|
year = current_year;
|
|
else
|
|
year = atoi(getparam(py));
|
|
if (year < 1970) {
|
|
sprintf(str, "Error: Year %s out of range", getparam(py));
|
|
strencode2(str2, str, sizeof(str2));
|
|
show_error(str2);
|
|
return -1;
|
|
}
|
|
|
|
/* if month not given, use current month */
|
|
if (isparam(pm)) {
|
|
month = atoi(getparam(pm));
|
|
} else
|
|
month = current_month;
|
|
|
|
if (isparam(pd))
|
|
day = atoi(getparam(pd));
|
|
else {
|
|
/* if day not given, use 1 if start date */
|
|
if (bstart)
|
|
day = 1;
|
|
else {
|
|
/* use last day of month */
|
|
memset(&tms, 0, sizeof(struct tm));
|
|
tms.tm_year = year - 1900;
|
|
tms.tm_mon = month - 1 + 1;
|
|
tms.tm_mday = 1;
|
|
tms.tm_hour = 12;
|
|
|
|
if (tms.tm_year < 90)
|
|
tms.tm_year += 100;
|
|
ltime = mktime(&tms);
|
|
ltime -= 3600 * 24;
|
|
memcpy(&tms, localtime(<ime), sizeof(struct tm));
|
|
day = tms.tm_mday;
|
|
}
|
|
|
|
}
|
|
|
|
/* if hour not given, use 0 */
|
|
if (isparam(ph)) {
|
|
hour = atoi(getparam(ph));
|
|
} else
|
|
hour = 0;
|
|
|
|
/* if minute not given, use 0 */
|
|
if (isparam(pn)) {
|
|
min = atoi(getparam(pn));
|
|
} else
|
|
min = 0;
|
|
|
|
/* if second not given, use 0 */
|
|
if (isparam(ps)) {
|
|
sec = atoi(getparam(ps));
|
|
} else
|
|
sec = 0;
|
|
|
|
memset(&tms, 0, sizeof(struct tm));
|
|
tms.tm_year = year - 1900;
|
|
tms.tm_mon = month - 1;
|
|
tms.tm_mday = day;
|
|
tms.tm_hour = hour;
|
|
tms.tm_min = min;
|
|
tms.tm_sec = sec;
|
|
tms.tm_isdst = -1;
|
|
|
|
if (tms.tm_year < 90)
|
|
tms.tm_year += 100;
|
|
|
|
ltime = mktime(&tms);
|
|
|
|
if (!bstart && isparam(ph) == 0)
|
|
/* end time is first second of next day */
|
|
ltime += 3600 * 24;
|
|
|
|
return ltime;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
time_t convert_date(char *date_string) {
|
|
/* convert date string in MM/DD/YY or DD.MM.YY format into Unix time */
|
|
int year, month, day;
|
|
char *p, str[256];
|
|
struct tm tms;
|
|
time_t ltime;
|
|
|
|
strlcpy(str, date_string, sizeof(str));
|
|
month = day = year = 0;
|
|
|
|
if (strchr(str, '[')) {
|
|
ltime = atoi(strchr(str, '[') + 1);
|
|
return ltime;
|
|
} else if (strchr(str, '/')) {
|
|
/* MM/DD/YY format */
|
|
p = strtok(str, "/");
|
|
if (p) {
|
|
month = atoi(p);
|
|
p = strtok(NULL, "/");
|
|
if (p) {
|
|
day = atoi(p);
|
|
p = strtok(NULL, "/");
|
|
if (p)
|
|
year = atoi(p);
|
|
}
|
|
}
|
|
} else if (strchr(str, '.')) {
|
|
/* DD.MM.YY format */
|
|
p = strtok(str, ".");
|
|
if (p) {
|
|
day = atoi(p);
|
|
p = strtok(NULL, ".");
|
|
if (p) {
|
|
month = atoi(p);
|
|
p = strtok(NULL, ".");
|
|
if (p)
|
|
year = atoi(p);
|
|
}
|
|
}
|
|
} else
|
|
return 0;
|
|
|
|
/* calculate years */
|
|
if (year > 1900) /* 1900-2100 */
|
|
year += 0;
|
|
else if (year < 70) /* 00-69 */
|
|
year += 2000;
|
|
else if (year < 100) /* 70-99 */
|
|
year += 1900;
|
|
|
|
/* use last day of month */
|
|
memset(&tms, 0, sizeof(struct tm));
|
|
tms.tm_year = year - 1900;
|
|
tms.tm_mon = month - 1;
|
|
tms.tm_mday = day;
|
|
tms.tm_hour = 12;
|
|
|
|
ltime = mktime(&tms);
|
|
|
|
return ltime;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
time_t convert_datetime(char *date_string) {
|
|
/* convert date string in MM/DD/YY h:m:s AM/PM or DD.MM.YY hh:m:s format into Unix time */
|
|
int year, month, day, hour, min = 0, sec = 0;
|
|
char *p, str[256];
|
|
struct tm tms;
|
|
time_t ltime;
|
|
|
|
strlcpy(str, date_string, sizeof(str));
|
|
month = day = year = 0;
|
|
|
|
if (strchr(str, '[')) {
|
|
ltime = atoi(strchr(str, '[') + 1);
|
|
return ltime;
|
|
} else if (strchr(str, '/')) {
|
|
/* MM/DD/YY format */
|
|
p = strtok(str, "/");
|
|
if (p) {
|
|
month = atoi(p);
|
|
p = strtok(NULL, "/");
|
|
if (p) {
|
|
day = atoi(p);
|
|
p = strtok(NULL, "/");
|
|
if (p)
|
|
year = atoi(p);
|
|
}
|
|
}
|
|
} else if (strchr(str, '.')) {
|
|
/* DD.MM.YY format */
|
|
p = strtok(str, ".");
|
|
if (p) {
|
|
day = atoi(p);
|
|
p = strtok(NULL, ".");
|
|
if (p) {
|
|
month = atoi(p);
|
|
p = strtok(NULL, ".");
|
|
if (p)
|
|
year = atoi(p);
|
|
}
|
|
}
|
|
} else
|
|
return 0;
|
|
|
|
if (!strchr(p, ' '))
|
|
return 0;
|
|
p = strchr(p, ' ') + 1;
|
|
|
|
strlcpy(str, p, sizeof(str));
|
|
p = strtok(str, ":");
|
|
if (p) {
|
|
hour = atoi(p);
|
|
p = strtok(NULL, ":");
|
|
if (p) {
|
|
min = atoi(p);
|
|
p = strtok(NULL, ":");
|
|
if (p)
|
|
sec = atoi(p);
|
|
}
|
|
} else
|
|
return 0;
|
|
|
|
if (stristr(p, "PM") && hour < 12)
|
|
hour += 12;
|
|
|
|
/* calculate years */
|
|
if (year > 1900) /* 1900-2100 */
|
|
year += 0;
|
|
else if (year < 70) /* 00-69 */
|
|
year += 2000;
|
|
else if (year < 100) /* 70-99 */
|
|
year += 1900;
|
|
|
|
/* use last day of month */
|
|
memset(&tms, 0, sizeof(struct tm));
|
|
tms.tm_year = year - 1900;
|
|
tms.tm_mon = month - 1;
|
|
tms.tm_mday = day;
|
|
tms.tm_hour = hour;
|
|
tms.tm_min = min;
|
|
tms.tm_sec = sec;
|
|
|
|
ltime = mktime(&tms);
|
|
|
|
return ltime;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_rss_feed(LOGBOOK *lbs) {
|
|
int i, n, size, index, message_id, offset;
|
|
char str[256], charset[256], url[1000], attrib[MAX_N_ATTR][NAME_LENGTH], date[1000], *text, title[2000],
|
|
slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], draft[1000];
|
|
time_t ltime;
|
|
struct tm *ts;
|
|
|
|
rsprintf("HTTP/1.1 200 Document follows\r\n");
|
|
rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
|
|
|
|
if (!getcfg("global", "charset", charset, sizeof(charset)))
|
|
strcpy(charset, DEFAULT_HTTP_CHARSET);
|
|
|
|
rsprintf("Content-Type: text/xml;charset=%s\r\n", charset);
|
|
rsprintf("\r\n");
|
|
|
|
rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", charset);
|
|
rsprintf("<rss version=\"2.0\">\n");
|
|
rsprintf("<channel>\n");
|
|
|
|
rsprintf("<title>ELOG %s</title>\n", lbs->name);
|
|
|
|
/* retrive URL */
|
|
getcfg(lbs->name, "URL", url, sizeof(url));
|
|
|
|
/* if HTTP request comes from localhost, use localhost as
|
|
absolute link (needed if running on DSL at home) */
|
|
if (!url[0] && strstr(http_host, "localhost")) {
|
|
if (_ssl_flag)
|
|
strcpy(url, "https://localhost");
|
|
else
|
|
strcpy(url, "http://localhost");
|
|
if (elog_tcp_port != 80)
|
|
sprintf(url + strlen(url), ":%d", elog_tcp_port);
|
|
strcat(url, "/");
|
|
}
|
|
|
|
if (!url[0]) {
|
|
/* assemble absolute path from host name and port */
|
|
if (_ssl_flag)
|
|
sprintf(url, "https://%s", host_name);
|
|
else
|
|
sprintf(url, "http://%s", host_name);
|
|
if (elog_tcp_port != 80)
|
|
sprintf(url + strlen(url), ":%d", elog_tcp_port);
|
|
strcat(url, "/");
|
|
}
|
|
|
|
/* add trailing '/' if not present */
|
|
if (url[strlen(url) - 1] != '/')
|
|
strcat(url, "/");
|
|
|
|
strlcat(url, lbs->name_enc, sizeof(url));
|
|
|
|
rsprintf("<link>%s</link>\n", url);
|
|
|
|
if (getcfg(lbs->name, "Comment", str, sizeof(str))) {
|
|
rsprintf("<description>");
|
|
xmlencode(str);
|
|
rsprintf("</description>\n");
|
|
}
|
|
|
|
rsprintf("<generator>ELOG V%s</generator>\n", VERSION);
|
|
|
|
rsprintf("<image>\n");
|
|
rsprintf("<url>%s/elog.png</url>\n", url);
|
|
rsprintf("<title>ELOG %s</title>\n", lbs->name_enc);
|
|
rsprintf("<link>%s</link>\n", url);
|
|
rsprintf("</image>\n");
|
|
|
|
/*---- show last <n> items ----*/
|
|
|
|
n = 15;
|
|
if (getcfg(lbs->name, "RSS Entries", str, sizeof(str)))
|
|
n = atoi(str);
|
|
|
|
text = (char *) xmalloc(TEXT_SIZE);
|
|
message_id = el_search_message(lbs, EL_LAST, 0, FALSE);
|
|
for (index = 0; index < n && message_id; index++) {
|
|
|
|
size = TEXT_SIZE;
|
|
el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, NULL, NULL,
|
|
NULL, NULL, NULL, draft);
|
|
|
|
/* skip drafts */
|
|
if (draft[0]) {
|
|
message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);
|
|
continue;
|
|
}
|
|
|
|
/* limit text size to 2k */
|
|
text[2048] = 0;
|
|
|
|
if (getcfg(lbs->name, "RSS Title", title, sizeof(title))) {
|
|
i = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib,
|
|
TRUE);
|
|
sprintf(str, "%d", message_id);
|
|
add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &i);
|
|
add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time",
|
|
date, &i, 0);
|
|
|
|
strsubst_list(title, sizeof(title), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
i);
|
|
} else {
|
|
|
|
title[0] = 0;
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
if (attrib[i][0]) {
|
|
strlcat(title, attrib[i], sizeof(title));
|
|
if (i < lbs->n_attr - 1)
|
|
strlcat(title, ", ", sizeof(title));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
rsprintf("<item>\n");
|
|
|
|
/* convert date to RFC-822 date */
|
|
setlocale(LC_ALL, "C");
|
|
ltime = date_to_ltime(date);
|
|
ts = localtime(<ime);
|
|
assert(ts);
|
|
strftime(str, sizeof(str), "%a, %d %b %Y %H:%M:%S", ts);
|
|
offset = (-(int) my_timezone());
|
|
if (ts->tm_isdst)
|
|
offset += 3600;
|
|
snprintf(date, sizeof(date) - 1, "%s %+03d%02d", str, (int) (offset / 3600), (int) ((abs((int) offset)
|
|
/ 60) % 60));
|
|
getcfg("global", "Language", str, sizeof(str));
|
|
if (str[0])
|
|
setlocale(LC_ALL, str);
|
|
|
|
rsprintf("<title>");
|
|
xmlencode(title);
|
|
rsprintf("</title>\n");
|
|
|
|
rsprintf("<link>");
|
|
strlcpy(str, url, sizeof(str));
|
|
sprintf(str + strlen(str), "/%d", message_id);
|
|
xmlencode(str);
|
|
rsprintf("</link>\n");
|
|
|
|
rsprintf("<description>\n");
|
|
xmlencode(text);
|
|
rsprintf("</description>\n");
|
|
|
|
rsprintf("<pubDate>\n");
|
|
rsprintf("%s", date);
|
|
rsprintf("</pubDate>\n");
|
|
|
|
rsprintf("</item>\n");
|
|
message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);
|
|
}
|
|
|
|
xfree(text);
|
|
rsprintf("</channel>\n");
|
|
rsprintf("</rss>\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void highlight_searchtext(regex_t *re_buf, char *src, char *dst, int hidden) {
|
|
char *pt, *pt1;
|
|
int size, status;
|
|
regmatch_t pmatch[10];
|
|
|
|
dst[0] = 0;
|
|
pt = src; /* original text */
|
|
pt1 = dst; /* text with inserted coloring */
|
|
do {
|
|
status = regexec(re_buf, pt, 10, pmatch, 0);
|
|
if (status != REG_NOMATCH) {
|
|
size = pmatch[0].rm_so;
|
|
|
|
/* abort if zero length match, for example from "m*" */
|
|
if (pmatch[0].rm_eo - pmatch[0].rm_so == 0) {
|
|
status = REG_NOMATCH;
|
|
strcpy(pt1, pt);
|
|
break;
|
|
}
|
|
/* copy first part original text */
|
|
memcpy(pt1, pt, size);
|
|
pt1 += size;
|
|
pt += size;
|
|
|
|
/* add coloring 1st part */
|
|
|
|
/* here: \001='<', \002='>', /003='"', and \004=' ' */
|
|
/* see also rsputs2(char* ) */
|
|
|
|
if (hidden)
|
|
strcpy(pt1, "\001B\004style=\003color:black;background-color:#ffff66\003\002");
|
|
else
|
|
strcpy(pt1, "<B style=\"color:black;background-color:#ffff66\">");
|
|
|
|
pt1 += strlen(pt1);
|
|
|
|
/* copy origial search text */
|
|
size = pmatch[0].rm_eo - pmatch[0].rm_so;
|
|
memcpy(pt1, pt, size);
|
|
pt1 += size;
|
|
pt += size;
|
|
|
|
/* add coloring 2nd part */
|
|
if (hidden)
|
|
strcpy(pt1, "\001/B\002");
|
|
else
|
|
strcpy(pt1, "</B>");
|
|
pt1 += strlen(pt1);
|
|
}
|
|
} while (status != REG_NOMATCH);
|
|
|
|
strcpy(pt1, pt);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
time_t search_last_reply(LOGBOOK *lbs, int *message_id) {
|
|
char reply_to[MAX_REPLY_TO * 10], date[80];
|
|
int n_reply, i, id;
|
|
char *list;
|
|
time_t lt, last;
|
|
|
|
list = (char *) xmalloc(MAX_REPLY_TO * NAME_LENGTH);
|
|
|
|
el_retrieve(lbs, *message_id, date, NULL, NULL, 0, NULL, 0, NULL, reply_to, NULL, NULL, NULL, NULL);
|
|
lt = date_to_ltime(date);
|
|
|
|
/* if no reply, this is the last message in thread */
|
|
if (reply_to[0] == 0) {
|
|
xfree(list);
|
|
return lt;
|
|
}
|
|
|
|
n_reply = strbreak(reply_to, (char (*)[NAME_LENGTH]) list, MAX_REPLY_TO, ",", FALSE);
|
|
last = lt;
|
|
for (i = 0; i < n_reply; i++) {
|
|
id = atoi(list + i * NAME_LENGTH);
|
|
lt = search_last_reply(lbs, &id);
|
|
if (lt > last) {
|
|
last = lt;
|
|
*message_id = id;
|
|
}
|
|
}
|
|
|
|
xfree(list);
|
|
|
|
return last;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_elog_list(LOGBOOK *lbs, int past_n, int last_n, int page_n, BOOL default_page, char *info) {
|
|
int i, j, n, index, size, status, d1, m1, y1, h1, n1, c1, d2, m2, y2, h2, n2, c2, n_line, flags,
|
|
printable, n_logbook, n_display, reverse, numeric,
|
|
n_attr_disp, n_msg, search_all, message_id, n_page, i_start, i_stop, in_reply_to_id,
|
|
page_mid, page_mid_head, level, refresh, disp_attr_flags[MAX_N_ATTR + 4];
|
|
char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], disp_attr[MAX_N_ATTR + 4][NAME_LENGTH], *list, *text,
|
|
*text1, in_reply_to[80], reply_to[MAX_REPLY_TO * 10], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH],
|
|
encoding[80], locked_by[256], str[NAME_LENGTH+100], ref[1700], img[80], comment[NAME_LENGTH], mode[80],
|
|
mid[80], menu_str[1000], menu_item[MAX_N_LIST][NAME_LENGTH], param[NAME_LENGTH], format[80],
|
|
sort_attr[MAX_N_ATTR + 4][NAME_LENGTH], mode_cookie[256], charset[25], sort_item[NAME_LENGTH],
|
|
refr[80], str2[80], draft[256], attr[NAME_LENGTH];
|
|
char *p, *pt1, *pt2, *slist, *svalue, *gattr, line[1024], iattr[1600];
|
|
BOOL show_attachments, threaded, csv, xml, raw, mode_commands, expand, filtering, date_filtering,
|
|
disp_filter, show_text, text_in_attr, searched, found, disp_attr_link[MAX_N_ATTR + 4],
|
|
sort_attributes, show_att_column = 0;
|
|
time_t ltime, ltime_start, ltime_end, now, ltime1, ltime2, entry_ltime;
|
|
struct tm tms, *ptms;
|
|
MSG_LIST *msg_list;
|
|
LOGBOOK *lbs_cur;
|
|
regex_t re_buf[MAX_N_ATTR + 1];
|
|
regmatch_t pmatch[10];
|
|
|
|
/* redirect if empty parameters */
|
|
if (strstr(_cmdline, "=&")) {
|
|
while ((pt1 = strstr(_cmdline, "=&")) != NULL) {
|
|
pt2 = pt1;
|
|
while (*pt1 != '&' && *pt1 != '?')
|
|
pt1--;
|
|
pt1++;
|
|
strcpy(param, pt1);
|
|
param[pt2 - pt1] = 0;
|
|
memmove(pt1, pt2 + 2, strlen(pt2 + 2) + 1);
|
|
|
|
/* remove param from lastcmd if present */
|
|
if ((pt1 = strstr(_cmdline, "lastcmd=")) != NULL) {
|
|
sprintf(str, "%s%%3D", param);
|
|
if ((pt1 = strstr(_cmdline, str)) != NULL) {
|
|
pt2 = pt1 + strlen(str);
|
|
while (*pt2 && *pt2 != '%')
|
|
pt2++;
|
|
if (*pt2 == '%')
|
|
pt2 += 3;
|
|
memmove(pt1, pt2, strlen(pt2) + 1);
|
|
}
|
|
}
|
|
}
|
|
if (_cmdline[strlen(_cmdline) - 1] == '=') {
|
|
pt1 = _cmdline + strlen(_cmdline) - 1;
|
|
while (*pt1 != '&' && *pt1 != '?')
|
|
pt1--;
|
|
pt1++;
|
|
strcpy(param, pt1);
|
|
if (param[strlen(param) - 1] == '=')
|
|
param[strlen(param) - 1] = 0;
|
|
*pt1 = 0;
|
|
|
|
/* remove param from lastcmd if present */
|
|
if ((pt1 = strstr(_cmdline, "lastcmd=")) != NULL) {
|
|
sprintf(str, "%s%%3D", param);
|
|
if ((pt1 = strstr(_cmdline, str)) != NULL) {
|
|
pt2 = pt1 + strlen(str);
|
|
while (*pt2 && *pt2 != '%' && *pt2 != '&')
|
|
pt2++;
|
|
if (*pt2 == '%')
|
|
pt2 += 3;
|
|
memmove(pt1, pt2, strlen(pt2) + 1);
|
|
}
|
|
}
|
|
}
|
|
if (_cmdline[strlen(_cmdline) - 1] == '&')
|
|
_cmdline[strlen(_cmdline) - 1] = 0;
|
|
redirect(lbs, _cmdline);
|
|
return;
|
|
}
|
|
|
|
/* redirect "go" command */
|
|
if (isparam("lastcmd")) {
|
|
|
|
strlcpy(str, getparam("lastcmd"), sizeof(str));
|
|
url_decode(str);
|
|
|
|
/* subsitute "last" in command line from new parameter */
|
|
if (isparam("last")) {
|
|
if (strieq(getparam("last"), "_all_"))
|
|
subst_param(str, sizeof(str), "last", "");
|
|
else
|
|
subst_param(str, sizeof(str), "last", getparam("last"));
|
|
}
|
|
|
|
/* subsitute attributes in command line from new parameter */
|
|
for (i = 0; i < MAX_N_ATTR; i++)
|
|
if (isparam(attr_list[i])) {
|
|
if (strieq(getparam(attr_list[i]), "_all_"))
|
|
subst_param(str, sizeof(str), attr_list[i], "");
|
|
else
|
|
subst_param(str, sizeof(str), attr_list[i], getparam(attr_list[i]));
|
|
}
|
|
|
|
/* do the same for subtext */
|
|
if (isparam("subtext"))
|
|
subst_param(str, sizeof(str), "subtext", getparam("subtext"));
|
|
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/* remove remaining "_all_" in parameters */
|
|
if (isparam("last") && strieq(getparam("last"), "_all_")) {
|
|
strlcpy(str, _cmdline, sizeof(str));
|
|
subst_param(str, sizeof(str), "last", "");
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/* remove remaining "_all_" or empty or "--+<attrib>+--" parameters */
|
|
strlcpy(str, _cmdline, sizeof(str));
|
|
found = 0;
|
|
for (i = 0; i < MAX_N_ATTR; i++) {
|
|
if (isparam(attr_list[i])) {
|
|
if (strieq(getparam(attr_list[i]), "_all_")) {
|
|
subst_param(str, sizeof(str), attr_list[i], "");
|
|
found = 1;
|
|
}
|
|
if (*getparam(attr_list[i]) == 0) {
|
|
subst_param(str, sizeof(str), attr_list[i], "");
|
|
found = 1;
|
|
}
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(ref, "-- %s --", attr);
|
|
if (strieq(getparam(attr_list[i]), ref)) {
|
|
subst_param(str, sizeof(str), attr_list[i], "");
|
|
found = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isparam("subtext")) {
|
|
if (*getparam("subtext") == 0) {
|
|
subst_param(str, sizeof(str), "subtext", "");
|
|
found = 1;
|
|
}
|
|
sprintf(ref, "-- %s --", loc("Text"));
|
|
if (strieq(getparam("subtext"), ref)) {
|
|
subst_param(str, sizeof(str), "subtext", "");
|
|
found = 1;
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
slist = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH);
|
|
svalue = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH);
|
|
gattr = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH);
|
|
list = (char *) xmalloc(10000);
|
|
|
|
printable = isparam("Printable") ? atoi(getparam("Printable")) : 0;
|
|
|
|
/* in printable mode, display all pages */
|
|
if (printable)
|
|
page_n = -1;
|
|
|
|
if (isparam("Reverse"))
|
|
reverse = atoi(getparam("Reverse"));
|
|
else {
|
|
reverse = 0;
|
|
if (getcfg(lbs->name, "Reverse sort", str, sizeof(str)))
|
|
reverse = atoi(str);
|
|
}
|
|
|
|
/* get message ID from "list" command */
|
|
if (isparam("id"))
|
|
page_mid = atoi(getparam("id"));
|
|
else
|
|
page_mid = 0;
|
|
page_mid_head = 0;
|
|
|
|
/* default mode */
|
|
strlcpy(mode, "Summary", sizeof(mode));
|
|
show_attachments = FALSE;
|
|
|
|
/* check for valid page_n */
|
|
if (page_n < -1)
|
|
page_n = 0;
|
|
|
|
if (past_n || last_n || page_n || page_mid || default_page) {
|
|
/* for page display, get mode from config file */
|
|
if (getcfg(lbs->name, "Display Mode", str, sizeof(str)))
|
|
strlcpy(mode, str, sizeof(mode));
|
|
|
|
/* supersede mode from cookie */
|
|
if (isparam("elmode"))
|
|
strlcpy(mode, getparam("elmode"), sizeof(mode));
|
|
|
|
/* supersede mode from direct parameter */
|
|
if (isparam("mode"))
|
|
strlcpy(mode, getparam("mode"), sizeof(mode));
|
|
} else {
|
|
/* for find result, get mode from find form */
|
|
if (isparam("mode"))
|
|
strlcpy(mode, getparam("mode"), sizeof(mode));
|
|
else
|
|
strlcpy(mode, "Full", sizeof(mode));
|
|
}
|
|
|
|
// strip any HTML
|
|
if (strchr(mode, '<'))
|
|
*strchr(mode, '<') = 0;
|
|
if (strchr(mode, '\"'))
|
|
*strchr(mode, '\"') = 0;
|
|
|
|
/* set cookie if mode changed */
|
|
mode_cookie[0] = 0;
|
|
if (strieq(mode, "Summary") || strieq(mode, "Full") || strieq(mode, "Threaded")) {
|
|
if (!isparam("elmode") || !strieq(getparam("elmode"), mode))
|
|
sprintf(mode_cookie, "elmode=%s", mode);
|
|
}
|
|
|
|
threaded = strieq(mode, "threaded");
|
|
csv = strieq(mode, "CSV1") || strieq(mode, "CSV2") || strieq(mode, "CSV3");
|
|
xml = strieq(mode, "XML");
|
|
raw = strieq(mode, "Raw");
|
|
|
|
if (csv || xml || raw) {
|
|
page_n = -1; /* display all pages */
|
|
show_attachments = FALSE; /* hide attachments */
|
|
}
|
|
|
|
/* show attachments in full mode by default */
|
|
if (strieq(mode, "Full"))
|
|
show_attachments = TRUE;
|
|
|
|
/* supersede attachment mode if in cookie */
|
|
if (isparam("elattach"))
|
|
show_attachments = atoi(getparam("elattach"));
|
|
|
|
/* supersede attachment mode if in parameter */
|
|
if (isparam("attach"))
|
|
show_attachments = atoi(getparam("attach"));
|
|
|
|
/* set cookie if attachment mode changed in full view */
|
|
if (mode_cookie[0] == 0 && strieq(mode, "Full")) {
|
|
if (!isparam("elattach") || atoi(getparam("elattach")) != show_attachments)
|
|
sprintf(mode_cookie, "elattach=%d", show_attachments);
|
|
}
|
|
|
|
/*---- convert dates to ltime ----*/
|
|
|
|
time(&now);
|
|
ptms = localtime(&now);
|
|
assert(ptms);
|
|
|
|
ltime_end = ltime_start = 0;
|
|
|
|
d1 = m1 = y1 = h1 = n1 = c1 = d2 = m2 = y2 = h2 = n2 = c2 = 0;
|
|
|
|
if (!past_n && !last_n) {
|
|
|
|
ltime_start = retrieve_date("a", TRUE);
|
|
if (ltime_start < 0) {
|
|
xfree(slist);
|
|
xfree(svalue);
|
|
xfree(gattr);
|
|
xfree(list);
|
|
return;
|
|
}
|
|
|
|
if (ltime_start) {
|
|
memcpy(&tms, localtime(<ime_start), sizeof(struct tm));
|
|
y1 = tms.tm_year + 1900;
|
|
m1 = tms.tm_mon + 1;
|
|
d1 = tms.tm_mday;
|
|
h1 = tms.tm_hour;
|
|
n1 = tms.tm_min;
|
|
c1 = tms.tm_sec;
|
|
}
|
|
|
|
ltime_end = retrieve_date("b", FALSE);
|
|
if (ltime_end < 0) {
|
|
xfree(slist);
|
|
xfree(svalue);
|
|
xfree(gattr);
|
|
xfree(list);
|
|
return;
|
|
}
|
|
|
|
if (ltime_end) {
|
|
|
|
if (ltime_end <= ltime_start) {
|
|
sprintf(str, "Error: Start date after end date");
|
|
show_error(str);
|
|
xfree(slist);
|
|
xfree(svalue);
|
|
xfree(gattr);
|
|
xfree(list);
|
|
return;
|
|
}
|
|
|
|
memcpy(&tms, localtime(<ime_end), sizeof(struct tm));
|
|
y2 = tms.tm_year + 1900;
|
|
m2 = tms.tm_mon + 1;
|
|
d2 = tms.tm_mday;
|
|
h2 = tms.tm_hour;
|
|
n2 = tms.tm_min;
|
|
c2 = tms.tm_sec;
|
|
}
|
|
}
|
|
|
|
if (ltime_start && ltime_end && ltime_start > ltime_end) {
|
|
show_error(loc("Error: start date after end date"));
|
|
xfree(slist);
|
|
xfree(svalue);
|
|
xfree(gattr);
|
|
xfree(list);
|
|
return;
|
|
}
|
|
|
|
/*---- if user present but not allowed, log it out (required when several logbooks are
|
|
used with different access rights and global passwords ----*/
|
|
if (isparam("unm") && !check_login_user(lbs, getparam("unm"))) {
|
|
unsetparam("unm");
|
|
sid_remove(getparam("sid"));
|
|
set_sid_cookie(lbs, "", "");
|
|
}
|
|
|
|
/*---- apply last login cut ----*/
|
|
|
|
if (isparam("new_entries") && atoi(getparam("new_entries")) == 1 && isparam("unm"))
|
|
get_user_line(lbs, getparam("unm"), NULL, NULL, NULL, NULL, <ime_start, NULL);
|
|
|
|
/*---- assemble message list ----*/
|
|
|
|
/* check for search all */
|
|
search_all = isparam("all") ? atoi(getparam("all")) : 0;
|
|
|
|
if (getcfg(lbs->name, "Search all logbooks", str, sizeof(str)) && atoi(str) == 0)
|
|
search_all = 0;
|
|
|
|
n_msg = 0;
|
|
n_display = 0;
|
|
if (search_all) {
|
|
/* count logbooks */
|
|
for (n_logbook = 0;; n_logbook++) {
|
|
if (!lb_list[n_logbook].name[0])
|
|
break;
|
|
|
|
if (lbs->top_group[0] && !strieq(lbs->top_group, lb_list[n_logbook].top_group))
|
|
continue;
|
|
|
|
if (isparam("unm") && !check_login_user(&lb_list[n_logbook], getparam("unm")))
|
|
continue;
|
|
|
|
n_msg += *lb_list[n_logbook].n_el_index;
|
|
}
|
|
} else {
|
|
n_logbook = 1;
|
|
n_msg = *lbs->n_el_index;
|
|
}
|
|
|
|
msg_list = (MSG_LIST *) xmalloc(sizeof(MSG_LIST) * n_msg);
|
|
|
|
lbs_cur = lbs;
|
|
numeric = TRUE;
|
|
for (i = n = 0; i < n_logbook; i++) {
|
|
if (search_all)
|
|
lbs_cur = &lb_list[i];
|
|
|
|
if (lbs->top_group[0] && !strieq(lbs->top_group, lbs_cur->top_group))
|
|
continue;
|
|
|
|
if (isparam("unm") && !check_login_user(lbs_cur, getparam("unm")))
|
|
continue;
|
|
|
|
for (j = 0; j < *lbs_cur->n_el_index; j++) {
|
|
msg_list[n].lbs = lbs_cur;
|
|
msg_list[n].index = j;
|
|
msg_list[n].number = (int) lbs_cur->el_index[j].file_time;
|
|
msg_list[n].in_reply_to = lbs_cur->el_index[j].in_reply_to;
|
|
n++;
|
|
}
|
|
}
|
|
|
|
/*---- apply start/end date cut ----*/
|
|
|
|
date_filtering = FALSE;
|
|
|
|
if (past_n > 0)
|
|
ltime_start = now - 3600 * 24 * past_n; // past n days
|
|
else if (past_n < 0)
|
|
ltime_start = now + 3600 * past_n; // past n hours
|
|
|
|
if (last_n && last_n < n_msg) {
|
|
date_filtering = TRUE;
|
|
for (i = n_msg - last_n - 1; i >= 0; i--)
|
|
msg_list[i].lbs = NULL;
|
|
}
|
|
|
|
if (ltime_start) {
|
|
date_filtering = TRUE;
|
|
for (i = 0; i < n_msg; i++)
|
|
if (msg_list[i].lbs && msg_list[i].lbs->el_index[msg_list[i].index].file_time < ltime_start)
|
|
msg_list[i].lbs = NULL;
|
|
}
|
|
|
|
if (ltime_end) {
|
|
date_filtering = TRUE;
|
|
for (i = 0; i < n_msg; i++)
|
|
if (msg_list[i].lbs && msg_list[i].lbs->el_index[msg_list[i].index].file_time > ltime_end)
|
|
msg_list[i].lbs = NULL;
|
|
}
|
|
|
|
if (isparam("last") || getcfg(lbs->name, "Last default", str, sizeof(str))) {
|
|
date_filtering = TRUE;
|
|
if (isparam("last"))
|
|
n = atoi(getparam("last"));
|
|
else
|
|
n = atoi(str);
|
|
|
|
if (n > 0) {
|
|
for (i = 0; i < n_msg; i++)
|
|
if (msg_list[i].lbs && msg_list[i].lbs->el_index[msg_list[i].index].file_time < now - 3600 * 24
|
|
* n)
|
|
msg_list[i].lbs = NULL;
|
|
}
|
|
}
|
|
|
|
/*---- filter message list ----*/
|
|
|
|
filtering = FALSE;
|
|
show_text = TRUE;
|
|
searched = found = FALSE;
|
|
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
/* check if attribute filter */
|
|
if (isparam(attr_list[i]))
|
|
break;
|
|
|
|
if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {
|
|
sprintf(str, "%da", i);
|
|
if (retrieve_date(str, TRUE))
|
|
break;
|
|
sprintf(str, "%db", i);
|
|
if (retrieve_date(str, TRUE))
|
|
break;
|
|
}
|
|
|
|
if (attr_flags[i] & AF_MULTI) {
|
|
for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "%s_%d", attr, j);
|
|
if (isparam(str)) {
|
|
filtering = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) {
|
|
for (j = 0; j < MAX_N_LIST; j++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "%s_%d", attr, j);
|
|
if (isparam(str)) {
|
|
filtering = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check if sort by attribute */
|
|
if ((isparam("sort") && strieq(getparam("sort"), attr_list[i]))
|
|
|| (isparam("rsort") && strieq(getparam("rsort"), attr_list[i])))
|
|
break;
|
|
}
|
|
|
|
/* turn on filtering if found */
|
|
if (i < lbs->n_attr)
|
|
filtering = TRUE;
|
|
|
|
if (isparam("subtext"))
|
|
filtering = TRUE;
|
|
|
|
if (getcfg(lbs->name, "Sort Attributes", list, 10000))
|
|
filtering = TRUE;
|
|
|
|
text = (char *) xmalloc(TEXT_SIZE);
|
|
text1 = (char *) xmalloc(TEXT_SIZE);
|
|
|
|
/* prepare for regex search */
|
|
memset(re_buf, 0, sizeof(re_buf));
|
|
|
|
/* compile regex for subtext */
|
|
if (isparam("subtext")) {
|
|
strlcpy(str, getparam("subtext"), sizeof(str));
|
|
flags = REG_EXTENDED;
|
|
if (!isparam("casesensitive"))
|
|
flags |= REG_ICASE;
|
|
status = regcomp(re_buf, str, flags);
|
|
if (status) {
|
|
sprintf(line, loc("Error in regular expression \"%s\""), str);
|
|
strlcat(line, ": ", sizeof(line));
|
|
regerror(status, re_buf, str, sizeof(str));
|
|
strlcat(line, str, sizeof(line));
|
|
strencode2(str, line, sizeof(str));
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* compile regex for attributes */
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
if (isparam(attr_list[i])) {
|
|
strlcpy(str, getparam(attr_list[i]), sizeof(str));
|
|
|
|
/* if value starts with '$', substitute it */
|
|
if (str[0] == '$') {
|
|
j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib,
|
|
TRUE);
|
|
add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time",
|
|
date, &j, 0);
|
|
|
|
strsubst_list(str, sizeof(str), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, j);
|
|
setparam(attr_list[i], str);
|
|
}
|
|
|
|
flags = REG_EXTENDED;
|
|
|
|
if (!isparam("casesensitive"))
|
|
flags |= REG_ICASE;
|
|
|
|
status = regcomp(re_buf + i + 1, str, flags);
|
|
if (status) {
|
|
sprintf(line, loc("Error in regular expression \"%s\""), str);
|
|
strlcat(line, ": ", sizeof(line));
|
|
regerror(status, re_buf + i + 1, str, sizeof(str));
|
|
strlcat(line, str, sizeof(line));
|
|
strencode2(str, line, sizeof(str));
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
sort_item[0] = 0;
|
|
if (isparam("sort"))
|
|
strlcpy(sort_item, getparam("sort"), sizeof(sort_item));
|
|
if (isparam("rsort"))
|
|
strlcpy(sort_item, getparam("rsort"), sizeof(sort_item));
|
|
|
|
sort_attributes = getcfg(lbs->name, "Sort Attributes", str, sizeof(str));
|
|
|
|
/* do filtering */
|
|
for (index = 0; index < n_msg; index++) {
|
|
if (!msg_list[index].lbs)
|
|
continue;
|
|
|
|
/* retrieve message */
|
|
size = TEXT_SIZE;
|
|
message_id = msg_list[index].lbs->el_index[msg_list[index].index].message_id;
|
|
|
|
if (filtering) {
|
|
status = el_retrieve(msg_list[index].lbs, message_id, date, attr_list, attrib, lbs->n_attr, text,
|
|
&size, in_reply_to, reply_to, attachment, encoding, locked_by, draft);
|
|
if (status != EL_SUCCESS)
|
|
break;
|
|
|
|
/* apply filter for attributes */
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
|
|
/* replace icon name with their comments if present */
|
|
if (attr_flags[i] & AF_ICON) {
|
|
sprintf(str, "Icon comment %s", attrib[i]);
|
|
if (getcfg(lbs->name, str, comment, sizeof(comment)))
|
|
strlcpy(attrib[i], comment, NAME_LENGTH);
|
|
}
|
|
|
|
/* check for multi attributes */
|
|
if (attr_flags[i] & AF_MULTI) {
|
|
|
|
/* OR of any of the values */
|
|
searched = found = FALSE;
|
|
for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
|
|
sprintf(str, "%s_%d", attr_list[i], j);
|
|
if (isparam(str)) {
|
|
searched = TRUE;
|
|
if (strstr(attrib[i], getparam(str))) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* search for parameter without '_' coming from quick filter */
|
|
if (isparam(attr_list[i])) {
|
|
searched = TRUE;
|
|
|
|
strlcpy(str, getparam(attr_list[i]), sizeof(str));
|
|
if (str[0] == '^' && str[strlen(str) - 1] == '$') {
|
|
str[strlen(str) - 1] = 0;
|
|
strlcpy(comment, str + 1, NAME_LENGTH);
|
|
} else
|
|
strlcpy(comment, str, NAME_LENGTH);
|
|
strlcpy(str, comment, sizeof(str));
|
|
|
|
if (strstr(attrib[i], str))
|
|
found = TRUE;
|
|
}
|
|
|
|
if (searched && !found)
|
|
break;
|
|
}
|
|
|
|
/* check for multi user list or multi user email */
|
|
else if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) {
|
|
|
|
/* OR of any of the values */
|
|
searched = found = FALSE;
|
|
for (j = 0; j < MAX_N_LIST; j++) {
|
|
sprintf(str, "%s_%d", attr_list[i], j);
|
|
if (isparam(str)) {
|
|
searched = TRUE;
|
|
if (strstr(attrib[i], getparam(str))) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* search for parameter without '_' coming from quick filter */
|
|
if (isparam(attr_list[i])) {
|
|
searched = TRUE;
|
|
if (strstr(attrib[i], getparam(attr_list[i])))
|
|
found = TRUE;
|
|
}
|
|
|
|
if (searched && !found)
|
|
break;
|
|
|
|
} else if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {
|
|
|
|
/* check for last[i]/next[i] */
|
|
|
|
ltime = isparam(attr_list[i]) ? atoi(getparam(attr_list[i])) : 0;
|
|
|
|
/* today 12h noon */
|
|
time(&now);
|
|
memcpy(&tms, localtime(&now), sizeof(struct tm));
|
|
tms.tm_hour = 12;
|
|
tms.tm_min = 0;
|
|
tms.tm_sec = 0;
|
|
now = mktime(&tms);
|
|
|
|
/* negative i: last [i] days */
|
|
if (ltime < 0)
|
|
if (atoi(attrib[i]) < now + ltime * 3600 * 24 - 3600 * 12 || atoi(attrib[i]) > now)
|
|
break;
|
|
|
|
/* positive i: next [i] days */
|
|
if (ltime > 0)
|
|
if (atoi(attrib[i]) > now + ltime * 3600 * 24 + 3600 * 12 || atoi(attrib[i]) < now)
|
|
break;
|
|
|
|
/* check for start date / end date */
|
|
sprintf(str, "%da", i);
|
|
ltime = retrieve_date(str, TRUE);
|
|
if (ltime > 0 && atoi(attrib[i]) < ltime)
|
|
break;
|
|
|
|
sprintf(str, "%db", i);
|
|
ltime = retrieve_date(str, FALSE);
|
|
if (ltime > 0 && (atoi(attrib[i]) > ltime || atoi(attrib[i]) == 0))
|
|
break;
|
|
|
|
} else {
|
|
|
|
strlcpy(str, isparam(attr_list[i]) ? getparam(attr_list[i]) : "", sizeof(str));
|
|
|
|
/* if value starts with '$', substitute it */
|
|
if (str[0] == '$') {
|
|
j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
attrib, TRUE);
|
|
sprintf(mid, "%d", message_id);
|
|
add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id",
|
|
mid, &j);
|
|
add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
"entry time", date, &j, 0);
|
|
|
|
strsubst_list(str, sizeof(str), (char (*)[NAME_LENGTH]) slist,
|
|
(char (*)[NAME_LENGTH]) svalue, j);
|
|
setparam(attr_list[i], str);
|
|
}
|
|
|
|
if (isparam(attr_list[i])) {
|
|
status = regexec(re_buf + 1 + i, attrib[i], 10, pmatch, 0);
|
|
if (status == REG_NOMATCH)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i < lbs->n_attr) {
|
|
msg_list[index].lbs = NULL;
|
|
continue;
|
|
}
|
|
|
|
if (isparam("subtext")) {
|
|
|
|
status = regexec(re_buf, text, 10, pmatch, 0);
|
|
if (isparam("sall") && atoi(getparam("sall")) && status == REG_NOMATCH) {
|
|
|
|
// search text in attributes
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
status = regexec(re_buf, attrib[i], 10, pmatch, 0);
|
|
if (status != REG_NOMATCH)
|
|
break;
|
|
}
|
|
|
|
if (i == lbs->n_attr) {
|
|
msg_list[index].lbs = NULL;
|
|
continue;
|
|
}
|
|
} else if (status == REG_NOMATCH) {
|
|
msg_list[index].lbs = NULL;
|
|
continue;
|
|
}
|
|
}
|
|
} // if (filtering)
|
|
|
|
/* evaluate "sort attributes" */
|
|
if (sort_attributes) {
|
|
getcfg(lbs->name, "Sort Attributes", list, 10000);
|
|
msg_list[index].string[0] = 0;
|
|
n = strbreak(list, sort_attr, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n; i++) {
|
|
for (j = 0; j < lbs->n_attr; j++) {
|
|
if (strieq(sort_attr[i], attr_list[j])) {
|
|
strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string));
|
|
strlcat(msg_list[index].string, attrib[j], sizeof(msg_list[index].string));
|
|
if (attr_flags[i] & (AF_NUMERIC | AF_DATETIME | AF_DATE)) {
|
|
msg_list[index].number = atoi(attrib[j]);
|
|
numeric = TRUE;
|
|
} else
|
|
numeric = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (strieq(sort_attr[i], loc("ID"))) {
|
|
strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string));
|
|
sprintf(str, "%08d", message_id);
|
|
strlcat(msg_list[index].string, str, sizeof(msg_list[index].string));
|
|
} else if (strieq(sort_attr[i], loc("Logbook"))) {
|
|
strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string));
|
|
strlcat(msg_list[index].string, msg_list[index].lbs->name, sizeof(msg_list[index].string));
|
|
} else if (strieq(sort_attr[i], loc("Date"))) {
|
|
strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string));
|
|
entry_ltime = date_to_ltime(date);
|
|
sprintf(str, "%08d", (int) entry_ltime);
|
|
strlcat(msg_list[index].string, str, sizeof(msg_list[index].string));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* add attribute for sorting */
|
|
if (sort_item[0]) {
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
if (strieq(sort_item, attr_list[i])) {
|
|
if (attr_flags[i] & (AF_NUMERIC | AF_DATETIME | AF_DATE)) {
|
|
numeric = TRUE;
|
|
msg_list[index].number = atoi(attrib[i]);
|
|
} else {
|
|
numeric = FALSE;
|
|
strlcpy(msg_list[index].string, attrib[i], 256);
|
|
}
|
|
}
|
|
|
|
if (strieq(sort_item, loc("ID"))) {
|
|
numeric = TRUE;
|
|
msg_list[index].number = message_id;
|
|
}
|
|
|
|
if (strieq(sort_item, loc("Logbook")))
|
|
strlcpy(msg_list[index].string, msg_list[index].lbs->name, 256);
|
|
}
|
|
|
|
if (isparam("rsort"))
|
|
reverse = 1;
|
|
|
|
if (isparam("sort"))
|
|
reverse = 0;
|
|
}
|
|
}
|
|
|
|
/*---- in threaded mode, set date of latest entry of thread ----*/
|
|
|
|
if (threaded && !filtering && !date_filtering) {
|
|
for (index = 0; index < n_msg; index++) {
|
|
if (!msg_list[index].lbs)
|
|
continue;
|
|
|
|
message_id = msg_list[index].lbs->el_index[msg_list[index].index].message_id;
|
|
in_reply_to_id = msg_list[index].lbs->el_index[msg_list[index].index].in_reply_to;
|
|
if (!in_reply_to_id)
|
|
continue;
|
|
|
|
do {
|
|
message_id = in_reply_to_id;
|
|
|
|
/* search index of message */
|
|
for (i = 0; i < *msg_list[index].lbs->n_el_index; i++)
|
|
if (msg_list[index].lbs->el_index[i].message_id == message_id)
|
|
break;
|
|
|
|
/* stop if not found */
|
|
if (i == *msg_list[index].lbs->n_el_index)
|
|
break;
|
|
|
|
in_reply_to_id = msg_list[index].lbs->el_index[i].in_reply_to;
|
|
|
|
} while (in_reply_to_id);
|
|
|
|
/* if head not found, skip message */
|
|
if (i == *msg_list[index].lbs->n_el_index) {
|
|
msg_list[index].lbs = NULL;
|
|
continue;
|
|
}
|
|
|
|
/* set new page message ID with head message */
|
|
if (page_mid && msg_list[index].lbs->el_index[msg_list[index].index].message_id == page_mid)
|
|
page_mid_head = message_id;
|
|
|
|
/* search message head in list */
|
|
for (j = 0; j < n_msg; j++)
|
|
if (msg_list[j].lbs == msg_list[index].lbs && msg_list[j].index == i)
|
|
break;
|
|
|
|
if (j < index) {
|
|
/* set date from current message, if later */
|
|
if (msg_list[j].number < msg_list[index].number)
|
|
msg_list[j].number = msg_list[index].number;
|
|
}
|
|
|
|
/* now delete current message, to leave only heads in list */
|
|
msg_list[index].lbs = NULL;
|
|
}
|
|
}
|
|
|
|
/*---- compact messasges ----*/
|
|
|
|
for (i = j = 0; i < n_msg; i++)
|
|
if (msg_list[i].lbs)
|
|
memcpy(&msg_list[j++], &msg_list[i], sizeof(MSG_LIST));
|
|
n_msg = j;
|
|
|
|
/*---- sort messasges ----*/
|
|
|
|
if (numeric)
|
|
qsort(msg_list, n_msg, sizeof(MSG_LIST), reverse ? msg_compare_reverse_numeric : msg_compare_numeric);
|
|
else
|
|
qsort(msg_list, n_msg, sizeof(MSG_LIST), reverse ? msg_compare_reverse : msg_compare);
|
|
|
|
/*---- search page for specific message ----*/
|
|
|
|
if (getcfg(lbs->name, "Entries per page", str, sizeof(str)))
|
|
n_page = atoi(str);
|
|
else
|
|
n_page = 20;
|
|
if (isparam("npp")) {
|
|
n_page = atoi(getparam("npp"));
|
|
if (n_page < 1)
|
|
n_page = 1;
|
|
if (n_page > 100000)
|
|
n_page = 100000;
|
|
}
|
|
|
|
if (page_mid) {
|
|
default_page = 0;
|
|
|
|
for (i = 0; i < n_msg; i++)
|
|
if (msg_list[i].lbs->el_index[msg_list[i].index].message_id == page_mid
|
|
|| msg_list[i].lbs->el_index[msg_list[i].index].message_id == page_mid_head)
|
|
break;
|
|
|
|
if (i < n_msg)
|
|
page_n = i / n_page + 1;
|
|
}
|
|
|
|
/*---- number of messages per page ----*/
|
|
|
|
n_attr_disp = n_line = 0;
|
|
i_start = 0;
|
|
i_stop = n_msg - 1;
|
|
if (!csv && !xml && !raw) {
|
|
if (page_n || default_page) {
|
|
if (default_page && page_n != -1)
|
|
page_n = reverse ? 1 : (n_msg - 1) / n_page + 1;
|
|
|
|
if (page_n != -1) {
|
|
i_start = (page_n - 1) * n_page;
|
|
i_stop = i_start + n_page - 1;
|
|
|
|
if (i_start >= n_msg && n_msg > 0) {
|
|
page_n = 1;
|
|
i_start = 0;
|
|
}
|
|
|
|
if (i_stop >= n_msg)
|
|
i_stop = n_msg - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---- header ----*/
|
|
|
|
if (getcfg(lbs->name, "List Page Title", str, sizeof(str))) {
|
|
i = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, NULL, TRUE);
|
|
strsubst_list(str, sizeof(str), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, i);
|
|
strip_html(str);
|
|
} else
|
|
sprintf(str, "ELOG %s", lbs->name);
|
|
|
|
if (csv) {
|
|
|
|
/* no menus and tables */
|
|
show_plain_header(0, "export.csv");
|
|
|
|
rsprintf("\"%s\"", loc("Message ID"));
|
|
if (strieq(mode, "CSV1"))
|
|
rsprintf(",");
|
|
else
|
|
rsprintf(";");
|
|
|
|
rsprintf("\"%s\"", loc("Date"));
|
|
if (strieq(mode, "CSV1"))
|
|
rsprintf(",");
|
|
else
|
|
rsprintf(";");
|
|
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(str, attr_list[i], sizeof(str));
|
|
if (str[0]) {
|
|
rsputs("\"");
|
|
pt1 = str;
|
|
while ((pt2 = strchr(pt1, '"')) != NULL) {
|
|
*pt2 = 0;
|
|
rsputs(pt1);
|
|
rsputs("\"\"");
|
|
pt1 = pt2 + 1;
|
|
}
|
|
rsputs(pt1);
|
|
rsputs("\"");
|
|
}
|
|
if (i < lbs->n_attr - 1) {
|
|
if (strieq(mode, "CSV1"))
|
|
rsprintf(",");
|
|
else
|
|
rsprintf(";");
|
|
} else {
|
|
if (strieq(mode, "CSV3"))
|
|
rsprintf(";\"Text\"");
|
|
rsprintf("\r\n");
|
|
}
|
|
}
|
|
|
|
} else if (xml) {
|
|
|
|
/* no menus and tables */
|
|
show_plain_header(0, "export.xml");
|
|
if (!getcfg("global", "charset", charset, sizeof(charset)))
|
|
strcpy(charset, DEFAULT_HTTP_CHARSET);
|
|
rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", charset);
|
|
rsprintf("<!-- ELOGD Version %s export.xml -->\n", VERSION);
|
|
rsprintf("<ELOG_LIST>\n");
|
|
|
|
} else if (raw) {
|
|
|
|
/* no menus and tables */
|
|
show_plain_header(0, "export.txt");
|
|
|
|
} else {
|
|
|
|
if (getcfg(lbs->name, "Refresh", refr, sizeof(refr)))
|
|
refresh = atoi(refr);
|
|
else
|
|
refresh = 0;
|
|
show_standard_header(lbs, TRUE, str, NULL, TRUE, mode_cookie, NULL, refresh);
|
|
|
|
/*---- title ----*/
|
|
|
|
strlcpy(str, ", ", sizeof(str));
|
|
if (past_n == 1)
|
|
strcat(str, loc("Last day"));
|
|
else if (past_n > 1)
|
|
sprintf(str + strlen(str), loc("Last %d days"), past_n);
|
|
else if (past_n < 0)
|
|
sprintf(str + strlen(str), loc("Last %d hours"), -past_n);
|
|
else if (last_n)
|
|
sprintf(str + strlen(str), loc("Last %d entries"), last_n);
|
|
else if (page_n == -1)
|
|
strlcpy(str + strlen(str), loc("all entries"), sizeof(str) - strlen(str));
|
|
else if (page_n)
|
|
sprintf(str + strlen(str), loc("Page %d of %d"), page_n, (n_msg - 1) / n_page + 1);
|
|
if (strlen(str) == 2)
|
|
str[0] = 0;
|
|
|
|
if (printable)
|
|
show_standard_title(lbs, str, 1);
|
|
else
|
|
show_standard_title(lbs, str, 0);
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
if (!printable) {
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
/* current command line for select command */
|
|
strlcpy(str, isparam("cmdline") ? getparam("cmdline") : "", sizeof(str));
|
|
|
|
/* remove select switch */
|
|
if (strstr(str, "select=1")) {
|
|
*strstr(str, "select=1") = 0;
|
|
if (strlen(str) > 1 && (str[strlen(str) - 1] == '&' || str[strlen(str) - 1] == '?'))
|
|
str[strlen(str) - 1] = 0;
|
|
}
|
|
|
|
/* store current command line as hidden parameter for page navigation */
|
|
if (str[0] && !strieq(str, "?")) {
|
|
rsprintf("<input type=hidden name=lastcmd value=\"");
|
|
rsputs3(str);
|
|
rsprintf("\">\n", str);
|
|
}
|
|
|
|
if (!getcfg(lbs->name, "Guest Find menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs))
|
|
getcfg(lbs->name, "Find menu commands", menu_str, sizeof(menu_str));
|
|
|
|
if (!menu_str[0]) {
|
|
if (!getcfg(lbs->name, "Guest list menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs))
|
|
getcfg(lbs->name, "list menu commands", menu_str, sizeof(menu_str));
|
|
}
|
|
|
|
/* default menu commands */
|
|
if (menu_str[0] == 0) {
|
|
strlcpy(menu_str, "New, Find, Select, Import, ", sizeof(menu_str));
|
|
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str)))
|
|
strlcat(menu_str, "Config, Logout, ", sizeof(menu_str));
|
|
else
|
|
strlcat(menu_str, "Config, ", sizeof(menu_str));
|
|
|
|
if (getcfg(lbs->name, "Mirror server", str, sizeof(str)))
|
|
strlcat(menu_str, "Synchronize, ", sizeof(menu_str));
|
|
|
|
strlcpy(str, loc("Last x"), sizeof(str));
|
|
strlcat(menu_str, "Last x, Help, ", sizeof(menu_str));
|
|
}
|
|
|
|
n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
if (is_user_allowed(lbs, menu_item[i])) {
|
|
if (strieq(menu_item[i], "Last x")) {
|
|
if (past_n > 0) {
|
|
sprintf(str, loc("Last %d days"), past_n * 2);
|
|
rsprintf(" <a href=\"past%d?mode=%s\">%s</a> |\n", past_n * 2, mode, str);
|
|
} else {
|
|
strlcpy(str, loc("Last day"), sizeof(str));
|
|
rsprintf(" <a href=\"past1?mode=%s\">%s</a> |\n", mode, str);
|
|
}
|
|
|
|
if (last_n) {
|
|
sprintf(str, loc("Last %d entries"), last_n * 2);
|
|
rsprintf(" <a href=\"last%d?mode=%s\">%s</a> |\n", last_n * 2, mode, str);
|
|
}
|
|
} else if (strieq(menu_item[i], "Select")) {
|
|
strlcpy(str, getparam("cmdline"), sizeof(str));
|
|
if (isparam("select") && atoi(getparam("select")) == 1) {
|
|
/* remove select switch */
|
|
if (strstr(str, "select=1")) {
|
|
*strstr(str, "select=1") = 0;
|
|
if (strlen(str) > 1 && (str[strlen(str) - 1] == '&' || str[strlen(str) - 1] == '?'))
|
|
str[strlen(str) - 1] = 0;
|
|
}
|
|
} else {
|
|
/* add select switch */
|
|
if (strchr(str, '?'))
|
|
strcat(str, "&select=1");
|
|
else
|
|
strcat(str, "?select=1");
|
|
}
|
|
rsprintf(" <a href=\"");
|
|
rsputs3(str);
|
|
rsprintf("\">%s</a> |\n", loc("Select"));
|
|
} else {
|
|
strlcpy(str, loc(menu_item[i]), sizeof(str));
|
|
url_encode(str, sizeof(str));
|
|
|
|
if (i < n - 1)
|
|
rsprintf(" <a href=\"?cmd=%s\">%s</a> |\n", str, loc(menu_item[i]));
|
|
else
|
|
rsprintf(" <a href=\"?cmd=%s\">%s</a> \n", str, loc(menu_item[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
rsprintf("</span></td></tr>\n\n");
|
|
}
|
|
|
|
/*---- list menu text ----*/
|
|
|
|
if ((getcfg(lbs->name, "find menu text", str, sizeof(str)) || getcfg(lbs->name, "list menu text", str,
|
|
sizeof(str))) && !printable) {
|
|
FILE *f;
|
|
char file_name[256], *buf;
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
/* check if file starts with an absolute directory */
|
|
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));
|
|
}
|
|
|
|
f = fopen(file_name, "rb");
|
|
if (f != NULL) {
|
|
fseek(f, 0, SEEK_END);
|
|
size = TELL(fileno(f));
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
buf = (char *) xmalloc(size + 1);
|
|
fread(buf, 1, size, f);
|
|
buf[size] = 0;
|
|
fclose(f);
|
|
|
|
rsputs(buf);
|
|
|
|
} else
|
|
rsprintf("<center><b>Error: file <i>\"%s\"</i> not found</b></center>", file_name);
|
|
|
|
rsprintf("</span></td></tr>");
|
|
}
|
|
|
|
/*---- display filters ----*/
|
|
|
|
disp_filter = isparam("ma") || isparam("ya") || isparam("da") || isparam("mb") || isparam("yb")
|
|
|| isparam("db") || isparam("subtext") || isparam("last");
|
|
|
|
for (i = 0; i < lbs->n_attr; i++)
|
|
if (isparam(attr_list[i]) && (attr_flags[i] & (AF_DATE | AF_DATETIME)) == 0)
|
|
disp_filter = TRUE;
|
|
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {
|
|
sprintf(str, "%da", i);
|
|
ltime = retrieve_date(str, TRUE);
|
|
if (ltime > 0)
|
|
disp_filter = TRUE;
|
|
sprintf(str, "%db", i);
|
|
ltime = retrieve_date(str, FALSE);
|
|
if (ltime > 0)
|
|
disp_filter = TRUE;
|
|
}
|
|
if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) {
|
|
for (j = 0; j < MAX_N_LIST; j++) {
|
|
sprintf(str, "%s_%d", attr_list[i], j);
|
|
if (isparam(str))
|
|
disp_filter = TRUE;
|
|
if (isparam(attr_list[i]))
|
|
disp_filter = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isparam("new_entries") && atoi(getparam("new_entries")) == 1) {
|
|
rsprintf("<tr><td class=\"listframe\">\n");
|
|
rsprintf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", loc("New entries since"));
|
|
memcpy(&tms, localtime(<ime_start), sizeof(struct tm));
|
|
my_strftime(str, sizeof(str), "%c", &tms);
|
|
rsprintf("<td class=\"attribvalue\">%s</td></tr>", str);
|
|
rsprintf("</table></td></tr>\n\n");
|
|
}
|
|
|
|
if (disp_filter) {
|
|
rsprintf("<tr><td class=\"listframe\">\n");
|
|
rsprintf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
|
|
|
|
if (isparam("last")) {
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>",
|
|
loc("Restrict search to last"));
|
|
strencode2(str, getparam("last"), sizeof(str));
|
|
rsprintf("<td class=\"attribvalue\">%s %s</td></tr>", str, loc("days"));
|
|
}
|
|
|
|
if (isparam("ma") || isparam("ya") || isparam("da") || isparam("ha") || isparam("na")
|
|
|| isparam("ca")) {
|
|
memset(&tms, 0, sizeof(struct tm));
|
|
tms.tm_year = y1 - 1900;
|
|
tms.tm_mon = m1 - 1;
|
|
tms.tm_mday = d1;
|
|
tms.tm_hour = h1;
|
|
tms.tm_min = n1;
|
|
tms.tm_sec = c1;
|
|
if (tms.tm_year < 90)
|
|
tms.tm_year += 100;
|
|
mktime(&tms);
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
strftime(str, sizeof(str), format, &tms);
|
|
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", loc("Start date"));
|
|
rsprintf("<td class=\"attribvalue\">%s</td></tr>", str);
|
|
}
|
|
|
|
if (isparam("mb") || isparam("yb") || isparam("db") || isparam("hb") || isparam("nb")
|
|
|| isparam("cb")) {
|
|
memset(&tms, 0, sizeof(struct tm));
|
|
tms.tm_year = y2 - 1900;
|
|
tms.tm_mon = m2 - 1;
|
|
tms.tm_mday = d2;
|
|
tms.tm_hour = h2;
|
|
tms.tm_min = n2;
|
|
tms.tm_sec = c2;
|
|
if (tms.tm_year < 90)
|
|
tms.tm_year += 100;
|
|
ltime = mktime(&tms);
|
|
memcpy(&tms, localtime(<ime), sizeof(struct tm));
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
strftime(str, sizeof(str), format, &tms);
|
|
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", loc("End date"));
|
|
|
|
rsprintf("<td class=\"attribvalue\">%s</td></tr>", str);
|
|
}
|
|
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {
|
|
|
|
sprintf(str, "%da", i);
|
|
ltime1 = retrieve_date(str, TRUE);
|
|
sprintf(str, "%db", i);
|
|
ltime2 = retrieve_date(str, TRUE);
|
|
|
|
if (ltime1 > 0 || ltime2 > 0) {
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", attr_list[i]);
|
|
rsprintf("<td class=\"attribvalue\">");
|
|
if (ltime1) {
|
|
memcpy(&tms, localtime(<ime1), sizeof(struct tm));
|
|
if (attr_flags[i] & AF_DATE)
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
else
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
my_strftime(str, sizeof(str), format, &tms);
|
|
if (ltime2 > 0)
|
|
rsprintf("%s %s", loc("From"), str);
|
|
else
|
|
rsprintf("%s %s", loc("After"), str);
|
|
}
|
|
if (ltime2) {
|
|
memcpy(&tms, localtime(<ime2), sizeof(struct tm));
|
|
if (attr_flags[i] & AF_DATE)
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
else
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
my_strftime(str, sizeof(str), format, &tms);
|
|
if (ltime1 > 0)
|
|
rsprintf(" %s %s", loc("to"), str);
|
|
else
|
|
rsprintf("%s %s", loc("Before"), str);
|
|
}
|
|
rsprintf("</td></tr>", comment);
|
|
}
|
|
|
|
} else if (attr_flags[i] & AF_MULTI) {
|
|
|
|
line[0] = 0;
|
|
|
|
for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(iattr, "%s_%d", attr, j);
|
|
if (isparam(iattr)) {
|
|
|
|
comment[0] = 0;
|
|
if (attr_flags[i] & AF_ICON) {
|
|
sprintf(str, "Icon comment %s", getparam(iattr));
|
|
getcfg(lbs->name, str, comment, sizeof(comment));
|
|
}
|
|
|
|
if (line[0])
|
|
strlcat(line, " | ", sizeof(line));
|
|
|
|
if (comment[0] == 0) {
|
|
strlcpy(str, getparam(iattr), sizeof(str));
|
|
if (str[0] == '^' && str[strlen(str) - 1] == '$') {
|
|
str[strlen(str) - 1] = 0;
|
|
strlcpy(comment, str + 1, NAME_LENGTH);
|
|
} else
|
|
strlcpy(comment, str, NAME_LENGTH);
|
|
strlcpy(str, comment, sizeof(str));
|
|
|
|
strencode2(line + strlen(line), str, sizeof(line) - strlen(line));
|
|
} else
|
|
strlcat(line, comment, sizeof(line));
|
|
}
|
|
}
|
|
|
|
if (isparam(attr_list[i])) {
|
|
comment[0] = 0;
|
|
if (attr_flags[i] & AF_ICON) {
|
|
sprintf(str, "Icon comment %s", getparam(attr_list[i]));
|
|
getcfg(lbs->name, str, comment, sizeof(comment));
|
|
}
|
|
|
|
if (line[0])
|
|
strlcat(line, " | ", sizeof(line));
|
|
|
|
if (comment[0] == 0) {
|
|
strlcpy(str, getparam(attr_list[i]), sizeof(str));
|
|
if (str[0] == '^' && str[strlen(str) - 1] == '$') {
|
|
str[strlen(str) - 1] = 0;
|
|
strlcpy(comment, str + 1, NAME_LENGTH);
|
|
} else
|
|
strlcpy(comment, str, NAME_LENGTH);
|
|
strlcpy(str, comment, sizeof(str));
|
|
strencode2(line + strlen(line), str, sizeof(line) - strlen(line));
|
|
} else
|
|
strlcat(line, comment, sizeof(line));
|
|
}
|
|
|
|
if (line[0]) {
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", attr_list[i]);
|
|
rsprintf("<td class=\"attribvalue\"><span style=\"color:black;background-color:#ffff66\">");
|
|
rsprintf("%s</span></td></tr>", line);
|
|
}
|
|
|
|
} else if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) {
|
|
|
|
line[0] = 0;
|
|
|
|
for (j = 0; j < MAX_N_LIST; j++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(iattr, "%s_%d", attr, j);
|
|
if (isparam(iattr)) {
|
|
if (line[0])
|
|
strlcat(line, " | ", sizeof(line));
|
|
strlcat(line, getparam(iattr), sizeof(line));
|
|
}
|
|
}
|
|
|
|
if (isparam(attr_list[i])) {
|
|
if (line[0])
|
|
strlcat(line, " | ", sizeof(line));
|
|
strencode2(line + strlen(line), getparam(attr_list[i]), sizeof(line) - strlen(line));
|
|
}
|
|
|
|
if (line[0]) {
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", attr_list[i]);
|
|
rsprintf("<td class=\"attribvalue\"><span style=\"color:black;background-color:#ffff66\">");
|
|
rsprintf("%s</span></td></tr>", line);
|
|
}
|
|
|
|
} else if (isparam(attr_list[i])) {
|
|
|
|
strlcpy(str, getparam(attr_list[i]), sizeof(str));
|
|
if (str[0] && !strieq(str, "_all_") && strncmp(str, "--", 2) != 0) {
|
|
comment[0] = 0;
|
|
if (attr_flags[i] & AF_ICON) {
|
|
sprintf(str, "Icon comment %s", getparam(attr_list[i]));
|
|
getcfg(lbs->name, str, comment, sizeof(comment));
|
|
}
|
|
|
|
if (comment[0] == 0) {
|
|
strlcpy(str, getparam(attr_list[i]), sizeof(str));
|
|
if (str[0] == '^' && str[strlen(str) - 1] == '$') {
|
|
str[strlen(str) - 1] = 0;
|
|
strlcpy(comment, str + 1, NAME_LENGTH);
|
|
} else
|
|
strlcpy(comment, str, NAME_LENGTH);
|
|
strlcpy(str, comment, sizeof(str));
|
|
strencode2(comment, str, sizeof(comment));
|
|
}
|
|
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", attr_list[i]);
|
|
rsprintf("<td class=\"attribvalue\"><span style=\"color:black;background-color:#ffff66\">");
|
|
rsprintf("%s</span></td></tr>", comment);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isparam("subtext")) {
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", loc("Text"));
|
|
rsprintf("<td class=\"attribvalue\"><span style=\"color:black;background-color:#ffff66\">");
|
|
strencode2(str, getparam("subtext"), sizeof(str));
|
|
rsprintf("%s</span></td></tr>", str);
|
|
}
|
|
|
|
rsprintf("</table></td></tr>\n\n");
|
|
}
|
|
|
|
/* get number of summary lines */
|
|
n_line = 3;
|
|
if (getcfg(lbs->name, "Summary lines", str, sizeof(str)))
|
|
n_line = atoi(str);
|
|
|
|
/* suppress summary completely if text body is disabled */
|
|
if (getcfg(lbs->name, "Show text", str, sizeof(str)) && atoi(str) == 0)
|
|
n_line = 0;
|
|
|
|
/* suppress attachment colum if switched off */
|
|
show_att_column = strieq(mode, "Summary");
|
|
if (getcfg(lbs->name, "Enable attachments", str, sizeof(str)) && atoi(str) == 0)
|
|
show_att_column = FALSE;
|
|
|
|
/* get mode commands flag */
|
|
mode_commands = TRUE;
|
|
if (getcfg(lbs->name, "Mode commands", str, sizeof(str)) && atoi(str) == 0)
|
|
mode_commands = FALSE;
|
|
|
|
/*---- evaluate conditions for quick filters */
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
attrib[i][0] = 0;
|
|
if (isparam(attr_list[i])) {
|
|
strlcpy(str, getparam(attr_list[i]), sizeof(str));
|
|
if (str[0] == '^' && str[strlen(str) - 1] == '$') {
|
|
str[strlen(str) - 1] = 0;
|
|
strlcpy(attrib[i], str + 1, NAME_LENGTH);
|
|
} else
|
|
strlcpy(attrib[i], str, NAME_LENGTH);
|
|
}
|
|
}
|
|
evaluate_conditions(lbs, attrib);
|
|
|
|
/*---- notification message ----*/
|
|
|
|
if (info && info[0]) {
|
|
rsprintf("<tr><td class=\"notifyleft\">%s</td></tr>\n", info);
|
|
}
|
|
|
|
/*---- page navigation ----*/
|
|
|
|
if (!printable) {
|
|
show_page_filters(lbs, n_msg, page_n, mode_commands, mode);
|
|
show_page_navigation(lbs, n_msg, page_n, n_page);
|
|
}
|
|
|
|
/*---- select navigation ----*/
|
|
|
|
if (isparam("select") && atoi(getparam("select")) == 1)
|
|
show_select_navigation(lbs);
|
|
|
|
/*---- table titles ----*/
|
|
|
|
/* overall listing table */
|
|
rsprintf("<tr><td><table class=\"listframe\" width=\"100%%\" cellspacing=0>\n");
|
|
|
|
size = printable ? 2 : 3;
|
|
show_text = TRUE;
|
|
text_in_attr = FALSE;
|
|
|
|
list[0] = 0;
|
|
getcfg(lbs->name, "List display", list, 10000);
|
|
|
|
/* evaluate Guest display list */
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str)) && getcfg(lbs->name, "Guest list display",
|
|
str, sizeof(str))
|
|
&& !isparam("unm")) {
|
|
|
|
strcpy(list, str);
|
|
|
|
n = strbreak(list, (char (*)[NAME_LENGTH]) gattr, MAX_N_ATTR, ",", FALSE);
|
|
for (j = 0; j < n; j++)
|
|
if (strieq(gattr + j * NAME_LENGTH, "text"))
|
|
break;
|
|
|
|
if (n > 0 && j == n)
|
|
show_text = FALSE;
|
|
else
|
|
text_in_attr = TRUE;
|
|
}
|
|
|
|
memset(disp_attr_flags, 0, sizeof(disp_attr_flags));
|
|
if (list[0]) {
|
|
n_attr_disp = strbreak(list, disp_attr, MAX_N_ATTR, ",", FALSE);
|
|
|
|
/* if text is in guest display list, adjust number of *real* attributes */
|
|
if (text_in_attr)
|
|
n_attr_disp--;
|
|
|
|
if (search_all) {
|
|
for (i = n_attr_disp - 1; i >= 0; i--)
|
|
strcpy(disp_attr[i + 1], disp_attr[i]);
|
|
strcpy(disp_attr[0], loc("Logbook"));
|
|
n_attr_disp++;
|
|
}
|
|
} else {
|
|
if (search_all) {
|
|
n_attr_disp = lbs->n_attr + 3;
|
|
|
|
strcpy(disp_attr[0], loc("Logbook"));
|
|
strcpy(disp_attr[1], loc("ID"));
|
|
strcpy(disp_attr[2], loc("Date"));
|
|
memcpy(disp_attr + 3, attr_list, sizeof(attr_list));
|
|
memcpy(disp_attr_flags + 3, attr_flags, sizeof(attr_flags));
|
|
} else {
|
|
n_attr_disp = lbs->n_attr + 2;
|
|
|
|
strcpy(disp_attr[0], loc("ID"));
|
|
strcpy(disp_attr[1], loc("Date"));
|
|
memcpy(disp_attr + 2, attr_list, sizeof(attr_list));
|
|
memcpy(disp_attr_flags + 2, attr_flags, sizeof(attr_flags));
|
|
}
|
|
}
|
|
|
|
list[0] = 0;
|
|
getcfg(lbs->name, "Link display", list, 10000);
|
|
if (list[0]) {
|
|
n = strbreak(list, (char (*)[NAME_LENGTH]) gattr, MAX_N_ATTR, ",", FALSE);
|
|
|
|
for (i = 0; i < n_attr_disp; i++) {
|
|
for (j = 0; j < n; j++)
|
|
if (strieq(gattr + j * NAME_LENGTH, disp_attr[i]))
|
|
break;
|
|
|
|
if (j < n)
|
|
disp_attr_link[i] = TRUE;
|
|
else
|
|
disp_attr_link[i] = FALSE;
|
|
}
|
|
} else
|
|
for (i = 0; i < n_attr_disp; i++)
|
|
disp_attr_link[i] = TRUE;
|
|
|
|
if (threaded) {
|
|
} else {
|
|
rsprintf("<tr>\n");
|
|
|
|
/* empty title for selection box */
|
|
if (isparam("select") && atoi(getparam("select")) == 1)
|
|
rsprintf("<th class=\"listtitle\"> </th>\n");
|
|
|
|
for (i = 0; i < n_attr_disp; i++) {
|
|
/* assemble current command line, replace sort statements */
|
|
strlcpy(ref, getparam("cmdline"), sizeof(ref));
|
|
|
|
strlcpy(str, disp_attr[i], sizeof(str));
|
|
url_encode(str, sizeof(str));
|
|
if (isparam("sort") && strcmp(getparam("sort"), disp_attr[i]) == 0) {
|
|
subst_param(ref, sizeof(ref), "sort", "");
|
|
subst_param(ref, sizeof(ref), "rsort", str);
|
|
} else {
|
|
if (ref[0] == 0) {
|
|
if (getcfg(lbs->name, "Reverse sort", str2, sizeof(str2)) && atoi(str2) == 1)
|
|
sprintf(ref, "?rsort=%s", str);
|
|
else
|
|
sprintf(ref, "?sort=%s", str);
|
|
} else {
|
|
subst_param(ref, sizeof(ref), "rsort", "");
|
|
subst_param(ref, sizeof(ref), "sort", str);
|
|
}
|
|
}
|
|
|
|
img[0] = 0;
|
|
if (isparam("sort") && strcmp(getparam("sort"), disp_attr[i]) == 0)
|
|
sprintf(img, "<img align=top src=\"up.png\" alt=\"%s\" title=\"%s\">", loc("up"), loc("up"));
|
|
else if (isparam("rsort") && strcmp(getparam("rsort"), disp_attr[i]) == 0)
|
|
sprintf(img, "<img align=top src=\"down.png\" alt=\"%s\" title=\"%s\">", loc("down"),
|
|
loc("down"));
|
|
|
|
strlcpy(attr, disp_attr[i], sizeof(attr));
|
|
sprintf(str, "Tooltip %s", attr);
|
|
if (getcfg(lbs->name, str, comment, sizeof(comment)))
|
|
sprintf(str, "title=\"%s\"", comment);
|
|
else
|
|
str[0] = 0;
|
|
|
|
if (strieq(disp_attr[i], "Edit") || strieq(disp_attr[i], "Delete"))
|
|
rsprintf("<th %s class=\"listtitle\">%s</th>\n", str, disp_attr[i]);
|
|
else {
|
|
rsprintf("<th %s class=\"listtitle\"><a href=\"", str);
|
|
rsputs3(ref);
|
|
rsprintf("\">%s</a>%s</th>\n", disp_attr[i], img);
|
|
}
|
|
}
|
|
|
|
if (!strieq(mode, "Full") && n_line > 0 && show_text)
|
|
rsprintf("<th class=\"listtitle2\">%s</th>\n", loc("Text"));
|
|
|
|
if (show_att_column)
|
|
rsprintf("<th class=\"listtitle3\"><img src=\"attachment.png\" alt=\"%s\" title=\"%s\"</th>",
|
|
loc("Attachments"), loc("Attachments"));
|
|
|
|
rsprintf("</tr>\n\n");
|
|
}
|
|
} /* if (!csv && !xml) */
|
|
|
|
/*---- display message list ----*/
|
|
|
|
for (index = i_start; index <= i_stop; index++) {
|
|
size = TEXT_SIZE;
|
|
message_id = msg_list[index].lbs->el_index[msg_list[index].index].message_id;
|
|
|
|
status = el_retrieve(msg_list[index].lbs, message_id, date, attr_list, attrib, lbs->n_attr, text,
|
|
&size, in_reply_to, reply_to, attachment, encoding, locked_by, draft);
|
|
if (status != EL_SUCCESS)
|
|
break;
|
|
|
|
/* skip drafts */
|
|
if (getcfg(lbs->name, "List drafts", str, sizeof(str)) && atoi(str) == 0)
|
|
if (draft[0])
|
|
continue;
|
|
|
|
if (csv) {
|
|
|
|
rsprintf("%d", message_id);
|
|
if (strieq(mode, "CSV1"))
|
|
rsprintf(",");
|
|
else
|
|
rsprintf(";");
|
|
|
|
strlcpy(str, date, sizeof(str));
|
|
while (strchr(str, ','))
|
|
*strchr(str, ',') = ' ';
|
|
rsprintf(str);
|
|
if (strieq(mode, "CSV1"))
|
|
rsprintf(",");
|
|
else
|
|
rsprintf(";");
|
|
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(str, attrib[i], sizeof(str));
|
|
if (str[0]) {
|
|
|
|
if (attr_flags[i] & AF_DATE) {
|
|
|
|
sprintf(str, "Date format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
ptms = localtime(<ime);
|
|
assert(ptms);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, ptms);
|
|
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
|
|
sprintf(str, "Time format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
ptms = localtime(<ime);
|
|
assert(ptms);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, ptms);
|
|
|
|
}
|
|
|
|
rsputs("\"");
|
|
pt1 = str;
|
|
while ((pt2 = strchr(pt1, '"')) != NULL) {
|
|
*pt2 = 0;
|
|
rsputs(pt1);
|
|
rsputs("\"\"");
|
|
pt1 = pt2 + 1;
|
|
}
|
|
rsputs(pt1);
|
|
rsputs("\"");
|
|
}
|
|
if (i < lbs->n_attr - 1) {
|
|
if (strieq(mode, "CSV1"))
|
|
rsprintf(",");
|
|
else
|
|
rsprintf(";");
|
|
} else {
|
|
if (strlen(text) > 0 && strieq(mode, "CSV3")) {
|
|
rsprintf(";");
|
|
strlcpy(str, text, sizeof(str));
|
|
rsputs("\"");
|
|
pt1 = str;
|
|
while ((pt2 = strchr(pt1, '"')) != NULL) {
|
|
*pt2 = 0;
|
|
rsputs(pt1);
|
|
rsputs("\"\"");
|
|
pt1 = pt2 + 1;
|
|
}
|
|
rsputs(pt1);
|
|
rsputs("\"");
|
|
}
|
|
rsprintf("\r\n");
|
|
}
|
|
}
|
|
|
|
} else if (xml) {
|
|
|
|
rsputs("\t<ENTRY>\n");
|
|
rsprintf("\t\t<MID>%d</MID>\n", message_id);
|
|
rsprintf("\t\t<DATE>%s</DATE>\n", date);
|
|
if (in_reply_to[0])
|
|
rsprintf("\t\t<IN_REPLY_TO>%s</IN_REPLY_TO>\n", in_reply_to);
|
|
if (reply_to[0])
|
|
rsprintf("\t\t<REPLY_TO>%s</REPLY_TO>\n", reply_to);
|
|
if (attachment[0][0]) {
|
|
rsprintf("\t\t<ATTACHMENT>");
|
|
rsprintf(attachment[0]);
|
|
for (i = 1; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment[i][0])
|
|
rsprintf(",%s", attachment[i]);
|
|
rsprintf("</ATTACHMENT>\n", attachment);
|
|
}
|
|
rsprintf("\t\t<ENCODING>%s</ENCODING>\n", encoding);
|
|
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strcpy(iattr, attr_list[i]);
|
|
for (j = 0; j < (int) strlen(iattr); j++)
|
|
/* replace special characters with "_", exclude any UTF-8 */
|
|
if (!isalnum(iattr[j]) && ((unsigned char) iattr[j] < 128))
|
|
iattr[j] = '_';
|
|
rsprintf("\t\t<%s>", iattr);
|
|
|
|
strlcpy(str, attrib[i], sizeof(str));
|
|
if (attr_flags[i] & AF_DATE) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Date format %s", attr);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
ptms = localtime(<ime);
|
|
assert(ptms);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, ptms);
|
|
sprintf(str + strlen(str), " [%ld]", (long)ltime);
|
|
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Time format %s", attr);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
ptms = localtime(<ime);
|
|
assert(ptms);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, ptms);
|
|
sprintf(str + strlen(str), " [%ld]", (long)ltime);
|
|
}
|
|
|
|
xmlencode(str);
|
|
rsprintf("</%s>\n", iattr);
|
|
}
|
|
rsputs("\t\t<TEXT>");
|
|
xmlencode(text);
|
|
rsputs("</TEXT>\n");
|
|
|
|
rsputs("\t</ENTRY>\n");
|
|
|
|
} else if (raw) {
|
|
|
|
rsprintf("$@MID@$: %d\r\n", message_id);
|
|
rsprintf("Date: %s\r\n", date);
|
|
|
|
if (reply_to[0])
|
|
rsprintf("Reply to: %s\r\n", reply_to);
|
|
|
|
if (in_reply_to[0])
|
|
rsprintf("In reply to: %s\r\n", in_reply_to);
|
|
|
|
for (i = 0; i < lbs->n_attr; i++)
|
|
rsprintf("%s: %s\r\n", attr_list[i], attrib[i]);
|
|
|
|
rsprintf("Attachment: ");
|
|
|
|
if (attachment[0][0]) {
|
|
rsprintf("%s", attachment[0]);
|
|
for (i = 1; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment[i][0])
|
|
rsprintf(",%s", attachment[i]);
|
|
}
|
|
rsprintf("\r\n");
|
|
|
|
rsprintf("Encoding: %s\r\n", encoding);
|
|
if (locked_by[0])
|
|
rsprintf("Locked by: %s\r\n", locked_by);
|
|
|
|
rsprintf("========================================\r\n");
|
|
rsputs(text);
|
|
rsputs("\r\n");
|
|
|
|
} else {
|
|
|
|
/*---- add highlighting for searched subtext ----*/
|
|
|
|
if (isparam("subtext")) {
|
|
highlight_searchtext(re_buf, text, text1, strieq(encoding, "plain") || strieq(encoding, "ELCode")
|
|
|| !strieq(mode, "Full"));
|
|
strlcpy(text, text1, TEXT_SIZE);
|
|
}
|
|
|
|
/*---- display line ----*/
|
|
|
|
expand = 1;
|
|
if (threaded) {
|
|
if (getcfg(lbs->name, "Expand default", str, sizeof(str)))
|
|
expand = atoi(str);
|
|
|
|
if (isparam("expand"))
|
|
expand = atoi(getparam("expand"));
|
|
}
|
|
|
|
level = 0;
|
|
if (!filtering && !date_filtering) {
|
|
if (expand == 0 && (!getcfg(lbs->name, "Collapse to last", str, sizeof(str)) || atoi(str) == 1)) {
|
|
/* search last entry in this thread */
|
|
if (reply_to[0]) {
|
|
search_last_reply(msg_list[index].lbs, &message_id);
|
|
size = TEXT_SIZE;
|
|
status =
|
|
el_retrieve(msg_list[index].lbs, message_id, date, attr_list, attrib, lbs->n_attr,
|
|
text,
|
|
&size, in_reply_to, reply_to, attachment, encoding, locked_by, draft);
|
|
if (status == SUCCESS)
|
|
level = 1;
|
|
}
|
|
}
|
|
} else if (in_reply_to[0])
|
|
level = 1;
|
|
|
|
display_line(msg_list[index].lbs, message_id, index, mode, expand, level, printable, n_line,
|
|
show_attachments, show_att_column, date, in_reply_to, reply_to, n_attr_disp, disp_attr,
|
|
disp_attr_link, attrib, lbs->n_attr, text, show_text, attachment, encoding,
|
|
isparam("select") ? atoi(getparam("select")) : 0, &n_display, locked_by, 0, re_buf,
|
|
page_mid, FALSE, draft);
|
|
|
|
if (threaded && !filtering && !date_filtering) {
|
|
if (reply_to[0] && expand > 0) {
|
|
p = reply_to;
|
|
do {
|
|
display_reply(msg_list[index].lbs, atoi(p), printable, expand, n_line, n_attr_disp,
|
|
disp_attr, show_text, 1, 0, re_buf, page_mid, FALSE);
|
|
|
|
while (*p && isdigit(*p))
|
|
p++;
|
|
while (*p && (*p == ',' || *p == ' '))
|
|
p++;
|
|
} while (*p);
|
|
}
|
|
}
|
|
} /* if (!csv && !xml) */
|
|
} /* for() */
|
|
|
|
if (!csv && !xml && !raw) {
|
|
rsprintf("</table>\n");
|
|
|
|
if (n_display)
|
|
rsprintf("<input type=hidden name=nsel value=%d>\n", n_display);
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
if (n_msg == 0)
|
|
rsprintf("<tr><td class=\"errormsg\">%s</td></tr>", loc("No entries found"));
|
|
|
|
/*---- page navigation ----*/
|
|
|
|
if (!printable)
|
|
show_page_navigation(lbs, n_msg, page_n, n_page);
|
|
|
|
rsprintf("</table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</form></body></html>\r\n");
|
|
}
|
|
|
|
if (xml) {
|
|
rsputs("</ELOG_LIST>\n");
|
|
}
|
|
|
|
regfree(re_buf);
|
|
for (i = 0; i < lbs->n_attr; i++)
|
|
regfree(re_buf + 1 + i);
|
|
|
|
xfree(slist);
|
|
xfree(svalue);
|
|
xfree(gattr);
|
|
xfree(list);
|
|
xfree(msg_list);
|
|
xfree(text);
|
|
xfree(text1);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int find_thread_head(LOGBOOK *lbs, int message_id) {
|
|
int i;
|
|
|
|
/* search index of message */
|
|
for (i = 0; i < *lbs->n_el_index; i++)
|
|
if (lbs->el_index[i].message_id == message_id)
|
|
break;
|
|
|
|
if (lbs->el_index[i].in_reply_to)
|
|
return find_thread_head(lbs, lbs->el_index[i].in_reply_to);
|
|
|
|
return message_id;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_elog_thread(LOGBOOK *lbs, int message_id, int absolute_links, int highlight_mid) {
|
|
int size, head_id, n_display, n_attr_disp;
|
|
char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
|
|
attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], encoding[80], locked_by[256], draft[256],
|
|
disp_attr[MAX_N_ATTR + 4][NAME_LENGTH];
|
|
char *p;
|
|
|
|
text = (char *) xmalloc(TEXT_SIZE);
|
|
|
|
/* retrieve message */
|
|
size = TEXT_SIZE;
|
|
el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to,
|
|
reply_to, attachment, encoding, locked_by, draft);
|
|
|
|
/* find message head */
|
|
if (atoi(in_reply_to))
|
|
head_id = find_thread_head(lbs, atoi(in_reply_to));
|
|
else
|
|
head_id = message_id;
|
|
|
|
n_attr_disp = lbs->n_attr + 2;
|
|
strcpy(disp_attr[0], loc("ID"));
|
|
strcpy(disp_attr[1], loc("Date"));
|
|
memcpy(disp_attr + 2, attr_list, sizeof(attr_list));
|
|
|
|
size = TEXT_SIZE;
|
|
el_retrieve(lbs, head_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to,
|
|
reply_to, attachment, encoding, locked_by, draft);
|
|
|
|
rsprintf("<tr><td><table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
|
|
|
|
display_line(lbs, head_id, 0, "Threaded", 1, 0, FALSE, 0,
|
|
FALSE, FALSE, date, in_reply_to, reply_to, n_attr_disp, disp_attr, NULL, attrib, lbs->n_attr,
|
|
text, FALSE, attachment, encoding, 0, &n_display, locked_by, message_id, NULL, highlight_mid,
|
|
absolute_links, draft);
|
|
|
|
if (reply_to[0]) {
|
|
p = reply_to;
|
|
do {
|
|
display_reply(lbs, atoi(p), FALSE, 1, 0, n_attr_disp, disp_attr, FALSE, 1, message_id, NULL,
|
|
highlight_mid, absolute_links);
|
|
|
|
while (*p && isdigit(*p))
|
|
p++;
|
|
while (*p && (*p == ',' || *p == ' '))
|
|
p++;
|
|
} while (*p);
|
|
}
|
|
|
|
rsprintf("</table>\n");
|
|
rsprintf("</td></tr>\n");
|
|
|
|
xfree(text);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int max_attachment_size(LOGBOOK *lbs, int message_id) {
|
|
char att_file[MAX_ATTACHMENTS][MAX_PATH_LENGTH];
|
|
int index, max_size, fh;
|
|
char str[256], subdir[256], file_name[256];
|
|
|
|
el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, att_file, NULL, NULL, NULL);
|
|
|
|
max_size = 0;
|
|
for (index = 0; index < MAX_ATTACHMENTS; index++) {
|
|
|
|
if (att_file[index][0] == 0)
|
|
continue;
|
|
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(att_file[index], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, att_file[index], sizeof(file_name));
|
|
if (is_image(file_name)) {
|
|
get_thumb_name(file_name, str, sizeof(str), 0);
|
|
if (str[0])
|
|
strlcpy(file_name, str, sizeof(file_name));
|
|
}
|
|
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
off_t size = lseek(fh, 0L, SEEK_END);
|
|
max_size = size > max_size ? size : max_size;
|
|
}
|
|
}
|
|
|
|
return max_size;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void format_email_attachments(LOGBOOK *lbs, int message_id, int attachment_type,
|
|
char att_file[MAX_ATTACHMENTS][256], char *mail_text, int size,
|
|
char *multipart_boundary, int content_id) {
|
|
int i, index, n_att, fh, n, is_inline, length;
|
|
char str[256], file_name[256], buffer[256], domain[256], subdir[256];
|
|
|
|
/* count attachments */
|
|
for (n_att = 0; att_file[n_att][0] && n_att < MAX_ATTACHMENTS; n_att++);
|
|
|
|
for (index = 0; index < MAX_ATTACHMENTS; index++) {
|
|
|
|
if (att_file[index][0] == 0)
|
|
continue;
|
|
|
|
is_inline = is_inline_attachment(getparam("encoding"), message_id,
|
|
getparam("text"), index, att_file[index]);
|
|
if (attachment_type == 1 && is_inline)
|
|
continue;
|
|
if (attachment_type == 2 && !is_inline)
|
|
continue;
|
|
|
|
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "\r\n--%s\r\n",
|
|
multipart_boundary);
|
|
|
|
/* return proper Content-Type for file type */
|
|
for (i = 0; i < (int) strlen(att_file[index]) && i < (int) sizeof(str) - 1; i++)
|
|
str[i] = toupper(att_file[index][i]);
|
|
str[i] = 0;
|
|
|
|
for (i = 0; filetype[i].ext[0]; i++)
|
|
if (strstr(str, filetype[i].ext))
|
|
break;
|
|
|
|
if (filetype[i].ext[0])
|
|
snprintf(str, sizeof(str), "Content-Type: %s; name=\"%s\"\r\n", filetype[i].type, att_file[index]
|
|
+ 14);
|
|
else if (strchr(str, '.') == NULL)
|
|
snprintf(str, sizeof(str), "Content-Type: text/plain; name=\"%s\"\r\n", att_file[index] + 14);
|
|
else
|
|
snprintf(str, sizeof(str), "Content-Type: application/octet-stream; name=\"%s\"\r\n",
|
|
att_file[index] + 14);
|
|
|
|
strlcat(mail_text, str, size);
|
|
|
|
strlcat(mail_text, "Content-Transfer-Encoding: BASE64\r\n", size);
|
|
|
|
if (content_id) {
|
|
retrieve_domain(domain, sizeof(domain));
|
|
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Content-ID: <att%d@%s>\r\n",
|
|
index, domain);
|
|
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
|
|
"Content-Disposition: inline; filename=\"%s\"\r\n\r\n", att_file[index] + 14);
|
|
} else
|
|
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
|
|
"Content-Disposition: attachment; filename=\"%s\"\r\n\r\n", att_file[index] + 14);
|
|
|
|
/* encode file */
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(att_file[index], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, att_file[index], sizeof(file_name));
|
|
if (is_image(file_name)) {
|
|
get_thumb_name(file_name, str, sizeof(str), 0);
|
|
if (str[0])
|
|
strlcpy(file_name, str, sizeof(file_name));
|
|
}
|
|
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
length = strlen(mail_text);
|
|
if (fh > 0) {
|
|
do {
|
|
n = my_read(fh, buffer, 45);
|
|
if (n <= 0)
|
|
break;
|
|
|
|
base64_bufenc((unsigned char *) buffer, n, str);
|
|
|
|
if (length + (int) strlen(str) + 2 < size) {
|
|
strcpy(mail_text + length, str);
|
|
length += strlen(str);
|
|
strcpy(mail_text + length, "\r\n");
|
|
length += 2;
|
|
}
|
|
} while (1);
|
|
|
|
close(fh);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void format_email_text(LOGBOOK *lbs, char attrib[MAX_N_ATTR][NAME_LENGTH],
|
|
char att_file[MAX_ATTACHMENTS][256], int old_mail, char *url, char *multipart_boundary,
|
|
char *mail_text, int size) {
|
|
int i, j, k, flags, n_email_attr, attr_index[MAX_N_ATTR];
|
|
char str[NAME_LENGTH + 100], str2[256], mail_from[256], mail_from_name[256], format[256],
|
|
list[MAX_N_ATTR][NAME_LENGTH], comment[256], charset[256], heading[256],
|
|
slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], attr[NAME_LENGTH];
|
|
time_t ltime;
|
|
struct tm *pts;
|
|
|
|
if (multipart_boundary[0]) {
|
|
if (!getcfg("global", "charset", charset, sizeof(charset)))
|
|
strcpy(charset, DEFAULT_HTTP_CHARSET);
|
|
|
|
strlcat(mail_text, "--", size);
|
|
strlcat(mail_text, multipart_boundary, size);
|
|
strlcat(mail_text, "\r\n", size);
|
|
sprintf(mail_text + strlen(mail_text), "Content-Type: text/plain; charset=%s; format=flowed\r\n",
|
|
charset);
|
|
sprintf(mail_text + strlen(mail_text), "Content-Transfer-Encoding: 7bit\r\n\r\n");
|
|
} else
|
|
strlcat(mail_text, "\r\n", size);
|
|
|
|
flags = 63;
|
|
if (getcfg(lbs->name, "Email format", str, sizeof(str)))
|
|
flags = atoi(str);
|
|
|
|
retrieve_email_from(lbs, mail_from, mail_from_name, attrib);
|
|
|
|
if (flags & 1) {
|
|
if (getcfg(lbs->name, "Use Email heading", heading, sizeof(heading))) {
|
|
if (old_mail) {
|
|
if (!getcfg(lbs->name, "Use Email heading edit", heading, sizeof(heading)))
|
|
getcfg(lbs->name, "Use Email heading", heading, sizeof(heading));
|
|
}
|
|
|
|
i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
strsubst_list(heading, sizeof(heading), slist, svalue, i);
|
|
|
|
strlcpy(mail_text + strlen(mail_text), heading, size - strlen(mail_text));
|
|
|
|
} else {
|
|
if (old_mail)
|
|
strlcpy(mail_text + strlen(mail_text), loc("An old ELOG entry has been updated"),
|
|
size - strlen(mail_text));
|
|
else
|
|
strlcpy(mail_text + strlen(mail_text), loc("A new ELOG entry has been submitted"),
|
|
size - strlen(mail_text));
|
|
strcat(mail_text, ":");
|
|
}
|
|
|
|
strlcpy(mail_text + strlen(mail_text), "\r\n\r\n", size - strlen(mail_text));
|
|
}
|
|
|
|
if (flags & 32)
|
|
sprintf(mail_text + strlen(mail_text), "%s : %s\r\n", loc("Logbook"), lbs->name);
|
|
|
|
if (flags & 2) {
|
|
|
|
if (getcfg(lbs->name, "Email attributes", str, sizeof(str))) {
|
|
n_email_attr = strbreak(str, list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n_email_attr; i++) {
|
|
for (j = 0; j < lbs->n_attr; j++)
|
|
if (strieq(attr_list[j], list[i]))
|
|
break;
|
|
if (!strieq(attr_list[j], list[i]))
|
|
/* attribute not found */
|
|
j = 0;
|
|
attr_index[i] = j;
|
|
}
|
|
} else {
|
|
for (i = 0; i < lbs->n_attr; i++)
|
|
attr_index[i] = i;
|
|
n_email_attr = lbs->n_attr;
|
|
}
|
|
|
|
for (j = 0; j < n_email_attr; j++) {
|
|
|
|
i = attr_index[j];
|
|
strcpy(str, " ");
|
|
memcpy(str, attr_list[i], strlen(attr_list[i]));
|
|
|
|
comment[0] = 0;
|
|
if (attr_flags[i] & AF_ICON) {
|
|
|
|
sprintf(str2, "Icon comment %s", attrib[i]);
|
|
getcfg(lbs->name, str2, comment, sizeof(comment));
|
|
|
|
} else if (attr_flags[i] & AF_DATE) {
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Date format %s", attr);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(comment, "-");
|
|
else
|
|
my_strftime(comment, sizeof(str), format, pts);
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Time format %s", attr_list[i]);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(comment, "-");
|
|
else
|
|
my_strftime(comment, sizeof(str), format, pts);
|
|
}
|
|
|
|
if (!comment[0])
|
|
strcpy(comment, attrib[i]);
|
|
|
|
if (strieq(attr_options[i][0], "boolean"))
|
|
strcpy(comment, atoi(attrib[i]) ? "1" : "0");
|
|
|
|
for (k = strlen(str) - 1; k > 0; k--)
|
|
if (str[k] != ' ')
|
|
break;
|
|
|
|
if (k < 20)
|
|
sprintf(str + 20, ": %s\r\n", comment);
|
|
else
|
|
sprintf(str + k + 1, ": %s\r\n", comment);
|
|
|
|
strcpy(mail_text + strlen(mail_text), str);
|
|
}
|
|
}
|
|
|
|
if (flags & 4)
|
|
sprintf(mail_text + strlen(mail_text), "\r\n%s URL : %s\r\n", loc("Logbook"), url);
|
|
|
|
if (flags & 64) {
|
|
for (i = 0; i < MAX_ATTACHMENTS && att_file[i][0]; i++)
|
|
sprintf(mail_text + strlen(mail_text), "\r\n%s %d : %s (%s/%d)\r\n", loc("Attachment"),
|
|
i + 1, att_file[i] + 14, url, i + 1);
|
|
}
|
|
|
|
if (flags & 8) {
|
|
if (isparam("text")) {
|
|
sprintf(mail_text + strlen(mail_text), "\r\n=================================\r\n\r\n%s",
|
|
getparam("text"));
|
|
}
|
|
}
|
|
|
|
strlcat(mail_text, "\r\n\r\n", size);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void format_email_html(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH],
|
|
char att_file[MAX_ATTACHMENTS][256], int old_mail, const char *encoding, char *url,
|
|
char *multipart_boundary, char *mail_text, int size) {
|
|
int i, j, k, flags, n_email_attr, attr_index[MAX_N_ATTR], max_att_size, max_allowed_att_size;
|
|
char str[NAME_LENGTH + 100], str2[256], mail_from[256], mail_from_name[256], format[256],
|
|
list[MAX_N_ATTR][NAME_LENGTH], comment[256], charset[256], multipart_boundary_related[256],
|
|
heading[256], slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], attr[NAME_LENGTH];
|
|
time_t ltime;
|
|
struct tm *pts;
|
|
|
|
if (!getcfg("global", "charset", charset, sizeof(charset)))
|
|
strcpy(charset, DEFAULT_HTTP_CHARSET);
|
|
|
|
if (multipart_boundary[0]) {
|
|
strlcat(mail_text, "--", size);
|
|
strlcat(mail_text, multipart_boundary, size);
|
|
strlcat(mail_text, "\r\n", size);
|
|
}
|
|
|
|
max_att_size = max_attachment_size(lbs, message_id);
|
|
max_allowed_att_size = (int) 10E6;
|
|
if (getcfg(lbs->name, "Max email attachment size", str, sizeof(str)))
|
|
max_allowed_att_size = atoi(str);
|
|
|
|
if (max_att_size) {
|
|
sprintf(multipart_boundary_related, "------------%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/related;\r\n boundary=\"%s\"\r\n\r\n",
|
|
multipart_boundary_related);
|
|
strlcat(mail_text, "--", size);
|
|
strlcat(mail_text, multipart_boundary_related, size);
|
|
strlcat(mail_text, "\r\n", size);
|
|
}
|
|
|
|
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
|
|
"Content-Type: text/html; charset=\"%s\"\r\n", charset);
|
|
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
|
|
"Content-Transfer-Encoding: 7bit\r\n\r\n");
|
|
|
|
retrieve_email_from(lbs, mail_from, mail_from_name, attrib);
|
|
|
|
flags = 63;
|
|
if (getcfg(lbs->name, "Email format", str, sizeof(str)))
|
|
flags = atoi(str);
|
|
|
|
// if attachments too large, include only links to attachments
|
|
if (max_att_size > max_allowed_att_size && (flags & 16) > 0) {
|
|
flags &= ~(16);
|
|
flags |= 64;
|
|
}
|
|
|
|
strcpy(mail_text + strlen(mail_text),
|
|
"<!DOCTYPE html>\r\n");
|
|
strcpy(mail_text + strlen(mail_text), "<html>\r\n<head>\r\n <title></title>\r\n</head>\r\n<body>\r\n");
|
|
|
|
if (flags & 1) {
|
|
strcpy(mail_text + strlen(mail_text), "<h3>\r\n");
|
|
if (getcfg(lbs->name, "Use Email heading", heading, sizeof(heading))) {
|
|
if (old_mail) {
|
|
if (!getcfg(lbs->name, "Use Email heading edit", heading, sizeof(heading)))
|
|
getcfg(lbs->name, "Use Email heading", heading, sizeof(heading));
|
|
}
|
|
|
|
i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
strsubst_list(heading, sizeof(heading), slist, svalue, i);
|
|
|
|
strlcpy(mail_text + strlen(mail_text), heading, size - strlen(mail_text));
|
|
|
|
} else {
|
|
if (old_mail)
|
|
sprintf(mail_text + strlen(mail_text), loc("A old entry has been updated on %s"), host_name);
|
|
else
|
|
sprintf(mail_text + strlen(mail_text), loc("A new entry has been submitted on %s"), host_name);
|
|
strcat(mail_text, ":");
|
|
}
|
|
|
|
strlcpy(mail_text + strlen(mail_text), "</h3>\r\n", size - strlen(mail_text));
|
|
}
|
|
|
|
strlcpy(mail_text + strlen(mail_text), "<table border=\"3\" cellpadding=\"4\">\r\n",
|
|
size - strlen(mail_text));
|
|
|
|
if (flags & 32) {
|
|
sprintf(mail_text + strlen(mail_text), "<tr><td bgcolor=\"#CCCCFF\">%s</td>", loc("Logbook"));
|
|
sprintf(mail_text + strlen(mail_text), "<td bgcolor=\"#DDEEBB\">%s</td></tr>\r\n", lbs->name);
|
|
}
|
|
|
|
if (flags & 2) {
|
|
|
|
if (getcfg(lbs->name, "Email attributes", str, sizeof(str))) {
|
|
n_email_attr = strbreak(str, list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n_email_attr; i++) {
|
|
for (j = 0; j < lbs->n_attr; j++)
|
|
if (strieq(attr_list[j], list[i]))
|
|
break;
|
|
if (!strieq(attr_list[j], list[i]))
|
|
/* attribute not found */
|
|
j = 0;
|
|
attr_index[i] = j;
|
|
}
|
|
} else {
|
|
for (i = 0; i < lbs->n_attr; i++)
|
|
attr_index[i] = i;
|
|
n_email_attr = lbs->n_attr;
|
|
}
|
|
|
|
for (j = 0; j < n_email_attr; j++) {
|
|
|
|
i = attr_index[j];
|
|
strcpy(str, " ");
|
|
memcpy(str, attr_list[i], strlen(attr_list[i]));
|
|
|
|
comment[0] = 0;
|
|
if (attr_flags[i] & AF_ICON) {
|
|
|
|
sprintf(str2, "Icon comment %s", attrib[i]);
|
|
getcfg(lbs->name, str2, comment, sizeof(comment));
|
|
|
|
} else if (attr_flags[i] & AF_DATE) {
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Date format %s", attr);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(comment, "-");
|
|
else
|
|
my_strftime(comment, sizeof(str), format, pts);
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Time format %s", attr);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(comment, "-");
|
|
else
|
|
my_strftime(comment, sizeof(str), format, pts);
|
|
}
|
|
|
|
if (!comment[0])
|
|
strcpy(comment, attrib[i]);
|
|
|
|
if (strieq(attr_options[i][0], "boolean"))
|
|
strcpy(comment, atoi(attrib[i]) ? "1" : "0");
|
|
|
|
for (k = strlen(str) - 1; k > 0; k--)
|
|
if (str[k] != ' ')
|
|
break;
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(mail_text + strlen(mail_text), "<tr><td bgcolor=\"#CCCCFF\">%s</td>", attr);
|
|
sprintf(mail_text + strlen(mail_text), "<td bgcolor=\"#DDEEBB\">%s</td></tr>\r\n", comment);
|
|
}
|
|
}
|
|
|
|
if (flags & 4) {
|
|
sprintf(mail_text + strlen(mail_text),
|
|
"<tr><td bgcolor=\"#CCCCFF\">%s URL</td><td bgcolor=\"#DDEEBB\">", loc("Logbook"));
|
|
sprintf(mail_text + strlen(mail_text), "<a href=\"%s\">%s</a></td></tr>\r\n", url, url);
|
|
}
|
|
|
|
if (flags & 64) {
|
|
for (i = 0; i < MAX_ATTACHMENTS && att_file[i][0]; i++) {
|
|
sprintf(mail_text + strlen(mail_text),
|
|
"<tr><td bgcolor=\"#CCCCFF\">%s %d</td><td bgcolor=\"#DDEEBB\">", loc("Attachment"), i + 1);
|
|
sprintf(mail_text + strlen(mail_text), "<a href=\"%s/%d\">%s</a></td></tr>\r\n", url, i + 1,
|
|
att_file[i] + 14);
|
|
}
|
|
}
|
|
|
|
sprintf(mail_text + strlen(mail_text), "</table>\r\n");
|
|
|
|
if (flags & 8) {
|
|
if (isparam("text")) {
|
|
if (encoding[0] == 'H')
|
|
sprintf(mail_text + strlen(mail_text), "\r\n<HR>\r\n%s", getparam("text"));
|
|
else if (encoding[0] == 'E') {
|
|
sprintf(mail_text + strlen(mail_text), "\r\n<HR>\r\n");
|
|
strlen_retbuf = 0;
|
|
rsputs_elcode(lbs, TRUE, getparam("text"));
|
|
strlcpy(mail_text + strlen(mail_text), return_buffer, TEXT_SIZE + 1000 - strlen(mail_text));
|
|
strlen_retbuf = 0;
|
|
} else
|
|
sprintf(mail_text + strlen(mail_text), "\r\n=================================\r\n\r\n%s",
|
|
getparam("text"));
|
|
}
|
|
}
|
|
|
|
strcpy(mail_text + strlen(mail_text), "\r\n</html></body>\r\n\r\n");
|
|
|
|
if (max_att_size && (flags & 64)) {
|
|
format_email_attachments(lbs, message_id, 2, att_file, mail_text, size, multipart_boundary_related,
|
|
TRUE);
|
|
strlcat(mail_text, "--", size);
|
|
strlcat(mail_text, multipart_boundary_related, size);
|
|
strlcat(mail_text, "--\r\n\r\n", size);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void format_email_html2(LOGBOOK *lbs, int message_id, char att_file[MAX_ATTACHMENTS][256], int old_mail,
|
|
char *multipart_boundary, char *mail_text, int size, int flags) {
|
|
char str[256], charset[256], multipart_boundary_related[256], command[256], *p;
|
|
int max_att_size;
|
|
|
|
sprintf(str, "%d", message_id);
|
|
if (!getcfg("global", "charset", charset, sizeof(charset)))
|
|
strcpy(charset, DEFAULT_HTTP_CHARSET);
|
|
|
|
if (multipart_boundary[0]) {
|
|
strlcat(mail_text, "--", size);
|
|
strlcat(mail_text, multipart_boundary, size);
|
|
strlcat(mail_text, "\r\n", size);
|
|
}
|
|
|
|
max_att_size = max_attachment_size(lbs, message_id);
|
|
|
|
if (max_att_size && (flags & 16) > 0) {
|
|
sprintf(multipart_boundary_related, "------------%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/related;\r\n boundary=\"%s\"\r\n\r\n",
|
|
multipart_boundary_related);
|
|
strlcat(mail_text, "--", size);
|
|
strlcat(mail_text, multipart_boundary_related, size);
|
|
strlcat(mail_text, "\r\n", size);
|
|
}
|
|
|
|
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
|
|
"Content-Type: text/html; charset=\"%s\"\r\n", charset);
|
|
snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
|
|
"Content-Transfer-Encoding: 7bit\r\n\r\n");
|
|
|
|
strlen_retbuf = 0;
|
|
if (old_mail)
|
|
strcpy(command, "oldemail");
|
|
else
|
|
strcpy(command, "email");
|
|
|
|
if ((flags & 64) > 0)
|
|
strlcat(command, "-att-links", sizeof(command));
|
|
|
|
show_elog_entry(lbs, str, command);
|
|
|
|
p = strstr(return_buffer, "\r\n\r\n");
|
|
if (p)
|
|
strlcpy(mail_text + strlen(mail_text), p + 4, size - strlen(mail_text));
|
|
strlen_retbuf = 0;
|
|
strlcat(mail_text, "\r\n", size);
|
|
|
|
if (max_att_size && (flags & 16) > 0) {
|
|
format_email_attachments(lbs, message_id, 2, att_file, mail_text, size, multipart_boundary_related,
|
|
TRUE);
|
|
strlcat(mail_text, "--", size);
|
|
strlcat(mail_text, multipart_boundary_related, size);
|
|
strlcat(mail_text, "--\r\n\r\n", size);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int compose_email(LOGBOOK *lbs, char *rcpt_to, char *mail_to, int message_id,
|
|
char attrib[MAX_N_ATTR][NAME_LENGTH], char *mail_param, int old_mail,
|
|
char att_file[MAX_ATTACHMENTS][256], const char *encoding, int reply_id) {
|
|
int i, n, flags, status, mail_encoding, mail_text_size, max_att_size, n_attachments, max_allowed_att_size;
|
|
char str[NAME_LENGTH + 100], mail_from[256], mail_from_name[256], *mail_text, smtp_host[256],
|
|
subject[256], error[256];
|
|
char list[MAX_PARAM][NAME_LENGTH], url[2048];
|
|
char slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH];
|
|
char multipart_boundary[80];
|
|
|
|
if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) {
|
|
show_error(loc("No SMTP host defined in [global] section of configuration file"));
|
|
return 0;
|
|
}
|
|
|
|
evaluate_conditions(lbs, attrib);
|
|
|
|
flags = 63;
|
|
if (getcfg(lbs->name, "Email format", str, sizeof(str)))
|
|
flags = atoi(str);
|
|
|
|
/* get initial HTML flag from message encoding */
|
|
mail_encoding = 1; // 1:text, 2:short HTML, 4:full HTML
|
|
if (encoding[0] == 'E' || encoding[0] == 'H')
|
|
mail_encoding = 4;
|
|
|
|
/* overwrite with config setting */
|
|
if (getcfg(lbs->name, "Email encoding", str, sizeof(str)))
|
|
mail_encoding = atoi(str);
|
|
|
|
max_allowed_att_size = (int) 10E6;
|
|
if (getcfg(lbs->name, "Max email attachment size", str, sizeof(str)))
|
|
max_allowed_att_size = atoi(str);
|
|
|
|
retrieve_email_from(lbs, mail_from, mail_from_name, attrib);
|
|
|
|
/* compose subject from attributes */
|
|
if (old_mail && getcfg(lbs->name, "Use Email Subject Edit", subject, sizeof(subject))) {
|
|
i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
sprintf(str, "%d", message_id);
|
|
add_subst_list(slist, svalue, "message id", str, &i);
|
|
strsubst_list(subject, sizeof(subject), slist, svalue, i);
|
|
} else if (getcfg(lbs->name, "Use Email Subject", subject, sizeof(subject))) {
|
|
i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
sprintf(str, "%d", message_id);
|
|
add_subst_list(slist, svalue, "message id", str, &i);
|
|
strsubst_list(subject, sizeof(subject), slist, svalue, i);
|
|
} else {
|
|
if (old_mail)
|
|
strcpy(subject, "Updated ELOG entry");
|
|
else
|
|
strcpy(subject, "New ELOG entry");
|
|
}
|
|
|
|
/* count attachments and get maximum size */
|
|
n_attachments = 0;
|
|
max_att_size = 0;
|
|
if (att_file) {
|
|
for (i = 0; att_file[i][0] && i < MAX_ATTACHMENTS; i++) {
|
|
if ((mail_encoding & 6) == 0 || !is_inline_attachment(encoding, message_id,
|
|
getparam("text"), i, att_file[i]))
|
|
n_attachments++;
|
|
}
|
|
max_att_size = max_attachment_size(lbs, message_id);
|
|
}
|
|
|
|
// if attachments too large, include only links to attachments
|
|
if (max_att_size > max_allowed_att_size && (flags & 16) > 0) {
|
|
flags &= ~(16);
|
|
flags |= 64;
|
|
}
|
|
|
|
compose_base_url(lbs, str, sizeof(str), TRUE);
|
|
sprintf(url, "%s%d", str, message_id);
|
|
|
|
mail_text_size = MAX_CONTENT_LENGTH + 1000;
|
|
mail_text = (char *)xmalloc(mail_text_size);
|
|
mail_text[0] = 0;
|
|
|
|
compose_email_header(lbs, subject, mail_from_name, mail_to, url, mail_text, mail_text_size, mail_encoding,
|
|
n_attachments, multipart_boundary, message_id, reply_id);
|
|
|
|
if (mail_encoding & 1)
|
|
format_email_text(lbs, attrib, att_file, old_mail, url, multipart_boundary, mail_text, mail_text_size);
|
|
if (mail_encoding & 2)
|
|
format_email_html(lbs, message_id, attrib, att_file, old_mail, encoding, url, multipart_boundary,
|
|
mail_text, mail_text_size);
|
|
if (mail_encoding & 4)
|
|
format_email_html2(lbs, message_id, att_file, old_mail, multipart_boundary, mail_text, mail_text_size,
|
|
flags);
|
|
|
|
if (n_attachments && (flags & 16)) {
|
|
if ((mail_encoding & 6) > 0)
|
|
/* only non-inline attachements */
|
|
format_email_attachments(lbs, message_id, 1, att_file, mail_text, mail_text_size,
|
|
multipart_boundary, FALSE);
|
|
else
|
|
/* all attachments */
|
|
format_email_attachments(lbs, message_id, 0, att_file, mail_text, mail_text_size,
|
|
multipart_boundary, FALSE);
|
|
}
|
|
|
|
if (multipart_boundary[0]) {
|
|
strlcat(mail_text, "--", mail_text_size);
|
|
strlcat(mail_text, multipart_boundary, mail_text_size);
|
|
strlcat(mail_text, "--\r\n\r\n", mail_text_size);
|
|
}
|
|
|
|
status = sendmail(lbs, smtp_host, mail_from, rcpt_to, mail_text, error, sizeof(error));
|
|
|
|
/*
|
|
{
|
|
int fh;
|
|
fh = open("mail.html", O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0644);
|
|
write(fh, mail_text, strlen(mail_text));
|
|
close(fh);
|
|
}
|
|
*/
|
|
|
|
if (status < 0) {
|
|
sprintf(str, loc("Error sending Email via \"%s\""), smtp_host);
|
|
if (error[0]) {
|
|
strlcat(str, ": ", sizeof(str));
|
|
strlcat(str, error, sizeof(str));
|
|
}
|
|
url_encode(str, sizeof(str));
|
|
sprintf(mail_param, "?error=%s", str);
|
|
} else if (error[0]) {
|
|
sprintf(str, loc("Error sending Email via \"%s\""), smtp_host);
|
|
strlcat(str, ": ", sizeof(str));
|
|
strlcat(str, error, sizeof(str));
|
|
url_encode(str, sizeof(str));
|
|
sprintf(mail_param, "?error=%s", str);
|
|
} else {
|
|
if (!getcfg(lbs->name, "Display email recipients", str, sizeof(str)) || atoi(str) == 1) {
|
|
if (mail_param[0] == 0)
|
|
strcpy(mail_param, "?");
|
|
else
|
|
strcat(mail_param, "&");
|
|
|
|
/* convert '"',CR,LF,TAB to ' ' */
|
|
while (strchr(mail_to, '"'))
|
|
*strchr(mail_to, '"') = ' ';
|
|
while (strchr(mail_to, '\r'))
|
|
*strchr(mail_to, '\r') = ' ';
|
|
while (strchr(mail_to, '\n'))
|
|
*strchr(mail_to, '\n') = ' ';
|
|
while (strchr(mail_to, '\t'))
|
|
*strchr(mail_to, '\t') = ' ';
|
|
|
|
n = strbreak(mail_to, list, MAX_PARAM, ",", FALSE);
|
|
|
|
if (n < 10) {
|
|
for (i = 0; i < n && i < MAX_PARAM; i++) {
|
|
strlcpy(str, list[i], sizeof(str));
|
|
url_encode(str, sizeof(str));
|
|
sprintf(mail_param + strlen(mail_param), "mail%d=%s", i, str);
|
|
if (i < n - 1)
|
|
strcat(mail_param, "&");
|
|
}
|
|
} else {
|
|
sprintf(str, "%d%%20%s", n, loc("recipients"));
|
|
sprintf(mail_param + strlen(mail_param), "mail0=%s", str);
|
|
}
|
|
}
|
|
}
|
|
|
|
xfree(mail_text);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int execute_shell(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH],
|
|
char att_file[MAX_ATTACHMENTS][256], char *sh_cmd) {
|
|
int i;
|
|
char slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH];
|
|
char shell_cmd[2048], tail[1000], str[2048+100], *p, subdir[256];
|
|
|
|
if (!enable_execute) {
|
|
eprintf("Shell execution not enabled via -x flag.\n");
|
|
return SUCCESS;
|
|
}
|
|
|
|
strlcpy(shell_cmd, sh_cmd, sizeof(shell_cmd));
|
|
|
|
i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
sprintf(str, "%d", message_id);
|
|
add_subst_list(slist, svalue, "message id", str, &i);
|
|
add_subst_list(slist, svalue, "text", getparam("text"), &i);
|
|
strsubst_list(shell_cmd, sizeof(shell_cmd), slist, svalue, i);
|
|
|
|
if (att_file && stristr(shell_cmd, "$attachments")) {
|
|
/* substitute attachments */
|
|
p = stristr(shell_cmd, "$attachments");
|
|
strlcpy(tail, p + strlen("$attachments"), sizeof(tail));
|
|
*p = 0;
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
generate_subdir_name(att_file[i], subdir, sizeof(subdir));
|
|
if (att_file[i][0] && strlen(shell_cmd) + strlen(lbs->data_dir) + strlen(subdir) + strlen(att_file[i])
|
|
< sizeof(shell_cmd) + 1) {
|
|
strcpy(p, "\"");
|
|
strcat(p, lbs->data_dir);
|
|
strlcat(str, subdir, sizeof(str));
|
|
strlcpy(str, att_file[i], sizeof(str));
|
|
str_escape(str, sizeof(str));
|
|
strcat(p, str);
|
|
strcat(p, "\" ");
|
|
p += strlen(p);
|
|
}
|
|
}
|
|
strlcat(shell_cmd, tail, sizeof(shell_cmd));
|
|
}
|
|
|
|
sprintf(str, "SHELL \"%s\"", shell_cmd);
|
|
write_logfile(lbs, str);
|
|
|
|
my_shell(shell_cmd, str, sizeof(str));
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int add_attribute_option(LOGBOOK *lbs, char *attrname, char *attrvalue, char *condition) {
|
|
int fh, i, length;
|
|
char str[NAME_LENGTH], av_encoded[NAME_LENGTH], *buf, *buf2, *p1, *p2, *p3;
|
|
|
|
fh = open(config_file, O_RDWR | O_BINARY, 0644);
|
|
if (fh < 0) {
|
|
sprintf(str, loc("Cannot open file <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
|
|
/* do not allow HTML code in value */
|
|
strencode2(av_encoded, attrvalue, sizeof(av_encoded));
|
|
|
|
/* read previous contents */
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buf = (char *)xmalloc(length + strlen(av_encoded) + 3);
|
|
read(fh, buf, length);
|
|
buf[length] = 0;
|
|
|
|
/* find location of options */
|
|
if (condition && condition[0])
|
|
set_condition(condition);
|
|
else
|
|
set_condition("");
|
|
|
|
sprintf(str, "Options %s", attrname);
|
|
p1 = (char *) find_param(buf, lbs->name, str);
|
|
if (p1 == NULL) {
|
|
sprintf(str, "MOptions %s", attrname);
|
|
p1 = (char *) find_param(buf, lbs->name, str);
|
|
}
|
|
if (p1 == NULL) {
|
|
sprintf(str, "ROptions %s", attrname);
|
|
p1 = (char *) find_param(buf, lbs->name, str);
|
|
}
|
|
if (p1 == NULL)
|
|
return 0;
|
|
|
|
p2 = strchr(p1, '\n');
|
|
if (p2 && *(p2 - 1) == '\r')
|
|
p2--;
|
|
|
|
/* save tail */
|
|
buf2 = NULL;
|
|
if (p2)
|
|
buf2 = xstrdup(p2);
|
|
|
|
/* add option */
|
|
p3 = strchr(p1, '\n');
|
|
if (p3 == NULL)
|
|
p3 = p1 + strlen(p1);
|
|
while (*(p3 - 1) == '\n' || *(p3 - 1) == '\r' || *(p3 - 1) == ' ' || *(p3 - 1) == '\t')
|
|
p3--;
|
|
|
|
sprintf(p3, ", %s", av_encoded);
|
|
if (p2) {
|
|
strlcat(buf, buf2, length + strlen(av_encoded) + 3);
|
|
xfree(buf2);
|
|
}
|
|
|
|
lseek(fh, 0, SEEK_SET);
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(str, loc("Cannot write to <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
close(fh);
|
|
xfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
xfree(buf);
|
|
|
|
/* force re-read of config file */
|
|
check_config_file(TRUE);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int set_attributes(LOGBOOK *lbs, char attributes[][NAME_LENGTH], int n) {
|
|
int fh, i, length, size;
|
|
char str[NAME_LENGTH], *buf, *buf2, *p1, *p2, *p3;
|
|
|
|
fh = open(config_file, O_RDWR | O_BINARY, 0644);
|
|
if (fh < 0) {
|
|
sprintf(str, loc("Cannot open file <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
|
|
/* determine length of attributes */
|
|
for (i = size = 0; i < n; i++)
|
|
size += strlen(attributes[i]) + 2;
|
|
|
|
/* read previous contents */
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buf = (char *)xmalloc(length + size + 3);
|
|
read(fh, buf, length);
|
|
buf[length] = 0;
|
|
|
|
/* find location of attributes */
|
|
p1 = (char *) find_param(buf, lbs->name, "Attributes");
|
|
if (p1 == NULL) {
|
|
sprintf(str, loc("No 'Attributes' option present in %s"), config_file);
|
|
show_error(str);
|
|
return 0;
|
|
}
|
|
|
|
p2 = strchr(p1, '\n');
|
|
if (p2 && *(p2 - 1) == '\r')
|
|
p2--;
|
|
|
|
/* save tail */
|
|
buf2 = NULL;
|
|
if (p2)
|
|
buf2 = xstrdup(p2);
|
|
|
|
/* add list */
|
|
p3 = strchr(p1, '=');
|
|
if (p3 == NULL)
|
|
return 0;
|
|
p3++;
|
|
while (*p3 == ' ')
|
|
p3++;
|
|
|
|
for (i = 0; i < n - 1; i++) {
|
|
sprintf(p3, "%s, ", attributes[i]);
|
|
p3 += strlen(p3);
|
|
}
|
|
sprintf(p3, "%s", attributes[i]);
|
|
|
|
if (p2) {
|
|
strlcat(buf, buf2, length + size + 3);
|
|
xfree(buf2);
|
|
}
|
|
|
|
lseek(fh, 0, SEEK_SET);
|
|
i = write(fh, buf, strlen(buf));
|
|
if (i < (int) strlen(buf)) {
|
|
sprintf(str, loc("Cannot write to <b>%s</b>"), config_file);
|
|
strcat(str, ": ");
|
|
strcat(str, strerror(errno));
|
|
show_error(str);
|
|
close(fh);
|
|
xfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
TRUNCATE(fh);
|
|
|
|
close(fh);
|
|
xfree(buf);
|
|
|
|
/* force re-read of config file */
|
|
check_config_file(TRUE);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int propagate_attrib(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
|
|
int n, i, j, status;
|
|
char str[NAME_LENGTH], att_file[MAX_ATTACHMENTS][256], *attr, *list, reply_to[MAX_REPLY_TO * 10];
|
|
|
|
list = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH);
|
|
attr = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH);
|
|
|
|
status = el_retrieve(lbs, message_id, NULL, attr_list, (char (*)[NAME_LENGTH]) attr, lbs->n_attr,
|
|
NULL, NULL, NULL, reply_to, att_file, NULL, NULL, NULL);
|
|
if (status != EL_SUCCESS) {
|
|
xfree(list);
|
|
xfree(attr);
|
|
return status;
|
|
}
|
|
|
|
getcfg(lbs->name, "Propagate attributes", str, sizeof(str));
|
|
n = strbreak(str, (char (*)[1500]) list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n; i++) {
|
|
for (j = 0; j < lbs->n_attr; j++)
|
|
if (stricmp(attr_list[j], list + i * NAME_LENGTH) == 0) {
|
|
strlcpy(attr + j * NAME_LENGTH, attrib[j], NAME_LENGTH);
|
|
break;
|
|
}
|
|
}
|
|
|
|
message_id = el_submit(lbs, message_id, TRUE, "<keep>", attr_list, (char (*)[1500]) attr, lbs->n_attr,
|
|
"<keep>",
|
|
"<keep>", "<keep>", "<keep>", att_file, TRUE, NULL, NULL);
|
|
if (message_id < 0) {
|
|
xfree(list);
|
|
xfree(attr);
|
|
return 0;
|
|
}
|
|
|
|
// go through all replies of this entry
|
|
n = strbreak(reply_to, (char (*)[1500]) list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
propagate_attrib(lbs, atoi(list + i * NAME_LENGTH), attrib);
|
|
|
|
xfree(list);
|
|
xfree(attr);
|
|
|
|
return EL_SUCCESS;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int submit_elog_reply(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], char *text) {
|
|
int n_reply, i, status;
|
|
char att_file[MAX_ATTACHMENTS][256], reply_to[MAX_REPLY_TO * 10],
|
|
list[MAX_N_ATTR][NAME_LENGTH];
|
|
|
|
status = el_retrieve(lbs, message_id, NULL, attr_list, NULL, 0,
|
|
NULL, NULL, NULL, reply_to, att_file, NULL, NULL, NULL);
|
|
if (status != EL_SUCCESS)
|
|
return status;
|
|
|
|
if ((text[0] == '-' && text[1] == '-' && text[2] == '-') ||
|
|
(text[3] == '-' && text[4] == '-' && text[5] == '-'))
|
|
message_id = el_submit(lbs, message_id, TRUE, "<keep>", attr_list, attrib, lbs->n_attr, "<keep>",
|
|
"<keep>", "<keep>", "<keep>", att_file, TRUE, NULL, NULL);
|
|
else
|
|
message_id = el_submit(lbs, message_id, TRUE, "<keep>", attr_list, attrib, lbs->n_attr, text, "<keep>",
|
|
"<keep>", "<keep>", att_file, TRUE, NULL, NULL);
|
|
|
|
if (message_id < 0)
|
|
return 0;
|
|
|
|
if (isparam("elmode") && strieq(getparam("elmode"), "threaded")) {
|
|
|
|
// go through all replies in threaded mode
|
|
n_reply = strbreak(reply_to, list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n_reply; i++) {
|
|
submit_elog_reply(lbs, atoi(list[i]), attrib, text);
|
|
}
|
|
}
|
|
|
|
return EL_SUCCESS;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void submit_elog(LOGBOOK *lbs) {
|
|
char str[2048], str2[2048], file_name[256], error[1000], date[80], *mail_list, *rcpt_list,
|
|
list[10000], *p, locked_by[256], encoding[80], attrib[MAX_N_ATTR][NAME_LENGTH],
|
|
subst_str[MAX_PATH_LENGTH], in_reply_to[80], reply_to[MAX_REPLY_TO * 10], user[256],
|
|
user_email[256], mail_param[1000], *mail_to, *rcpt_to, full_name[256],
|
|
att_file[MAX_ATTACHMENTS][256], slist[MAX_N_ATTR + 10][NAME_LENGTH],
|
|
svalue[MAX_N_ATTR + 10][NAME_LENGTH], ua[NAME_LENGTH], draft[256], attr[NAME_LENGTH];
|
|
int i, j, k, n, missing, first, index, mindex, suppress, message_id, resubmit_orig, mail_to_size,
|
|
rcpt_to_size, ltime, year, month, day, hour, min, sec, n_attr, email_notify[1000], allowed_encoding,
|
|
status, bdraft, old_mail;
|
|
BOOL bedit, bmultiedit;
|
|
struct tm tms;
|
|
|
|
bmultiedit = isparam("nsel");
|
|
bedit = isparam("edit_id") && atoi(getparam("edit_id"));
|
|
bdraft = isparam("draft");
|
|
|
|
/* check for condition */
|
|
if (isparam("condition")) {
|
|
set_condition(getparam("condition"));
|
|
|
|
/* rescan attributes */
|
|
n_attr = scan_attributes(lbs->name);
|
|
} else
|
|
n_attr = lbs->n_attr;
|
|
|
|
/* check for editing interval */
|
|
if (isparam("edit_id"))
|
|
if (!check_edit_time(lbs, atoi(getparam("edit_id"))))
|
|
return;
|
|
|
|
/* check for required attributs */
|
|
missing = 0;
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(ua, attr_list[i], sizeof(ua));
|
|
stou(ua);
|
|
|
|
if (attr_flags[i] & AF_REQUIRED) {
|
|
if (attr_flags[i] & AF_DATE) {
|
|
sprintf(str, "d%d", i);
|
|
if (isparam(str) == 0)
|
|
missing = 1;
|
|
sprintf(str, "m%d", i);
|
|
if (isparam(str) == 0)
|
|
missing = 1;
|
|
sprintf(str, "y%d", i);
|
|
if (isparam(str) == 0)
|
|
missing = 1;
|
|
if (missing)
|
|
break;
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
sprintf(str, "d%d", i);
|
|
if (isparam(str) == 0)
|
|
missing = 1;
|
|
sprintf(str, "m%d", i);
|
|
if (isparam(str) == 0)
|
|
missing = 1;
|
|
sprintf(str, "y%d", i);
|
|
if (isparam(str) == 0)
|
|
missing = 1;
|
|
sprintf(str, "h%d", i);
|
|
if (isparam(str) == 0)
|
|
missing = 1;
|
|
sprintf(str, "n%d", i);
|
|
if (isparam(str) == 0)
|
|
missing = 1;
|
|
if (missing)
|
|
break;
|
|
} else if ((attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL))) {
|
|
for (j = 0; j < MAX_N_LIST; j++) {
|
|
sprintf(str, "%s_%d", ua, j);
|
|
if (isparam(str))
|
|
break;
|
|
|
|
/* check for attributes without the _<i> from elog */
|
|
if (isparam(ua))
|
|
break;
|
|
}
|
|
|
|
if (j == MAX_N_LIST) {
|
|
missing = 1;
|
|
break;
|
|
}
|
|
} else if (isparam(ua) == 0 || *getparam(ua) == 0) {
|
|
missing = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (missing && !bdraft) {
|
|
sprintf(error, "<i>");
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(error + strlen(error), loc("Error: Attribute <b>%s</b> not supplied"), attr);
|
|
sprintf(error + strlen(error), ".</i><p>\n");
|
|
sprintf(error + strlen(error), loc("Please go back and enter the <b>%s</b> field"), attr);
|
|
strcat(error, ".\n");
|
|
|
|
show_error(error);
|
|
return;
|
|
}
|
|
|
|
/* check for numeric attributes */
|
|
if (!bdraft) {
|
|
for (index = 0; index < lbs->n_attr; index++)
|
|
if (attr_flags[index] & AF_NUMERIC) {
|
|
strcpy(ua, attr_list[index]);
|
|
stou(ua);
|
|
strlcpy(str, isparam(ua) ? getparam(ua) : "", sizeof(str));
|
|
|
|
for (j = 0; i < (int) strlen(str); i++)
|
|
if (!isdigit(str[i]))
|
|
break;
|
|
|
|
sprintf(str2, "- %s -", loc("keep original values"));
|
|
if (i < (int) strlen(str) && strcmp(str, "<keep>") != 0 && strcmp(str, str2) != 0) {
|
|
sprintf(error, loc("Error: Attribute <b>%s</b> must be numeric"), attr_list[index]);
|
|
show_error(error);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < n_attr; i++) {
|
|
strlcpy(ua, attr_list[i], sizeof(ua));
|
|
stou(ua);
|
|
if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL))
|
|
strcat(ua, "_0");
|
|
|
|
if (isparam(ua) && *getparam(ua) && attr_options[i][0][0]) {
|
|
|
|
if (strieq(attr_options[i][0], "boolean") && !bdraft) {
|
|
if (atoi(getparam(ua)) != 0 && atoi(getparam(ua)) != 1 && strcmp(getparam(ua), "<keep>") != 0) {
|
|
strencode2(str, getparam(ua), sizeof(str));
|
|
sprintf(error, loc("Error: Value <b>%s</b> not allowed for boolean attributes"), str);
|
|
show_error(error);
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
|
|
/* check if option exists */
|
|
for (j = 0; attr_options[i][j][0]; j++)
|
|
if (strieq(attr_options[i][j], getparam(ua)))
|
|
break;
|
|
|
|
/* check if option without {n} exists */
|
|
if (!attr_options[i][j][0]) {
|
|
for (j = 0; attr_options[i][j][0]; j++) {
|
|
strlcpy(str, attr_options[i][j], sizeof(str));
|
|
if (strchr(str, '{'))
|
|
*strchr(str, '{') = 0;
|
|
if (strieq(str, getparam(ua)))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!attr_options[i][j][0] && isparam(ua) && strcmp(getparam(ua), "<keep>") != 0) {
|
|
if (attr_flags[i] & AF_EXTENDABLE) {
|
|
|
|
/* check if maximal number of options exceeded */
|
|
if (attr_options[i][MAX_N_LIST - 1][0]) {
|
|
strcpy(error, loc("Maximum number of attribute options exceeded"));
|
|
strcat(error, "<br>");
|
|
strcat(error, loc("Please increase MAX_N_LIST in elogd.c and recompile"));
|
|
show_error(error);
|
|
return;
|
|
}
|
|
|
|
if (!add_attribute_option(lbs, attr_list[i], getparam(ua), getparam("condition")))
|
|
return;
|
|
} else {
|
|
char encoded[100];
|
|
strencode2(encoded, getparam(ua), sizeof(encoded));
|
|
sprintf(error, loc("Error: Attribute option <b>%s</b> not existing"), encoded);
|
|
show_error(error);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check if allowed encoding */
|
|
if (getcfg(lbs->name, "Allowed encoding", str, sizeof(str)))
|
|
allowed_encoding = atoi(str);
|
|
else
|
|
allowed_encoding = 7;
|
|
|
|
strlcpy(encoding, isparam("encoding") ? getparam("encoding") : "plain", sizeof(encoding));
|
|
|
|
/* check for valid encoding */
|
|
if (!strieq(encoding, "plain") && !strieq(encoding, "ELCode") && !strieq(encoding, "HTML"))
|
|
strcpy(encoding, "plain");
|
|
|
|
if (strieq(encoding, "plain") && (allowed_encoding & 1) == 0) {
|
|
show_error("Plain encoding not allowed");
|
|
return;
|
|
}
|
|
if (strieq(encoding, "ELCode") && (allowed_encoding & 2) == 0) {
|
|
show_error("ELCode encoding not allowed");
|
|
return;
|
|
}
|
|
if (strieq(encoding, "HTML") && (allowed_encoding & 4) == 0) {
|
|
show_error("HTML encoding not allowed");
|
|
return;
|
|
}
|
|
|
|
/* get attachments */
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
sprintf(str, "attachment%d", i);
|
|
strcpy(att_file[i], isparam(str) ? getparam(str) : "");
|
|
}
|
|
|
|
/* retrieve attributes */
|
|
for (i = 0; i < n_attr; i++) {
|
|
|
|
strlcpy(ua, attr_list[i], sizeof(ua));
|
|
stou(ua);
|
|
|
|
if (strieq(attr_options[i][0], "boolean") && !isparam(ua)) {
|
|
|
|
strcpy(attrib[i], "0");
|
|
|
|
} else if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) {
|
|
|
|
if (isparam(ua)) {
|
|
strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
|
|
} else {
|
|
attrib[i][0] = 0;
|
|
first = 1;
|
|
for (j = 0; j < MAX_N_LIST; j++) {
|
|
sprintf(str, "%s_%d", ua, j);
|
|
if (isparam(str)) {
|
|
if (first)
|
|
first = 0;
|
|
else
|
|
strlcat(attrib[i], " | ", NAME_LENGTH);
|
|
if (strlen(attrib[i]) + strlen(getparam(str)) < NAME_LENGTH - 2)
|
|
strlcat(attrib[i], getparam(str), NAME_LENGTH);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (attr_flags[i] & AF_DATE) {
|
|
|
|
if (isparam(ua)) // from edit/reply of fixed attributes
|
|
strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
|
|
else {
|
|
|
|
sprintf(str, "m%d", i);
|
|
if (isparam(str) && strieq(getparam(str), "<keep>"))
|
|
strcpy(attrib[i], "<keep>");
|
|
else {
|
|
sprintf(str, "y%d", i);
|
|
year = isparam(str) ? atoi(getparam(str)) : 0;
|
|
if (year < 100)
|
|
year += 2000;
|
|
|
|
sprintf(str, "m%d", i);
|
|
month = isparam(str) ? atoi(getparam(str)) : 0;
|
|
|
|
sprintf(str, "d%d", i);
|
|
day = isparam(str) ? atoi(getparam(str)) : 0;
|
|
|
|
if (month == 0 || day == 0)
|
|
strcpy(attrib[i], "");
|
|
else {
|
|
memset(&tms, 0, sizeof(struct tm));
|
|
tms.tm_year = year - 1900;
|
|
tms.tm_mon = month - 1;
|
|
tms.tm_mday = day;
|
|
tms.tm_hour = 12;
|
|
|
|
ltime = (int) mktime(&tms);
|
|
|
|
if (ltime == -1) {
|
|
show_error(loc("Date must be between 1970 and 2037"));
|
|
return;
|
|
}
|
|
sprintf(attrib[i], "%d", ltime);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
|
|
if (isparam(ua)) // from edit/reply of fixed attributes
|
|
strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
|
|
else {
|
|
|
|
sprintf(str, "m%d", i);
|
|
if (isparam(str) && strieq(getparam(str), "<keep>"))
|
|
strcpy(attrib[i], "<keep>");
|
|
else {
|
|
sprintf(str, "y%d", i);
|
|
year = isparam(str) ? atoi(getparam(str)) : 0;
|
|
if (year < 100)
|
|
year += 2000;
|
|
|
|
sprintf(str, "m%d", i);
|
|
month = isparam(str) ? atoi(getparam(str)) : 0;
|
|
|
|
sprintf(str, "d%d", i);
|
|
day = isparam(str) ? atoi(getparam(str)) : 0;
|
|
|
|
sprintf(str, "h%d", i);
|
|
hour = isparam(str) ? atoi(getparam(str)) : 0;
|
|
|
|
sprintf(str, "n%d", i);
|
|
min = isparam(str) ? atoi(getparam(str)) : 0;
|
|
|
|
sprintf(str, "c%d", i);
|
|
sec = isparam(str) ? atoi(getparam(str)) : 0;
|
|
|
|
if (month == 0 || day == 0)
|
|
strcpy(attrib[i], "");
|
|
else {
|
|
memset(&tms, 0, sizeof(struct tm));
|
|
tms.tm_year = year - 1900;
|
|
tms.tm_mon = month - 1;
|
|
tms.tm_mday = day;
|
|
tms.tm_hour = hour;
|
|
tms.tm_min = min;
|
|
tms.tm_sec = sec;
|
|
tms.tm_isdst = -1;
|
|
|
|
ltime = (int) mktime(&tms);
|
|
|
|
if (ltime == -1) {
|
|
show_error(loc("Date must be between 1970 and 2037"));
|
|
return;
|
|
}
|
|
sprintf(attrib[i], "%d", ltime);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
strlcpy(attrib[i], isparam(ua) ? getparam(ua) : "", NAME_LENGTH);
|
|
|
|
/* remove any CR/LF */
|
|
if (strchr(attrib[i], '\r'))
|
|
*strchr(attrib[i], '\r') = 0;
|
|
if (strchr(attrib[i], '\n'))
|
|
*strchr(attrib[i], '\n') = 0;
|
|
|
|
/* strip trailing "{...}" */
|
|
if (is_cond_attr(i) && strchr(attrib[i], '{') && strchr(attrib[i], '}'))
|
|
*strchr(attrib[i], '{') = 0;
|
|
}
|
|
|
|
}
|
|
|
|
/* compile substitution list */
|
|
n = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
if (bedit)
|
|
add_subst_list(slist, svalue, "message id", getparam("edit_id"), &n);
|
|
|
|
/* substitute attributes */
|
|
if (!bedit && !isparam("reply_to")) {
|
|
for (index = 0; index < n_attr; index++) {
|
|
strlcpy(attr, attr_list[index], sizeof(attr));
|
|
sprintf(str, "Subst %s", attr);
|
|
if (getcfg(lbs->name, str, subst_str, sizeof(subst_str))) {
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
|
|
strsubst_list(subst_str, sizeof(subst_str), slist, svalue, i);
|
|
|
|
/* check for index substitution if not in edit mode */
|
|
if (!bedit && strchr(subst_str, '#')) {
|
|
/* get index */
|
|
get_auto_index(lbs, index, subst_str, str, sizeof(str));
|
|
strcpy(subst_str, str);
|
|
}
|
|
strcpy(attrib[index], subst_str);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* subst attributes for edits */
|
|
if (bedit) {
|
|
for (index = 0; index < n_attr; index++) {
|
|
sprintf(str, "Subst on edit %s", attr_list[index]);
|
|
if (getcfg(lbs->name, str, str2, sizeof(str2))) {
|
|
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
|
|
add_subst_list(slist, svalue, "message id", getparam("edit_id"), &i);
|
|
|
|
strsubst_list(str2, sizeof(str2), slist, svalue, i);
|
|
if (strlen(str2) > NAME_LENGTH - 100) {
|
|
if (strstr(str2 + 100, "<br>")) {
|
|
strlcpy(str, strstr(str2 + 100, "<br>"), sizeof(str));
|
|
} else
|
|
strlcpy(str, str2 + 100, sizeof(str));
|
|
|
|
strcpy(str2, "...");
|
|
strlcat(str2, str, sizeof(str2));
|
|
}
|
|
if (strncmp(str2, "<br>", 4) == 0)
|
|
strcpy(attrib[index], str2 + 4);
|
|
else
|
|
strcpy(attrib[index], str2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* subst attributes for replies */
|
|
if (isparam("reply_to")) {
|
|
for (index = 0; index < n_attr; index++) {
|
|
sprintf(str, "Subst on reply %s", attr_list[index]);
|
|
if (getcfg(lbs->name, str, str2, sizeof(str2))) {
|
|
/* do not format date for date attributes */
|
|
i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
|
|
== 0);
|
|
if (isparam("reply_to"))
|
|
add_subst_list(slist, svalue, "Reply to", getparam("reply_to"), &i);
|
|
strsubst_list(str2, sizeof(str2), slist, svalue, i);
|
|
strcpy(attrib[index], str2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for attributes to keep */
|
|
if (bmultiedit) {
|
|
sprintf(str, "- %s -", loc("keep original values"));
|
|
for (i = 0; i < n_attr; i++) {
|
|
if (strieq(str, attrib[i]))
|
|
strlcpy(attrib[i], "<keep>", NAME_LENGTH);
|
|
}
|
|
}
|
|
|
|
message_id = 0;
|
|
reply_to[0] = 0;
|
|
in_reply_to[0] = 0;
|
|
date[0] = 0;
|
|
resubmit_orig = 0;
|
|
locked_by[0] = 0;
|
|
draft[0] = 0;
|
|
if (isparam("draft"))
|
|
strlcpy(draft, getparam("draft"), sizeof(draft));
|
|
|
|
if (bedit && isparam("resubmit") && atoi(getparam("resubmit")) == 1) {
|
|
resubmit_orig = atoi(getparam("edit_id"));
|
|
|
|
/* get old links */
|
|
el_retrieve(lbs, resubmit_orig, NULL, NULL, NULL, 0, NULL, 0, in_reply_to, reply_to, NULL, NULL, NULL,
|
|
NULL);
|
|
|
|
/* if not message head, move all preceeding messages */
|
|
/* outcommented, users want only resubmitted message occur at end (see what's new)
|
|
if (in_reply_to[0])
|
|
{
|
|
do
|
|
{
|
|
resubmit_orig = atoi(in_reply_to);
|
|
el_retrieve(lbs, resubmit_orig, NULL, NULL, NULL, 0,
|
|
NULL, 0, in_reply_to, reply_to, NULL, NULL);
|
|
} while (in_reply_to[0]);
|
|
}
|
|
*/
|
|
|
|
message_id = atoi(getparam("edit_id"));
|
|
strcpy(in_reply_to, "<keep>");
|
|
strcpy(reply_to, "<keep>");
|
|
date[0] = 0;
|
|
} else {
|
|
if (bedit) {
|
|
message_id = atoi(getparam("edit_id"));
|
|
strcpy(in_reply_to, "<keep>");
|
|
strcpy(reply_to, "<keep>");
|
|
strcpy(date, "<keep>");
|
|
if (bdraft)
|
|
strcpy(locked_by, "<keep>");
|
|
} else
|
|
strcpy(in_reply_to, isparam("reply_to") ? getparam("reply_to") : "");
|
|
}
|
|
|
|
if (bmultiedit) {
|
|
for (i = n = 0; i < atoi(getparam("nsel")); i++) {
|
|
sprintf(str, "s%d", i);
|
|
if (isparam(str)) {
|
|
|
|
message_id = atoi(getparam(str));
|
|
|
|
status = submit_elog_reply(lbs, message_id, attrib, getparam("text"));
|
|
if (status != EL_SUCCESS) {
|
|
sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs->data_dir);
|
|
strcat(str, "\n<p>");
|
|
strcat(str,
|
|
loc("Please check that it exists and elogd has write access and disk is not full"));
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
redirect(lbs, isparam("redir") ? getparam("redir") : "");
|
|
return; /* no email notifications etc */
|
|
} else {
|
|
message_id = el_submit(lbs, message_id, bedit, date, attr_list, attrib, n_attr, getparam("text"),
|
|
in_reply_to, reply_to, encoding, att_file, TRUE, locked_by, draft);
|
|
|
|
if (message_id <= 0) {
|
|
sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs->data_dir);
|
|
strcat(str, "\n<p>");
|
|
strcat(str, loc("Please check that it exists and elogd has write access and disk is not full"));
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
if (bdraft) {
|
|
show_http_header(lbs, FALSE, NULL, 200);
|
|
rsprintf("OK %d\n", message_id);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_logging_level > 1) {
|
|
if (bmultiedit)
|
|
sprintf(str, "EDIT multiple entries");
|
|
else if (bdraft)
|
|
sprintf(str, "DRAFT entry #%d", message_id);
|
|
else if (bedit && !resubmit_orig)
|
|
sprintf(str, "EDIT entry #%d", message_id);
|
|
else
|
|
sprintf(str, "NEW entry #%d", message_id);
|
|
|
|
write_logfile(lbs, str);
|
|
}
|
|
|
|
/* evaluate propagation of attributes */
|
|
if (getcfg(lbs->name, "Propagate attributes", str, sizeof(str)))
|
|
propagate_attrib(lbs, find_thread_head(lbs, message_id), attrib);
|
|
|
|
/* resubmit thread if requested */
|
|
if (resubmit_orig)
|
|
message_id = el_move_message_thread(lbs, resubmit_orig);
|
|
|
|
/* retrieve submission date */
|
|
if (date[0] == 0)
|
|
el_retrieve(lbs, message_id, date, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
/*---- replace relative elog:/x link by elog:n/x link */
|
|
if (stristr(getparam("text"), "elog:/")) {
|
|
p = getparam("text");
|
|
if (stricmp(encoding, "HTML") == 0) {
|
|
sprintf(str, "%d/", message_id);
|
|
} else
|
|
sprintf(str, "elog:%d/", message_id);
|
|
strsubst(p, TEXT_SIZE, "elog:/", str);
|
|
el_submit(lbs, message_id, TRUE, date, attr_list, attrib, n_attr, p, in_reply_to, reply_to, encoding,
|
|
att_file, TRUE, NULL, NULL);
|
|
}
|
|
|
|
/*---- replace elog: by HTML link ----*/
|
|
if (strieq(encoding, "HTML") && stristr(getparam("text"), "elog:")) {
|
|
p = stristr(getparam("text"), "elog:");
|
|
while (p) {
|
|
for (i = 0; i < 5 || (p[i] == '/' || isalnum(p[i])); i++)
|
|
str[i] = p[i];
|
|
str[i] = 0;
|
|
convert_elog_link(lbs, str + 5, str + 5, str2, 0, message_id);
|
|
strsubst(p, TEXT_SIZE, str, str2);
|
|
p += strlen(str2);
|
|
p = stristr(p, "elog:");
|
|
}
|
|
el_submit(lbs, message_id, TRUE, date, attr_list, attrib, n_attr, getparam("text"), in_reply_to,
|
|
reply_to, encoding, att_file, TRUE, NULL, NULL);
|
|
}
|
|
|
|
/*---- email notifications ----*/
|
|
|
|
suppress = isparam("suppress") ? atoi(getparam("suppress")) : 0;
|
|
|
|
/* check for mail submissions */
|
|
mail_param[0] = 0;
|
|
mail_to = (char *) xmalloc(256);
|
|
mail_to[0] = 0;
|
|
mail_to_size = 256;
|
|
rcpt_to = (char *) xmalloc(256);
|
|
rcpt_to[0] = 0;
|
|
rcpt_to_size = 256;
|
|
mail_list = (char *) xmalloc(MAX_N_EMAIL * NAME_LENGTH);
|
|
rcpt_list = (char *) xmalloc(MAX_N_EMAIL * NAME_LENGTH);
|
|
|
|
if (suppress == 1 || suppress == 3) {
|
|
if (suppress == 1)
|
|
strcpy(mail_param, "?suppress=1");
|
|
} else {
|
|
/* go throuch "Email xxx" in configuration file */
|
|
for (index = mindex = 0; index <= n_attr; index++) {
|
|
|
|
strcpy(ua, attr_list[index]);
|
|
stou(ua);
|
|
|
|
if (index < n_attr) {
|
|
strcpy(str, "Email ");
|
|
if (strchr(attr_list[index], ' '))
|
|
sprintf(str + strlen(str), "\"%s\"", attr_list[index]);
|
|
else
|
|
strlcat(str, attr_list[index], sizeof(str));
|
|
strcat(str, " ");
|
|
|
|
if (attr_flags[index] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) {
|
|
sprintf(str2, "%s_%d", ua, mindex);
|
|
|
|
mindex++;
|
|
if (mindex == MAX_N_LIST)
|
|
mindex = 0;
|
|
else
|
|
index--; /* repeat this loop */
|
|
} else
|
|
strlcpy(str2, ua, sizeof(str2));
|
|
|
|
if (isparam(str2)) {
|
|
if (strchr(getparam(str2), ' ')) {
|
|
strlcat(str, "\"", sizeof(str));
|
|
strlcat(str, getparam(str2), sizeof(str));
|
|
strlcat(str, "\"", sizeof(str));
|
|
} else
|
|
strlcat(str, getparam(str2), sizeof(str));
|
|
}
|
|
} else
|
|
sprintf(str, "Email ALL");
|
|
|
|
if (getcfg(lbs->name, str, list, sizeof(list))) {
|
|
i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
strsubst_list(list, sizeof(list), slist, svalue, i);
|
|
|
|
n = strbreak(list, (char (*)[1500]) mail_list, 200, ",", FALSE);
|
|
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("\n%s to %s\n\n", str, list);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
/* remove possible 'mailto:' */
|
|
if ((p = strstr(&mail_list[i * NAME_LENGTH], "mailto:")) != NULL)
|
|
memmove(p, p + 7, strlen(p + 7) + 1);
|
|
|
|
if ((int) strlen(mail_to) + (int) strlen(&mail_list[i * NAME_LENGTH]) + 10 >= mail_to_size) {
|
|
mail_to_size += 256;
|
|
mail_to = (char *)xrealloc(mail_to, mail_to_size);
|
|
}
|
|
strcat(mail_to, &mail_list[i * NAME_LENGTH]);
|
|
strcat(mail_to, ",");
|
|
|
|
if ((int) strlen(rcpt_to) + (int) strlen(&mail_list[i * NAME_LENGTH]) + 10 >= rcpt_to_size) {
|
|
rcpt_to_size += 256;
|
|
rcpt_to = (char *)xrealloc(rcpt_to, rcpt_to_size);
|
|
}
|
|
strcat(rcpt_to, &mail_list[i * NAME_LENGTH]);
|
|
strcat(rcpt_to, ",");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!getcfg(lbs->name, "Suppress Email to users", str, sizeof(str)) || atoi(str) == 0) {
|
|
/* go through password file */
|
|
for (index = 0;; index++) {
|
|
if (!enum_user_line(lbs, index, user, sizeof(user)))
|
|
break;
|
|
|
|
get_user_line(lbs, user, NULL, full_name, user_email, email_notify, NULL, NULL);
|
|
|
|
for (i = 0; lb_list[i].name[0] && i < 1000; i++)
|
|
if (strieq(lb_list[i].name, lbs->name))
|
|
break;
|
|
|
|
if (email_notify[i]) {
|
|
/* check if user has access to this logbook */
|
|
if (!check_login_user(lbs, user))
|
|
continue;
|
|
|
|
sprintf(str, "\"%s\" <%s>,", full_name, user_email);
|
|
if ((int) strlen(mail_to) + (int) strlen(str) + 1 >= mail_to_size) {
|
|
mail_to_size += 256;
|
|
mail_to = (char *)xrealloc(mail_to, mail_to_size);
|
|
}
|
|
strcat(mail_to, str);
|
|
|
|
sprintf(str, "%s,", user_email);
|
|
if ((int) strlen(rcpt_to) + (int) strlen(str) + 1 >= rcpt_to_size) {
|
|
rcpt_to_size += 256;
|
|
rcpt_to = (char *)xrealloc(rcpt_to, rcpt_to_size);
|
|
}
|
|
strcat(rcpt_to, str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strlen(mail_to) > 0) {
|
|
/* convert any '|' to ',', remove duplicate email to's */
|
|
n = strbreak(rcpt_to, (char (*)[1500])rcpt_list, MAX_N_EMAIL, ",|", TRUE);
|
|
strbreak(mail_to, (char (*)[1500])mail_list, MAX_N_EMAIL, ",|", TRUE);
|
|
for (i = 0; i < n - 1; i++) {
|
|
for (j = i + 1; j < n; j++) {
|
|
if (rcpt_list[i * NAME_LENGTH] && rcpt_list[j * NAME_LENGTH] &&
|
|
strstr(&rcpt_list[i * NAME_LENGTH], &rcpt_list[j * NAME_LENGTH])) {
|
|
for (k = j; k < n - 1; k++) {
|
|
memcpy(&rcpt_list[k * NAME_LENGTH], &rcpt_list[(k + 1) * NAME_LENGTH], NAME_LENGTH);
|
|
memcpy(&mail_list[k * NAME_LENGTH], &mail_list[(k + 1) * NAME_LENGTH], NAME_LENGTH);
|
|
}
|
|
memset(&rcpt_list[k * NAME_LENGTH], 0, NAME_LENGTH);
|
|
memset(&mail_list[k * NAME_LENGTH], 0, NAME_LENGTH);
|
|
j = j - 1;
|
|
n = n - 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
rcpt_to[0] = 0;
|
|
mail_to[0] = 0;
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if ((int) strlen(rcpt_to) + (int) strlen(&rcpt_list[i * NAME_LENGTH]) + 5 >= rcpt_to_size) {
|
|
rcpt_to_size += 256;
|
|
rcpt_to = (char *)xrealloc(rcpt_to, rcpt_to_size);
|
|
}
|
|
strcat(rcpt_to, &rcpt_list[i * NAME_LENGTH]);
|
|
|
|
if ((int) strlen(mail_to) + (int) strlen(&mail_list[i * NAME_LENGTH]) + 5 >= mail_to_size) {
|
|
mail_to_size += 256;
|
|
mail_to = (char *)xrealloc(mail_to, mail_to_size);
|
|
}
|
|
strcat(mail_to, &mail_list[i * NAME_LENGTH]);
|
|
|
|
if (i < MAX_N_EMAIL - 1 && rcpt_list[(i + 1) * NAME_LENGTH]) {
|
|
strcat(rcpt_to, ",");
|
|
strcat(mail_to, ",\r\n\t");
|
|
}
|
|
}
|
|
|
|
/* fix for edited draft messages. new_entry is a hidden field persisting draft cycles */
|
|
old_mail = bedit;
|
|
if (isparam("new_entry"))
|
|
old_mail = 0;
|
|
if (resubmit_orig)
|
|
old_mail = 0;
|
|
|
|
if (compose_email(lbs, rcpt_to, mail_to, message_id, attrib, mail_param, old_mail, att_file,
|
|
isparam("encoding") ? getparam("encoding") : "plain", atoi(in_reply_to)) == 0) {
|
|
xfree(mail_to);
|
|
xfree(rcpt_to);
|
|
xfree(mail_list);
|
|
xfree(rcpt_list);
|
|
return;
|
|
}
|
|
}
|
|
|
|
xfree(mail_to);
|
|
xfree(rcpt_to);
|
|
xfree(mail_list);
|
|
xfree(rcpt_list);
|
|
|
|
/*---- shell execution ----*/
|
|
|
|
if (!(isparam("shell_suppress") && atoi(getparam("shell_suppress")))) {
|
|
if (!bedit) {
|
|
if (getcfg(lbs->name, "Execute new", str, sizeof(str)))
|
|
execute_shell(lbs, message_id, attrib, att_file, str);
|
|
} else {
|
|
if (getcfg(lbs->name, "Execute edit", str, sizeof(str)))
|
|
execute_shell(lbs, message_id, attrib, att_file, str);
|
|
}
|
|
}
|
|
|
|
/*---- custom submit page ----*/
|
|
|
|
if (getcfg(lbs->name, "Submit page", str, sizeof(str))) {
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
send_file_direct(file_name);
|
|
return;
|
|
}
|
|
|
|
if (getcfg(lbs->name, "List after submit", str, sizeof(str)) && atoi(str) == 1)
|
|
str[0] = 0;
|
|
else
|
|
sprintf(str, "%d%s", message_id, mail_param);
|
|
redirect(lbs, str);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void submit_elog_mirror(LOGBOOK *lbs) {
|
|
char str[1000], date[80], attrib_value[MAX_N_ATTR][NAME_LENGTH], attrib_name[MAX_N_ATTR][NAME_LENGTH],
|
|
in_reply_to[80], encoding[80], reply_to[MAX_REPLY_TO * 10], att_file[MAX_ATTACHMENTS][256],
|
|
name[NAME_LENGTH], value[NAME_LENGTH];
|
|
int i, message_id, n_attr;
|
|
BOOL bedit;
|
|
|
|
/* get attachments */
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
sprintf(str, "attachment%d", i);
|
|
strcpy(att_file[i], isparam(str) ? getparam(str) : "");
|
|
}
|
|
|
|
reply_to[0] = 0;
|
|
in_reply_to[0] = 0;
|
|
date[0] = 0;
|
|
encoding[0] = 0;
|
|
bedit = FALSE;
|
|
n_attr = 0;
|
|
message_id = 0;
|
|
|
|
/* retrieve attributes */
|
|
for (i = 0; i < MAX_PARAM; i++) {
|
|
if (enumparam(i, name, value)) {
|
|
|
|
if (strieq(name, "mirror_id"))
|
|
message_id = atoi(value);
|
|
else if (strieq(name, "entry_date"))
|
|
strlcpy(date, value, sizeof(date));
|
|
else if (strieq(name, "reply_to"))
|
|
strlcpy(reply_to, value, sizeof(reply_to));
|
|
else if (strieq(name, "in_reply_to"))
|
|
strlcpy(in_reply_to, value, sizeof(in_reply_to));
|
|
else if (strieq(name, "encoding"))
|
|
strlcpy(encoding, value, sizeof(encoding));
|
|
else if (!strieq(name, "cmd") && !strieq(name, "full_name") && !strieq(name, "user_email")
|
|
&& !strieq(name, "unm") && !strieq(name, "upwd") && !strieq(name, "wpwd") && strncmp(name,
|
|
"attachment",
|
|
10) !=
|
|
0) {
|
|
strlcpy(attrib_name[n_attr], name, NAME_LENGTH);
|
|
strlcpy(attrib_value[n_attr++], value, NAME_LENGTH);
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (message_id == 0 || date[0] == 0) {
|
|
show_error(loc("Invalid mirror_id or entry_date"));
|
|
return;
|
|
}
|
|
|
|
/* check if message already exists */
|
|
for (i = 0; i < *lbs->n_el_index; i++)
|
|
if (lbs->el_index[i].message_id == message_id) {
|
|
bedit = TRUE;
|
|
break;
|
|
}
|
|
|
|
message_id = el_submit(lbs, message_id, bedit, date, attrib_name, attrib_value, n_attr, getparam("text"),
|
|
in_reply_to, reply_to, encoding, att_file, FALSE, NULL, NULL);
|
|
|
|
if (message_id <= 0) {
|
|
sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs->data_dir);
|
|
strcat(str, "\n<p>");
|
|
strcat(str, loc("Please check that it exists and elogd has write access"));
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
sprintf(str, "%d", message_id);
|
|
redirect(lbs, str);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void copy_to(LOGBOOK *lbs, int src_id, const char *dest_logbook, int move, int orig_id) {
|
|
int size, i, j, n, n_reply, index, status, fh, source_id, message_id,
|
|
thumb_status, next_id = 0;
|
|
char str[2048], str2[1024], file_name[MAX_PATH_LENGTH], thumb_name[MAX_PATH_LENGTH],
|
|
*attrib, date[80], *text, msg_str[32], in_reply_to[80], subdir[256],
|
|
reply_to[MAX_REPLY_TO * 10], *attachment, encoding[80], locked_by[256], draft[256],
|
|
*buffer, *list;
|
|
LOGBOOK *lbs_dest;
|
|
BOOL bedit;
|
|
|
|
attachment = (char *)xmalloc(MAX_ATTACHMENTS * MAX_PATH_LENGTH);
|
|
attrib = (char *)xmalloc(MAX_N_ATTR * NAME_LENGTH);
|
|
list = (char *)xmalloc(MAX_N_ATTR * NAME_LENGTH);
|
|
text = (char *)xmalloc(TEXT_SIZE);
|
|
|
|
for (i = 0; lb_list[i].name[0]; i++)
|
|
if (strieq(lb_list[i].name, dest_logbook))
|
|
break;
|
|
if (!lb_list[i].name[0])
|
|
return;
|
|
lbs_dest = &lb_list[i];
|
|
|
|
if (src_id)
|
|
n = 1;
|
|
else
|
|
n = isparam("nsel") ? atoi(getparam("nsel")) : 0;
|
|
|
|
source_id = status = next_id = 0;
|
|
for (index = 0; index < n; index++) {
|
|
if (src_id)
|
|
source_id = src_id;
|
|
else {
|
|
sprintf(str, "s%d", index);
|
|
if (!isparam(str))
|
|
continue;
|
|
|
|
source_id = isparam(str) ? atoi(getparam(str)) : 0;
|
|
}
|
|
|
|
/* get message */
|
|
size = TEXT_SIZE;
|
|
status =
|
|
el_retrieve(lbs, source_id, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text, &size,
|
|
in_reply_to,
|
|
reply_to, (char (*)[256]) attachment, encoding, locked_by, draft);
|
|
|
|
if (status != EL_SUCCESS) {
|
|
sprintf(msg_str, "%d", source_id);
|
|
sprintf(str, loc("Entry %s cannot be read from logbook \"%s\""), msg_str, lbs->name);
|
|
show_error(str);
|
|
xfree(attachment);
|
|
xfree(attrib);
|
|
xfree(list);
|
|
xfree(text);
|
|
return;
|
|
}
|
|
|
|
if (orig_id == 0) {
|
|
/* search message head */
|
|
while (atoi(in_reply_to) > 0) {
|
|
source_id = atoi(in_reply_to);
|
|
size = TEXT_SIZE;
|
|
status = el_retrieve(lbs, source_id, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text, &size,
|
|
in_reply_to, reply_to, (char (*)[256]) attachment, encoding, locked_by, draft);
|
|
|
|
if (status != EL_SUCCESS) {
|
|
sprintf(msg_str, "%d", source_id);
|
|
sprintf(str, loc("Entry %s cannot be read from logbook \"%s\""), msg_str, lbs->name);
|
|
show_error(str);
|
|
xfree(attachment);
|
|
xfree(attrib);
|
|
xfree(list);
|
|
xfree(text);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* read attachments */
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment[i * MAX_PATH_LENGTH]) {
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(attachment + i * MAX_PATH_LENGTH, subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, attachment + i * MAX_PATH_LENGTH, sizeof(file_name));
|
|
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
lseek(fh, 0, SEEK_END);
|
|
size = TELL(fh);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buffer = (char *)xmalloc(size);
|
|
read(fh, buffer, size);
|
|
close(fh);
|
|
|
|
/* keep original file name for inline references */
|
|
strlcpy(file_name, attachment + i * MAX_PATH_LENGTH, MAX_PATH_LENGTH);
|
|
|
|
el_submit_attachment(lbs_dest, file_name, buffer, size, NULL);
|
|
|
|
if (buffer)
|
|
xfree(buffer);
|
|
} else
|
|
/* attachment is invalid */
|
|
attachment[i * MAX_PATH_LENGTH] = 0;
|
|
|
|
/* check for thumbnail */
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(attachment + i * MAX_PATH_LENGTH, subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, attachment + i * MAX_PATH_LENGTH, sizeof(file_name));
|
|
thumb_status = get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0);
|
|
|
|
if (thumb_status == 1) {
|
|
fh = open(thumb_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
lseek(fh, 0, SEEK_END);
|
|
size = TELL(fh);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buffer = (char *)xmalloc(size);
|
|
read(fh, buffer, size);
|
|
close(fh);
|
|
|
|
/* keep original file name for inline references */
|
|
if (strrchr(thumb_name, '\\'))
|
|
strlcpy(str, strrchr(thumb_name, '\\') + 1, sizeof(str));
|
|
else
|
|
strlcpy(str, thumb_name, sizeof(str));
|
|
|
|
el_submit_attachment(lbs_dest, str, buffer, size, NULL);
|
|
|
|
if (buffer)
|
|
xfree(buffer);
|
|
}
|
|
}
|
|
if (thumb_status == 2) {
|
|
for (j = 0;; j++) {
|
|
get_thumb_name(file_name, thumb_name, sizeof(thumb_name), j);
|
|
if (thumb_name[0]) {
|
|
fh = open(thumb_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
lseek(fh, 0, SEEK_END);
|
|
size = TELL(fh);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buffer = (char *)xmalloc(size);
|
|
read(fh, buffer, size);
|
|
close(fh);
|
|
|
|
/* keep original file name for inline references */
|
|
if (strrchr(thumb_name, '\\'))
|
|
strlcpy(str, strrchr(thumb_name, '\\') + 1, sizeof(str));
|
|
else
|
|
strlcpy(str, thumb_name, sizeof(str));
|
|
|
|
el_submit_attachment(lbs_dest, str, buffer, size, NULL);
|
|
|
|
if (buffer)
|
|
xfree(buffer);
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* correct possible references to attachments */
|
|
if (strieq(encoding, "ELCode")) {
|
|
sprintf(str, "[IMG]elog:%d/", src_id);
|
|
while (stristr(text, str))
|
|
strsubst(text, TEXT_SIZE, str, "[IMG]elog:/");
|
|
} else if (strieq(encoding, "HTML")) {
|
|
sprintf(str, "?lb=%s\"", lbs->name_enc);
|
|
sprintf(str2, "?lb=%s\"", dest_logbook);
|
|
while (stristr(text, str))
|
|
strsubst(text, TEXT_SIZE, str, str2);
|
|
sprintf(str, "?lb=%s&", lbs->name_enc);
|
|
sprintf(str2, "?lb=%s&", dest_logbook);
|
|
while (stristr(text, str))
|
|
strsubst(text, TEXT_SIZE, str, str2);
|
|
}
|
|
|
|
/* keep original message ID if requested */
|
|
message_id = 0;
|
|
bedit = FALSE;
|
|
if (getcfg(lbs->name, "Preserve IDs", str, sizeof(str)) && atoi(str) == 1) {
|
|
message_id = source_id;
|
|
|
|
/* test if entry exists already */
|
|
status =
|
|
el_retrieve(lbs_dest, message_id, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL,
|
|
NULL);
|
|
bedit = (status == EL_SUCCESS);
|
|
}
|
|
|
|
/* submit in destination logbook without links, submit all attributes from
|
|
the source logbook even if the destination has a differnt number of attributes */
|
|
|
|
if (getcfg(lbs->name, "Preserve IDs", str, sizeof(str)) && atoi(str) == 1)
|
|
message_id =
|
|
el_submit(lbs_dest, message_id, bedit, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text,
|
|
in_reply_to, reply_to, encoding, (char (*)[256]) attachment, FALSE, NULL, NULL);
|
|
else {
|
|
/* if called recursively (for threads), put in correct in_reply_to */
|
|
str[0] = 0;
|
|
if (orig_id)
|
|
sprintf(str, "%d", orig_id);
|
|
|
|
message_id =
|
|
el_submit(lbs_dest, message_id, bedit, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text,
|
|
str, "", encoding, (char (*)[256]) attachment, TRUE, NULL, NULL);
|
|
}
|
|
|
|
if (message_id <= 0) {
|
|
sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs_dest->data_dir);
|
|
strcat(str, "\n<p>");
|
|
strcat(str, loc("Please check that it exists and elogd has write access"));
|
|
show_error(str);
|
|
xfree(attachment);
|
|
xfree(attrib);
|
|
xfree(list);
|
|
xfree(text);
|
|
return;
|
|
}
|
|
|
|
/* submit all replies */
|
|
n_reply = strbreak(reply_to, (char (*)[1500]) list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n_reply; i++) {
|
|
copy_to(lbs, atoi(list + i * NAME_LENGTH), dest_logbook, move, message_id);
|
|
}
|
|
|
|
/* delete original message for move */
|
|
next_id = source_id;
|
|
if (move && orig_id == 0) {
|
|
|
|
/* find next message head */
|
|
next_id = el_search_message(lbs, EL_NEXT, source_id, TRUE);
|
|
if (next_id <= 0)
|
|
next_id = el_search_message(lbs, EL_LAST, 0, FALSE);
|
|
|
|
el_delete_message(lbs, source_id, TRUE, NULL, TRUE, TRUE);
|
|
}
|
|
}
|
|
|
|
xfree(attachment);
|
|
xfree(attrib);
|
|
xfree(list);
|
|
xfree(text);
|
|
|
|
if (orig_id)
|
|
return;
|
|
|
|
/* redirect to next entry of source logbook */
|
|
if (next_id)
|
|
sprintf(str, "%d", next_id);
|
|
else
|
|
str[0] = 0;
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int is_inline_attachment(const char *encoding, int message_id, const char *text, int i, char *att) {
|
|
char str[1024], att_enc[256], domain[256], *pt, *p;
|
|
|
|
if (text == NULL)
|
|
return 0;
|
|
if (strieq(encoding, "ELCode")) {
|
|
sprintf(str, "[img]elog:/%d[/img]", i + 1);
|
|
if (stristr(text, str))
|
|
return 1;
|
|
sprintf(str, "[img]elog:%d/%d[/img]", message_id, i + 1);
|
|
if (stristr(text, str))
|
|
return 1;
|
|
} else if (strieq(encoding, "HTML")) {
|
|
strlcpy(att_enc, att, sizeof(att_enc));
|
|
att_enc[13] = '/';
|
|
pt = (char *)text;
|
|
while (*pt && stristr(pt, att_enc)) {
|
|
/* make sure that it's really an inline image */
|
|
for (p = stristr(pt, att_enc); p > pt; p--)
|
|
if (*p == '<')
|
|
break;
|
|
if (p > pt) {
|
|
strncpy(str, p, 5);
|
|
if (stristr(str, "<img "))
|
|
return 1;
|
|
}
|
|
pt = stristr(pt, att_enc) + 1;
|
|
}
|
|
retrieve_domain(domain, sizeof(domain));
|
|
sprintf(str, "cid:att%d@%s", i, domain);
|
|
if (*text && stristr(text, str))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int create_thumbnail(LOGBOOK *lbs, char *file_name) {
|
|
char str[1024], cmd[3000], fcmd[3500], ts[256], thumb_size[1024], thumb_options[256];
|
|
int i;
|
|
|
|
if (!image_magick_exist)
|
|
return 0;
|
|
|
|
if (getcfg(lbs->name, "Thumbnail size", ts, sizeof(ts))) {
|
|
if (strcmp(ts, "0") == 0)
|
|
return 0;
|
|
sprintf(thumb_size, " -thumbnail '%s'", ts);
|
|
} else
|
|
thumb_size[0] = 0;
|
|
|
|
getcfg(lbs->name, "Thumbnail options", thumb_options, sizeof(thumb_options));
|
|
|
|
if (!chkext(file_name, ".ps") && !chkext(file_name, ".pdf") && !chkext(file_name, ".eps")
|
|
&& !chkext(file_name, ".gif") && !chkext(file_name, ".jpg") && !chkext(file_name, ".jpeg")
|
|
&& !chkext(file_name, ".png") && !chkext(file_name, ".ico") && !chkext(file_name, ".tif")
|
|
&& !chkext(file_name, ".svg"))
|
|
return 0;
|
|
|
|
i = get_thumb_name(file_name, str, sizeof(str), 0);
|
|
if (i)
|
|
return i;
|
|
|
|
strlcpy(str, file_name, sizeof(str));
|
|
if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) {
|
|
if (strrchr(str, '.'))
|
|
*strrchr(str, '.') = 0;
|
|
}
|
|
if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
|
|
strlcat(str, "-%d.png", sizeof(str));
|
|
else
|
|
strlcat(str, ".png", sizeof(str));
|
|
|
|
if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
|
|
snprintf(cmd, sizeof(cmd), "%s %s '%s[0-7]'%s '%s'", _convert_cmd, thumb_options, file_name, thumb_size,
|
|
str);
|
|
else
|
|
snprintf(cmd, sizeof(cmd), "%s %s '%s'%s '%s'", _convert_cmd, thumb_options, file_name, thumb_size, str);
|
|
|
|
#ifdef OS_WINNT
|
|
for (i = 0; i < (int) strlen(cmd); i++)
|
|
if (cmd[i] == '\'')
|
|
cmd[i] = '\"';
|
|
#endif
|
|
|
|
strlcpy(fcmd, "SHELL \"", sizeof(fcmd));
|
|
strlcat(fcmd, cmd, sizeof(fcmd));
|
|
strlcat(fcmd, "\"", sizeof(fcmd));
|
|
write_logfile(lbs, fcmd);
|
|
if (get_verbose() >= VERBOSE_INFO) {
|
|
eprintf(fcmd);
|
|
eprintf("\n");
|
|
}
|
|
|
|
my_shell(cmd, str, sizeof(str));
|
|
|
|
i = get_thumb_name(file_name, str, sizeof(str), 0);
|
|
if (i)
|
|
return i;
|
|
|
|
return 3;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int get_thumb_name(const char *file_name, char *thumb_name, int size, int index) {
|
|
char str[MAX_PATH_LENGTH];
|
|
|
|
thumb_name[0] = 0;
|
|
|
|
/* append .png for all files as thumbnail name, except for PDF files (convert bug!) */
|
|
memset(str, 0, sizeof(str));
|
|
if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) {
|
|
strlcpy(str, file_name, sizeof(str));
|
|
if (strrchr(str, '.'))
|
|
*strrchr(str, '.') = 0;
|
|
snprintf(str + strlen(str), sizeof(str) - strlen(str) - 1, "-%d.png", index);
|
|
if (file_exist(str)) {
|
|
strlcpy(thumb_name, str, size);
|
|
return 2;
|
|
}
|
|
|
|
if (index > 0)
|
|
return 0;
|
|
|
|
strlcpy(str, file_name, sizeof(str));
|
|
if (strrchr(str, '.'))
|
|
*strrchr(str, '.') = 0;
|
|
strlcat(str, ".png", sizeof(str));
|
|
if (file_exist(str)) {
|
|
strlcpy(thumb_name, str, size);
|
|
return 1;
|
|
}
|
|
} else {
|
|
strlcpy(str, file_name, sizeof(str));
|
|
sprintf(str + strlen(str), "-%d.png", index);
|
|
if (file_exist(str)) {
|
|
strlcpy(thumb_name, str, size);
|
|
return 2;
|
|
}
|
|
|
|
if (index > 0)
|
|
return 0;
|
|
|
|
strlcpy(str, file_name, sizeof(str));
|
|
strlcat(str, ".png", sizeof(str));
|
|
if (file_exist(str)) {
|
|
strlcpy(thumb_name, str, size);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void call_image_magick(LOGBOOK *lbs) {
|
|
char str[1024], cmd[1024], file_name[256], thumb_name[256], subdir[256];
|
|
int cur_width, cur_height, new_size, cur_rot, new_rot, thumb_status;
|
|
|
|
if (!isparam("req") || !isparam("img")) {
|
|
show_error("Unknown IM request received");
|
|
return;
|
|
}
|
|
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(getparam("img"), subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, getparam("img"), sizeof(file_name));
|
|
thumb_status = get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0);
|
|
|
|
sprintf(cmd, "%s -format '%%wx%%h %%c' '%s'", _identify_cmd, thumb_name);
|
|
#ifdef OS_WINNT
|
|
{
|
|
int i;
|
|
for (i = 0; i < (int) strlen(cmd); i++)
|
|
if (cmd[i] == '\'')
|
|
cmd[i] = '\"';
|
|
}
|
|
#endif
|
|
my_shell(cmd, str, sizeof(str));
|
|
|
|
if (atoi(str) > 0) {
|
|
cur_width = atoi(str);
|
|
if (strchr(str, 'x')) {
|
|
cur_height = atoi(strchr(str, 'x') + 1);
|
|
} else
|
|
cur_height = cur_width;
|
|
if (strchr(str, ' ')) {
|
|
cur_rot = atoi(strchr(str, ' ') + 1);
|
|
} else
|
|
cur_rot = 0;
|
|
} else {
|
|
show_http_header(NULL, FALSE, NULL, 200);
|
|
rsputs(str);
|
|
return;
|
|
}
|
|
|
|
if (thumb_status == 2)
|
|
strsubst(thumb_name, sizeof(thumb_name), "-0", "-%d");
|
|
|
|
cmd[0] = 0;
|
|
if (strieq(getparam("req"), "rotleft")) {
|
|
new_rot = (cur_rot + 360 - 90) % 360;
|
|
sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name,
|
|
new_rot,
|
|
cur_height, new_rot, thumb_name);
|
|
}
|
|
|
|
if (strieq(getparam("req"), "rotright")) {
|
|
new_rot = (cur_rot + 90) % 360;
|
|
sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name,
|
|
new_rot,
|
|
cur_height, new_rot, thumb_name);
|
|
}
|
|
|
|
if (strieq(getparam("req"), "original")) {
|
|
new_size = (int) (cur_width / 1.5);
|
|
sprintf(cmd, "%s '%s' '%s'", _convert_cmd, file_name, thumb_name);
|
|
}
|
|
|
|
if (strieq(getparam("req"), "smaller")) {
|
|
new_size = (int) (cur_width / 1.5);
|
|
sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name,
|
|
cur_rot,
|
|
new_size, cur_rot, thumb_name);
|
|
}
|
|
|
|
if (strieq(getparam("req"), "larger")) {
|
|
new_size = (int) (cur_width * 1.5);
|
|
sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name,
|
|
cur_rot,
|
|
new_size, cur_rot, thumb_name);
|
|
}
|
|
|
|
if (cmd[0]) {
|
|
#ifdef OS_WINNT
|
|
int i;
|
|
for (i = 0; i < (int) strlen(cmd); i++)
|
|
if (cmd[i] == '\'')
|
|
cmd[i] = '\"';
|
|
#endif
|
|
my_shell(cmd, str, sizeof(str));
|
|
show_http_header(NULL, TRUE, NULL, 200);
|
|
rsputs(str);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_elog_entry(LOGBOOK *lbs, char *dec_path, char *command) {
|
|
int size, i, j, k, n, n_log, status, fh, length, message_error, index, n_hidden, message_id,
|
|
orig_message_id, format_flags[MAX_N_ATTR], att_hide[MAX_ATTACHMENTS], att_inline[MAX_ATTACHMENTS],
|
|
n_attachments, n_lines, n_disp_attr, attr_index[MAX_N_ATTR], thumb_status, max_n_lines;
|
|
char str[2 * NAME_LENGTH], str2[NAME_LENGTH], ref[4096], file_enc[256], attrib[MAX_N_ATTR][NAME_LENGTH];
|
|
char date[80], text[TEXT_SIZE], menu_str[1000], cmd[256], script[256], orig_tag[80],
|
|
reply_tag[MAX_REPLY_TO * 10], display[NAME_LENGTH], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH],
|
|
encoding[80], locked_by[256], att[256], lattr[2000], mid[80], menu_item[MAX_N_LIST][NAME_LENGTH],
|
|
format[80], slist[MAX_N_ATTR + 10][NAME_LENGTH], file_name[MAX_PATH_LENGTH],
|
|
gattr[MAX_N_ATTR][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], *p,
|
|
lbk_list[MAX_N_LIST][NAME_LENGTH], comment[256], class_name[80], class_value[80],
|
|
fl[8][NAME_LENGTH], list[MAX_N_ATTR][NAME_LENGTH], domain[256], subdir[256], draft[256], attr[NAME_LENGTH];
|
|
FILE *f;
|
|
BOOL first, show_text, display_inline, subtable, email, att_links;
|
|
struct tm *pts;
|
|
struct tm ts;
|
|
struct stat st;
|
|
time_t ltime, entry_ltime;
|
|
|
|
message_id = atoi(dec_path);
|
|
message_error = EL_SUCCESS;
|
|
_current_message_id = message_id;
|
|
email = (strstr(command, "email") != NULL);
|
|
att_links = (strstr(command, "att-links") != NULL);
|
|
|
|
/* check for custom form to display entry */
|
|
if (getcfg(lbs->name, "Custom display form", str, sizeof(str))) {
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, logbook_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
send_file_direct(str);
|
|
return;
|
|
}
|
|
|
|
/* check for guest access */
|
|
if (!getcfg(lbs->name, "Guest Menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs))
|
|
getcfg(lbs->name, "Menu commands", menu_str, sizeof(menu_str));
|
|
|
|
/* default menu commands */
|
|
if (menu_str[0] == 0) {
|
|
strcpy(menu_str, "List, New, Edit, Delete, Reply, Duplicate, Find, ");
|
|
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str))) {
|
|
strcat(menu_str, "Config, Logout, ");
|
|
} else {
|
|
strcat(menu_str, "Config, ");
|
|
}
|
|
|
|
strcat(menu_str, "Help");
|
|
} else {
|
|
/* check for admin command */
|
|
n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE);
|
|
menu_str[0] = 0;
|
|
for (i = 0; i < n; i++) {
|
|
if (strcmp(menu_item[i], "Admin") == 0) {
|
|
if (!is_admin_user(lbs, getparam("unm")))
|
|
continue;
|
|
}
|
|
strcat(menu_str, menu_item[i]);
|
|
if (i < n - 1)
|
|
strcat(menu_str, ", ");
|
|
}
|
|
}
|
|
|
|
/*---- check next/previous message -------------------------------*/
|
|
|
|
if (strieq(command, loc("Next")) || strieq(command, loc("Previous")) || strieq(command, loc("Last"))
|
|
|| strieq(command, loc("First"))) {
|
|
orig_message_id = message_id;
|
|
|
|
if (strieq(command, loc("Last")))
|
|
message_id = el_search_message(lbs, EL_LAST, 0, FALSE);
|
|
|
|
if (strieq(command, loc("First")))
|
|
message_id = el_search_message(lbs, EL_FIRST, 0, FALSE);
|
|
|
|
/* avoid display of "invalid id '0'", if "start page = 0?cmd=Last" */
|
|
if (!message_id)
|
|
dec_path[0] = 0;
|
|
|
|
first = TRUE;
|
|
do {
|
|
if (strieq(command, loc("Next")))
|
|
message_id = el_search_message(lbs, EL_NEXT, message_id, FALSE);
|
|
|
|
if (strieq(command, loc("Previous")))
|
|
message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);
|
|
|
|
if (!first) {
|
|
if (strieq(command, loc("First")))
|
|
message_id = el_search_message(lbs, EL_NEXT, message_id, FALSE);
|
|
|
|
if (strieq(command, loc("Last")))
|
|
message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);
|
|
} else
|
|
first = FALSE;
|
|
|
|
if (message_id == 0) {
|
|
if (strieq(command, loc("Next")))
|
|
message_error = EL_LAST_MSG;
|
|
else
|
|
message_error = EL_FIRST_MSG;
|
|
|
|
message_id = orig_message_id;
|
|
break;
|
|
}
|
|
|
|
size = sizeof(text);
|
|
el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, orig_tag, reply_tag,
|
|
attachment, encoding, locked_by, draft);
|
|
|
|
/* check for locked attributes */
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(lattr, "l%s", attr);
|
|
if (isparam(lattr) == '1'
|
|
&& !(isparam(attr_list[i]) && strieq(getparam(attr_list[i]), attrib[i])))
|
|
break;
|
|
}
|
|
if (i < lbs->n_attr)
|
|
continue;
|
|
|
|
/* check for attribute filter if not browsing */
|
|
if (!isparam("browsing")) {
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
if (isparam(attr_list[i]) && !(isparam(attr_list[i]) && strieq(getparam(attr_list[i]),
|
|
attrib[i])))
|
|
break;
|
|
}
|
|
if (i < lbs->n_attr)
|
|
continue;
|
|
}
|
|
|
|
sprintf(str, "%d", message_id);
|
|
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(lattr, "l%s", attr);
|
|
if (isparam(lattr) == '1') {
|
|
if (strchr(str, '?') == NULL)
|
|
sprintf(str + strlen(str), "?%s=1", lattr);
|
|
else
|
|
sprintf(str + strlen(str), "&%s=1", lattr);
|
|
}
|
|
}
|
|
|
|
redirect(lbs, str);
|
|
return;
|
|
|
|
} while (TRUE);
|
|
}
|
|
|
|
/*---- check for valid URL ---------------------------------------*/
|
|
|
|
if (dec_path[0] && atoi(dec_path) == 0) {
|
|
strencode2(str2, dec_path, sizeof(str2));
|
|
sprintf(str, "%s: <b>%s</b>", loc("Invalid URL"), str2);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
/*---- get current message ---------------------------------------*/
|
|
|
|
if (message_id == 0)
|
|
message_id = el_search_message(lbs, EL_LAST, 0, FALSE);
|
|
|
|
status = 0;
|
|
reply_tag[0] = orig_tag[0] = 0;
|
|
if (message_id) {
|
|
size = sizeof(text);
|
|
status = el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, orig_tag,
|
|
reply_tag, attachment, encoding, locked_by, draft);
|
|
|
|
if (status != EL_SUCCESS)
|
|
message_error = status;
|
|
else {
|
|
if (_logging_level > 2) {
|
|
sprintf(str, "READ entry #%d", message_id);
|
|
write_logfile(lbs, str);
|
|
}
|
|
}
|
|
} else
|
|
message_error = EL_EMPTY;
|
|
|
|
/*---- check for conditional attribute ----*/
|
|
|
|
evaluate_conditions(lbs, attrib);
|
|
|
|
/*---- header ----*/
|
|
|
|
/* header */
|
|
if (status == EL_SUCCESS && message_error != EL_EMPTY) {
|
|
str[0] = 0;
|
|
|
|
if (getcfg(lbs->name, "Page Title", str, sizeof(str))) {
|
|
i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
|
|
sprintf(mid, "%d", message_id);
|
|
add_subst_list(slist, svalue, "message id", mid, &i);
|
|
add_subst_time(lbs, slist, svalue, "entry time", date, &i, 0);
|
|
strsubst_list(str, sizeof(str), slist, svalue, i);
|
|
strip_html(str);
|
|
} else
|
|
strcpy(str, "ELOG");
|
|
|
|
if (email) {
|
|
/* embed CSS */
|
|
show_html_header(lbs, FALSE, str, TRUE, FALSE, NULL, TRUE, 0, 200);
|
|
rsprintf("<body>\n");
|
|
} else {
|
|
sprintf(ref, "%d", message_id);
|
|
strlcpy(script, "OnLoad=\"document.onkeypress=browse;\"", sizeof(script));
|
|
if (str[0])
|
|
show_standard_header(lbs, TRUE, str, ref, FALSE, NULL, script, 0);
|
|
else
|
|
show_standard_header(lbs, TRUE, lbs->name, ref, FALSE, NULL, script, 0);
|
|
}
|
|
} else
|
|
show_standard_header(lbs, TRUE, "", "", FALSE, NULL, NULL, 0);
|
|
|
|
/*---- title ----*/
|
|
|
|
if (email)
|
|
rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\">\n");
|
|
else
|
|
show_standard_title(lbs, "", 0);
|
|
|
|
/*---- menu buttons ----*/
|
|
|
|
if (!email) {
|
|
|
|
rsprintf("<tr><td class=\"menuframe\">\n");
|
|
rsprintf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
|
|
rsprintf("<tr>\n");
|
|
|
|
/*---- next/previous buttons ----*/
|
|
|
|
if (!getcfg(lbs->name, "Enable browsing", str, sizeof(str)) || atoi(str) == 1) {
|
|
rsprintf("<td class=\"menu1a\">\n");
|
|
|
|
/* check if first.png exists, just put link there if not */
|
|
strlcpy(file_name, resource_dir, sizeof(file_name));
|
|
if (file_name[0] && file_name[strlen(file_name) - 1] != DIR_SEPARATOR)
|
|
strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
|
|
strlcat(file_name, "themes", sizeof(file_name));
|
|
strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
|
|
if (theme_name[0]) {
|
|
strlcat(file_name, theme_name, sizeof(file_name));
|
|
strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
|
|
}
|
|
strlcat(file_name, "first.png", sizeof(file_name));
|
|
if (stat(file_name, &st) >= 0) {
|
|
rsprintf("<input type=image name=cmd_first alt=\"%s\" title=\"%s\" src=\"first.png\">\n",
|
|
loc("First entry, Ctrl-Home"), loc("First entry, Ctrl-Home"));
|
|
rsprintf("<input type=image name=cmd_previous alt=\"%s\" title=\"%s\" src=\"previous.png\">\n",
|
|
loc("Previous entry, Ctrl-PgUp"), loc("Previous entry, Ctrl-PgUp"));
|
|
rsprintf("<input type=image name=cmd_next alt=\"%s\" title=\"%s\" src=\"next.png\">\n",
|
|
loc("Next entry, Ctrl-PgDn"), loc("Next entry, Ctrl-PgDn"));
|
|
rsprintf("<input type=image name=cmd_last alt=\"%s\" title=\"%s\" src=\"last.png\">\n",
|
|
loc("Last entry, Ctrl-End"), loc("Last entry, Ctrl-End"));
|
|
} else {
|
|
rsprintf("<a href=\"%d?cmd=%s\">|<</a> \n", message_id, loc("First"));
|
|
rsprintf("<a href=\"%d?cmd=%s\"><</a> \n", message_id, loc("Previous"));
|
|
rsprintf("<a href=\"%d?cmd=%s\">></a> \n", message_id, loc("Next"));
|
|
rsprintf("<a href=\"%d?cmd=%s\">>|</a> \n", message_id, loc("Last"));
|
|
}
|
|
|
|
rsprintf("</td>\n");
|
|
}
|
|
|
|
n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE);
|
|
|
|
rsprintf("<td class=\"menu1\">\n");
|
|
|
|
for (i = 0; i < n; i++) {
|
|
/* display menu item */
|
|
strcpy(cmd, menu_item[i]);
|
|
|
|
/* only display allowed commands */
|
|
if (!is_user_allowed(lbs, cmd))
|
|
continue;
|
|
|
|
if (strieq(cmd, "Copy to") || strieq(cmd, "Move to")) {
|
|
rsprintf(" <input type=submit name=cmd value=\"%s\">\n", loc(cmd));
|
|
if (strieq(cmd, "Copy to"))
|
|
rsprintf("<select name=destc>\n");
|
|
else
|
|
rsprintf("<select name=destm>\n");
|
|
|
|
if (getcfg(lbs->name, cmd, str, sizeof(str))) {
|
|
n_log = strbreak(str, lbk_list, MAX_N_LIST, ",", FALSE);
|
|
|
|
for (j = 0; j < n_log; j++)
|
|
rsprintf("<option value=\"%s\">%s\n", lbk_list[j], lbk_list[j]);
|
|
} else {
|
|
for (j = 0;; j++) {
|
|
if (!enumgrp(j, str))
|
|
break;
|
|
|
|
if (!is_logbook(str))
|
|
continue;
|
|
|
|
if (strieq(str, lbs->name))
|
|
continue;
|
|
|
|
rsprintf("<option value=\"%s\">%s\n", str, str);
|
|
}
|
|
}
|
|
rsprintf("</select>\n");
|
|
|
|
if (i < n - 1)
|
|
rsprintf(" |\n");
|
|
} else {
|
|
strlcpy(str, loc(menu_item[i]), sizeof(str));
|
|
url_encode(str, sizeof(str));
|
|
|
|
if (strieq(menu_item[i], "list")) {
|
|
if (getcfg(lbs->name, "Back to main", str, sizeof(str)) && atoi(str) == 1)
|
|
rsprintf(" <a href=\"../\">%s</a> \n", loc(menu_item[i]));
|
|
else
|
|
rsprintf(" <a href=\".?id=%d\">%s</a> \n", message_id, loc(menu_item[i]));
|
|
} else
|
|
rsprintf(" <a href=\"%d?cmd=%s\">%s</a> \n", message_id, str, loc(menu_item[i]));
|
|
|
|
if (i < n - 1)
|
|
rsprintf("|\n");
|
|
else
|
|
rsprintf("\n");
|
|
}
|
|
}
|
|
|
|
rsprintf("</td>\n\n");
|
|
|
|
rsprintf("</table></td></tr>\n\n");
|
|
|
|
/*---- menu text ----*/
|
|
|
|
if (getcfg(lbs->name, "menu text", str, sizeof(str))) {
|
|
FILE *file;
|
|
char filename[256], *buf;
|
|
|
|
rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
|
|
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(filename, str);
|
|
else {
|
|
strlcpy(filename, logbook_dir, sizeof(filename));
|
|
strlcat(filename, str, sizeof(filename));
|
|
}
|
|
|
|
file = fopen(filename, "rb");
|
|
if (file != NULL) {
|
|
fseek(file, 0, SEEK_END);
|
|
size = TELL(fileno(file));
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
buf = (char *)xmalloc(size + 1);
|
|
fread(buf, 1, size, file);
|
|
buf[size] = 0;
|
|
fclose(file);
|
|
|
|
rsputs(buf);
|
|
|
|
} else
|
|
rsprintf("<center><b>Error: file <i>\"%s\"</i> not found</b></center>", filename);
|
|
|
|
rsprintf("</span></td></tr>");
|
|
}
|
|
} // if (!email)
|
|
|
|
/*---- message ----*/
|
|
|
|
if (reply_tag[0] || orig_tag[0])
|
|
show_elog_thread(lbs, message_id, email, 0);
|
|
|
|
if (message_error == EL_EMPTY)
|
|
rsprintf("<tr><td class=\"errormsg\" colspan=2>%s</td></tr>\n", loc("Logbook is empty"));
|
|
else if (message_error == EL_NO_MSG)
|
|
rsprintf("<tr><td class=\"errormsg\" colspan=2>%s</td></tr>\n", loc("This entry has been deleted"));
|
|
else {
|
|
/* overall message table */
|
|
rsprintf("<tr><td><table class=\"listframe\" width=\"100%%\" cellspacing=\"0\" cellpadding=\"0\">\n");
|
|
|
|
/* check for locked attributes */
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(lattr, "l%s", attr);
|
|
if (isparam(lattr) == '1')
|
|
break;
|
|
}
|
|
if (i < lbs->n_attr) {
|
|
if (isparam(attr))
|
|
sprintf(str, " %s <i>\"%s = %s\"</i>", loc("with"), attr, getparam(attr));
|
|
} else
|
|
str[0] = 0;
|
|
|
|
if (message_error == EL_LAST_MSG)
|
|
rsprintf("<tr><td class=\"notifymsg\" colspan=2>%s %s</td></tr>\n", loc("This is the last entry"),
|
|
str);
|
|
|
|
if (message_error == EL_FIRST_MSG)
|
|
rsprintf("<tr><td class=\"notifymsg\" colspan=2>%s %s</td></tr>\n", loc("This is the first entry"),
|
|
str);
|
|
|
|
/* check for mail submissions */
|
|
if (isparam("suppress")) {
|
|
rsprintf("<tr><td class=\"notifymsg\" colspan=2>%s</td></tr>\n",
|
|
loc("Email notification suppressed"));
|
|
} else if (isparam("error")) {
|
|
strencode2(str, getparam("error"), sizeof(str));
|
|
rsprintf("<tr><td class=\"errormsg\" colspan=2>%s</td></tr>\n", str);
|
|
} else {
|
|
for (i = 0;; i++) {
|
|
sprintf(str, "mail%d", i);
|
|
if (isparam(str)) {
|
|
if (i == 0)
|
|
rsprintf("<tr><td class=\"notifymsg\" colspan=2>");
|
|
strencode2(str, getparam(str), sizeof(str));
|
|
rsprintf("%s <b>%s</b><br>\n", loc("Email sent to"), str);
|
|
} else
|
|
break;
|
|
}
|
|
if (i > 0)
|
|
rsprintf("</tr>\n");
|
|
}
|
|
|
|
/*---- display message ID ----*/
|
|
|
|
_current_message_id = message_id;
|
|
|
|
if (email) {
|
|
rsprintf("<tr><td class=\"title1\">\n");
|
|
if (strstr(command, "oldemail") != NULL)
|
|
rsprintf("%s:", loc("An old ELOG entry has been updated"));
|
|
else
|
|
rsprintf("%s:", loc("A new ELOG entry has been submitted"));
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
if (locked_by[0]) {
|
|
sprintf(str, "%s %s", loc("Entry is currently edited by"), locked_by);
|
|
rsprintf
|
|
("<tr><td nowrap colspan=2 class=\"errormsg\"><img src=\"stop.png\" alt=\"%s\" title=\"%s\">\n",
|
|
loc("stop"), loc("stop"));
|
|
rsprintf("%s.<br>%s.<br>%s.</td></tr>\n", str,
|
|
loc("You can \"steal\" the lock by editing this entry"),
|
|
loc("You might however then overwrite each other's modifications"));
|
|
} else {
|
|
if (draft[0]) {
|
|
rsprintf("<tr><td nowrap colspan=2 class=\"errormsg\">%s\n",
|
|
loc("This is a draft message, edit and submit it to make it permanent"));
|
|
rsprintf(" <input type=button value=\"%s\" onClick=\"window.location.href='%d?cmd=%s';\">",
|
|
loc("Edit"), message_id, loc("Edit"));
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
}
|
|
|
|
rsprintf("<tr><td class=\"attribhead\">\n");
|
|
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
strencode2(str, attrib[i], sizeof(str));
|
|
rsprintf("<input type=hidden name=\"%s\" value=\"%s\">\n", attr_list[i], str);
|
|
}
|
|
|
|
/* browsing flag to distinguish "/../<attr>=<value>" from browsing */
|
|
rsprintf("<input type=hidden name=browsing value=1>\n");
|
|
|
|
if (getcfg(lbs->name, "ID display", display, sizeof(display))) {
|
|
j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib,
|
|
TRUE);
|
|
sprintf(str, "%d", message_id);
|
|
add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &j);
|
|
add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time",
|
|
date, &j, 0);
|
|
|
|
strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist,
|
|
(char (*)[NAME_LENGTH]) svalue, j);
|
|
|
|
} else
|
|
sprintf(display, "%d", message_id);
|
|
|
|
if (email) {
|
|
compose_base_url(lbs, str, sizeof(str), TRUE);
|
|
sprintf(str + strlen(str), "%d", message_id);
|
|
rsprintf("%s: <b>%s</b> ", loc("Logbook"), lbs->name);
|
|
rsprintf("%s: <a href=\"%s\"><b>%d</b></a>", loc("Message ID"), str, message_id);
|
|
} else
|
|
rsprintf("%s: <b>%s</b>\n", loc("Message ID"), display);
|
|
|
|
/*---- display date ----*/
|
|
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
entry_ltime = date_to_ltime(date);
|
|
pts = localtime(&entry_ltime);
|
|
assert(pts);
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
rsprintf(" %s: <b>%s</b>\n", loc("Entry time"), str);
|
|
|
|
/*---- link to original message or reply ----*/
|
|
|
|
if (message_error != EL_FILE_ERROR && (reply_tag[0] || orig_tag[0])) {
|
|
|
|
if (orig_tag[0]) {
|
|
if (email)
|
|
compose_base_url(lbs, ref, sizeof(ref), TRUE);
|
|
else
|
|
ref[0] = 0;
|
|
sprintf(ref + strlen(ref), "%s", orig_tag);
|
|
rsprintf(" %s: ", loc("In reply to"));
|
|
rsprintf("<b><a href=\"%s\">%s</a></b>\n", ref, orig_tag);
|
|
}
|
|
if (reply_tag[0]) {
|
|
rsprintf(" %s: <b>", loc("Reply to this"));
|
|
|
|
p = strtok(reply_tag, ",");
|
|
do {
|
|
if (email)
|
|
compose_base_url(lbs, ref, sizeof(ref), TRUE);
|
|
else
|
|
ref[0] = 0;
|
|
sprintf(ref + strlen(ref), "%s", p);
|
|
rsprintf("<a href=\"%s\">%s</a>\n", ref, p);
|
|
|
|
p = strtok(NULL, ",");
|
|
|
|
if (p)
|
|
rsprintf(" \n");
|
|
|
|
} while (p);
|
|
|
|
rsprintf("</b>\n");
|
|
}
|
|
}
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
/*---- display attributes ----*/
|
|
|
|
/* retrieve attribute flags */
|
|
for (i = 0; i < lbs->n_attr; i++) {
|
|
format_flags[i] = 0;
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Format %s", attr);
|
|
if (getcfg(lbs->name, str, format, sizeof(format))) {
|
|
n = strbreak(format, fl, 8, ",", FALSE);
|
|
if (n > 0)
|
|
format_flags[i] = atoi(fl[0]);
|
|
}
|
|
}
|
|
|
|
/* 2 column table for all attributes */
|
|
rsprintf("<tr><td><table width=\"100%%\" cellpadding=\"0\" cellspacing=\"0\">");
|
|
subtable = 0;
|
|
|
|
/* generate list of attributes to show */
|
|
if (getcfg(lbs->name, "Show attributes", str, sizeof(str))) {
|
|
n_disp_attr = strbreak(str, list, MAX_N_ATTR, ",", FALSE);
|
|
for (i = 0; i < n_disp_attr; i++) {
|
|
for (j = 0; j < lbs->n_attr; j++)
|
|
if (strieq(attr_list[j], list[i]))
|
|
break;
|
|
if (!strieq(attr_list[j], list[i]))
|
|
/* attribute not found */
|
|
j = 0;
|
|
attr_index[i] = j;
|
|
}
|
|
} else {
|
|
for (i = 0; i < lbs->n_attr; i++)
|
|
attr_index[i] = i;
|
|
n_disp_attr = lbs->n_attr;
|
|
}
|
|
|
|
for (j = 0; j < n_disp_attr; j++) {
|
|
|
|
i = attr_index[j];
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str)) && getcfg(lbs->name, "Guest display", str,
|
|
sizeof(str)) && !isparam("unm")) {
|
|
|
|
n = strbreak(str, gattr, MAX_N_ATTR, ",", FALSE);
|
|
for (k = 0; k < n; k++)
|
|
if (strieq(gattr[k], attr_list[i]))
|
|
break;
|
|
|
|
if (k == n)
|
|
continue;
|
|
}
|
|
|
|
strcpy(class_name, "attribname");
|
|
strcpy(class_value, "attribvalue");
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Format %s", attr);
|
|
if (getcfg(lbs->name, str, format, sizeof(format))) {
|
|
n = strbreak(format, fl, 8, ",", FALSE);
|
|
if (n > 1)
|
|
strlcpy(class_name, fl[1], sizeof(class_name));
|
|
if (n > 2)
|
|
strlcpy(class_value, fl[2], sizeof(class_value));
|
|
}
|
|
|
|
if (format_flags[i] & AFF_SAME_LINE)
|
|
/* if attribute on same line, do nothing */
|
|
rsprintf("");
|
|
else if (i < lbs->n_attr - 1 && (format_flags[i + 1] & AFF_SAME_LINE)) {
|
|
/* if next attribute on same line, start a new subtable */
|
|
rsprintf("<tr><td colspan=2><table width=\"100%%\" cellpadding=\"0\" cellspacing=\"0\"><tr>");
|
|
subtable = 1;
|
|
} else
|
|
/* for normal attribute, start new row */
|
|
rsprintf("<tr>");
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(lattr, "l%s", attr);
|
|
|
|
/* display cell with optional tooltip */
|
|
sprintf(str, "Tooltip %s", attr);
|
|
if (getcfg(lbs->name, str, comment, sizeof(comment)))
|
|
rsprintf("<td nowrap title=\"%s\" class=\"%s\">", comment, class_name);
|
|
else
|
|
rsprintf("<td nowrap class=\"%s\">", class_name);
|
|
|
|
if (getcfg(lbs->name, "Filtered browsing", str, sizeof(str)) && atoi(str) == 1) {
|
|
if (isparam(lattr) == '1')
|
|
rsprintf("<input type=\"checkbox\" checked name=\"%s\" value=\"1\"> ", lattr);
|
|
else
|
|
rsprintf("<input alt=\"text\" title=\"text\"type=\"checkbox\" name=\"%s\" value=\"1\"> ",
|
|
lattr);
|
|
}
|
|
|
|
/* display checkbox for boolean attributes */
|
|
if (strieq(attr_options[i][0], "boolean")) {
|
|
if (atoi(attrib[i]) == 1)
|
|
rsprintf("%s:</td><td class=\"%s\"><input type=checkbox checked disabled></td>\n",
|
|
attr_list[i], class_value);
|
|
else
|
|
rsprintf("%s:</td><td class=\"%s\"><input type=checkbox disabled></td>\n", attr_list[i],
|
|
class_value);
|
|
}
|
|
/* display image for icon */
|
|
else if (attr_flags[i] & AF_ICON) {
|
|
rsprintf("%s:</td><td class=\"%s\">\n", attr_list[i], class_value);
|
|
if (attrib[i][0]) {
|
|
sprintf(str, "Icon comment %s", attrib[i]);
|
|
getcfg(lbs->name, str, comment, sizeof(comment));
|
|
|
|
if (comment[0])
|
|
rsprintf("<img src=\"icons/%s\" alt=\"%s\" title=\"%s\">", attrib[i], comment, comment);
|
|
else
|
|
rsprintf("<img src=\"icons/%s\" alt=\"%s\" title=\"%s\">", attrib[i], attrib[i], attrib[i]);
|
|
}
|
|
rsprintf(" </td>\n");
|
|
} else if ((attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) && (format_flags[i]
|
|
& AFF_MULTI_LINE)) {
|
|
rsprintf("%s:</td><td class=\"%s\">\n", attr_list[i], class_value);
|
|
|
|
/* separate options into individual lines */
|
|
strlcpy(str, attrib[i], sizeof(str));
|
|
p = strtok(str, "|");
|
|
while (p) {
|
|
while (*p == ' ')
|
|
p++;
|
|
|
|
rsputs2(lbs, email, p);
|
|
|
|
p = strtok(NULL, "|");
|
|
|
|
if (p)
|
|
rsprintf("<br>");
|
|
}
|
|
|
|
rsprintf("</td>\n");
|
|
} else if (attr_flags[i] & AF_DATE) {
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Date format %s", attr);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_DATE_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
rsprintf("%s:</td><td class=\"%s\">%s </td>\n", attr_list[i], class_value, str);
|
|
|
|
} else if (attr_flags[i] & AF_DATETIME) {
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Time format %s", attr);
|
|
if (!getcfg(lbs->name, str, format, sizeof(format)))
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
ltime = atoi(attrib[i]);
|
|
pts = localtime(<ime);
|
|
assert(pts);
|
|
if (ltime == 0)
|
|
strcpy(str, "-");
|
|
else
|
|
my_strftime(str, sizeof(str), format, pts);
|
|
|
|
rsprintf("%s:</td><td class=\"%s\">%s </td>\n", attr_list[i], class_value, str);
|
|
|
|
} else {
|
|
rsprintf("%s:</td><td class=\"%s\">\n", attr_list[i], class_value);
|
|
|
|
strlcpy(attr, attr_list[i], sizeof(attr));
|
|
sprintf(str, "Change %s", attr);
|
|
if (getcfg(lbs->name, str, display, sizeof(display))) {
|
|
k = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
attrib, TRUE);
|
|
sprintf(str, "%d", message_id);
|
|
add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id",
|
|
str, &k);
|
|
add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
|
|
"entry time", date, &k, attr_flags[i]);
|
|
|
|
strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist,
|
|
(char (*)[NAME_LENGTH]) svalue, k);
|
|
|
|
} else
|
|
strcpy(display, attrib[i]);
|
|
|
|
if (is_html(display) && !is_script(display) && html_allowed(lbs))
|
|
rsputs(display);
|
|
else
|
|
rsputs2(lbs, email, display);
|
|
|
|
rsprintf(" </td>\n");
|
|
}
|
|
|
|
if (i < lbs->n_attr - 1 && (format_flags[i + 1] & AFF_SAME_LINE) == 0) {
|
|
/* if next attribute not on same line, close row or subtable */
|
|
if (subtable) {
|
|
rsprintf("</table></td></tr>\n");
|
|
subtable = 0;
|
|
} else
|
|
rsprintf("</tr>");
|
|
}
|
|
|
|
/* if last attribute, close row or subtable */
|
|
if (i == lbs->n_attr - 1) {
|
|
if (subtable) {
|
|
rsprintf("</table></td></tr>\n");
|
|
subtable = 0;
|
|
} else
|
|
rsprintf("</tr>");
|
|
}
|
|
}
|
|
|
|
rsputs("</table></td></tr>\n"); // 2 column table
|
|
|
|
rsputs("</table><!-- listframe -->\n");
|
|
|
|
rsputs("</td></tr>\n");
|
|
|
|
/*---- message text ----*/
|
|
|
|
show_text = !getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1;
|
|
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str)) && getcfg(lbs->name, "Guest display", str,
|
|
sizeof(str)) && !isparam("unm")) {
|
|
|
|
n = strbreak(str, gattr, MAX_N_ATTR, ",", FALSE);
|
|
for (j = 0; j < n; j++)
|
|
if (strieq(gattr[j], "text"))
|
|
break;
|
|
|
|
if (j == n)
|
|
show_text = FALSE;
|
|
}
|
|
|
|
if (show_text) {
|
|
rsprintf("<tr><td class=\"messageframe\">");
|
|
|
|
if (strieq(encoding, "html")) {
|
|
if (email)
|
|
replace_inline_img(lbs, text);
|
|
rsputs(text);
|
|
} else if (strieq(encoding, "ELCode")) {
|
|
rsputs_elcode(lbs, email, text);
|
|
} else {
|
|
rsputs("<pre class=\"messagepre\">");
|
|
rsputs2(lbs, email, text);
|
|
rsputs("</pre>");
|
|
}
|
|
|
|
rsputs("</td></tr>\n");
|
|
|
|
n_hidden = 0;
|
|
for (i = 0, n_attachments = 0; i < MAX_ATTACHMENTS; i++) {
|
|
att_inline[i] = 0;
|
|
att_hide[i] = getcfg(lbs->name, "Show attachments", str, sizeof(str)) && atoi(str) == 0;
|
|
|
|
if (is_inline_attachment(encoding, message_id, text, i, attachment[i]))
|
|
att_inline[i] = 1;
|
|
|
|
if (attachment[i][0])
|
|
n_attachments++;
|
|
}
|
|
|
|
if (isparam("hide")) {
|
|
strlcpy(str, getparam("hide"), sizeof(str));
|
|
p = strtok(str, ",");
|
|
while (p != NULL) {
|
|
if (atoi(p) < MAX_ATTACHMENTS) {
|
|
att_hide[atoi(p)] = 1;
|
|
n_hidden++;
|
|
}
|
|
p = strtok(NULL, ",");
|
|
}
|
|
}
|
|
if (isparam("show")) {
|
|
strlcpy(str, getparam("show"), sizeof(str));
|
|
p = strtok(str, ",");
|
|
while (p != NULL) {
|
|
if (atoi(p) < MAX_ATTACHMENTS) {
|
|
att_hide[atoi(p)] = 0;
|
|
}
|
|
p = strtok(NULL, ",");
|
|
}
|
|
}
|
|
|
|
for (index = 0; index < MAX_ATTACHMENTS; index++) {
|
|
if (attachment[index][0] && strlen(attachment[index]) > 14 && !att_inline[index]) {
|
|
for (i = 0; i < (int) strlen(attachment[index]); i++)
|
|
att[i] = toupper(attachment[index][i]);
|
|
att[i] = 0;
|
|
|
|
/* determine size of attachment */
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(attachment[index], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, attachment[index], sizeof(file_name));
|
|
thumb_status = create_thumbnail(lbs, file_name);
|
|
|
|
length = 0;
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
if (fh > 0) {
|
|
lseek(fh, 0, SEEK_END);
|
|
length = TELL(fh);
|
|
close(fh);
|
|
}
|
|
|
|
strlcpy(str, attachment[index], sizeof(str));
|
|
str[13] = 0;
|
|
strcpy(file_enc, attachment[index] + 14);
|
|
url_encode(file_enc, sizeof(file_enc)); /* for file names with special characters like "+" */
|
|
if (email && !att_links) {
|
|
retrieve_domain(domain, sizeof(domain));
|
|
sprintf(ref, "cid:att%d@%s", index, domain);
|
|
} else if (email) {
|
|
compose_base_url(lbs, ref, sizeof(ref), TRUE);
|
|
sprintf(ref + strlen(ref), "%s", str);
|
|
sprintf(ref + strlen(ref), "/%s", file_enc);
|
|
} else {
|
|
sprintf(ref, "%s/%s", str, file_enc);
|
|
}
|
|
|
|
/* overall table */
|
|
rsprintf("<tr><td><table class=\"listframe\" width=\"100%%\" cellspacing=0>\n");
|
|
|
|
rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s %d:</td>\n",
|
|
loc("Attachment"), index + 1);
|
|
|
|
if (email && !att_links)
|
|
rsprintf("<td class=\"attribvalue\">%s\n", attachment[index] + 14);
|
|
else
|
|
rsprintf("<td class=\"attribvalue\"><a href=\"%s\" target=\"_blank\">%s</a>\n", ref,
|
|
attachment[index] + 14);
|
|
|
|
rsprintf(" <span class=\"bytes\">");
|
|
|
|
if (length < 1024)
|
|
rsprintf("%d Bytes", length);
|
|
else if (length < 1024 * 1024)
|
|
rsprintf("%d kB", length / 1024);
|
|
else
|
|
rsprintf("%1.3lf MB", length / 1024.0 / 1024.0);
|
|
|
|
rsprintf("</span>\n");
|
|
|
|
/* retrieve submission date */
|
|
memset(&ts, 0, sizeof(ts));
|
|
ts.tm_mon = (attachment[index][2] - '0') * 10 + attachment[index][3] - '0' - 1;
|
|
ts.tm_mday = (attachment[index][4] - '0') * 10 + attachment[index][5] - '0';
|
|
ts.tm_year = (attachment[index][0] - '0') * 10 + attachment[index][1] - '0';
|
|
|
|
ts.tm_hour = (attachment[index][7] - '0') * 10 + attachment[index][8] - '0';
|
|
ts.tm_min = (attachment[index][9] - '0') * 10 + attachment[index][10] - '0';
|
|
ts.tm_sec = (attachment[index][11] - '0') * 10 + attachment[index][12] - '0';
|
|
|
|
if (ts.tm_year < 90)
|
|
ts.tm_year += 100;
|
|
ltime = mktime(&ts);
|
|
|
|
/* show upload date/time only if different from entry date/time */
|
|
if (abs((int) (ltime - entry_ltime)) > 3600) {
|
|
if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
|
|
my_strftime(str, sizeof(str), format, &ts);
|
|
|
|
rsprintf(" <span class=\"uploaded\">");
|
|
rsprintf("Uploaded %s", str);
|
|
rsprintf("</span>\n");
|
|
}
|
|
|
|
/* determine if displayed inline */
|
|
display_inline = is_image(file_name) || is_ascii(file_name);
|
|
if (chkext(att, ".PS") || chkext(att, ".PDF"))
|
|
display_inline = 0;
|
|
if ((chkext(att, ".HTM") || chkext(att, ".HTML")) && is_full_html(file_name))
|
|
display_inline = 0;
|
|
if (thumb_status)
|
|
display_inline = 1;
|
|
|
|
if (display_inline) {
|
|
/* hide this / show this */
|
|
if (!email) {
|
|
rsprintf("<span class=\"bytes\">");
|
|
|
|
rsprintf(" | ");
|
|
if (att_hide[index]) {
|
|
rsprintf("<a href=\"%d?hide=", message_id);
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (att_hide[i] && i != index) {
|
|
rsprintf("%d,", i);
|
|
}
|
|
rsprintf("&show=%d", index);
|
|
rsprintf("\">%s</a>", loc("Show"));
|
|
} else {
|
|
rsprintf("<a href=\"%d?hide=", message_id);
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (att_hide[i] || i == index) {
|
|
rsprintf("%d,", i);
|
|
}
|
|
rsprintf("\">%s</a>", loc("Hide"));
|
|
}
|
|
|
|
/* hide all */
|
|
if (n_hidden < n_attachments) {
|
|
rsprintf(" | <a href=\"%d?hide=", message_id);
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (attachment[i][0]) {
|
|
rsprintf("%d,", i);
|
|
}
|
|
rsprintf("\">%s</a>", loc("Hide all"));
|
|
}
|
|
|
|
/* show all */
|
|
if (n_hidden > 0) {
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (att_hide[i])
|
|
break;
|
|
if (i < MAX_ATTACHMENTS) {
|
|
rsprintf(" | <a href=\"%d?show=", message_id);
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++)
|
|
if (att_hide[i])
|
|
rsprintf("%d,", i);
|
|
rsprintf("\">%s</a>", loc("Show all"));
|
|
}
|
|
}
|
|
|
|
rsprintf("</span>\n");
|
|
}
|
|
}
|
|
|
|
rsprintf("</td></tr></table></td></tr>\n");
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(attachment[index], subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, attachment[index], sizeof(file_name));
|
|
|
|
if (!att_hide[index] && display_inline) {
|
|
|
|
if (thumb_status) {
|
|
rsprintf("<tr><td class=\"attachmentframe\">\n");
|
|
|
|
if (thumb_status == 3) {
|
|
rsprintf("<font color=red><b>%s</b></font>\n",
|
|
loc("Cannot create thumbnail, please check ImageMagick installation"));
|
|
} else {
|
|
if (thumb_status == 2 && !email) {
|
|
for (i = 0;; i++) {
|
|
strlcpy(str, file_name, sizeof(str));
|
|
if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
|
|
if (strrchr(str, '.'))
|
|
*strrchr(str, '.') = 0;
|
|
sprintf(str + strlen(str), "-%d.png", i);
|
|
if (file_exist(str)) {
|
|
strlcpy(str, ref, sizeof(str));
|
|
if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
|
|
if (strrchr(str, '.'))
|
|
*strrchr(str, '.') = 0;
|
|
sprintf(str + strlen(str), "-%d.png", i);
|
|
rsprintf("<a name=\"att%d\" href=\"%s\">\n", index + 1, ref);
|
|
rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\"></a>\n", str,
|
|
attachment[index] + 14, attachment[index] + 14);
|
|
} else
|
|
break;
|
|
}
|
|
} else {
|
|
if (!email) {
|
|
rsprintf("<a name=\"att%d\" href=\"%s\">\n", index + 1, ref);
|
|
strlcpy(str, ref, sizeof(str));
|
|
if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
|
|
if (strrchr(str, '.'))
|
|
*strrchr(str, '.') = 0;
|
|
strlcat(str, ".png", sizeof(str));
|
|
rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\"></a>\n", str,
|
|
attachment[index] + 14, attachment[index] + 14);
|
|
}
|
|
}
|
|
}
|
|
rsprintf("</td></tr>\n\n");
|
|
} else if (is_image(att)) {
|
|
if (!email) {
|
|
rsprintf("<tr><td class=\"attachmentframe\">\n");
|
|
rsprintf("<a name=\"att%d\"></a>\n", index + 1);
|
|
rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\">\n", ref, attachment[index] + 14,
|
|
attachment[index] + 14);
|
|
rsprintf("</td></tr>\n\n");
|
|
}
|
|
} else {
|
|
if (is_ascii(file_name)) {
|
|
/* display attachment */
|
|
rsprintf("<tr><td class=\"messageframe\">\n");
|
|
|
|
/* anchor for references */
|
|
rsprintf("<a name=\"att%d\"></a>\n", index + 1);
|
|
|
|
if (!chkext(att, ".HTML"))
|
|
rsprintf("<pre class=\"messagepre\">");
|
|
|
|
f = fopen(file_name, "rt");
|
|
n_lines = 0;
|
|
if (getcfg(lbs->name, "Attachment lines", str, sizeof(str)))
|
|
max_n_lines = atoi(str);
|
|
else
|
|
max_n_lines = 300;
|
|
|
|
if (f != NULL) {
|
|
while (!feof(f)) {
|
|
str[0] = 0;
|
|
fgets(str, sizeof(str), f);
|
|
|
|
if (n_lines < max_n_lines) {
|
|
if (!chkext(att, ".HTML"))
|
|
rsputs2(lbs, email, str);
|
|
else
|
|
rsputs(str);
|
|
}
|
|
n_lines++;
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
if (!chkext(att, ".HTML"))
|
|
rsprintf("</pre>");
|
|
rsprintf("\n");
|
|
if (max_n_lines == 0)
|
|
rsprintf("<i><b>%d lines</b></i>\n", n_lines);
|
|
else if (n_lines > max_n_lines)
|
|
rsprintf("<i><b>... %d more lines ...</b></i>\n", n_lines - max_n_lines);
|
|
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* overall table (class "frame" from show_standard_header) */
|
|
rsprintf("\r\n</table><!-- show_standard_title -->\r\n");
|
|
|
|
show_bottom_text(lbs);
|
|
if (!email)
|
|
rsprintf("</form>\n");
|
|
rsprintf("</body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL convert_password_file(char *file_name) {
|
|
char name[256], password[256], full_name[256], email[256], email_notify[256];
|
|
int i, len, fh, status;
|
|
char *buf, *p;
|
|
PMXML_NODE root, list, node, npwd;
|
|
|
|
printf("Converting password file \"%s\" to new XML format ... ", file_name);
|
|
|
|
fh = open(file_name, O_RDONLY | O_BINARY);
|
|
if (fh < 0)
|
|
return FALSE;
|
|
len = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
buf = (char *)xmalloc(len + 1);
|
|
assert(buf);
|
|
i = my_read(fh, buf, len);
|
|
buf[i] = 0;
|
|
close(fh);
|
|
|
|
/* create backup */
|
|
strlcpy(name, file_name, sizeof(name));
|
|
strlcat(name, "_bak", sizeof(name));
|
|
fh = open(name, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0644);
|
|
if (fh > 0) {
|
|
write(fh, buf, len);
|
|
close(fh);
|
|
}
|
|
|
|
p = buf;
|
|
|
|
/* skip leading spaces or new lines */
|
|
while (*p && isspace(*p))
|
|
p++;
|
|
|
|
root = mxml_create_root_node();
|
|
list = mxml_add_node(root, "list", NULL);
|
|
|
|
while (*p) {
|
|
|
|
/* skip comment lines */
|
|
if (*p != ';' && *p != '#') {
|
|
for (i = 0; i < (int) sizeof(name) - 1 && *p && *p != ':'; i++)
|
|
name[i] = *p++;
|
|
name[i] = 0;
|
|
if (*p++ != ':') {
|
|
xfree(buf);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < (int) sizeof(password) - 1 && *p && *p != ':'; i++)
|
|
password[i] = *p++;
|
|
password[i] = 0;
|
|
if (*p++ != ':') {
|
|
xfree(buf);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < (int) sizeof(full_name) - 1 && *p && *p != ':'; i++)
|
|
full_name[i] = *p++;
|
|
full_name[i] = 0;
|
|
if (*p++ != ':') {
|
|
xfree(buf);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < (int) sizeof(email) - 1 && *p && *p != ':'; i++)
|
|
email[i] = *p++;
|
|
email[i] = 0;
|
|
if (*p++ != ':') {
|
|
xfree(buf);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < (int) sizeof(email_notify) - 1 && *p && *p != '\r' && *p != '\n'; i++)
|
|
email_notify[i] = *p++;
|
|
email_notify[i] = 0;
|
|
if (*p && *p != '\n' && *p != '\r') {
|
|
xfree(buf);
|
|
return FALSE;
|
|
}
|
|
|
|
while (*p && (*p == '\r' || *p == '\n'))
|
|
p++;
|
|
|
|
node = mxml_add_node(list, "user", NULL);
|
|
mxml_add_node(node, "name", name);
|
|
npwd = mxml_add_node(node, "password", password);
|
|
mxml_add_attribute(npwd, "encoding", "SHA256");
|
|
mxml_add_node(node, "full_name", full_name);
|
|
mxml_add_node(node, "last_logout", "0");
|
|
mxml_add_node(node, "last_activity", "0");
|
|
mxml_add_node(node, "email", email);
|
|
mxml_add_node(node, "email_notify", email_notify);
|
|
mxml_add_node(node, "inactive", "0");
|
|
}
|
|
|
|
while (*p && isspace(*p))
|
|
p++;
|
|
}
|
|
|
|
status = mxml_write_tree(file_name, root);
|
|
mxml_free_tree(root);
|
|
xfree(buf);
|
|
|
|
if (status)
|
|
printf("Ok\n");
|
|
else {
|
|
printf("Error writing to password file\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL convert_password_encoding(LOGBOOK *lbs) {
|
|
PMXML_NODE node, pwd;
|
|
int i;
|
|
char str[256], oldpwd[256], file_name[256];
|
|
|
|
if (lbs->pwd_xml_tree == NULL)
|
|
return FALSE;
|
|
|
|
if ((node = mxml_find_node(lbs->pwd_xml_tree, "/list/user[1]/password")) == NULL)
|
|
return FALSE;
|
|
|
|
str[0] = 0;
|
|
if (mxml_get_attribute(node, "encoding") != NULL)
|
|
strlcpy(str, mxml_get_attribute(node, "encoding"), sizeof(str));
|
|
|
|
if (!strieq(str, "SHA256")) {
|
|
if ((node = mxml_find_node(lbs->pwd_xml_tree, "/list")) == NULL)
|
|
return FALSE;
|
|
|
|
printf("Converting password file for logbook \"%s\" to new encoding ... ", lbs->name);
|
|
|
|
for (i = 0; i < mxml_get_number_of_children(node); i++) {
|
|
sprintf(str, "/list/user[%d]/password", i + 1);
|
|
pwd = mxml_find_node(lbs->pwd_xml_tree, str);
|
|
|
|
if (pwd && mxml_get_value(pwd)) {
|
|
strlcpy(str, mxml_get_value(pwd), sizeof(str));
|
|
|
|
/* assume base64 encoding, might be wrong if HAVE_CRYPT was used */
|
|
base64_decode(str, oldpwd);
|
|
do_crypt(oldpwd, str, sizeof(str));
|
|
|
|
mxml_replace_node_value(pwd, str);
|
|
mxml_add_attribute(pwd, "encoding", "SHA256");
|
|
}
|
|
}
|
|
|
|
if (get_password_file(lbs, file_name, sizeof(file_name)))
|
|
mxml_write_tree(file_name, lbs->pwd_xml_tree);
|
|
|
|
printf("ok\n");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
PMXML_NODE load_password_file(LOGBOOK *lbs, char *error, int error_size) {
|
|
PMXML_NODE root, xml_tree;
|
|
char str[1024], line[256], file_name[256];
|
|
int fh;
|
|
struct stat st;
|
|
|
|
if (error)
|
|
error[0] = 0;
|
|
|
|
if (!get_password_file(lbs, file_name, sizeof(file_name)))
|
|
return NULL;
|
|
|
|
fh = open(file_name, O_RDONLY);
|
|
|
|
/* if password file doen't exist, try to create it */
|
|
if (fh < 0) {
|
|
fh = open(file_name, O_CREAT | O_RDWR, 0600);
|
|
if (fh < 0) {
|
|
sprintf(str, "Cannot open file \"%s\"", file_name);
|
|
strcat(str, ": ");
|
|
strlcat(str, strerror(errno), sizeof(str));
|
|
show_error(str);
|
|
eprintf(str);
|
|
strlcpy(error, str, error_size);
|
|
return NULL;
|
|
}
|
|
close(fh);
|
|
|
|
/* put empty XML tree into password file */
|
|
printf("\nCreate empty password file \"%s\"\n", file_name);
|
|
root = mxml_create_root_node();
|
|
mxml_add_node(root, "list", NULL);
|
|
mxml_write_tree(file_name, root);
|
|
mxml_free_tree(root);
|
|
} else {
|
|
|
|
/* check for write access to password file */
|
|
if (stat(file_name, &st) < 0) {
|
|
sprintf(str, "Cannot access password file \"%s\"", file_name);
|
|
strlcpy(error, str, error_size);
|
|
return NULL;
|
|
}
|
|
#ifdef OS_WINNT
|
|
if ((st.st_mode & _S_IWRITE) == 0) {
|
|
#else
|
|
if ((st.st_mode & S_IWUSR) == 0) {
|
|
#endif
|
|
sprintf(str, "Cannot access write protected password file \"%s\"", file_name);
|
|
strlcpy(error, str, error_size);
|
|
return NULL;
|
|
}
|
|
|
|
/* check if in XML format, otherwise convert it */
|
|
line[0] = 0;
|
|
read(fh, line, sizeof(line));
|
|
close(fh);
|
|
if (strstr(line, "<?xml") == 0) {
|
|
if (!convert_password_file(file_name)) {
|
|
sprintf(str, "Cannot convert password file \"%s\"", file_name);
|
|
strlcpy(error, str, error_size);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((xml_tree = mxml_parse_file(file_name, str, sizeof(str), NULL)) == NULL) {
|
|
show_error(str);
|
|
strlcpy(error, str, error_size);
|
|
eprintf("Cannot load password file \"%s\": %s", file_name, error);
|
|
return NULL;
|
|
}
|
|
|
|
return xml_tree;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int load_password_files() {
|
|
int i, j;
|
|
char str1[256], str2[256], error[256];
|
|
PMXML_NODE xml_tree;
|
|
|
|
for (i = 0; lb_list[i].name[0]; i++) {
|
|
if (lb_list[i].pwd_xml_tree == NULL) {
|
|
if (lb_list[i].top_group[0])
|
|
setcfg_topgroup(lb_list[i].top_group);
|
|
xml_tree = load_password_file(&lb_list[i], error, sizeof(error));
|
|
if (error[0]) {
|
|
puts(error);
|
|
return 0;
|
|
}
|
|
if (xml_tree) {
|
|
lb_list[i].pwd_xml_tree = xml_tree;
|
|
|
|
/* if other logbook has same password file, copy pointer */
|
|
if (lb_list[i].top_group[0])
|
|
setcfg_topgroup(lb_list[i].top_group);
|
|
getcfg(lb_list[i].name, "Password file", str1, sizeof(str1));
|
|
|
|
for (j = i + 1; lb_list[j].name[0]; j++) {
|
|
if (lb_list[j].top_group[0])
|
|
setcfg_topgroup(lb_list[j].top_group);
|
|
getcfg(lb_list[j].name, "Password file", str2, sizeof(str2));
|
|
if (strieq(str1, str2)) {
|
|
lb_list[j].pwd_xml_tree = xml_tree;
|
|
}
|
|
}
|
|
|
|
if (lb_list[i].top_group[0])
|
|
setcfg_topgroup(lb_list[i].top_group);
|
|
|
|
/* convert old password encoding into new format */
|
|
convert_password_encoding(&lb_list[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
LOGBOOK *get_first_lbs_with_global_passwd() {
|
|
int i;
|
|
LOGBOOK *lbs;
|
|
char str[256], global[256], orig_topgroup[256];
|
|
|
|
orig_topgroup[0] = 0;
|
|
if (!getcfg("global", "Password file", global, sizeof(global)))
|
|
return NULL;
|
|
|
|
if (getcfg_topgroup() && *getcfg_topgroup())
|
|
strcpy(orig_topgroup, getcfg_topgroup());
|
|
|
|
for (i = 0; lb_list[i].name[0]; i++) {
|
|
if (lb_list[i].top_group[0])
|
|
setcfg_topgroup(lb_list[i].top_group);
|
|
getcfg(lb_list[i].name, "Password file", str, sizeof(str));
|
|
if (str[0] && strieq(str, global)) {
|
|
lbs = lb_list + i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!lb_list[i].name[0])
|
|
return NULL;
|
|
|
|
if (orig_topgroup[0])
|
|
setcfg_topgroup(orig_topgroup);
|
|
|
|
return lbs;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int get_user_line(LOGBOOK *lbs, char *user, char *password, char *full_name, char *email,
|
|
BOOL email_notify[1000], time_t *last_logout, int *inactive)
|
|
/* return value: 0:cannot access password file, 1: OK, 2: user not found */
|
|
{
|
|
int i, j;
|
|
char str[1024];
|
|
PMXML_NODE user_node, node, subnode;
|
|
|
|
if (password)
|
|
password[0] = 0;
|
|
if (full_name)
|
|
full_name[0] = 0;
|
|
if (email && user[0])
|
|
email[0] = 0;
|
|
if (email_notify)
|
|
email_notify[0] = 0;
|
|
if (last_logout)
|
|
*last_logout = 0;
|
|
if (inactive)
|
|
*inactive = 0;
|
|
|
|
/* if global password file is requested, search for first
|
|
logbook with same password file than global section */
|
|
if (lbs == NULL)
|
|
lbs = get_first_lbs_with_global_passwd();
|
|
if (lbs == NULL)
|
|
return 0;
|
|
|
|
getcfg(lbs->name, "Password file", str, sizeof(str));
|
|
|
|
if (!str[0])
|
|
return 0;
|
|
|
|
if (lbs->pwd_xml_tree) {
|
|
if (user[0]) {
|
|
sprintf(str, "/list/user[name=%s]", user);
|
|
if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL)
|
|
return 2;
|
|
} else if (email && email[0]) {
|
|
sprintf(str, "/list/user[email=%s]", email);
|
|
if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL)
|
|
return 2;
|
|
} else
|
|
return 0;
|
|
|
|
/* if user found, retrieve other info */
|
|
if ((node = mxml_find_node(user_node, "name")) != NULL && user && mxml_get_value(node))
|
|
strlcpy(user, mxml_get_value(node), 256);
|
|
if ((node = mxml_find_node(user_node, "password")) != NULL && password && mxml_get_value(node))
|
|
strlcpy(password, mxml_get_value(node), 256);
|
|
if ((node = mxml_find_node(user_node, "full_name")) != NULL && full_name && mxml_get_value(node))
|
|
strlcpy(full_name, mxml_get_value(node), 256);
|
|
if ((node = mxml_find_node(user_node, "email")) != NULL && email && mxml_get_value(node))
|
|
strlcpy(email, mxml_get_value(node), 256);
|
|
if ((node = mxml_find_node(user_node, "last_logout")) != NULL && last_logout && mxml_get_value(node)) {
|
|
*last_logout = date_to_ltime(mxml_get_value(node));
|
|
if (*last_logout == -1)
|
|
*last_logout = 0;
|
|
}
|
|
if ((node = mxml_find_node(user_node, "inactive")) != NULL && inactive && mxml_get_value(node))
|
|
*inactive = atoi(mxml_get_value(node));
|
|
|
|
if ((node = mxml_find_node(user_node, "email_notify")) != NULL && email_notify) {
|
|
if (mxml_get_number_of_children(node)) {
|
|
for (i = 0; i < 1000; i++)
|
|
email_notify[i] = FALSE;
|
|
|
|
for (i = 0; i < mxml_get_number_of_children(node); i++) {
|
|
subnode = mxml_subnode(node, i);
|
|
for (j = 0; lb_list[j].name[0]; j++)
|
|
if (strieq(lb_list[j].name, mxml_get_value(subnode))) {
|
|
email_notify[j] = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
for (i = 0; i < 1000; i++)
|
|
if (strieq(mxml_get_value(node), "all"))
|
|
email_notify[i] = TRUE;
|
|
else
|
|
email_notify[i] = FALSE;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
} else {
|
|
if (!user[0])
|
|
return 1;
|
|
|
|
/* open password file */
|
|
load_password_files();
|
|
return get_user_line(lbs, user, password, full_name, email, email_notify, last_logout, inactive);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int get_full_name(LOGBOOK *lbs, char *uname, char *full_name) {
|
|
return get_user_line(lbs, uname, NULL, full_name, NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int is_file_system_full(char *file_name) {
|
|
char str[256];
|
|
char buf[1024];
|
|
int n, fh;
|
|
|
|
strlcpy(str, file_name, sizeof(str));
|
|
strlcat(str, ".tmp", sizeof(str));
|
|
fh = open(str, O_CREAT | O_RDWR, 0644);
|
|
if (fh < 0)
|
|
return 0;
|
|
n = write(fh, buf, sizeof(buf));
|
|
close(fh);
|
|
remove(str);
|
|
return n < (int) sizeof(buf);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int set_user_login_time(LOGBOOK *lbs, char *user) {
|
|
int i;
|
|
char str[256], global[256], orig_topgroup[256], file_name[256];
|
|
PMXML_NODE user_node, node;
|
|
time_t last, now;
|
|
|
|
/* if global password file is requested, search for first
|
|
logbook with same password file than global section */
|
|
orig_topgroup[0] = 0;
|
|
if (lbs == NULL) {
|
|
getcfg("global", "Password file", global, sizeof(global));
|
|
if (getcfg_topgroup() && *getcfg_topgroup())
|
|
strcpy(orig_topgroup, getcfg_topgroup());
|
|
|
|
for (i = 0; lb_list[i].name[0]; i++) {
|
|
if (lb_list[i].top_group[0])
|
|
setcfg_topgroup(lb_list[i].top_group);
|
|
getcfg(lb_list[i].name, "Password file", str, sizeof(str));
|
|
if (strieq(str, global)) {
|
|
lbs = lb_list + i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!lb_list[i].name[0])
|
|
return 1;
|
|
|
|
if (orig_topgroup[0])
|
|
setcfg_topgroup(orig_topgroup);
|
|
}
|
|
|
|
getcfg(lbs->name, "Password file", str, sizeof(str));
|
|
|
|
if (!str[0] || !user[0])
|
|
return 1;
|
|
|
|
if (lbs->pwd_xml_tree) {
|
|
sprintf(str, "/list/user[name=%s]", user);
|
|
if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL)
|
|
return 1;
|
|
|
|
if ((node = mxml_find_node(user_node, "last_activity")) != NULL) {
|
|
strlcpy(str, mxml_get_value(node), sizeof(str));
|
|
last = date_to_ltime(str);
|
|
} else
|
|
last = 0;
|
|
|
|
time(&now);
|
|
|
|
/* check if activity time changed significantly */
|
|
if (now > last + 60) {
|
|
|
|
/* if last activity is more than one hour ago, set new logout time from last activity */
|
|
if (now > last + 3600) {
|
|
strcpy(str, "0");
|
|
if ((node = mxml_find_node(user_node, "last_activity")) != NULL)
|
|
strlcpy(str, mxml_get_value(node), sizeof(str));
|
|
|
|
if ((node = mxml_find_node(user_node, "last_logout")) != NULL)
|
|
mxml_replace_node_value(node, str);
|
|
else
|
|
mxml_add_node(user_node, "last_logout", str);
|
|
}
|
|
|
|
/* set new last activity */
|
|
strcpy(str, ctime(&now));
|
|
str[24] = 0;
|
|
if ((node = mxml_find_node(user_node, "last_activity")) != NULL)
|
|
mxml_replace_node_value(node, str);
|
|
else
|
|
mxml_add_node(user_node, "last_activity", str);
|
|
|
|
/* flush to password file */
|
|
if (get_password_file(lbs, file_name, sizeof(file_name))) {
|
|
/* check if file system if full */
|
|
if (is_file_system_full(file_name))
|
|
return 0;
|
|
|
|
mxml_write_tree(file_name, lbs->pwd_xml_tree);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int set_user_inactive(LOGBOOK *lbs, char *user, int inactive) {
|
|
int i;
|
|
char str[256], global[256], orig_topgroup[256], file_name[256];
|
|
PMXML_NODE user_node, node;
|
|
|
|
/* if global password file is requested, search for first
|
|
logbook with same password file than global section */
|
|
orig_topgroup[0] = 0;
|
|
if (lbs == NULL) {
|
|
getcfg("global", "Password file", global, sizeof(global));
|
|
if (getcfg_topgroup() && *getcfg_topgroup())
|
|
strcpy(orig_topgroup, getcfg_topgroup());
|
|
|
|
for (i = 0; lb_list[i].name[0]; i++) {
|
|
if (lb_list[i].top_group[0])
|
|
setcfg_topgroup(lb_list[i].top_group);
|
|
getcfg(lb_list[i].name, "Password file", str, sizeof(str));
|
|
if (strieq(str, global)) {
|
|
lbs = lb_list + i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!lb_list[i].name[0])
|
|
return 1;
|
|
|
|
if (orig_topgroup[0])
|
|
setcfg_topgroup(orig_topgroup);
|
|
}
|
|
|
|
getcfg(lbs->name, "Password file", str, sizeof(str));
|
|
|
|
if (!str[0] || !user[0])
|
|
return 1;
|
|
|
|
if (lbs->pwd_xml_tree) {
|
|
sprintf(str, "/list/user[name=%s]", user);
|
|
if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL)
|
|
return 0;
|
|
|
|
sprintf(str, "%d", inactive);
|
|
if ((node = mxml_find_node(user_node, "inactive")) != NULL)
|
|
mxml_replace_node_value(node, str);
|
|
else
|
|
mxml_add_node(user_node, "inactive", str);
|
|
|
|
/* flush to password file */
|
|
if (get_password_file(lbs, file_name, sizeof(file_name))) {
|
|
/* check if file system if full */
|
|
if (is_file_system_full(file_name))
|
|
return 0;
|
|
|
|
mxml_write_tree(file_name, lbs->pwd_xml_tree);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int set_user_password(LOGBOOK *lbs, char *user, char *password) {
|
|
int i;
|
|
char str[256], pwd_enc[256], file_name[256], orig_topgroup[256], global[256];
|
|
PMXML_NODE user_node, node, npwd;
|
|
|
|
orig_topgroup[0] = 0;
|
|
if (lbs == NULL) {
|
|
getcfg("global", "Password file", global, sizeof(global));
|
|
if (getcfg_topgroup() && *getcfg_topgroup())
|
|
strcpy(orig_topgroup, getcfg_topgroup());
|
|
|
|
for (i = 0; lb_list[i].name[0]; i++) {
|
|
if (lb_list[i].top_group[0])
|
|
setcfg_topgroup(lb_list[i].top_group);
|
|
getcfg(lb_list[i].name, "Password file", str, sizeof(str));
|
|
if (str[0] && strieq(str, global)) {
|
|
lbs = lb_list + i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!lb_list[i].name[0])
|
|
return 0;
|
|
|
|
if (orig_topgroup[0])
|
|
setcfg_topgroup(orig_topgroup);
|
|
}
|
|
|
|
if (lbs->pwd_xml_tree) {
|
|
sprintf(str, "/list/user[name=%s]", user);
|
|
if ((user_node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL)
|
|
return 0;
|
|
|
|
#ifdef HAVE_PAM
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (!stristr(str, "PAM")) {
|
|
#endif /* HAVE_PAM */
|
|
do_crypt(password, pwd_enc, sizeof(pwd_enc));
|
|
if ((node = mxml_find_node(user_node, "password")) != NULL)
|
|
mxml_replace_node_value(node, pwd_enc);
|
|
else {
|
|
npwd = mxml_add_node(user_node, "password", pwd_enc);
|
|
mxml_add_attribute(npwd, "encoding", "SHA256");
|
|
}
|
|
#ifdef HAVE_PAM
|
|
}
|
|
#endif /* HAVE_PAM */
|
|
|
|
/* flush to password file */
|
|
if (get_password_file(lbs, file_name, sizeof(file_name))) {
|
|
/* check if file system if full */
|
|
if (is_file_system_full(file_name))
|
|
return 0;
|
|
|
|
mxml_write_tree(file_name, lbs->pwd_xml_tree);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL enum_user_line(LOGBOOK *lbs, int n, char *user, int size) {
|
|
char str[256], file_name[256];
|
|
int i;
|
|
PMXML_NODE node;
|
|
|
|
if (lbs == NULL) {
|
|
getcfg(NULL, "password file", file_name, sizeof(file_name));
|
|
for (i = 0; lb_list[i].name[0]; i++) {
|
|
getcfg(lb_list[i].name, "password file", str, sizeof(str));
|
|
if (strieq(file_name, str))
|
|
break;
|
|
}
|
|
if (lb_list[i].name[0] == 0)
|
|
lbs = &lb_list[0];
|
|
else
|
|
lbs = &lb_list[i];
|
|
}
|
|
|
|
if (!lbs)
|
|
return FALSE;
|
|
|
|
if (lbs->pwd_xml_tree == NULL)
|
|
return FALSE;
|
|
|
|
sprintf(str, "/list/user[%d]/name", n + 1);
|
|
if ((node = mxml_find_node(lbs->pwd_xml_tree, str)) == NULL)
|
|
return FALSE;
|
|
|
|
strlcpy(user, mxml_get_value(node), size);
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL check_login_user(LOGBOOK *lbs, char *user) {
|
|
int i, n, status;
|
|
char str[1000];
|
|
char list[MAX_N_LIST][NAME_LENGTH];
|
|
|
|
if (user == NULL)
|
|
return FALSE;
|
|
|
|
/* check if usr is in password file */
|
|
status = get_user_line(lbs, user, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
if (status == 2)
|
|
return FALSE;
|
|
|
|
/* treat admin user as login user */
|
|
if (getcfg(lbs->name, "Admin user", str, sizeof(str)) && user[0]) {
|
|
n = strbreak(str, list, MAX_N_LIST, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
if (strcmp(user, list[i]) == 0)
|
|
return TRUE;
|
|
}
|
|
|
|
if (getcfg(lbs->name, "Login user", str, sizeof(str)) && user[0]) {
|
|
n = strbreak(str, list, MAX_N_LIST, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
if (strcmp(user, list[i]) == 0)
|
|
break;
|
|
|
|
if (i == n)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL is_admin_user(LOGBOOK *lbs, char *user) {
|
|
int i, n;
|
|
char str[1000];
|
|
char list[MAX_N_LIST][NAME_LENGTH];
|
|
char logbook[1000];
|
|
|
|
if (lbs == NULL)
|
|
strlcpy(logbook, "global", sizeof(logbook));
|
|
else
|
|
strlcpy(logbook, lbs->name, sizeof(logbook));
|
|
|
|
/* Removed user[0] for cloning, have to check implications, same below.
|
|
if (getcfg(logbook, "Admin user", str, sizeof(str)) && user[0]) { */
|
|
|
|
if (user == NULL)
|
|
return FALSE;
|
|
|
|
if (getcfg(logbook, "Admin user", str, sizeof(str))) {
|
|
n = strbreak(str, list, MAX_N_LIST, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
if (strcmp(user, list[i]) == 0)
|
|
break;
|
|
|
|
if (i == n)
|
|
return FALSE;
|
|
}
|
|
|
|
/* make sure user is logged in */
|
|
if (strcmp(user, http_user) == 0)
|
|
return TRUE;
|
|
if (lbs && !logged_in(lbs))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL is_admin_user_global(char *user) {
|
|
int i, n;
|
|
char str[1000];
|
|
char list[MAX_N_LIST][NAME_LENGTH];
|
|
|
|
if (user == NULL)
|
|
return FALSE;
|
|
|
|
if (getcfg_simple("global", "Admin user", str, sizeof(str), FALSE)) {
|
|
n = strbreak(str, list, MAX_N_LIST, ",", FALSE);
|
|
for (i = 0; i < n; i++)
|
|
if (strcmp(user, list[i]) == 0)
|
|
break;
|
|
|
|
if (i == n)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_login_page(LOGBOOK *lbs, const char *redir, int fail) {
|
|
char str[256], str2[256];
|
|
int show_forgot_link, show_self_register;
|
|
|
|
/* if URL is specified in configuration file, check if login happens for
|
|
the specified host, in order to get cookies right... */
|
|
|
|
if (getcfg(lbs->name, "URL", str, sizeof(str))) {
|
|
extract_host(str);
|
|
strlcpy(str2, http_host, sizeof(str));
|
|
if (strchr(str2, ':'))
|
|
*strchr(str2, ':') = 0;
|
|
if (strchr(str2, ',')) // needed for more than one proxy in a row
|
|
*strchr(str2, ',') = 0;
|
|
if (!strieq(str, str2)) {
|
|
redirect(lbs, _cmdline);
|
|
return;
|
|
}
|
|
}
|
|
|
|
sprintf(str, "ELOG %s", loc("Login"));
|
|
show_html_header(lbs, TRUE, str, TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
|
|
/* set focus on name field */
|
|
rsprintf("<body OnLoad=\"document.form1.uname.focus();\">\n");
|
|
|
|
rsprintf(
|
|
"<form name=\"form1\" id=\"form1\" method=\"POST\" action=\"./\" enctype=\"multipart/form-data\">\n\n");
|
|
|
|
/* define hidden fields for current destination */
|
|
strlcpy(str, redir, sizeof(str));
|
|
if (strchr(str, '<'))
|
|
url_encode(str, sizeof(str));
|
|
if (strchr(str, ' '))
|
|
return;
|
|
|
|
rsprintf("<input type=hidden name=redir value=\"%s\">\n", str);
|
|
|
|
rsprintf("<table class=\"login_frame\" cellspacing=0 align=center>");
|
|
|
|
rsprintf("<tr><td class=\"login_title\">%s</td></tr>\n", loc("Please login"));
|
|
|
|
if (fail == 1) {
|
|
strlcpy(str, loc("Invalid user name or password"), sizeof(str));
|
|
rsprintf("<tr><td class=\"dlgerror\">%s!</td></tr>\n", str);
|
|
}
|
|
|
|
if (fail == 2) {
|
|
sprintf(str, loc("User \"%s\" has no access to this logbook"), getparam("unm"));
|
|
rsprintf("<tr><td class=\"dlgerror\">%s!</td></tr>\n", str);
|
|
}
|
|
|
|
if (isparam("unm"))
|
|
strencode2(str, getparam("unm"), sizeof(str));
|
|
|
|
rsprintf("<tr><td class=\"login_form\">\n");
|
|
rsprintf("<span class=\"overlay_wrapper\">\n");
|
|
rsprintf("<label for=\"uname\" id=\"uname\" class=\"overlabel\">%s</label>\n", loc("Username"));
|
|
rsprintf(
|
|
"<input type=\"text\" class=\"login_input\" name=\"uname\" value=\"%s\" title=\"%s\" onInput=\"document.getElementById('uname').style.display='none';\">\n",
|
|
isparam("unm") ? str : "", loc("Username"));
|
|
rsprintf("</span></td></tr>\n");
|
|
|
|
rsprintf("<tr><td class=\"login_form\">\n");
|
|
rsprintf("<span class=\"overlay_wrapper\">\n");
|
|
rsprintf("<label for=\"upassword\" id=\"upassword\" class=\"overlabel\">%s</label>\n", loc("Password"));
|
|
rsprintf(
|
|
"<input type=\"password\" class=\"login_input\" name=\"upassword\" onInput=\"document.getElementById('upassword').style.display='none';\">\n");
|
|
rsprintf("</span></td></tr>\n");
|
|
|
|
if (!getcfg(lbs->name, "Login expiration", str, sizeof(str)) || atof(str) > 0) {
|
|
rsprintf("<tr><td align=center class=\"login_form\">");
|
|
|
|
if (isparam("urem") && atoi(getparam("urem")) == 0)
|
|
rsprintf("<input type=checkbox name=remember value=1>\n");
|
|
else
|
|
rsprintf("<input type=checkbox checked name=remember value=1>\n");
|
|
rsprintf("%s\n", loc("Keep me logged in on this computer"));
|
|
|
|
if (str[0] == 0)
|
|
rsprintf(loc("for the next %d days"), 31);
|
|
else if (atof(str) < 1)
|
|
rsprintf(loc("for the next %d minutes"), (int) (atof(str) * 60));
|
|
else if (atof(str) == 1)
|
|
rsprintf(loc("for the next hour"));
|
|
else if (atof(str) <= 48)
|
|
rsprintf(loc("for the next %d hours"), (int) atof(str));
|
|
else
|
|
rsprintf(loc("for the next %d days"), (int) (atof(str) / 24));
|
|
|
|
rsprintf(" %s", loc("or until I log out"));
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
show_forgot_link = (!getcfg(lbs->name, "allow password change", str, sizeof(str)) || atoi(str) == 1);
|
|
#ifdef HAVE_PAM
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (stristr(str, "PAM"))
|
|
show_self_register = 0;
|
|
else
|
|
#endif /* HAVE_PAM */
|
|
show_self_register = (getcfg(lbs->name, "Self register", str, sizeof(str)) && atoi(str) > 0);
|
|
|
|
if (show_forgot_link || show_self_register)
|
|
rsprintf("<tr><td align=center class=\"login_form\">\n");
|
|
|
|
if (show_forgot_link)
|
|
rsprintf("<a href=\"?cmd=%s\">%s</a>", loc("Forgot"), loc("Forgot password?"));
|
|
|
|
if (show_self_register) {
|
|
strlcpy(str, loc("New user"), sizeof(str));
|
|
url_encode(str, sizeof(str));
|
|
if (show_forgot_link)
|
|
rsprintf("<br>");
|
|
rsprintf("<a href=\"?cmd=%s\">%s</a></td></tr>", str, loc("Register as new user"));
|
|
}
|
|
|
|
if (show_forgot_link || show_self_register)
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf(
|
|
"<tr><td align=center class=\"login_form_bottom\"><input type=\"submit\" class=\"login_submit\" value=\"%s\"></td></tr>",
|
|
loc("Submit"));
|
|
|
|
rsprintf("</table>\n");
|
|
|
|
show_bottom_text_login(lbs);
|
|
|
|
rsprintf("</form></body></html>\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL check_login(LOGBOOK *lbs, const char *sid) {
|
|
char str[1000], pwd_file[256], user_name[256], upwd[256];
|
|
int status, inactive, skip_sid_check;
|
|
|
|
#ifdef HAVE_PAM
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (!stristr(str, "PAM")) {
|
|
#endif /* HAVE_PAM */
|
|
/* show new user screen if password file is empty */
|
|
if (!enum_user_line(lbs, 0, str, sizeof(str))) {
|
|
if (isparam("new_user_name"))
|
|
return TRUE;
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (stristr(str, "Webserver")) {
|
|
show_new_user_page(lbs, http_user);
|
|
} else {
|
|
show_new_user_page(lbs, NULL);
|
|
}
|
|
return FALSE;
|
|
}
|
|
#ifdef HAVE_PAM
|
|
}
|
|
#endif /* HAVE_PAM */
|
|
|
|
/* check for "forgot password" */
|
|
if (isparam("cmd") && strcmp(getparam("cmd"), loc("Forgot")) == 0) {
|
|
if (getcfg(lbs->name, "forgot password link", str, sizeof(str)) && atoi(str) == 0)
|
|
return FALSE;
|
|
|
|
show_forgot_pwd_page(lbs);
|
|
return FALSE;
|
|
}
|
|
|
|
/* check for password login (elog & mirroring) */
|
|
skip_sid_check = FALSE;
|
|
if (isparam("unm") && isparam("upwd")) {
|
|
get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL);
|
|
if (strcmp(upwd, getparam("upwd")) != 0) {
|
|
show_login_page(lbs, "", 0);
|
|
return FALSE;
|
|
} else {
|
|
strlcpy(user_name, getparam("unm"), sizeof(user_name));
|
|
skip_sid_check = TRUE;
|
|
}
|
|
}
|
|
|
|
/* if invalid or no session ID, show login page,
|
|
unless we outsourced the authentication to webserver
|
|
*/
|
|
if (!skip_sid_check && !sid_check(sid, user_name)) {
|
|
if (isparam("redir"))
|
|
strlcpy(str, getparam("redir"), sizeof(str));
|
|
else
|
|
strlcpy(str, isparam("cmdline") ? getparam("cmdline") : _cmdline, sizeof(str));
|
|
/* avoid recursive loops with ?cmd=Login */
|
|
if (stristr(str, loc("Login")))
|
|
str[0] = 0;
|
|
show_login_page(lbs, str, 0);
|
|
return FALSE;
|
|
}
|
|
|
|
/* if user not in password file (external authentication!) obtain user info */
|
|
status = get_user_line(lbs, user_name, NULL, NULL, NULL, NULL, NULL, &inactive);
|
|
|
|
/* fail if password file cannot be accessed */
|
|
if (status == 0) {
|
|
getcfg(lbs->name, "Password file", pwd_file, sizeof(pwd_file));
|
|
sprintf(str, loc("Cannot open file <b>%s</b>"), pwd_file);
|
|
strcat(str, ": ");
|
|
strlcat(str, strerror(errno), sizeof(str));
|
|
show_error(str);
|
|
return FALSE;
|
|
}
|
|
|
|
/* if user cannot be found in password file, ask for new user info */
|
|
if (status == 2 && !isparam("new_user_name")) {
|
|
show_new_user_page(lbs, user_name);
|
|
return FALSE;
|
|
}
|
|
|
|
/* show error for inactive account */
|
|
if (inactive) {
|
|
show_error("This account is currently deactivated");
|
|
return FALSE;
|
|
}
|
|
|
|
/* remember user name for other places */
|
|
setparam("unm", user_name);
|
|
|
|
/* check if user has access to logbook */
|
|
if (!check_login_user(lbs, user_name) && !isparam("new_user_name")) {
|
|
show_login_page(lbs, "", 2);
|
|
return FALSE;
|
|
}
|
|
|
|
/* set access time for user */
|
|
if (user_name[0] && !isparam("new_user_name")) {
|
|
if (!set_user_login_time(lbs, user_name)) {
|
|
show_error(loc("File system full, ELOG cannot continue to work"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int node_contains(LBLIST pn, char *logbook) {
|
|
int i;
|
|
|
|
for (i = 0; i < pn->n_members; i++) {
|
|
|
|
/* check if logbook in this group */
|
|
if (strieq(pn->member[i]->name, logbook))
|
|
return 1;
|
|
|
|
/* check if loogbook is in subgroups */
|
|
if (pn->member[i]->n_members > 0 && node_contains(pn->member[i], logbook))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_logbook_node(LBLIST plb, LBLIST pparent, int level, int btop) {
|
|
int i, index, j, expand, expand_all, message_id;
|
|
char str[10000], date[256], slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH],
|
|
mid[80];
|
|
|
|
if (plb->n_members > 0) {
|
|
|
|
expand = 0;
|
|
if (isparam("gexp")) {
|
|
if (strieq(plb->name, getparam("gexp")) || node_contains(plb, getparam("gexp"))
|
|
|| strieq(getparam("gexp"), "all"))
|
|
expand = 1;
|
|
}
|
|
|
|
if (!getcfg(plb->name, "Expand selection page", str, sizeof(str)) || atoi(str) == 1)
|
|
expand_all = 1;
|
|
else
|
|
expand_all = 0;
|
|
|
|
/* do not display top groups */
|
|
if (!plb->is_top) {
|
|
|
|
rsprintf("<tr>");
|
|
for (i = 0; i < level; i++)
|
|
rsprintf("<td class=\"selspace\"> </td>\n");
|
|
rsprintf("<td colspan=%d class=\"selgroup\">", 13 - level);
|
|
for (i = 0; i < level; i++)
|
|
rsprintf(" ");
|
|
if (expand) {
|
|
|
|
if (expand_all)
|
|
rsprintf("%s", plb->name);
|
|
else {
|
|
if (pparent != NULL) {
|
|
if (getcfg_topgroup())
|
|
rsprintf("<a href=\"../%s/?gexp=%s\">- %s</a> ", getcfg_topgroup(), pparent->name,
|
|
plb->name);
|
|
else
|
|
rsprintf("<a href=\"?gexp=%s\">- %s</a> ", pparent->name, plb->name);
|
|
} else {
|
|
if (getcfg_topgroup())
|
|
rsprintf("<a href=\"../%s/\">- %s</a> ", getcfg_topgroup(), plb->name);
|
|
else
|
|
rsprintf("<a href=\".\">- %s</a> ", plb->name);
|
|
}
|
|
}
|
|
} else {
|
|
if (expand_all)
|
|
rsprintf("%s", plb->name);
|
|
else {
|
|
if (getcfg_topgroup())
|
|
rsprintf("<a href=\"../%s/?gexp=%s\">+ %s</a> ", getcfg_topgroup(), plb->name, plb->name);
|
|
else
|
|
rsprintf("<a href=\".?gexp=%s\">+ %s</a> ", plb->name, plb->name);
|
|
}
|
|
}
|
|
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
if (plb->is_top || expand || expand_all)
|
|
for (i = 0; i < plb->n_members; i++)
|
|
show_logbook_node(plb->member[i], plb->is_top ? NULL : plb, level + 1, btop);
|
|
} else {
|
|
|
|
if (!getcfg(plb->name, "Hidden", str, sizeof(str)) || atoi(str) == 0) {
|
|
|
|
/* search logbook in list */
|
|
for (index = 0; lb_list[index].name[0]; index++)
|
|
if (strieq(plb->name, lb_list[index].name))
|
|
break;
|
|
if (!lb_list[index].name[0])
|
|
return;
|
|
|
|
rsprintf("<tr>");
|
|
for (j = 0; j < level; j++)
|
|
rsprintf("<td class=\"selspace\"> </td>\n");
|
|
rsprintf("<td colspan=%d class=\"sellogbook\">", 10 - level);
|
|
|
|
if (btop)
|
|
rsprintf("<a href=\"../%s/\">%s</a>", lb_list[index].name_enc, lb_list[index].name);
|
|
else
|
|
rsprintf("<a href=\"%s\">%s</a>", lb_list[index].name_enc, lb_list[index].name);
|
|
|
|
if ((getcfg(lb_list[index].name, "Password file", str, sizeof(str)) &&
|
|
!getcfg(lb_list[index].name, "Guest menu commands", str, sizeof(str))))
|
|
rsprintf(" <img src=\"lock.png\" alt=\"%s\" title=\"%s\">",
|
|
loc("This logbook requires authentication"),
|
|
loc("This logbook requires authentication"));
|
|
rsprintf("<br>\n");
|
|
|
|
if (!getcfg(plb->name, "Hide Comments", str, sizeof(str)) || atoi(str) == 0) {
|
|
str[0] = 0;
|
|
getcfg(lb_list[index].name, "Comment", str, sizeof(str));
|
|
rsprintf("<span class=\"selcomment\">");
|
|
if (is_html(str))
|
|
rsputs(str);
|
|
else
|
|
rsputs3(str);
|
|
rsprintf("</span></td>\n");
|
|
}
|
|
|
|
rsprintf("<td nowrap class=\"selentries\">");
|
|
rsprintf("%d", *lb_list[index].n_el_index);
|
|
rsprintf("</td>\n");
|
|
rsprintf("<td nowrap class=\"selentries\">");
|
|
if (*lb_list[index].n_el_index == 0)
|
|
rsprintf("-");
|
|
else {
|
|
char attrib[MAX_N_ATTR][NAME_LENGTH];
|
|
|
|
lb_list[index].n_attr = scan_attributes(lb_list[index].name);
|
|
message_id = el_search_message(&lb_list[index], EL_LAST, 0, FALSE);
|
|
el_retrieve(&lb_list[index], message_id, date, attr_list, attrib, lb_list[index].n_attr, NULL, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL);
|
|
if (!getcfg(lb_list[index].name, "Last submission", str, sizeof(str))) {
|
|
sprintf(str, "$entry time");
|
|
for (i = 0; i < lb_list[index].n_attr; i++)
|
|
if (strieq(attr_list[i], "Author"))
|
|
break;
|
|
if (i < lb_list[index].n_attr)
|
|
sprintf(str + strlen(str), " %s $author", loc("by"));
|
|
}
|
|
j = build_subst_list(&lb_list[index], slist, svalue, attrib, TRUE);
|
|
sprintf(mid, "%d", message_id);
|
|
add_subst_list(slist, svalue, "message id", mid, &j);
|
|
add_subst_time(&lb_list[index], slist, svalue, "entry time", date, &j, 0);
|
|
strsubst_list(str, sizeof(str), slist, svalue, j);
|
|
rsputs(str);
|
|
}
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_top_selection_page() {
|
|
int i;
|
|
char str[10000], name[NAME_LENGTH], name_enc[NAME_LENGTH];
|
|
LBLIST phier;
|
|
|
|
/* if selection page protected, check password */
|
|
if (getcfg("global", "password file", str, sizeof(str)) && getcfg("global", "protect selection page", str,
|
|
sizeof(str)) && atoi(str) == 1)
|
|
if (!check_login(NULL, getparam("sid")))
|
|
return;
|
|
|
|
if (getcfg("global", "Page Title", str, sizeof(str))) {
|
|
strip_html(str);
|
|
show_html_header(NULL, TRUE, str, TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
} else
|
|
show_html_header(NULL, TRUE, "ELOG Logbook Selection", TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
rsprintf("<body>\n\n");
|
|
rsprintf("<table class=\"selframe\" cellspacing=0 align=center>\n");
|
|
rsprintf("<tr><td class=\"dlgtitle\">\n");
|
|
|
|
if (getcfg("global", "Welcome title", str, sizeof(str))) {
|
|
rsputs(str);
|
|
} else {
|
|
rsprintf("%s.<BR>\n", loc("Several logbooks groups are defined on this host"));
|
|
rsprintf("%s:\n", loc("Please select one to list the logbooks in that group"));
|
|
}
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
phier = get_logbook_hierarchy();
|
|
for (i = 0; i < phier->n_members; i++)
|
|
if (phier->member[i]->is_top) {
|
|
rsprintf("<tr><td class=\"sellogbook\">");
|
|
strlcpy(name, phier->member[i]->name, sizeof(name));
|
|
strlcpy(name_enc, name, sizeof(name_enc));
|
|
url_encode(name_enc, sizeof(name_enc));
|
|
rsprintf("<a href=\"%s/\">%s</a>", name_enc, name);
|
|
rsprintf("</td></tr>\n");
|
|
}
|
|
|
|
free_logbook_hierarchy(phier);
|
|
rsprintf("</table></body>\n");
|
|
rsprintf("</html>\r\n\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_selection_page(void) {
|
|
int i, j, expand_all, show_title;
|
|
char str[10000], file_name[256];
|
|
LBLIST phier;
|
|
|
|
/* check if at least one logbook defined */
|
|
if (!lb_list[0].name[0]) {
|
|
show_standard_header(NULL, FALSE, "ELOG", "", FALSE, NULL, NULL, 0);
|
|
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
rsprintf("<tr><td class=\"dlgtitle\">\n");
|
|
rsprintf(loc("No logbook defined on this server"));
|
|
rsprintf("</td></tr>\n");
|
|
|
|
rsprintf("<tr><td align=center class=\"dlgform\">\n");
|
|
rsprintf("<a href=\"?cmd=%s\">%s</a>", loc("Create new logbook"), loc("Create new logbook"));
|
|
rsprintf("</td></tr></table>\n");
|
|
rsprintf("</body></html>\n");
|
|
return;
|
|
}
|
|
|
|
/* check for Guest Selection Page */
|
|
if (getcfg("global", "Guest Selection Page", str, sizeof(str)) && !(isparam("unm"))) {
|
|
/* check for URL */
|
|
if (strstr(str, "http://") || strstr(str, "https://")) {
|
|
redirect(NULL, str);
|
|
return;
|
|
}
|
|
|
|
/* check if file starts with an absolute directory */
|
|
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));
|
|
}
|
|
send_file_direct(file_name);
|
|
return;
|
|
}
|
|
|
|
/* top group present and no top group in URL */
|
|
if (exist_top_group() && getcfg_topgroup() == NULL) {
|
|
if (getcfg("global", "show top groups", str, sizeof(str)) && atoi(str) == 1) {
|
|
show_top_selection_page();
|
|
return;
|
|
} else
|
|
return; /* abort connection */
|
|
}
|
|
|
|
/* if selection page protected, check password */
|
|
if (getcfg("global", "password file", str, sizeof(str)) && getcfg("global", "protect selection page", str,
|
|
sizeof(str)) && atoi(str) == 1)
|
|
if (!check_login(NULL, getparam("sid")))
|
|
return;
|
|
|
|
if (getcfg("global", "Page Title", str, sizeof(str))) {
|
|
strip_html(str);
|
|
show_html_header(NULL, TRUE, str, TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
} else
|
|
show_html_header(NULL, TRUE, "ELOG Logbook Selection", TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
|
|
rsprintf("<body>\n\n");
|
|
rsprintf("<table class=\"selframe\" cellspacing=0 align=center>\n");
|
|
rsprintf("<tr><td colspan=13 class=\"dlgtitle\">\n");
|
|
|
|
if (getcfg("global", "Welcome title", str, sizeof(str))) {
|
|
rsputs(str);
|
|
} else {
|
|
rsprintf("%s.<BR>\n", loc("Several logbooks are defined on this host"));
|
|
rsprintf("%s:\n", loc("Please select the one to connect to"));
|
|
}
|
|
|
|
rsprintf("</td></tr>\n");
|
|
|
|
if (getcfg("global", "mirror server", str, sizeof(str))) {
|
|
|
|
/* only admin user sees synchronization link */
|
|
if (is_admin_user(NULL, getparam("unm"))) {
|
|
rsprintf("<tr>\n");
|
|
rsprintf("<td colspan=13 class=\"seltitle\">\n");
|
|
rsprintf("<a href=\"?cmd=Synchronize\">%s</a></td>\n", loc("Synchronize all logbooks"));
|
|
rsprintf("</tr>\n");
|
|
}
|
|
}
|
|
|
|
phier = get_logbook_hierarchy();
|
|
show_title = 0;
|
|
|
|
if (getcfg_topgroup()) {
|
|
for (i = 0; i < phier->n_members; i++)
|
|
if (strieq(getcfg_topgroup(), phier->member[i]->name)) {
|
|
if (phier->member[i]->n_members == 0)
|
|
show_title = 1;
|
|
else
|
|
for (j = 0; j < phier->member[i]->n_members; j++)
|
|
if (phier->member[i]->member[j]->n_members == 0)
|
|
show_title = 1;
|
|
|
|
break;
|
|
}
|
|
} else
|
|
for (i = 0; i < phier->n_members; i++)
|
|
if (phier->member[i]->n_members == 0)
|
|
show_title = 1;
|
|
|
|
if (!getcfg("global", "Expand selection page", str, sizeof(str)) || atoi(str) == 1)
|
|
expand_all = 1;
|
|
else
|
|
expand_all = 0;
|
|
|
|
if (isparam("gexp") || expand_all)
|
|
show_title = 1;
|
|
|
|
if (show_title) {
|
|
rsprintf("<tr>\n");
|
|
rsprintf("<th colspan=10 class=\"seltitle\">%s</th>\n", loc("Logbook"));
|
|
rsprintf("<th class=\"seltitle\">%s</th>\n", loc("Entries"));
|
|
rsprintf("<th class=\"seltitle\">%s</th>\n", loc("Last submission"));
|
|
rsprintf("</tr>\n");
|
|
} else {
|
|
rsprintf("<tr>\n");
|
|
rsprintf("<td colspan=13 class=\"selexp\">\n");
|
|
rsprintf("<a href=\"?gexp=all\">%s</a></td>\n", loc("Expand all"));
|
|
rsprintf("</tr>\n");
|
|
}
|
|
|
|
if (getcfg_topgroup()) {
|
|
for (i = 0; i < phier->n_members; i++)
|
|
if (strieq(getcfg_topgroup(), phier->member[i]->name)) {
|
|
show_logbook_node(phier->member[i], NULL, -1, 1);
|
|
break;
|
|
}
|
|
} else
|
|
for (i = 0; i < phier->n_members; i++)
|
|
show_logbook_node(phier->member[i], NULL, 0, 0);
|
|
|
|
free_logbook_hierarchy(phier);
|
|
rsprintf("</table></body>\n");
|
|
rsprintf("</html>\r\n\r\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int do_self_register(LOGBOOK *lbs, char *command)
|
|
/* evaluate self-registration commands */
|
|
{
|
|
char str[1024];
|
|
|
|
if (command == NULL)
|
|
return 1;
|
|
|
|
/* display new user page if "self register" is clicked */
|
|
if (strieq(command, loc("New user"))) {
|
|
show_new_user_page(lbs, NULL);
|
|
return 0;
|
|
}
|
|
|
|
/* save user info if "save" is pressed */
|
|
if (strieq(command, loc("Save")) && isparam("new_user_name") && !isparam("config")) {
|
|
if (!save_user_config(lbs, getparam("new_user_name"), TRUE))
|
|
return 0;
|
|
if (lbs)
|
|
sprintf(str, "../%s/", lbs->name_enc);
|
|
else
|
|
sprintf(str, ".");
|
|
redirect(lbs, str);
|
|
return 0;
|
|
}
|
|
|
|
/* display account request notification */
|
|
if (strieq(command, loc("Requested"))) {
|
|
show_standard_header(lbs, FALSE, loc("ELOG registration"), "", FALSE, NULL, NULL, 0);
|
|
rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
|
|
rsprintf("<tr><td colspan=2 class=\"dlgtitle\">\n");
|
|
rsprintf("%s.", loc("Your request has been forwarded to the administrator"));
|
|
rsprintf("%s.", loc("You will be notified by email upon activation of your new account"));
|
|
rsprintf("</td></tr></table>\n");
|
|
show_bottom_text(lbs);
|
|
rsprintf("</body></html>\n");
|
|
return 0;
|
|
}
|
|
|
|
/* indicate continue */
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_day(const char *css_class, const char *day) {
|
|
if (day[0]) {
|
|
rsprintf("<td class=\"%s\" ", css_class);
|
|
rsprintf("onClick='submit_day(\"%s\")' ", day);
|
|
rsprintf("onMouseOver=\"this.className='calsel';\" ");
|
|
rsprintf("onMouseOut=\"this.className='%s';\" ", css_class);
|
|
rsprintf(">%s</td>\n", day);
|
|
} else {
|
|
/* empty cell */
|
|
rsprintf("<td class=\"%s\"> </td>\n", css_class);
|
|
}
|
|
}
|
|
|
|
void show_calendar(LOGBOOK *lbs) {
|
|
int i, j, cur_mon, cur_year, today_day, today_mon, today_year;
|
|
time_t now, stime;
|
|
struct tm *ts;
|
|
char str[256], index[10];
|
|
|
|
time(&now);
|
|
ts = localtime(&now);
|
|
assert(ts);
|
|
today_mon = ts->tm_mon + 1;
|
|
today_day = ts->tm_mday;
|
|
today_year = ts->tm_year + 1900;
|
|
|
|
if (isparam("m") && isparam("y")) {
|
|
cur_mon = atoi(getparam("m"));
|
|
cur_year = atoi(getparam("y"));
|
|
ts->tm_mday = 1;
|
|
ts->tm_mon = cur_mon - 1;
|
|
ts->tm_year = cur_year - 1900;
|
|
mktime(ts);
|
|
} else {
|
|
cur_mon = ts->tm_mon + 1;
|
|
cur_year = ts->tm_year + 1900;
|
|
}
|
|
if (isparam("i"))
|
|
strencode2(index, getparam("i"), sizeof(index));
|
|
else
|
|
strcpy(index, "1");
|
|
|
|
show_html_header(lbs, FALSE, loc("Calendar"), TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
rsprintf("<body class=\"calwindow\"><form name=\"form1\" method=\"GET\" action=\"cal.html\">\n");
|
|
rsprintf("<input type=hidden name=\"i\" value=\"%s\">\n", index);
|
|
rsprintf("<input type=hidden name=\"y\" value=\"%d\">\n", cur_year);
|
|
|
|
rsprintf("<script type=\"text/javascript\">\n\n");
|
|
rsprintf("function submit_day(day)\n");
|
|
rsprintf("{\n");
|
|
rsprintf(" opener.document.form1.d%s.value = day;\n", index, cur_year);
|
|
rsprintf(" opener.document.form1.m%s.value = \"%d\";\n", index, cur_mon);
|
|
rsprintf(" opener.document.form1.y%s.value = \"%d\";\n", index, cur_year);
|
|
rsprintf(" window.close();\n");
|
|
rsprintf("}\n");
|
|
rsprintf("</script>\n\n");
|
|
|
|
rsprintf("<table border=1 width=300><tr>");
|
|
rsprintf("<td colspan=7 class=\"caltitle\">\n");
|
|
|
|
rsprintf("<select name=\"m\" onChange=\"document.form1.submit()\">\n");
|
|
for (i = 0; i < 12; i++)
|
|
if (i + 1 == cur_mon)
|
|
rsprintf("<option selected value=\"%d\">%s\n", i + 1, month_name(i));
|
|
else
|
|
rsprintf("<option value=\"%d\">%s\n", i + 1, month_name(i));
|
|
rsprintf("</select>\n");
|
|
|
|
/* link to previous year */
|
|
rsprintf(" ");
|
|
rsprintf("<a href=\"?i=%s&m=%d&y=%d\">", index, cur_mon, cur_year - 1);
|
|
rsprintf("<img border=0 align=\"absmiddle\" src=\"cal_prev.png\" alt=\"%s\" title=\"%s\"></a>",
|
|
loc("Previous Year"), loc("Previous Year"));
|
|
|
|
/* current year */
|
|
rsprintf(" %d ", cur_year);
|
|
|
|
/* link to next year */
|
|
rsprintf("<a href=\"?i=%s&m=%d&y=%d\">", index, cur_mon, cur_year + 1);
|
|
rsprintf("<img border=0 align=\"absmiddle\" src=\"cal_next.png\" alt=\"%s\" title=\"%s\"></a>",
|
|
loc("Next Year"), loc("Next Year"));
|
|
|
|
/* go to first day of month */
|
|
ts->tm_mday = 1;
|
|
stime = mktime(ts);
|
|
if (stime < 0) {
|
|
rsprintf("<tr><td>Invalid date</td></tr>");
|
|
rsprintf("</table>\n</form></body></html>\n");
|
|
return;
|
|
}
|
|
|
|
/* go to last sunday */
|
|
stime = stime - 3600 * 24 * ts->tm_wday;
|
|
|
|
rsprintf("<tr>\n");
|
|
for (i = 0; i < 7; i++) {
|
|
ts = localtime(&stime);
|
|
assert(ts);
|
|
strftime(str, sizeof(str), "%a", ts);
|
|
rsprintf("<td class=\"calhead\">%s</td>\n", str);
|
|
stime += 3600 * 24;
|
|
}
|
|
rsprintf("</tr>\n");
|
|
stime -= 3600 * 24 * 7;
|
|
ts = localtime(&stime);
|
|
assert(ts);
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
rsprintf("<tr>\n");
|
|
|
|
for (j = 0; j < 7; j++) {
|
|
if (ts->tm_mon + 1 == cur_mon)
|
|
sprintf(str, "%d", ts->tm_mday);
|
|
else
|
|
strcpy(str, "");
|
|
|
|
if (ts->tm_mday == today_day && ts->tm_mon + 1 == today_mon && ts->tm_year + 1900 == today_year)
|
|
show_day("calcurday", str);
|
|
else {
|
|
if (j == 0)
|
|
show_day("calsun", str);
|
|
else if (j == 6)
|
|
show_day("calsat", str);
|
|
else
|
|
show_day("calday", str);
|
|
}
|
|
stime += 3600 * 24;
|
|
ts = localtime(&stime);
|
|
assert(ts);
|
|
}
|
|
|
|
rsprintf("</tr>\n");
|
|
if (ts->tm_mon + 1 != cur_mon)
|
|
break;
|
|
}
|
|
|
|
rsprintf("</table>\n</form></body></html>\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_uploader(LOGBOOK *lbs) {
|
|
char str[256];
|
|
|
|
show_html_header(lbs, FALSE, loc("Upload image"), TRUE, FALSE, NULL, FALSE, 0, 200);
|
|
rsprintf("<body class=\"uploadwindow\"><form name=\"form1\" method=\"POST\" action=\".\" ");
|
|
rsprintf("enctype=\"multipart/form-data\">\n");
|
|
rsprintf("<input type=hidden name=\"jcmd\" value=\"JUpload\">\n");
|
|
|
|
rsprintf("<script type=\"text/javascript\">\n\n");
|
|
rsprintf(" function upload()\n");
|
|
rsprintf(" {\n");
|
|
rsprintf(" if (document.form1.attfile.value == \"\") {\n");
|
|
rsprintf(" alert(\"%s\");\n", loc("Please enter filename or URL"));
|
|
rsprintf(" return false;\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" return true;\n");
|
|
rsprintf(" }\n");
|
|
|
|
rsprintf("</script>\n\n");
|
|
|
|
rsprintf("<table border=0 width=500>");
|
|
|
|
strcpy(str, loc("Maximum allowed file size is"));
|
|
if (MAX_CONTENT_LENGTH >= 1024 * 1024)
|
|
sprintf(str + strlen(str), " %d MB", MAX_CONTENT_LENGTH / 1024 / 1024);
|
|
else
|
|
sprintf(str + strlen(str), " %d kB", MAX_CONTENT_LENGTH / 1024);
|
|
rsprintf("<tr><td nowrap class=\"uploadtext\"><b>%s:</b> <i>(%s)</i></td></tr>\n",
|
|
loc("Enter filename or URL"), str);
|
|
rsprintf("<tr><td class=\"uploadvalue\"><input type=\"file\" size=\"60\" ");
|
|
rsprintf("maxlength=\"200\" name=\"attfile\"></td></tr>\n");
|
|
|
|
rsprintf("<tr><td class=\"menuframe\" align=center><br>\n");
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return upload();\">\n",
|
|
loc("Upload"));
|
|
rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"window.close();\">\n", loc("Cancel"));
|
|
rsprintf("<br><br></td></tr>\n");
|
|
|
|
rsprintf("</table>\n");
|
|
rsprintf("</form></body></html>\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_uploader_finished(LOGBOOK *lbs) {
|
|
int i;
|
|
char str[256], att[256], base_url[256], file_enc[256], ref[1024], ref_thumb[1024];
|
|
|
|
show_html_header(lbs, FALSE, loc("Image uploaded successfully"), FALSE, FALSE, NULL, FALSE, 0, 200);
|
|
|
|
rsprintf("<script type=\"text/javascript\" src=\"../elcode.js\"></script>\n\n");
|
|
|
|
rsprintf("</head>\n");
|
|
rsprintf("<body OnLoad=\"update();\">\n");
|
|
|
|
/* find last attachment */
|
|
att[0] = 0;
|
|
for (i = MAX_ATTACHMENTS - 1; i >= 0; i--) {
|
|
sprintf(str, "attachment%d", i);
|
|
if (isparam(str)) {
|
|
strlcpy(att, getparam(str), sizeof(att));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (att[0]) {
|
|
strlcpy(str, att, sizeof(str));
|
|
str[13] = 0;
|
|
strcpy(file_enc, att + 14);
|
|
url_encode(file_enc, sizeof(file_enc)); /* for file names with special characters like "+" */
|
|
sprintf(ref, "%s/%s?lb=%s", str, file_enc, lbs->name_enc);
|
|
sprintf(ref_thumb, "%s/%s?lb=%s&thumb=1", str, file_enc, lbs->name_enc);
|
|
|
|
compose_base_url(lbs, base_url, sizeof(base_url), TRUE);
|
|
|
|
rsprintf("<script type=\"text/javascript\">\n\n");
|
|
|
|
rsprintf(" function update()\n");
|
|
rsprintf(" {\n");
|
|
rsprintf(" if (opener.document.title == \"FCKeditor\") {\n");
|
|
rsprintf(" i = opener.parent.next_attachment;\n");
|
|
rsprintf(" opener.FCKeditorAPI.GetInstance('Text').\n");
|
|
rsprintf
|
|
("InsertHtml('<a href=\"%s\"><img border=0 alt=\"%s\" src=\"%s\" name=\"att'+(i-1)+'\" id=\"att'+(i-1)+'\"></a>');\n",
|
|
ref, att + 14, ref_thumb);
|
|
rsprintf(" opener.parent.document.form1.inlineatt.value = '%s';\n", att);
|
|
rsprintf(" opener.parent.document.form1.jcmd.value = 'Upload';\n");
|
|
rsprintf(" opener.parent.document.form1.submit();\n");
|
|
rsprintf(" } else {\n");
|
|
rsprintf(" i = opener.document.form1.next_attachment.value;\n");
|
|
rsprintf(" elcode2(opener.document, opener.document.form1.Text, 'IMG', 'elog:/'+i);\n");
|
|
rsprintf(" opener.document.form1.inlineatt.value = '%s';\n", att);
|
|
rsprintf(" opener.document.form1.jcmd.value = 'Upload';\n");
|
|
rsprintf(" opener.cond_submit();\n");
|
|
rsprintf(" }\n");
|
|
rsprintf(" window.close();\n");
|
|
rsprintf(" }\n\n");
|
|
|
|
/* strings for elcode.js */
|
|
show_browser(browser);
|
|
|
|
rsprintf("</script>\n\n");
|
|
|
|
rsprintf("<center>\n");
|
|
rsprintf(loc("Image \"%s\" uploaded successfully"), att + 14);
|
|
rsprintf("</center>\n");
|
|
}
|
|
|
|
rsprintf("</body></html>\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void show_uploader_json(LOGBOOK *lbs) {
|
|
char charset[256];
|
|
char filename[256], thumbname[256], attchname[256], subdir[256];
|
|
int i, j, attch_count;
|
|
|
|
// maximum number of files that can be uploaded this way (drag and drop into the editor)
|
|
const long MAX_FILE_COUNT = 100;
|
|
|
|
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");
|
|
rsprintf("Pragma: no-cache\r\n");
|
|
rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n");
|
|
if (keep_alive) {
|
|
rsprintf("Connection: Keep-Alive\r\n");
|
|
rsprintf("Keep-Alive: timeout=60, max=10\r\n");
|
|
}
|
|
if (!getcfg("global", "charset", charset, sizeof(charset)))
|
|
strcpy(charset, DEFAULT_HTTP_CHARSET);
|
|
rsprintf("Content-Type: application/json;charset=%s\r\n\r\n", charset);
|
|
|
|
if (isparam("drop-count"))
|
|
attch_count = strtol(getparam("drop-count"), NULL, 10);
|
|
else
|
|
attch_count = 0;
|
|
|
|
// limit the number of files that can be uploaded
|
|
if (attch_count > MAX_FILE_COUNT) {
|
|
attch_count = MAX_FILE_COUNT;
|
|
}
|
|
|
|
rsprintf("{\r\n");
|
|
rsprintf(" \"attachments\" : [\r\n");
|
|
|
|
for (i = 0; i < attch_count; i++) {
|
|
sprintf(attchname, "attachment%d", i);
|
|
if (!isparam(attchname))
|
|
continue;
|
|
|
|
rsprintf(" {\r\n");
|
|
rsprintf(" \"fullName\": \"%s\",\r\n", getparam(attchname));
|
|
|
|
strlcpy(filename, lbs->data_dir, sizeof(filename));
|
|
generate_subdir_name(getparam(attchname), subdir, sizeof(subdir));
|
|
strlcat(filename, subdir, sizeof(filename));
|
|
strlcat(filename, getparam(attchname), sizeof(filename));
|
|
|
|
|
|
if (create_thumbnail(lbs, filename)) {
|
|
get_thumb_name(filename, thumbname, sizeof(thumbname), 0);
|
|
if (strrchr(thumbname, '/'))
|
|
rsprintf(" \"thumbName\": \"%s\",\r\n", strrchr(thumbname, '/') + 1);
|
|
else
|
|
rsprintf(" \"thumbName\": \"%s\",\r\n", thumbname);
|
|
}
|
|
|
|
rsprintf(" \"contentType\": ");
|
|
|
|
for (j = 0; filetype[j].ext[0]; j++)
|
|
if (chkext(filename, filetype[j].ext))
|
|
break;
|
|
|
|
if (filetype[j].ext[0])
|
|
rsprintf("\"%s\"\r\n", filetype[j].type);
|
|
else if (is_ascii(filename))
|
|
rsprintf("\"%s\"\r\n", "text/plain");
|
|
else
|
|
rsprintf("\"%s\"\r\n", "application/octet-stream\r\n");
|
|
|
|
if (i == attch_count - 1)
|
|
rsprintf(" }\r\n");
|
|
else
|
|
rsprintf(" },\r\n");
|
|
}
|
|
|
|
rsprintf(" ]\r\n");
|
|
rsprintf("}\r\n");
|
|
|
|
return;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void interprete(char *lbook, const char *path)
|
|
/********************************************************************
|
|
Routine: interprete
|
|
|
|
Purpose: Interprete parametersand generate HTML output.
|
|
|
|
Input:
|
|
char *path Message path
|
|
|
|
<implicit>
|
|
_param/_value array accessible via getparam()
|
|
|
|
\********************************************************************/
|
|
{
|
|
int status, i, j, n, message_id, inactive;
|
|
const char *s;
|
|
char list[1000], section[256], str[NAME_LENGTH+100], str1[NAME_LENGTH], str2[NAME_LENGTH],
|
|
edit_id[80], file_name[256], command[256], enc_path[256], dec_path[256], uname[80],
|
|
full_name[256], user_email[256], logbook[256], logbook_enc[256], *experiment,
|
|
group[256], css[256], *pfile, attachment[MAX_PATH_LENGTH], str3[NAME_LENGTH],
|
|
thumb_name[256], sid[32], error_str[256], subdir[256];
|
|
LOGBOOK *lbs;
|
|
FILE *f;
|
|
|
|
/* encode path for further usage */
|
|
strcpy(dec_path, path);
|
|
url_decode(dec_path);
|
|
strcpy(enc_path, dec_path);
|
|
url_encode(enc_path, sizeof(enc_path));
|
|
strencode2(command, isparam("cmd") ? getparam("cmd") : "", sizeof(command));
|
|
strencode2(group, isparam("group") ? getparam("group") : "", sizeof(group));
|
|
experiment = getparam("exp");
|
|
if (getcfg(lbook, "Logging Level", str, sizeof(str)))
|
|
_logging_level = atoi(str);
|
|
else
|
|
_logging_level = 2;
|
|
set_condition("");
|
|
message_id = atoi(dec_path);
|
|
|
|
/* evaluate "jcmd" */
|
|
if (isparam("jcmd") && *getparam("jcmd"))
|
|
strlcpy(command, getparam("jcmd"), sizeof(command));
|
|
|
|
/* check for localization command */
|
|
if (stricmp(command, "loc") == 0) {
|
|
show_http_header(NULL, FALSE, NULL, 200);
|
|
if (isparam("value") && *getparam("value")) {
|
|
strencode2(str, getparam("value"), sizeof(str));
|
|
rsputs(loc(str));
|
|
}
|
|
|
|
/* dummy strings for JS-only translations */
|
|
s = loc("Drop attachments here...");
|
|
s = loc("Insert Timestamp");
|
|
if (s)
|
|
s = NULL; // avoid compiler warning
|
|
return;
|
|
}
|
|
|
|
/* if experiment given, use it as logbook (for elog!) */
|
|
if (experiment && experiment[0]) {
|
|
strcpy(logbook_enc, experiment);
|
|
strcpy(logbook, experiment);
|
|
url_decode(logbook);
|
|
/* check if logbook exists */
|
|
for (i = 0;; i++) {
|
|
if (!enumgrp(i, str))
|
|
break;
|
|
if (strieq(logbook, str))
|
|
break;
|
|
}
|
|
if (!strieq(logbook, str)) {
|
|
sprintf(str, "Error: logbook \"%s\" not defined in %s", logbook_enc, CFGFILE);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
strcpy(logbook_enc, lbook);
|
|
strcpy(logbook, lbook);
|
|
url_decode(logbook);
|
|
}
|
|
|
|
/* check for top group */
|
|
setcfg_topgroup("");
|
|
|
|
sprintf(str, "Top group %s", logbook);
|
|
if (getcfg("global", str, list, sizeof(list))) {
|
|
setcfg_topgroup(logbook);
|
|
logbook[0] = 0;
|
|
}
|
|
|
|
/* check if new logbook */
|
|
for (i = j = 0;; i++) {
|
|
if (!enumgrp(i, str))
|
|
break;
|
|
|
|
if (is_logbook(str)) {
|
|
/* redo index if logbooks in cfg file do not match lb_list */
|
|
if (!strieq(str, lb_list[j++].name)) {
|
|
el_index_logbooks();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for deleted logbook */
|
|
if (lb_list[j].name[0] != 0)
|
|
el_index_logbooks();
|
|
|
|
/*---- direct commands (registration etc) ----*/
|
|
|
|
if (!logbook[0]) {
|
|
|
|
/* check for self register */
|
|
if (getcfg(group, "Self register", str, sizeof(str)) && atoi(str) > 0) {
|
|
if (!do_self_register(NULL, getparam("cmd")))
|
|
return;
|
|
}
|
|
|
|
/* check for activate */
|
|
if (strieq(command, loc("Activate")) && isparam("new_user_name") && isparam("code")) {
|
|
|
|
if (!activate_user(NULL, getparam("new_user_name"), atoi(getparam("code"))))
|
|
return;
|
|
|
|
setparam("cfg_user", getparam("new_user_name"));
|
|
get_user_line(NULL, getparam("new_user_name"), NULL, full_name, user_email, NULL, NULL, NULL);
|
|
sprintf(str, "%s <b><%s></b>", full_name, user_email);
|
|
sprintf(str2, loc("Activation notice has been sent to %s"), str);
|
|
setparam("notice", str2);
|
|
|
|
show_config_page(NULL);
|
|
return;
|
|
}
|
|
|
|
/* check for save after activate */
|
|
if (strieq(command, loc("Save"))) {
|
|
if (isparam("config")) {
|
|
/* change existing user */
|
|
if (!isparam("config") || !save_user_config(NULL, getparam("config"), FALSE))
|
|
return;
|
|
}
|
|
|
|
redirect(NULL, ".");
|
|
return;
|
|
}
|
|
|
|
/* check for password recovery */
|
|
if (isparam("cmd") || isparam("newpwd")) {
|
|
if (isparam("newpwd") || strieq(command, loc("Change password"))) {
|
|
/* if logged in via SID, set user name */
|
|
if (sid_check(getparam("sid"), uname))
|
|
setparam("unm", uname);
|
|
|
|
show_change_pwd_page(NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* check for new login */
|
|
if (isparam("uname") && isparam("upassword")) {
|
|
/* log logins */
|
|
strlcpy(uname, getparam("uname"), sizeof(uname));
|
|
sprintf(str, "LOGIN user \"%s\" (attempt) for logbook selection page", uname);
|
|
write_logfile(NULL, str);
|
|
if (isparam("redir"))
|
|
strlcpy(str, getparam("redir"), sizeof(str));
|
|
else
|
|
strlcpy(str, isparam("cmdline") ? getparam("cmdline") : "", sizeof(str));
|
|
|
|
/* authorize user via password file or site authentication */
|
|
if (!auth_verify_password(NULL, uname, getparam("upassword"), error_str, sizeof(error_str))) {
|
|
if (error_str[0])
|
|
show_error(error_str);
|
|
else
|
|
show_login_page(NULL, str, 1);
|
|
return;
|
|
}
|
|
|
|
/* check if user in password file */
|
|
if (get_user_line(NULL, uname, NULL, full_name, NULL, NULL, NULL, NULL) == 2) {
|
|
/* if self registering not allowed, go back to login screen */
|
|
if (!getcfg(group, "Self register", str, sizeof(str)) || atoi(str) == 0) {
|
|
show_login_page(NULL, str, 1);
|
|
return;
|
|
}
|
|
#ifdef HAVE_PAM
|
|
/* show new user page if username is not in password file */
|
|
getcfg(NULL, "Authentication", str, sizeof(str));
|
|
if (stristr(str, "PAM")) {
|
|
show_new_user_page(NULL, uname);
|
|
return;
|
|
}
|
|
#endif /* HAVE_PAM */
|
|
}
|
|
|
|
/* put encoded password into password file */
|
|
set_user_password(NULL, uname, getparam("upassword"));
|
|
|
|
sprintf(str, "LOGIN user \"%s\" (success)", uname);
|
|
write_logfile(NULL, str);
|
|
|
|
/* get a new session ID */
|
|
sid_new(NULL, uname, (char *) inet_ntoa(rem_addr), sid);
|
|
|
|
/* set SID cookie */
|
|
set_sid_cookie(NULL, sid, full_name);
|
|
return;
|
|
}
|
|
|
|
/* check for global selection page if no logbook given */
|
|
if (!logbook[0] && getcfg("global", "Selection page", str, sizeof(str))) {
|
|
/* check for URL */
|
|
if (strstr(str, "http://") || strstr(str, "https://")) {
|
|
redirect(NULL, str);
|
|
return;
|
|
}
|
|
|
|
/* check if file starts with an absolute directory */
|
|
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));
|
|
}
|
|
send_file_direct(file_name);
|
|
return;
|
|
}
|
|
|
|
/* check for global synchronization */
|
|
if (strieq(command, "Synchronize")) {
|
|
synchronize(NULL, SYNC_HTML);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* count logbooks */
|
|
for (n = 0; lb_list[n].name[0]; n++);
|
|
|
|
/* if no logbook given, display logbook selection page */
|
|
if (!logbook[0] && !path[0]) {
|
|
if (n > 1) {
|
|
|
|
/* check for forgot password page */
|
|
if (strieq(command, loc("Forgot"))) {
|
|
show_forgot_pwd_page(NULL);
|
|
return;
|
|
}
|
|
|
|
show_selection_page();
|
|
return;
|
|
}
|
|
|
|
strcpy(logbook, lb_list[0].name);
|
|
strcpy(logbook_enc, logbook);
|
|
url_encode(logbook_enc, sizeof(logbook_enc));
|
|
}
|
|
|
|
/* get logbook from list */
|
|
for (i = 0; lb_list[i].name[0]; i++)
|
|
if (strieq(logbook, lb_list[i].name))
|
|
break;
|
|
lbs = &lb_list[i];
|
|
|
|
/* set top level group for logbook */
|
|
if (lbs->top_group[0])
|
|
setcfg_topgroup(lbs->top_group);
|
|
|
|
/* get theme for logbook */
|
|
if (getcfg(logbook, "Theme", str, sizeof(str)))
|
|
strlcpy(theme_name, str, sizeof(theme_name));
|
|
else
|
|
strlcpy(theme_name, "default", sizeof(theme_name));
|
|
lbs = lb_list + i;
|
|
lbs->n_attr = scan_attributes(lbs->name);
|
|
|
|
/* check for error during attribute scan */
|
|
if (lbs->n_attr < 0)
|
|
return;
|
|
|
|
/* evaluate AJAX xommand */
|
|
if (isparam("acmd") && *getparam("acmd")) {
|
|
if (strieq(getparam("acmd"), "Upload"))
|
|
show_uploader_json(lbs);
|
|
return;
|
|
}
|
|
|
|
/* if we outsource the authentication to Webserver and have no sid, just set a new sid */
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (stristr(str, "Webserver")) {
|
|
if (http_user[0]) {
|
|
char user[256];
|
|
bool flag = sid_check(getparam("sid"), user);
|
|
if (flag && strcmp(http_user , user) != 0) { /* user changed */
|
|
sid_remove(getparam("sid"));
|
|
flag=FALSE;
|
|
}
|
|
if (!flag) { /* if we don't have a sid yet, set it */
|
|
/* get a new session ID */
|
|
sid_new(lbs, http_user, (char *) inet_ntoa(rem_addr), sid);
|
|
/* set SID cookie */
|
|
set_sid_cookie(lbs, sid, http_user);
|
|
}
|
|
} else {
|
|
sprintf(str, "Error: Misconfigured webserver, did not get X-Forwarded-User from it.");
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* check for new login */
|
|
if (isparam("uname") && isparam("upassword")) {
|
|
/* log logins */
|
|
strlcpy(uname, getparam("uname"), sizeof(uname));
|
|
sprintf(str, "LOGIN user \"%s\" (attempt)", uname);
|
|
write_logfile(lbs, str);
|
|
if (isparam("redir"))
|
|
strlcpy(str, getparam("redir"), sizeof(str));
|
|
else
|
|
strlcpy(str, isparam("cmdline") ? getparam("cmdline") : "", sizeof(str));
|
|
|
|
/* authorize user via password file or site authentication */
|
|
if (!auth_verify_password(lbs, uname, getparam("upassword"), error_str, sizeof(error_str))) {
|
|
if (error_str[0])
|
|
show_error(error_str);
|
|
else
|
|
show_login_page(lbs, str, 1);
|
|
return;
|
|
}
|
|
|
|
/* check if user in password file */
|
|
if (get_user_line(lbs, uname, NULL, full_name, NULL, NULL, NULL, &inactive) == 2) {
|
|
/* if self registering not allowed, go back to login screen */
|
|
if (!getcfg(lbs->name, "Self register", str, sizeof(str)) || atoi(str) == 0) {
|
|
show_login_page(lbs, str, 1);
|
|
return;
|
|
}
|
|
#ifdef HAVE_PAM
|
|
/* show new user page if username is not in password file */
|
|
getcfg(lbs->name, "Authentication", str, sizeof(str));
|
|
if (stristr(str, "PAM")) {
|
|
show_new_user_page(lbs, uname);
|
|
return;
|
|
}
|
|
#endif /* HAVE_PAM */
|
|
}
|
|
|
|
/* show error for inactive account */
|
|
if (inactive) {
|
|
show_error("This account is currently deactivated");
|
|
return;
|
|
}
|
|
|
|
/* check if user has access to logbook */
|
|
if (!check_login_user(lbs, getparam("uname"))) {
|
|
show_error("User has no access to this logbook");
|
|
return;
|
|
}
|
|
|
|
/* put encoded password into password file */
|
|
set_user_password(lbs, uname, getparam("upassword"));
|
|
|
|
sprintf(str, "LOGIN user \"%s\" (success)", uname);
|
|
write_logfile(lbs, str);
|
|
|
|
/* get a new session ID */
|
|
sid_new(lbs, uname, (char *) inet_ntoa(rem_addr), sid);
|
|
|
|
/* set SID cookie */
|
|
set_sid_cookie(lbs, sid, full_name);
|
|
return;
|
|
}
|
|
|
|
/* deliver icons without password */
|
|
if (chkext(path, ".gif") || chkext(path, ".jpg") || chkext(path, ".png") || chkext(path, ".ico")
|
|
|| chkext(path, ".htm") || chkext(path, ".css")) {
|
|
/* check if file in resource directory */
|
|
strlcpy(str, resource_dir, sizeof(str));
|
|
strlcat(str, path, sizeof(str));
|
|
if (exist_file(str)) {
|
|
send_file_direct(str);
|
|
return;
|
|
} else {
|
|
/* else search file in themes directory */
|
|
strlcpy(str, resource_dir, sizeof(str));
|
|
strlcat(str, "themes", sizeof(str));
|
|
strlcat(str, DIR_SEPARATOR_STR, sizeof(str));
|
|
strlcat(str, theme_name, sizeof(str));
|
|
strlcat(str, DIR_SEPARATOR_STR, sizeof(str));
|
|
strlcat(str, path, sizeof(str));
|
|
if (exist_file(str)) {
|
|
send_file_direct(str);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for valid logbook */
|
|
if (!logbook[0]) {
|
|
strencode2(str2, path, sizeof(str2));
|
|
sprintf(str, "%s: <b>%s</b>", loc("Invalid URL"), str2);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
/* check for self activation */
|
|
if (strieq(command, loc("Activate")) && isparam("unm") && isparam("code") && !isparam("new_user_name")) {
|
|
|
|
strlcpy(uname, getparam("unm"), sizeof(uname));
|
|
|
|
if (!activate_user(lbs, uname, atoi(getparam("code"))))
|
|
return;
|
|
|
|
sprintf(str, "ACTIVATE user \"%s\" (success)", uname);
|
|
write_logfile(lbs, str);
|
|
|
|
/* get a new session ID */
|
|
sid_new(lbs, uname, (char *) inet_ntoa(rem_addr), sid);
|
|
|
|
/* redirect to config page */
|
|
sprintf(str, "?cmd=%s¬ice=%s. %s", loc("Config"), loc("Your account has been activated"),
|
|
loc("Please subscribe to logbooks if you want to receive automatic email notifications"));
|
|
setparam("redir", str);
|
|
|
|
/* set SID cookie */
|
|
set_sid_cookie(lbs, sid, uname);
|
|
return;
|
|
}
|
|
|
|
#ifdef HAVE_PAM
|
|
/* save new user */
|
|
if (strieq(command, loc("Save")) && isparam("new_user_name") && !isparam("config")) {
|
|
save_user_config(NULL, getparam("new_user_name"), 1);
|
|
redirect(lbs, "");
|
|
check_login(lbs, NULL);
|
|
return;
|
|
}
|
|
#endif /* USE_PAM */
|
|
|
|
/* if password file given, check session ID */
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str))) {
|
|
/* get current 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));
|
|
|
|
/* check if guest access */
|
|
if (getcfg(lbs->name, "Guest menu commands", str, sizeof(str))) {
|
|
/* if logged in via SID, set user name */
|
|
if (sid_check(getparam("sid"), uname))
|
|
setparam("unm", uname);
|
|
}
|
|
|
|
/* set access time for user */
|
|
if (uname[0] && !isparam("new_user_name")) {
|
|
if (!set_user_login_time(lbs, uname)) {
|
|
show_error(loc("File system full, ELOG cannot continue to work"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!(getcfg(lbs->name, "Guest menu commands", str, sizeof(str)) && !isparam("fail"))) {
|
|
if (strcmp(path, css) != 0) {
|
|
/* if no guest menu commands but self register, evaluate new user commands */
|
|
if (getcfg(lbs->name, "Self register", str, sizeof(str)) && atoi(str) > 0) {
|
|
if (!do_self_register(lbs, command))
|
|
return;
|
|
}
|
|
|
|
/* check for correct session ID */
|
|
if (!check_login(lbs, getparam("sid")))
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strieq(command, loc("Login"))) {
|
|
check_login(lbs, "");
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("New")) || strieq(command, loc("Edit")) || strieq(command, loc("Reply"))
|
|
|| strieq(command, loc("Duplicate")) || strieq(command, loc("Delete")) || strieq(command,
|
|
loc("Upload"))
|
|
|| strieq(command, loc("Submit")) || strieq(command, loc("Preview"))) {
|
|
sprintf(str, "%s?cmd=%s", path, command);
|
|
}
|
|
|
|
if (strieq(command, loc("Delete")) || strieq(command, loc("Config")) || strieq(command, loc("Copy to"))
|
|
|| strieq(command, loc("Move to"))) {
|
|
sprintf(str, "%s?cmd=%s", path, command);
|
|
}
|
|
|
|
/* check for "Back" button */
|
|
if (strieq(command, loc("Back"))) {
|
|
if (isparam("edit_id")) {
|
|
|
|
/* unlock message */
|
|
el_lock_message(lbs, atoi(getparam("edit_id")), NULL, FALSE);
|
|
|
|
/* remove draft */
|
|
el_draft_message(lbs, atoi(getparam("edit_id")), NULL, FALSE);
|
|
|
|
/* redirect to message */
|
|
strlcpy(edit_id, getparam("edit_id"), sizeof(edit_id));
|
|
sprintf(str, "../%s/%s", logbook_enc, edit_id);
|
|
} else
|
|
sprintf(str, "../%s/", logbook_enc);
|
|
|
|
if (getcfg(lbs->name, "Back to main", str, sizeof(str)) && atoi(str) == 1)
|
|
strcpy(str, "../");
|
|
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/* check for "Detelte" button */
|
|
if (strieq(command, "XDelete")) {
|
|
if (getparam("edit_id")) {
|
|
status = el_delete_message(lbs, atoi(getparam("edit_id")), TRUE, NULL, TRUE, TRUE);
|
|
if (status != EL_SUCCESS) {
|
|
sprintf(str, "%s = %d", loc("Error deleting message: status"), status);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
redirect(lbs, "");
|
|
return;
|
|
}
|
|
|
|
/* check for "Unlock" command */
|
|
if (strieq(command, "Unlock")) {
|
|
if (isparam("edit_id")) {
|
|
|
|
/* unlock message and remove draft */
|
|
el_lock_message(lbs, atoi(getparam("edit_id")), NULL, FALSE);
|
|
|
|
/* redirect to message */
|
|
strlcpy(edit_id, getparam("edit_id"), sizeof(edit_id));
|
|
sprintf(str, "../%s/%s", logbook_enc, edit_id);
|
|
} else
|
|
sprintf(str, "../%s/", logbook_enc);
|
|
|
|
if (getcfg(lbs->name, "Back to main", str, sizeof(str)) && atoi(str) == 1)
|
|
strcpy(str, "../");
|
|
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/* check for "List" button */
|
|
if (strieq(command, loc("List"))) {
|
|
|
|
if (getcfg(lbs->name, "Back to main", str, sizeof(str)) && atoi(str) == 1) {
|
|
redirect(lbs, "../");
|
|
return;
|
|
}
|
|
|
|
show_elog_list(lbs, 0, 0, 0, TRUE, NULL);
|
|
return;
|
|
}
|
|
|
|
/* check for "Cancel" button */
|
|
if (strieq(command, loc("Cancel"))) {
|
|
sprintf(str, "../%s/%s", logbook_enc, path);
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/* check for "Last n*2 Entries" */
|
|
strlcpy(str, isparam("last") ? getparam("last") : "", sizeof(str));
|
|
if (strchr(str, ' ')) {
|
|
i = atoi(strchr(str, ' '));
|
|
sprintf(str, "last%d", i);
|
|
if (isparam("mode")) {
|
|
sprintf(str + strlen(str), "?mode=");
|
|
strlcat(str, getparam("mode"), sizeof(str));
|
|
}
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
strlcpy(str, isparam("past") ? getparam("past") : "", sizeof(str));
|
|
if (strchr(str, ' ')) {
|
|
i = atoi(strchr(str, ' '));
|
|
sprintf(str, "past%d", i);
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/* check for pastxx */
|
|
if (strncmp(path, "past", 4) == 0 && (isdigit(path[4]) || isdigit(path[5]))
|
|
&& isparam("cmd") == 0) {
|
|
show_elog_list(lbs, atoi(path + 4), 0, 0, FALSE, NULL);
|
|
return;
|
|
}
|
|
|
|
if (strncmp(path, "last", 4) == 0 && !chkext(path, ".png") && (!isparam("cmd") || strieq(getparam("cmd"),
|
|
loc("Select")))
|
|
&& !isparam("newpwd") && atoi(path + 4) > 0) {
|
|
show_elog_list(lbs, 0, atoi(path + 4), 0, FALSE, NULL);
|
|
return;
|
|
}
|
|
|
|
if (strncmp(path, "page", 4) == 0 && isparam("cmd") == 0) {
|
|
if (!path[4])
|
|
show_elog_list(lbs, 0, 0, -1, FALSE, NULL);
|
|
else
|
|
show_elog_list(lbs, 0, 0, atoi(path + 4), FALSE, NULL);
|
|
return;
|
|
}
|
|
|
|
/* check for calender */
|
|
if (strieq(dec_path, "cal.html")) {
|
|
show_calendar(lbs);
|
|
return;
|
|
}
|
|
|
|
/* check for rss-feed */
|
|
if (strieq(dec_path, "elog.rdf")) {
|
|
show_rss_feed(lbs);
|
|
return;
|
|
}
|
|
|
|
/* check for upload window */
|
|
if (strieq(dec_path, "upload.html")) {
|
|
show_uploader(lbs);
|
|
return;
|
|
}
|
|
|
|
/* check for finished JavaScript upload */
|
|
if (isparam("jcmd") && isparam("jcmd") && strieq(getparam("jcmd"), "JUpload")) {
|
|
show_uploader_finished(lbs);
|
|
return;
|
|
}
|
|
|
|
/*---- check if file requested -----------------------------------*/
|
|
|
|
/* skip elog message id in front of possible attachment */
|
|
pfile = dec_path;
|
|
if (strchr(pfile, '/') && pfile[13] != '/' && isdigit(pfile[0]))
|
|
pfile = strchr(pfile, '/') + 1;
|
|
|
|
if ((strlen(pfile) > 13 && pfile[6] == '_' && pfile[13] == '_') || (strlen(pfile) > 13 && pfile[6] == '_'
|
|
&& pfile[13] == '/')
|
|
|| chkext(pfile, ".gif") || chkext(pfile, ".ico") || chkext(pfile, ".jpg")
|
|
|| chkext(pfile, ".jpeg") || chkext(pfile, ".png") || chkext(pfile, ".css") || chkext(pfile, ".js")
|
|
|| chkext(pfile, ".html")) {
|
|
if ((strlen(pfile) > 13 && pfile[6] == '_' && pfile[13] == '_') || (strlen(pfile) > 13 && pfile[6]
|
|
== '_' &&
|
|
pfile[13] == '/')) {
|
|
if (pfile[13] == '/')
|
|
pfile[13] = '_';
|
|
/* file from data directory requested */
|
|
strlcpy(file_name, lbs->data_dir, sizeof(file_name));
|
|
generate_subdir_name(pfile, subdir, sizeof(subdir));
|
|
strlcat(file_name, subdir, sizeof(file_name));
|
|
strlcat(file_name, pfile, sizeof(file_name));
|
|
} else {
|
|
/* file from theme directory requested */
|
|
strlcpy(file_name, resource_dir, sizeof(file_name));
|
|
if (file_name[0] && file_name[strlen(file_name)
|
|
- 1] != DIR_SEPARATOR)
|
|
strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
|
|
strlcat(file_name, "themes", sizeof(file_name));
|
|
strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
|
|
if (theme_name[0]) {
|
|
strlcat(file_name, theme_name, sizeof(file_name));
|
|
strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
|
|
}
|
|
strlcat(file_name, pfile, sizeof(file_name));
|
|
}
|
|
|
|
if (isparam("thumb")) {
|
|
get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0);
|
|
if (thumb_name[0])
|
|
send_file_direct(thumb_name);
|
|
else
|
|
send_file_direct(file_name);
|
|
} else
|
|
send_file_direct(file_name);
|
|
return;
|
|
}
|
|
|
|
/* from here on, logbook must be valid */
|
|
if (!logbook[0]) {
|
|
show_selection_page();
|
|
return;
|
|
}
|
|
|
|
/*---- check if attachment requested -----------------------------*/
|
|
|
|
if (strchr(dec_path, '/')) {
|
|
message_id = atoi(dec_path);
|
|
n = atoi(strchr(dec_path, '/') + 1) - 1;
|
|
status = el_retrieve_attachment(lbs, message_id, n, attachment);
|
|
if (status != EL_SUCCESS || n >= MAX_ATTACHMENTS) {
|
|
sprintf(str, "Attachment #%d of entry #%d not found", n + 1, message_id);
|
|
show_error(str);
|
|
} else {
|
|
if (isparam("thumb"))
|
|
strlcat(attachment, "?thumb=1", sizeof(attachment));
|
|
redirect(lbs, attachment);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* check for new syntax in config file */
|
|
if (getcfg(lbs->name, "Types", str, sizeof(str))) {
|
|
show_upgrade_page(lbs);
|
|
return;
|
|
}
|
|
|
|
/* correct for image buttons */
|
|
if (isparam("cmd_first.x"))
|
|
strcpy(command, loc("First"));
|
|
if (isparam("cmd_previous.x"))
|
|
strcpy(command, loc("Previous"));
|
|
if (isparam("cmd_next.x"))
|
|
strcpy(command, loc("Next"));
|
|
if (isparam("cmd_last.x"))
|
|
strcpy(command, loc("Last"));
|
|
/* check if command allowed for current user */
|
|
if (command[0] && !is_user_allowed(lbs, command)) {
|
|
if (isparam("uname"))
|
|
get_full_name(lbs, getparam("uname"), full_name);
|
|
else
|
|
full_name[0] = 0;
|
|
|
|
strencode2(str2, command, sizeof(str2));
|
|
strencode2(str3, full_name, sizeof(str3));
|
|
sprintf(str, loc("Error: Command \"<b>%s</b>\" is not allowed for user \"<b>%s</b>\""), str2, str3);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
/* check if command in menu list */
|
|
if (!is_command_allowed(lbs, command, message_id)) {
|
|
/* redirect to login page for new command */
|
|
if (strieq(command, loc("New")) && !isparam("unm")) {
|
|
show_login_page(lbs, _cmdline, 0);
|
|
return;
|
|
}
|
|
|
|
strencode2(str2, command, sizeof(str3));
|
|
sprintf(str, loc("Error: Command \"<b>%s</b>\" not allowed"), str2);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
/*---- check for various commands --------------------------------*/
|
|
|
|
if (strieq(command, loc("Help"))) {
|
|
if (getcfg(lbs->name, "Help URL", str, sizeof(str))) {
|
|
|
|
/* if URL is given, redirect */
|
|
if (strstr(str, "http://") || strstr(str, "https://")) {
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/* send file from resource directory */
|
|
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, str, sizeof(file_name));
|
|
f = fopen(file_name, "r");
|
|
if (f == NULL) {
|
|
sprintf(str, "Cannot find file \"%s\"", file_name);
|
|
show_error(str);
|
|
} else {
|
|
fclose(f);
|
|
send_file_direct(file_name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* send local help file */
|
|
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, "eloghelp_", sizeof(file_name));
|
|
if (getcfg("global", "Language", str, sizeof(str))) {
|
|
for (i = 0; i < (int) strlen(str); i++)
|
|
str[i] = my_tolower(str[i]);
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
} else
|
|
strlcat(file_name, "english", sizeof(file_name));
|
|
strlcat(file_name, ".html", sizeof(file_name));
|
|
f = fopen(file_name, "r");
|
|
if (f == NULL)
|
|
redirect(lbs, "https://elog.psi.ch/elog/eloghelp_english.html");
|
|
else {
|
|
fclose(f);
|
|
send_file_direct(file_name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("HelpELCode"))) {
|
|
/* send local help file */
|
|
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, "elcode_", sizeof(file_name));
|
|
if (getcfg("global", "Language", str, sizeof(str))) {
|
|
for (i = 0; i < (int) strlen(str); i++)
|
|
str[i] = my_tolower(str[i]);
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
} else
|
|
strlcat(file_name, "english", sizeof(file_name));
|
|
strlcat(file_name, ".html", sizeof(file_name));
|
|
f = fopen(file_name, "r");
|
|
if (f == NULL)
|
|
redirect(lbs, "https://elog.psi.ch/elog/elcode_english.html");
|
|
else {
|
|
fclose(f);
|
|
send_file_direct(file_name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("New"))) {
|
|
if (check_drafts(lbs))
|
|
return;
|
|
|
|
show_edit_form(lbs, 0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE);
|
|
return;
|
|
}
|
|
|
|
/* check for deletion of attachments */
|
|
for (i = 0; i < MAX_ATTACHMENTS; i++) {
|
|
sprintf(str, "delatt%d", i);
|
|
if (isparam(str) || (isparam("smcmd") && stricmp(getparam("smcmd"), str) == 0)) {
|
|
sprintf(str, "attachment%d", i);
|
|
strlcpy(file_name, getparam(str), sizeof(file_name));
|
|
el_delete_attachment(lbs, file_name);
|
|
/* re-order attachments */
|
|
for (j = i; j < MAX_ATTACHMENTS; j++) {
|
|
sprintf(str, "attachment%d", j + 1);
|
|
if (isparam(str))
|
|
strlcpy(file_name, getparam(str), sizeof(file_name));
|
|
else
|
|
file_name[0] = 0;
|
|
sprintf(str, "attachment%d", j);
|
|
if (file_name[0])
|
|
setparam(str, file_name);
|
|
else
|
|
unsetparam(str);
|
|
}
|
|
|
|
show_edit_form(lbs, isparam("edit_id") ? atoi(getparam("edit_id")) : 0,
|
|
FALSE, TRUE, TRUE, FALSE, FALSE, FALSE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (strieq(command, loc("Upload")) || strieq(command, "Upload")) {
|
|
show_edit_form(lbs, isparam("edit_id") ? atoi(getparam("edit_id")) : 0,
|
|
FALSE, TRUE, TRUE, FALSE, FALSE, FALSE);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Edit"))) {
|
|
if (message_id) {
|
|
show_edit_form(lbs, message_id, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE);
|
|
return;
|
|
} else if (isparam("nsel")) {
|
|
show_edit_form(lbs, 0, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (strieq(command, loc("Reply"))) {
|
|
show_edit_form(lbs, message_id, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Update"))) {
|
|
show_edit_form(lbs, isparam("edit_id") ? atoi(getparam("edit_id")) : 0,
|
|
FALSE, TRUE, FALSE, TRUE, FALSE, FALSE);
|
|
return;
|
|
}
|
|
|
|
if ((strieq(command, loc("Save")) || strieq(command, "Save")) && !isparam("cfgpage") &&
|
|
!isparam("new_user_name")) {
|
|
/* save draft message */
|
|
if (isparam("unm"))
|
|
strlcpy(str, getparam("unm"), sizeof(str));
|
|
else
|
|
strlcpy(str, loc("user"), sizeof(str));
|
|
|
|
setparam("draft", str);
|
|
submit_elog(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Duplicate"))) {
|
|
if (message_id) {
|
|
show_edit_form(lbs, message_id, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (strieq(command, loc("Preview"))) {
|
|
show_edit_form(lbs, isparam("edit_id") ? atoi(getparam("edit_id")) : 0,
|
|
FALSE, TRUE, FALSE, TRUE, FALSE, TRUE);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Submit")) || strieq(command, "Submit")) {
|
|
|
|
if (isparam("mirror_id"))
|
|
submit_elog_mirror(lbs);
|
|
else
|
|
submit_elog(lbs);
|
|
|
|
/* elog command line utility wants to remove sid after submission */
|
|
if (isparam("sidclose"))
|
|
sid_remove(getparam("sid"));
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Find"))) {
|
|
/* stip message id */
|
|
if (dec_path[0]) {
|
|
sprintf(str, "../%s/?cmd=%s", lbs->name_enc, loc("Find"));
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
show_find_form(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Search"))) {
|
|
if (dec_path[0] && atoi(dec_path) == 0 && strchr(dec_path, '/') != NULL) {
|
|
strencode2(str2, dec_path, sizeof(str2));
|
|
sprintf(str, "%s: <b>%s</b>", loc("Invalid URL"), str2);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
show_elog_list(lbs, 0, 0, 0, TRUE, NULL);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Last day"))) {
|
|
redirect(lbs, "past1");
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Last 10"))) {
|
|
redirect(lbs, "last10");
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Copy to"))) {
|
|
copy_to(lbs, message_id, isparam("destc") ? getparam("destc") : "", 0, 0);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Move to"))) {
|
|
copy_to(lbs, message_id, isparam("destm") ? getparam("destm") : "", 1, 0);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Admin")) || strieq(command, loc("Change config file"))) {
|
|
show_admin_page(lbs, NULL);
|
|
return;
|
|
}
|
|
|
|
sprintf(str, loc("Change %s"), "[global]");
|
|
if (strieq(command, str)) {
|
|
show_admin_page(lbs, "global");
|
|
return;
|
|
}
|
|
|
|
sprintf(str2, "[global %s]", lbs->top_group);
|
|
sprintf(str, loc("Change %s"), str2);
|
|
if (strieq(command, str)) {
|
|
show_admin_page(lbs, lbs->top_group);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Delete this logbook"))) {
|
|
show_logbook_delete(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Rename this logbook"))) {
|
|
show_logbook_rename(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Create new logbook"))) {
|
|
if (isparam("tmp") && strieq(getparam("tmp"), "Cancel")) {
|
|
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str)))
|
|
sprintf(str, "?cmd=%s", loc("Change config file"));
|
|
else
|
|
sprintf(str, "?cmd=%s", loc("Config"));
|
|
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
show_logbook_new(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, "GetPwdFile")) {
|
|
char allow[256];
|
|
allow[0] = 0;
|
|
getcfg("global", "Allow clone", allow, sizeof(allow));
|
|
if (atoi(allow) == 1) {
|
|
if (get_password_file(lbs, file_name, sizeof(file_name)))
|
|
send_file_direct(file_name);
|
|
} else {
|
|
show_http_header(NULL, FALSE, NULL, 200);
|
|
rsputs(loc("Cloning not allowed. Set \"Allow clone = 1\" to enable cloning."));
|
|
rsputs("\r\n");
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Change password")) || (isparam("newpwd") && !strieq(command, loc("Cancel"))
|
|
&& !strieq(command, loc("Save")))) {
|
|
show_change_pwd_page(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Save")) && (isparam("cfgpage") || isparam("new_user_name"))) {
|
|
if (isparam("config") && isparam("new_user_name")) {
|
|
if (!strieq(getparam("config"), getparam("new_user_name"))) {
|
|
if (get_user_line(lbs, getparam("new_user_name"), NULL, NULL, NULL, NULL, NULL, NULL) == 1) {
|
|
sprintf(str, "%s \"%s\" %s", loc("Login name"), getparam("new_user_name"),
|
|
loc("exists already"));
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* change existing user */
|
|
if (!save_user_config(lbs, isparam("config") ? getparam("config") : "", FALSE))
|
|
return;
|
|
|
|
/* go back to logbook */
|
|
redirect(lbs, "");
|
|
return;
|
|
|
|
} else if (isparam("new_user_name")) {
|
|
/* new user */
|
|
if (!save_user_config(lbs, getparam("new_user_name"), TRUE))
|
|
return;
|
|
} else {
|
|
|
|
if (isparam("global")) {
|
|
if (strieq(getparam("global"), "global"))
|
|
strcpy(section, "global");
|
|
else {
|
|
sprintf(section, "global ");
|
|
strlcat(section, getparam("global"), sizeof(section));
|
|
}
|
|
} else
|
|
strlcpy(section, lbs->name, sizeof(section));
|
|
|
|
if (!save_admin_config(section, _mtext, str)) { /* save cfg file */
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
if (lbs)
|
|
sprintf(str, "../%s/", lbs->name_enc);
|
|
else
|
|
sprintf(str, ".");
|
|
if (isparam("new_user_name")) {
|
|
sprintf(str + strlen(str), "?cmd=%s&cfg_user=", loc("Config"));
|
|
strlcat(str, getparam("new_user_name"), sizeof(str));
|
|
} else if (isparam("cfg_user")) {
|
|
sprintf(str + strlen(str), "?cmd=%s&cfg_user=", loc("Config"));
|
|
strlcat(str, getparam("cfg_user"), sizeof(str));
|
|
} else if (getcfg(lbs->name, "password file", str2, sizeof(str2)))
|
|
sprintf(str + strlen(str), "?cmd=%s", loc("Config"));
|
|
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Activate")) && isparam("new_user_name") && isparam("code")) {
|
|
|
|
if (!activate_user(lbs, getparam("new_user_name"), atoi(getparam("code"))))
|
|
return;
|
|
|
|
setparam("cfg_user", getparam("new_user_name"));
|
|
get_user_line(lbs, getparam("new_user_name"), NULL, full_name, user_email, NULL, NULL, NULL);
|
|
sprintf(str, "%s <b><%s></b>", full_name, user_email);
|
|
sprintf(str2, loc("Activation notice has been sent to %s"), str);
|
|
setparam("notice", str2);
|
|
|
|
show_config_page(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Remove user")) && isparam("config")) {
|
|
if (!remove_user(lbs, getparam("config")))
|
|
return;
|
|
/* if removed user is current user, do logout */
|
|
if (isparam("unm") && strieq(getparam("config"), getparam("unm"))) {
|
|
/* log activity */
|
|
write_logfile(lbs, "LOGOUT");
|
|
/* set cookies */
|
|
sid_remove(getparam("sid"));
|
|
set_sid_cookie(lbs, "", "");
|
|
}
|
|
|
|
/* continue configuration as administrator */
|
|
unsetparam("cfg_user");
|
|
show_config_page(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("New user"))) {
|
|
show_new_user_page(lbs, NULL);
|
|
return;
|
|
}
|
|
|
|
/* check for forgot password page */
|
|
if (strieq(command, loc("Forgot"))) {
|
|
show_forgot_pwd_page(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Config"))) {
|
|
if (!getcfg(lbs->name, "Password file", str, sizeof(str)))
|
|
show_admin_page(lbs, NULL);
|
|
else
|
|
show_config_page(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Download")) || strieq(command, "Download")) {
|
|
show_download_page(lbs, dec_path);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Import"))) {
|
|
strcpy(str, loc("CSV Import"));
|
|
url_encode(str, sizeof(str));
|
|
strlcpy(str1, "?cmd=", sizeof(str1));
|
|
strlcat(str1, str, sizeof(str1));
|
|
|
|
strcpy(str, loc("XML Import"));
|
|
url_encode(str, sizeof(str));
|
|
strlcpy(str2, "?cmd=", sizeof(str2));
|
|
strlcat(str2, str, sizeof(str2));
|
|
|
|
show_query(lbs, loc("ELOG import"), loc("Please choose format to import:"), "CSV", str1, "XML", str2);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("CSV Import"))) {
|
|
show_import_page_csv(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("XML Import"))) {
|
|
show_import_page_xml(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, "getmd5")) {
|
|
show_md5_page(lbs);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Synchronize"))) {
|
|
synchronize(lbs, SYNC_HTML);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Logout"))) {
|
|
/* log activity */
|
|
write_logfile(lbs, "LOGOUT");
|
|
if (getcfg(lbs->name, "Logout to main", str, sizeof(str)) && atoi(str) == 1) {
|
|
sprintf(str, "../");
|
|
setparam("redir", str);
|
|
} else {
|
|
if (getcfg(lbs->name, "Logout to URL", str, sizeof(str)))
|
|
setparam("redir", str);
|
|
}
|
|
set_sid_cookie(lbs, "", "");
|
|
sid_remove(getparam("sid"));
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, loc("Delete"))) {
|
|
show_elog_delete(lbs, message_id);
|
|
return;
|
|
}
|
|
|
|
if (strieq(command, "IM")) {
|
|
call_image_magick(lbs);
|
|
return;
|
|
}
|
|
|
|
/* check for welcome page */
|
|
if (!_cmdline[0] && getcfg(lbs->name, "Welcome page", str, sizeof(str)) && str[0]) {
|
|
/* check if file starts with an absolute directory */
|
|
if (str[0] == DIR_SEPARATOR || str[1] == ':')
|
|
strcpy(file_name, str);
|
|
else {
|
|
strlcpy(file_name, resource_dir, sizeof(file_name));
|
|
strlcat(file_name, str, sizeof(file_name));
|
|
}
|
|
send_file_direct(file_name);
|
|
return;
|
|
}
|
|
|
|
/* check for start page */
|
|
if (!_cmdline[0] && getcfg(lbs->name, "Start page", str, sizeof(str)) && str[0]) {
|
|
redirect(lbs, str);
|
|
return;
|
|
}
|
|
|
|
/* show page listing or display single entry */
|
|
if (dec_path[0] == 0)
|
|
show_elog_list(lbs, 0, 0, 0, TRUE, NULL);
|
|
else
|
|
show_elog_entry(lbs, dec_path, command);
|
|
return;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void decode_get(char *logbook, char *string) {
|
|
char path[256];
|
|
char *p, *pitem;
|
|
|
|
setparam("cmdline", string);
|
|
strlcpy(path, string, sizeof(path));
|
|
path[255] = 0;
|
|
if (strchr(path, '?'))
|
|
*strchr(path, '?') = 0;
|
|
setparam("path", path);
|
|
if (strchr(string, '?')) {
|
|
p = strchr(string, '?') + 1;
|
|
/* cut trailing "/" from netscape */
|
|
if (p[strlen(p) - 1] == '/')
|
|
p[strlen(p) - 1] = 0;
|
|
p = strtok(p, "&");
|
|
while (p != NULL) {
|
|
pitem = p;
|
|
p = strchr(p, '=');
|
|
if (p != NULL) {
|
|
*p++ = 0;
|
|
url_decode(pitem);
|
|
url_decode(p);
|
|
if (!setparam(pitem, p))
|
|
return;
|
|
|
|
p = strtok(NULL, "&");
|
|
}
|
|
}
|
|
}
|
|
|
|
interprete(logbook, path);
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void hexdump(void *p, int len)
|
|
{
|
|
unsigned char *buffer = (unsigned char *)p;
|
|
for (int i = 0; i < len; i++) {
|
|
if (i % 16 == 0)
|
|
printf("%p ", buffer+i);
|
|
printf("%02X ", buffer[i]);
|
|
if (i % 16 == 7)
|
|
printf(" ");
|
|
if (i % 16 == 15) {
|
|
printf(" |");
|
|
for (int j = i - 15; j <= i; j++) {
|
|
printf("%c", buffer[j] < 32 || buffer[j] > 128 ? '.' : buffer[j]);
|
|
}
|
|
printf("|\n");
|
|
}
|
|
}
|
|
if (len % 16 != 0) {
|
|
if (len % 16 < 8)
|
|
printf(" ");
|
|
for (int j = 0; j < 16 - len % 16; j++)
|
|
printf(" ");
|
|
printf(" |");
|
|
for (int j = len - (len % 16); j < len; j++) {
|
|
printf("%c", buffer[j] < 32 || buffer[j] > 128 ? '.' : buffer[j]);
|
|
}
|
|
printf("|\n");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void decode_post(char *logbook, LOGBOOK *lbs, char *string, const char *boundary, int length) {
|
|
int n_att, size, status, header_size;
|
|
char *pinit, *p;
|
|
char *pctmp, *pbody;
|
|
char *buffer, *ptmp;
|
|
char file_name[MAX_PATH_LENGTH], full_name[MAX_PATH_LENGTH], str[NAME_LENGTH+100], str2[NAME_LENGTH],
|
|
line[NAME_LENGTH], item[NAME_LENGTH];
|
|
|
|
n_att = 0;
|
|
pinit = string;
|
|
/* return if no boundary defined */
|
|
if (!boundary[0])
|
|
return;
|
|
/* skip first boundary */
|
|
if (strstr(string, boundary))
|
|
string = strstr(string, boundary) + strlen(boundary);
|
|
do {
|
|
if (strstr(string, "name=")) {
|
|
strlcpy(line, strstr(string, "name=") + 5, sizeof(line));
|
|
if (strchr(line, '\r'))
|
|
*strchr(line, '\r') = 0;
|
|
if (strchr(line, '\n'))
|
|
*strchr(line, '\n') = 0;
|
|
strlcpy(item, line, sizeof(item));
|
|
if (item[0] == '\"') {
|
|
strlcpy(item, line + 1, sizeof(item));
|
|
if (strchr(item, '\"'))
|
|
*strchr(item, '\"') = 0;
|
|
} else if (strchr(item, ' '))
|
|
*strchr(item, ' ') = 0;
|
|
if (strncmp(item, "attachment", 10) == 0) {
|
|
/* attachment names from previous uploads */
|
|
n_att = atoi(item + 10) + 1;
|
|
}
|
|
|
|
if (strncmp(item, "csvfile", 7) == 0 || strncmp(item, "xmlfile", 7) == 0) {
|
|
/* evaluate CSV/XML import file */
|
|
if (strstr(string, "filename=")) {
|
|
p = strstr(string, "filename=") + 9;
|
|
if (*p == '\"')
|
|
p++;
|
|
if (strstr(p, "\r\n\r\n"))
|
|
string = strstr(p, "\r\n\r\n") + 4;
|
|
else if (strstr(p, "\r\r\n\r\r\n"))
|
|
string = strstr(p, "\r\r\n\r\r\n") + 6;
|
|
if (strchr(p, '\"'))
|
|
*strchr(p, '\"') = 0;
|
|
/* set attachment filename */
|
|
strlcpy(file_name, p, sizeof(file_name));
|
|
if (file_name[0]) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("decode_post: Found CSV/XML import file\n");
|
|
}
|
|
|
|
/* find next boundary */
|
|
pctmp = string;
|
|
do {
|
|
while (*pctmp != '-' && pctmp < pinit + length)
|
|
pctmp++;
|
|
if (pctmp == pinit + length)
|
|
return;
|
|
if ((p = strstr(pctmp, boundary)) != NULL) {
|
|
if (*(p - 1) == '-')
|
|
p--;
|
|
while (*p == '-')
|
|
p--;
|
|
if (*p == 10)
|
|
p--;
|
|
if (*p == 13)
|
|
p--;
|
|
p++;
|
|
break;
|
|
} else
|
|
pctmp += strlen(pctmp);
|
|
} while (TRUE);
|
|
|
|
/* import CSV/XML file */
|
|
if (file_name[0] && !(isparam("cmd") && strieq(getparam("cmd"), loc("Cancel")))) {
|
|
if (strncmp(item, "csvfile", 7) == 0) {
|
|
setparam("csvfile", file_name);
|
|
csv_import(lbs, string, file_name);
|
|
return;
|
|
} else if (strncmp(item, "xmlfile", 7) == 0) {
|
|
setparam("xmlfile", file_name);
|
|
xml_import(lbs, string, file_name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
string = strstr(p, boundary) + strlen(boundary);
|
|
} else
|
|
string = strstr(string, boundary) + strlen(boundary);
|
|
|
|
} else if (strncmp(item, "attfile", 7) == 0) {
|
|
/* evaluate file attachment */
|
|
if (strstr(string, "filename=")) {
|
|
p = strstr(string, "filename=") + 9;
|
|
if (*p == '\"')
|
|
p++;
|
|
if (strstr(p, "\r\n\r\n"))
|
|
string = strstr(p, "\r\n\r\n") + 4;
|
|
else if (strstr(p, "\r\r\n\r\r\n"))
|
|
string = strstr(p, "\r\r\n\r\r\n") + 6;
|
|
if (strchr(p, '\"'))
|
|
*strchr(p, '\"') = 0;
|
|
/* set attachment filename */
|
|
strlcpy(file_name, p, sizeof(file_name));
|
|
|
|
/* remove spaces */
|
|
btou(file_name);
|
|
if (file_name[0]) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("decode_post: Found attachment %s\n", file_name);
|
|
/* check filename for invalid characters */
|
|
if (strpbrk(file_name, ",;+=")) {
|
|
strencode2(str2, file_name, sizeof(str2));
|
|
sprintf(str, "Error: Filename \"%s\" contains invalid character", str2);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* find next boundary */
|
|
pctmp = string;
|
|
do {
|
|
while (*pctmp != '-' && pctmp < pinit + length)
|
|
pctmp++;
|
|
if (pctmp == pinit + length)
|
|
return;
|
|
if ((p = strstr(pctmp, boundary)) != NULL) {
|
|
if (*(p - 1) == '-')
|
|
p--;
|
|
while (*p == '-')
|
|
p--;
|
|
if (*p == 10)
|
|
p--;
|
|
if (*p == 13)
|
|
p--;
|
|
p++;
|
|
break;
|
|
} else
|
|
pctmp += strlen(pctmp);
|
|
} while (TRUE);
|
|
|
|
/* check attachment size */
|
|
if (file_name[0] && (p - string) == 0) {
|
|
|
|
/* check for URL */
|
|
if (stristr(file_name, "http://") || stristr(file_name, "https://")) {
|
|
|
|
// check for logbook access
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str))) {
|
|
if (!check_login(lbs, getparam("sid"))) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
size = retrieve_url(lbs, file_name, stristr(file_name, "https://") != NULL, &buffer, FALSE);
|
|
if (size <= 0) {
|
|
strencode2(str2, file_name, sizeof(str2));
|
|
sprintf(str, loc("Cannot retrieve file from URL \"%s\""), str2);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
|
|
/* check for HTTP header */
|
|
pbody = strstr(buffer, "\r\n\r\n");
|
|
if (!pbody) {
|
|
show_error(loc("Invalid HTTP header"));
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
pbody += 4;
|
|
header_size = pbody - buffer;
|
|
|
|
/* check for file found */
|
|
if (strchr(buffer, ' ')) {
|
|
status = atoi(strchr(buffer, ' ') + 1);
|
|
if (status != 200) {
|
|
strencode2(str2, file_name, sizeof(str2));
|
|
sprintf(str, loc("File not found at URL \"%s\""), str2);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// check for logbook access
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str))) {
|
|
if (!check_login(lbs, getparam("sid"))) {
|
|
xfree(buffer);
|
|
return;
|
|
}
|
|
}
|
|
|
|
el_submit_attachment(lbs, file_name, pbody, size - header_size, full_name);
|
|
xfree(buffer);
|
|
sprintf(str, "attachment%d", n_att++);
|
|
setparam(str, full_name);
|
|
} else {
|
|
strencode2(str2, file_name, sizeof(str2));
|
|
sprintf(str, loc("Attachment file <b>\"%s\"</b> empty or not found"), str2);
|
|
show_error(str);
|
|
return;
|
|
}
|
|
} else if (file_name[0]) {
|
|
// check for logbook access
|
|
if (getcfg(lbs->name, "Password file", str, sizeof(str))) {
|
|
if (!check_login(lbs, getparam("sid"))) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* save attachment */
|
|
if (el_submit_attachment(lbs, file_name, string, (int) (p - string), full_name) < 0)
|
|
return;
|
|
sprintf(str, "attachment%d", n_att++);
|
|
setparam(str, full_name);
|
|
}
|
|
|
|
string = strstr(p, boundary) + strlen(boundary);
|
|
} else
|
|
string = strstr(string, boundary) + strlen(boundary);
|
|
} else {
|
|
p = string;
|
|
if (strstr(p, "\r\n\r\n"))
|
|
p = strstr(p, "\r\n\r\n") + 4;
|
|
else if (strstr(p, "\r\r\n\r\r\n"))
|
|
p = strstr(p, "\r\r\n\r\r\n") + 6;
|
|
if (strstr(p, boundary)) {
|
|
|
|
string = strstr(p, boundary) + strlen(boundary);
|
|
|
|
if (stricmp(item, "text") == 0) {
|
|
if (string - p >= TEXT_SIZE-100) {
|
|
sprintf(str,
|
|
"Error: Entry text too big. Please increase TEXT_SIZE and recompile elogd\n");
|
|
show_error(str);
|
|
return;
|
|
}
|
|
strlcpy(_mtext, p, sizeof(_mtext));
|
|
if (strstr(_mtext, boundary))
|
|
*strstr(_mtext, boundary) = 0;
|
|
ptmp = _mtext + (strlen(_mtext) - 1);
|
|
while (ptmp >= _mtext && *ptmp == '-' )
|
|
*ptmp-- = 0;
|
|
while (ptmp >= _mtext && (*ptmp == '\n' || *ptmp == '\r'))
|
|
*ptmp-- = 0;
|
|
} else {
|
|
strlcpy(str, p, sizeof(str));
|
|
if (strstr(str, boundary))
|
|
*strstr(str, boundary) = 0;
|
|
ptmp = str + (strlen(str) - 1);
|
|
while (ptmp >= str && *ptmp == '-')
|
|
*ptmp-- = 0;
|
|
while (ptmp >= str && (*ptmp == '\n' || *ptmp == '\r'))
|
|
*ptmp-- = 0;
|
|
|
|
if (setparam(item, str) == 0)
|
|
return;
|
|
}
|
|
} else {
|
|
strlcpy(str, p, sizeof(str));
|
|
if (setparam(item, str) == 0)
|
|
return;
|
|
string = p + strlen(p);
|
|
}
|
|
}
|
|
|
|
while (*string == '-' || *string == '\n' || *string == '\r')
|
|
string++;
|
|
} else
|
|
return; /* invalid request */
|
|
|
|
} while ((int) (string - pinit) < length);
|
|
|
|
if (lbs)
|
|
interprete(lbs->name, "");
|
|
else
|
|
interprete(logbook, "");
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
#define N_MAX_CONNECTION 100
|
|
|
|
int ka_sock[N_MAX_CONNECTION];
|
|
int ka_time[N_MAX_CONNECTION];
|
|
#ifdef HAVE_SSL
|
|
SSL *ka_ssl_con[N_MAX_CONNECTION];
|
|
#endif
|
|
struct in_addr remote_addr[N_MAX_CONNECTION];
|
|
char remote_host[N_MAX_CONNECTION][256];
|
|
|
|
int process_http_request(const char *crequest, int i_conn) {
|
|
int i, n, authorized, header_length, content_length, strsize;
|
|
char str2[1000], url[2000], format[256], cookie[256], boundary[256],
|
|
list[1000], theme[256], host_list[MAX_N_LIST][NAME_LENGTH], logbook[256], logbook_enc[256],
|
|
global_cmd[256];
|
|
char *p, *str, *request;
|
|
struct hostent *phe;
|
|
time_t now;
|
|
struct tm *ts;
|
|
|
|
const char *cookie_list[] =
|
|
{"upwd", "unm", "ufnm", "elmode", "urem", "wpwd", "apwd", "uname", "upassword", "elattach", "hsm",
|
|
"sid",
|
|
NULL
|
|
};
|
|
|
|
if (!strchr(crequest, '\r'))
|
|
return 0;
|
|
|
|
content_length = header_length = 0;
|
|
strsize = strlen(crequest) + 1001;
|
|
|
|
/* extract header length */
|
|
if (strstr(crequest, "\r\n\r\n"))
|
|
header_length = strstr(crequest, "\r\n\r\n") - crequest + 4;
|
|
else if (strstr(crequest, "\r\r\n\r\r\n"))
|
|
header_length = strstr(crequest, "\r\r\n\r\r\n") - crequest + 6;
|
|
else {
|
|
show_error("Invalid POST header");
|
|
return 1;
|
|
}
|
|
|
|
/* extract content length */
|
|
if (strstr(crequest, "Content-Length:")) {
|
|
content_length = atoi(strstr(crequest, "Content-Length:") + 15);
|
|
|
|
strsize = content_length + header_length + 15;
|
|
}
|
|
|
|
str = (char *)xmalloc(strsize);
|
|
request = (char *)xmalloc(strsize);
|
|
memcpy(request, crequest, strsize);
|
|
|
|
if (get_verbose() < VERBOSE_DEBUG) {
|
|
if (get_verbose() > 0) {
|
|
strlcpy(str, request, strsize);
|
|
if (strchr(str, '\r'))
|
|
*strchr(str, '\r') = 0;
|
|
if (strchr(str, '\n'))
|
|
*strchr(str, '\n') = 0;
|
|
eputs(str);
|
|
}
|
|
} else if (get_verbose() >= VERBOSE_DEBUG) {
|
|
eputs("\n");
|
|
eputs(request);
|
|
}
|
|
|
|
/* initialize parametr array */
|
|
initparam();
|
|
|
|
/* extract cookies */
|
|
if ((p = stristr(request, "Cookie:")) != NULL) {
|
|
p += 6;
|
|
do {
|
|
p++;
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
if (*p == '\r' || *p == '\n')
|
|
break;
|
|
strlcpy(str, p, strsize);
|
|
for (i = 0; i < (int) strlen(str); i++)
|
|
if (str[i] == '=' || str[i] == ';')
|
|
break;
|
|
if (str[i] == '=') {
|
|
str[i] = 0;
|
|
p += i + 1;
|
|
for (i = 0; *p && *p != ';' && *p != '\r' && *p != '\n'; p++)
|
|
if (i < (int) sizeof(cookie) - 1)
|
|
cookie[i++] = *p;
|
|
cookie[i] = 0;
|
|
} else {
|
|
/* empty cookie */
|
|
str[i] = 0;
|
|
cookie[0] = 0;
|
|
p += i;
|
|
}
|
|
|
|
/* store cookie as parameter */
|
|
for (i = 0; cookie_list[i]; i++) {
|
|
if (strcmp(cookie_list[i], str) == 0) {
|
|
setparam(str, cookie);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cookie_list[i] == NULL && get_verbose() >= VERBOSE_INFO)
|
|
eprintf("Received unknown cookie \"%s\"\n", str);
|
|
|
|
} while (*p && *p == ';');
|
|
}
|
|
|
|
/* extract referer */
|
|
referer[0] = 0;
|
|
if ((p = stristr(request, "Referer:")) != NULL) {
|
|
p += 9;
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
strlcpy(referer, p, sizeof(referer));
|
|
if (strchr(referer, '\r'))
|
|
*strchr(referer, '\r') = 0;
|
|
if (strchr(referer, '?'))
|
|
*strchr(referer, '?') = 0;
|
|
for (p = referer + strlen(referer) - 1; p > referer && *p != '/'; p--)
|
|
*p = 0;
|
|
if (strchr(referer, ' '))
|
|
url_encode(referer, sizeof(referer));
|
|
}
|
|
|
|
/* extract browser */
|
|
browser[0] = 0;
|
|
if ((p = stristr(request, "User-Agent:")) != NULL) {
|
|
p += 11;
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
strlcpy(browser, p, sizeof(browser));
|
|
if (strchr(browser, '\r'))
|
|
*strchr(browser, '\r') = 0;
|
|
}
|
|
|
|
/* extract host */
|
|
http_host[0] = 0;
|
|
if ((p = stristr(request, "Host:")) != NULL) {
|
|
p += 5;
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
strlcpy(http_host, p, sizeof(http_host));
|
|
if (strchr(http_host, '\r'))
|
|
*strchr(http_host, '\r') = 0;
|
|
}
|
|
|
|
/* extract X-Forwarded-Host, overwrite "Host:" if found */
|
|
if ((p = stristr(request, "X-Forwarded-Host:")) != NULL) {
|
|
p += 17;
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
strlcpy(http_host, p, sizeof(http_host));
|
|
if (strchr(http_host, '\r'))
|
|
*strchr(http_host, '\r') = 0;
|
|
}
|
|
|
|
/* extract X-Forwarded-User into http_user if Authentication==Webserver */
|
|
http_user[0] = 0;
|
|
if ((p = stristr(request, "X-Forwarded-User:")) != NULL) {
|
|
p += 17;
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
strlcpy(http_user, p, sizeof(http_user));
|
|
if (strchr(http_user, '\r'))
|
|
*strchr(http_user, '\r') = 0;
|
|
}
|
|
|
|
/* extract "X-Forwarded-For:" */
|
|
if ((p = stristr(request, "X-Forwarded-For:")) != NULL) {
|
|
p += 16;
|
|
while (*p && *p == ' ')
|
|
p++;
|
|
strlcpy(str, p, strsize);
|
|
if (strchr(str, '\r'))
|
|
*strchr(str, '\r') = 0;
|
|
#ifdef OS_WINNT
|
|
rem_addr.S_un.S_addr = inet_addr(str);
|
|
#else
|
|
rem_addr.s_addr = inet_addr(str);
|
|
#endif
|
|
|
|
if (getcfg("global", "Resolve host names", str, strsize) && atoi(str) == 1) {
|
|
phe = gethostbyaddr((char *) &rem_addr, 4, PF_INET);
|
|
if (phe != NULL)
|
|
strcpy(remote_host[i_conn], phe->h_name);
|
|
else
|
|
strcpy(remote_host[i_conn], (char *) inet_ntoa(rem_addr));
|
|
} else
|
|
strcpy(remote_host[i_conn], (char *) inet_ntoa(rem_addr));
|
|
|
|
strcpy(rem_host, remote_host[i_conn]);
|
|
}
|
|
|
|
if (_logging_level > 3) {
|
|
strlcpy(str, request, strsize);
|
|
if (strchr(str, '\r'))
|
|
*strchr(str, '\r') = 0;
|
|
write_logfile(NULL, str);
|
|
}
|
|
|
|
memset(return_buffer, 0, return_buffer_size);
|
|
strlen_retbuf = 0;
|
|
if (strncmp(request, "GET", 3) != 0 && strncmp(request, "POST", 4) != 0) {
|
|
xfree(str);
|
|
xfree(request);
|
|
return 0;
|
|
}
|
|
|
|
return_length = 0;
|
|
|
|
/* check for Keep-alive */
|
|
if (stristr(request, "Keep-Alive") != NULL && use_keepalive)
|
|
keep_alive = TRUE;
|
|
|
|
/* extract logbook */
|
|
if (strchr(request, '/') == NULL || strchr(request, '\r') == NULL || strstr(request, "HTTP") == NULL) {
|
|
/* invalid request, make valid */
|
|
strcpy(str, "GET / HTTP/1.0\r\n\r\n");
|
|
xfree(str);
|
|
xfree(request);
|
|
return process_http_request(str, i_conn);
|
|
}
|
|
|
|
/* initialize topgroups */
|
|
setcfg_topgroup("");
|
|
|
|
p = strchr(request, '/') + 1;
|
|
|
|
/* check for ../.. to avoid serving of files on top of the elog directory */
|
|
for (i = 0; p[i] && p[i] != ' ' && p[i] != '?' && i < (int) sizeof(url); i++)
|
|
url[i] = p[i];
|
|
url[i] = 0;
|
|
|
|
if (strstr(url, "../..")) {
|
|
strencode2(str2, url, sizeof(str2));
|
|
sprintf(str, "%s: <b>%s</b>", loc("Invalid URL"), str2);
|
|
show_error(str);
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
|
|
/* check if file is in scripts directory or in its subdirs */
|
|
for (i = 0; p[i] && p[i] != ' ' && p[i] != '?' && i < (int) sizeof(url); i++)
|
|
url[i] = (p[i] == '/') ? DIR_SEPARATOR : p[i];
|
|
url[i] = 0;
|
|
if (strchr(url, '.')) {
|
|
|
|
/* do not allow '..' in file name */
|
|
if (strstr(url, "..")) {
|
|
strencode2(str2, url, sizeof(str2));
|
|
sprintf(str, "%s: <b>%s</b>", loc("Invalid URL"), str2);
|
|
show_error(str);
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
|
|
strlcpy(str, resource_dir, strsize);
|
|
strlcat(str, "scripts", strsize);
|
|
strlcat(str, DIR_SEPARATOR_STR, strsize);
|
|
strlcat(str, url, strsize);
|
|
if (exist_file(str)) {
|
|
send_file_direct(str);
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
logbook[0] = 0;
|
|
for (i = 0; *p && *p != '/' && *p != '?' && *p != ' ' && i < (int) sizeof(logbook); i++)
|
|
logbook[i] = *p++;
|
|
logbook[i] = 0;
|
|
strcpy(logbook_enc, logbook);
|
|
url_decode(logbook);
|
|
/* check for trailing '/' after logbook */
|
|
if (strncmp(request, "POST", 4) != 0) { // fix for konqueror
|
|
if (logbook[0] && *p == ' ') {
|
|
if (!chkext(logbook, ".css") && !chkext(logbook, ".htm") && !chkext(logbook, ".gif")
|
|
&& !chkext(logbook, ".jpg") && !chkext(logbook, ".png") && !chkext(logbook, ".ico")) {
|
|
sprintf(str, "%s/", logbook_enc);
|
|
redirect(NULL, str);
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for trailing '/' after logbook/ID */
|
|
if (logbook[0] && *p == '/' && *(p + 1) != ' ') {
|
|
sprintf(url, "%s", logbook_enc);
|
|
for (i = strlen(url); *p && *p != ' ' && i < (int) sizeof(url); i++)
|
|
url[i] = *p++;
|
|
url[i] = 0;
|
|
if (*(p - 1) == '/') {
|
|
strencode2(str2, url, sizeof(str2));
|
|
sprintf(str, "%s: <b>%s</b>", loc("Invalid URL"), str2);
|
|
show_error(str);
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* check for invalid URL in the form "http:/server//path" */
|
|
if (!logbook[0] && *p == '/') {
|
|
for (i = 0; *p && *p != ' ' && i < (int) sizeof(url); i++)
|
|
url[i] = *p++;
|
|
url[i] = 0;
|
|
strencode2(str2, url, sizeof(str2));
|
|
sprintf(str, "%s: <b>%s</b>", loc("Invalid URL"), str2);
|
|
show_error(str);
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
|
|
/* check for global command */
|
|
global_cmd[0] = 0;
|
|
if ((p = strstr(request, "?cmd=")) != NULL) {
|
|
p += 5;
|
|
strlcpy(global_cmd, p, sizeof(global_cmd));
|
|
if (strchr(global_cmd, ' '))
|
|
*strchr(global_cmd, ' ') = 0;
|
|
if (strchr(global_cmd, '\r'))
|
|
*strchr(global_cmd, '\r') = 0;
|
|
}
|
|
|
|
/* redirect image request from inside CKeditor */
|
|
if (strieq(logbook, "ckeditor")) {
|
|
if (strstr(url, "?lb=")) {
|
|
strlcpy(logbook, strstr(url, "?lb=") + 4, sizeof(logbook));
|
|
if (strchr(logbook, '&'))
|
|
*strchr(logbook, '&') = 0;
|
|
url_decode(logbook);
|
|
}
|
|
}
|
|
|
|
/* check if logbook exists */
|
|
for (i = 0;; i++) {
|
|
if (!enumgrp(i, str))
|
|
break;
|
|
if (strieq(logbook, str) && is_logbook(logbook))
|
|
break;
|
|
}
|
|
|
|
if (chkext(logbook, ".gif") || chkext(logbook, ".jpg") || chkext(logbook, ".jpg") || chkext(logbook,
|
|
".png")
|
|
|| chkext(logbook, ".ico") || chkext(logbook, ".htm") || chkext(logbook, ".css")
|
|
|| chkext(logbook, ".js")) {
|
|
|
|
/* do not allow '..' in file name */
|
|
if (strstr(logbook, "..")) {
|
|
strencode2(str2, logbook, sizeof(str2));
|
|
sprintf(str, "%s: <b>%s</b>", loc("Invalid URL"), str2);
|
|
show_error(str);
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
|
|
/* check if file in resource directory */
|
|
strlcpy(str, resource_dir, strsize);
|
|
strlcat(str, logbook, strsize);
|
|
if (exist_file(str))
|
|
send_file_direct(str);
|
|
else {
|
|
/* else search file in themes directory */
|
|
strlcpy(str, resource_dir, strsize);
|
|
strlcat(str, "themes", strsize);
|
|
strlcat(str, DIR_SEPARATOR_STR, strsize);
|
|
if (getcfg("global", "theme", theme, sizeof(theme)))
|
|
strlcat(str, theme, strsize);
|
|
else
|
|
strlcat(str, "default", strsize);
|
|
strlcat(str, DIR_SEPARATOR_STR, strsize);
|
|
strlcat(str, logbook, strsize);
|
|
send_file_direct(str);
|
|
}
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
|
|
} else {
|
|
if (logbook[0] && (!strieq(logbook, str) || !is_logbook(logbook))) {
|
|
|
|
/* check for top group */
|
|
sprintf(str, "Top group %s", logbook);
|
|
if (!getcfg("global", str, list, sizeof(list))) {
|
|
|
|
sprintf(str, "Error: logbook \"%s\" not defined in %s", logbook_enc, CFGFILE);
|
|
show_error(str);
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if no logbook is given and only one logbook defined, use this one */
|
|
if (!logbook[0] && !global_cmd[0]) {
|
|
for (i = n = 0;; i++) {
|
|
if (!enumgrp(i, str))
|
|
break;
|
|
if (is_logbook(str))
|
|
n++;
|
|
}
|
|
|
|
if (n == 1) {
|
|
strlcpy(logbook, str, sizeof(logbook));
|
|
strlcpy(logbook_enc, logbook, sizeof(logbook_enc));
|
|
url_encode(logbook_enc, sizeof(logbook_enc));
|
|
strlcat(logbook_enc, "/", sizeof(logbook_enc));
|
|
/* redirect to logbook, necessary to get optional cookies for that logbook */
|
|
redirect(NULL, logbook_enc);
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*---- check "hosts deny" ----*/
|
|
|
|
authorized = 1;
|
|
if (getcfg(logbook, "Hosts deny", list, sizeof(list))) {
|
|
strcpy(rem_host_ip, (char *) inet_ntoa(rem_addr));
|
|
n = strbreak(list, host_list, MAX_N_LIST, ",", FALSE);
|
|
/* check if current connection matches anyone on the list */
|
|
for (i = 0; i < n; i++) {
|
|
if (strieq(rem_host, host_list[i]) || strieq(rem_host_ip, host_list[i]) || strieq(host_list[i],
|
|
"all")) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts deny\". Access denied.\n",
|
|
strieq(rem_host_ip, host_list[i]) ? rem_host_ip : rem_host, host_list[i]);
|
|
authorized = 0;
|
|
break;
|
|
}
|
|
if (host_list[i][0] == '.') {
|
|
if (strlen(rem_host) > strlen(host_list[i]) && strieq(host_list[i], rem_host + strlen(rem_host)
|
|
- strlen(host_list[i]))) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts deny\". Access denied.\n", rem_host,
|
|
host_list[i]);
|
|
authorized = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (host_list[i][strlen(host_list[i]) - 1] == '.') {
|
|
strcpy(str, rem_host_ip);
|
|
if (strlen(str) > strlen(host_list[i]))
|
|
str[strlen(host_list[i])] = 0;
|
|
if (strieq(host_list[i], str)) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts deny\". Access denied.\n",
|
|
rem_host_ip, host_list[i]);
|
|
authorized = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---- check "hosts allow" ----*/
|
|
|
|
if (getcfg(logbook, "Hosts allow", list, sizeof(list))) {
|
|
strcpy(rem_host_ip, (char *) inet_ntoa(rem_addr));
|
|
n = strbreak(list, host_list, MAX_N_LIST, ",", FALSE);
|
|
/* check if current connection matches anyone on the list */
|
|
for (i = 0; i < n; i++) {
|
|
if (strieq(rem_host, host_list[i]) || strieq(rem_host_ip, host_list[i]) || strieq(host_list[i],
|
|
"all")) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts allow\". Access granted.\n",
|
|
strieq(rem_host_ip, host_list[i]) ? rem_host_ip : rem_host, host_list[i]);
|
|
authorized = 1;
|
|
break;
|
|
}
|
|
if (host_list[i][0] == '.') {
|
|
if (strlen(rem_host) > strlen(host_list[i]) && strieq(host_list[i], rem_host + strlen(rem_host)
|
|
- strlen(host_list[i]))) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts allow\". Access granted.\n",
|
|
rem_host, host_list[i]);
|
|
authorized = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (host_list[i][strlen(host_list[i]) - 1] == '.') {
|
|
strcpy(str, rem_host_ip);
|
|
if (strlen(str) > strlen(host_list[i]))
|
|
str[strlen(host_list[i])] = 0;
|
|
if (strieq(host_list[i], str)) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("Remote host \"%s\" matches \"%s\" in \"Hosts allow\". Access granted.\n",
|
|
rem_host_ip, host_list[i]);
|
|
authorized = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!authorized) {
|
|
keep_alive = FALSE;
|
|
xfree(str);
|
|
xfree(request);
|
|
return 0;
|
|
}
|
|
|
|
if (!logbook[0] && global_cmd[0] && stricmp(global_cmd, "GetConfig") == 0) {
|
|
char allow[256];
|
|
allow[0] = 0;
|
|
getcfg("global", "Allow clone", allow, sizeof(allow));
|
|
if (atoi(allow) == 1)
|
|
download_config();
|
|
else {
|
|
show_http_header(NULL, FALSE, NULL, 200);
|
|
rsputs(loc("Cloning not allowed. Set \"Allow clone = 1\" to enable cloning."));
|
|
rsputs("\r\n");
|
|
return 1;
|
|
}
|
|
} else if (stricmp(global_cmd, "gettimedate") == 0) {
|
|
if (!getcfg(logbook, "Time format", format, sizeof(format)))
|
|
strcpy(format, DEFAULT_TIME_FORMAT);
|
|
time(&now);
|
|
ts = localtime(&now);
|
|
my_strftime(str, strsize, format, ts);
|
|
show_http_header(NULL, FALSE, NULL, 200);
|
|
rsputs(str);
|
|
rsputs(" ");
|
|
} else if (strncmp(request, "GET", 3) == 0) {
|
|
/* extract path and commands */
|
|
if (strchr(request, '\r'))
|
|
*strchr(request, '\r') = 0;
|
|
if (!strstr(request, "HTTP/1")) {
|
|
xfree(str);
|
|
xfree(request);
|
|
return 0;
|
|
}
|
|
*(strstr(request, "HTTP/1") - 1) = 0;
|
|
/* strip logbook from path */
|
|
strlcpy(str, request + 5, strsize);
|
|
p = str;
|
|
for (i = 0; *p && *p != '/' && *p != '?'; p++);
|
|
while (*p && *p == '/')
|
|
p++;
|
|
if (strncmp(p, "editor/", 7) == 0) // fix for image request inside CKeditor
|
|
p += 7;
|
|
/* decode command and return answer */
|
|
decode_get(logbook, p);
|
|
} else if (strncmp(request, "POST", 4) == 0) {
|
|
|
|
/* extract content length */
|
|
if (strstr(request, "Content-Length:"))
|
|
content_length = atoi(strstr(request, "Content-Length:") + 15);
|
|
else if (strstr(request, "Content-length:"))
|
|
content_length = atoi(strstr(request, "Content-length:") + 15);
|
|
if (content_length <= 0) {
|
|
show_error("Invalid Content-Length in header");
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
|
|
/* extract boundary */
|
|
if (strstr(request, "boundary=")) {
|
|
strlcpy(boundary, strstr(request, "boundary=") + 9, sizeof(boundary));
|
|
if (strchr(boundary, '\r'))
|
|
*strchr(boundary, '\r') = 0;
|
|
}
|
|
|
|
/* get logbook from list (needed for attachment dir) */
|
|
for (i = 0; lb_list[i].name[0]; i++)
|
|
if (strieq(logbook, lb_list[i].name))
|
|
break;
|
|
if (!lb_list[i].name[0])
|
|
/* must be login page of top group */
|
|
decode_post(logbook, NULL, request + header_length, boundary, content_length);
|
|
else
|
|
decode_post(logbook, &lb_list[i], request + header_length, boundary, content_length);
|
|
} else {
|
|
strencode2(str2, request, sizeof(str2));
|
|
sprintf(str, "Unknown request:<p>%s", str2);
|
|
show_error(str);
|
|
}
|
|
|
|
xfree(str);
|
|
xfree(request);
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
#ifdef HAVE_SSL
|
|
|
|
void send_return(int s, SSL *ssl_con, const char *net_buffer)
|
|
#else
|
|
void send_return(int s, const char *net_buffer)
|
|
#endif
|
|
{
|
|
int length, header_length;
|
|
char str[NAME_LENGTH];
|
|
char *p;
|
|
#ifndef HAVE_SSL
|
|
void *ssl_con = NULL;
|
|
#endif
|
|
|
|
if (!_ssl_flag)
|
|
ssl_con = NULL;
|
|
|
|
if (return_length != -1) {
|
|
if (return_length == 0)
|
|
return_length = strlen_retbuf;
|
|
|
|
if (return_length == 0)
|
|
return;
|
|
|
|
if (_logging_level > 3) {
|
|
strlcpy(str, net_buffer, sizeof(str));
|
|
sprintf(str, "Return %d bytes", return_length);
|
|
write_logfile(NULL, str);
|
|
}
|
|
|
|
length = 0;
|
|
if ((keep_alive && strstr(return_buffer, "Content-Length") == NULL) || strstr(return_buffer,
|
|
"Content-Length") >
|
|
strstr(return_buffer,
|
|
"\r\n\r\n")) {
|
|
|
|
/*---- add content-length ----*/
|
|
|
|
p = strstr(return_buffer, "\r\n\r\n");
|
|
if (p != NULL) {
|
|
header_length = (int) (p - return_buffer);
|
|
length = return_length - header_length - 4;
|
|
if (header_length + 100 > (int) sizeof(header_buffer))
|
|
header_length = sizeof(header_buffer) - 100;
|
|
memcpy(header_buffer, return_buffer, header_length);
|
|
sprintf(header_buffer + header_length, "\r\nContent-Length: %d\r\n\r\n", length);
|
|
|
|
send_with_timeout(ssl_con, s, header_buffer, strlen(header_buffer));
|
|
send_with_timeout(ssl_con, s, p + 4, length);
|
|
|
|
if (get_verbose() < VERBOSE_DEBUG) {
|
|
if (get_verbose() > 0)
|
|
eprintf("Returned %d bytes\n", length);
|
|
} else if (get_verbose() >= VERBOSE_DEBUG) {
|
|
if (strrchr(net_buffer, '/'))
|
|
strlcpy(str, strrchr(net_buffer, '/') + 1, sizeof(str));
|
|
else
|
|
str[0] = 0;
|
|
eprintf("==== Return ================================\n");
|
|
eputs(header_buffer);
|
|
if (chkext(net_buffer, ".gif") || chkext(net_buffer, ".jpg") || chkext(net_buffer, ".png")
|
|
|| chkext(net_buffer, ".ico") || chkext(net_buffer, ".pdf") || return_length > 10000)
|
|
eprintf("\n<%d bytes of \"%s\">\n\n", length, str);
|
|
else
|
|
eputs(p + 4);
|
|
eprintf("\n");
|
|
}
|
|
} else {
|
|
eprintf("Internal error, no valid header!\n");
|
|
keep_alive = FALSE;
|
|
}
|
|
} else {
|
|
|
|
if (!keep_alive) {
|
|
/* no keepalive, so add connection close */
|
|
p = strstr(return_buffer, "\r\n\r\n");
|
|
if (p != NULL) {
|
|
header_length = (int) (p - return_buffer);
|
|
length = return_length - header_length - 4;
|
|
if (header_length + 100 > (int) sizeof(header_buffer))
|
|
header_length = sizeof(header_buffer) - 100;
|
|
memcpy(header_buffer, return_buffer, header_length);
|
|
sprintf(header_buffer + header_length, "\r\nConnection: Close\r\n\r\n");
|
|
}
|
|
|
|
send_with_timeout(ssl_con, s, header_buffer, strlen(header_buffer));
|
|
send_with_timeout(ssl_con, s, p + 4, length);
|
|
|
|
} else {
|
|
|
|
send_with_timeout(ssl_con, s, return_buffer, return_length);
|
|
|
|
}
|
|
|
|
if (get_verbose() < VERBOSE_DEBUG) {
|
|
if (get_verbose() > 0)
|
|
eprintf("Returned %d bytes\n", return_length);
|
|
} else if (get_verbose() == VERBOSE_DEBUG) {
|
|
if (strrchr(net_buffer, '/'))
|
|
strlcpy(str, strrchr(net_buffer, '/') + 1, sizeof(str));
|
|
else
|
|
str[0] = 0;
|
|
eprintf("==== Return ================================\n");
|
|
if (chkext(net_buffer, ".gif") || chkext(net_buffer, ".jpg") || chkext(net_buffer, ".png")
|
|
|| chkext(net_buffer, ".ico") || chkext(net_buffer, ".pdf") || return_length > 10000) {
|
|
if (str[0])
|
|
eprintf("\n<%d bytes of \"%s\">\r\n", return_length, str);
|
|
else
|
|
eprintf("\n<%d bytes>\r\n", return_length);
|
|
} else
|
|
eputs(return_buffer);
|
|
eprintf("\n\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL cron_match(char *str, int value, BOOL ignore_star) {
|
|
int low, high;
|
|
|
|
if (atoi(str) == value)
|
|
return TRUE;
|
|
|
|
if (!ignore_star && str[0] == '*')
|
|
return TRUE;
|
|
|
|
/* check range */
|
|
if (strchr(str, '-')) {
|
|
low = atoi(str);
|
|
high = atoi(strchr(str, '-') + 1);
|
|
return value >= low && value <= high;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void check_cron()
|
|
/* check 'mirror cron' etnry in configuration file
|
|
|
|
minute (0-59)
|
|
hour (0-23)
|
|
day of month (1-31)
|
|
month of year (1-12)
|
|
day of week (0-6, 0 is Sunday)
|
|
|
|
Each of these patterns might be an asterisk (meaning all legal values)
|
|
or a list of elements separated by commas.
|
|
*/
|
|
{
|
|
int i, j, n;
|
|
BOOL min_flag, hour_flag, day_flag, mon_flag, wday_flag;
|
|
time_t now;
|
|
char *p, str[256], cron[5][256];
|
|
struct tm *ts;
|
|
static struct tm last_time;
|
|
char list[60][NAME_LENGTH];
|
|
|
|
if (!getcfg("global", "mirror cron", str, sizeof(str)))
|
|
return;
|
|
|
|
for (i = 0; i < 5; i++)
|
|
strcpy(cron[i], "*");
|
|
|
|
i = 0;
|
|
p = strtok(str, " ");
|
|
while (p) {
|
|
strcpy(cron[i++], p);
|
|
p = strtok(NULL, " ");
|
|
}
|
|
|
|
time(&now);
|
|
ts = localtime(&now);
|
|
assert(ts);
|
|
|
|
/* check once every minute */
|
|
if (last_time.tm_year && last_time.tm_min != ts->tm_min) {
|
|
|
|
min_flag = hour_flag = day_flag = mon_flag = wday_flag = FALSE;
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
n = strbreak(cron[i], list, 60, ",", FALSE);
|
|
|
|
for (j = 0; j < n; j++) {
|
|
/* minutes */
|
|
if (i == 0 && cron_match(list[j], ts->tm_min, FALSE))
|
|
min_flag = TRUE;
|
|
|
|
/* hours */
|
|
if (i == 1 && cron_match(list[j], ts->tm_hour, FALSE))
|
|
hour_flag = TRUE;
|
|
|
|
/* day of month */
|
|
if (i == 2 && cron_match(list[j], ts->tm_mday, FALSE))
|
|
day_flag = TRUE;
|
|
|
|
/* month of year */
|
|
if (i == 3 && cron_match(list[j], ts->tm_mon, FALSE))
|
|
mon_flag = TRUE;
|
|
|
|
/* weekday */
|
|
if (i == 4 && cron_match(list[j], ts->tm_wday, TRUE))
|
|
wday_flag = TRUE;
|
|
}
|
|
}
|
|
|
|
if (min_flag && hour_flag && ((day_flag && mon_flag) || wday_flag)) {
|
|
|
|
rem_host[0] = 0;
|
|
write_logfile(NULL, "Cron job started");
|
|
|
|
/* synchronize all logbooks */
|
|
setcfg_topgroup("");
|
|
synchronize(NULL, SYNC_CRON);
|
|
}
|
|
}
|
|
|
|
memcpy(&last_time, ts, sizeof(struct tm));
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
BOOL _abort = FALSE;
|
|
BOOL _hup = FALSE;
|
|
|
|
void ctrlc_handler(int sig) {
|
|
if (sig)
|
|
_abort = TRUE;
|
|
}
|
|
|
|
void hup_handler(int sig) {
|
|
if (sig)
|
|
_hup = TRUE;
|
|
}
|
|
|
|
#ifndef OS_WINNT
|
|
|
|
void alarm_handler(int sig) {
|
|
if (sig)
|
|
alarm(3);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
#ifdef HAVE_SSL
|
|
|
|
SSL_CTX *init_ssl(void) {
|
|
char str[256], pwd[256];
|
|
SSL_METHOD *meth;
|
|
SSL_CTX *ctx;
|
|
|
|
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);
|
|
|
|
#if OPENSSL_VERSION_NUMBER > 0x1010000fL
|
|
// disable obsolete SSL and TLS, need TLSv1_2 for Internet Explorer
|
|
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
|
SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
|
|
|
|
if (SSL_CTX_set_cipher_list(ctx,
|
|
"ECDH+AESGCM:ECDH+AES256:ECDH+AES:DH+AESGCM:DH+AES256:DH+AES:!aNULL:!ADH:!DSS:!kDH:!kECDH") <= 0) {
|
|
eprintf("Error setting the cipher list.\n");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
if (getcfg("global", "SSL Passphrase", pwd, sizeof(pwd))) {
|
|
SSL_CTX_set_default_passwd_cb_userdata(ctx, pwd);
|
|
}
|
|
|
|
strlcpy(str, resource_dir, sizeof(str));
|
|
strlcat(str, "ssl/server.crt", sizeof(str));
|
|
if (!file_exist(str)) {
|
|
eprintf("Cerificate file \"%s\" not found, aborting\n", str);
|
|
return NULL;
|
|
}
|
|
if (SSL_CTX_use_certificate_file(ctx, str, SSL_FILETYPE_PEM) == 0)
|
|
return NULL;
|
|
|
|
strlcpy(str, resource_dir, sizeof(str));
|
|
strlcat(str, "ssl/server.key", sizeof(str));
|
|
if (!file_exist(str)) {
|
|
eprintf("Key file \"%s\" not found, aborting\n", str);
|
|
return NULL;
|
|
}
|
|
|
|
if (SSL_CTX_use_PrivateKey_file(ctx, str, SSL_FILETYPE_PEM) == 0)
|
|
return NULL;
|
|
if (SSL_CTX_check_private_key(ctx) < 0)
|
|
return NULL;
|
|
|
|
strlcpy(str, resource_dir, sizeof(str));
|
|
strlcat(str, "ssl/chain.crt", sizeof(str));
|
|
if (file_exist(str)) {
|
|
if (SSL_CTX_use_certificate_chain_file(ctx, str) == 0)
|
|
return NULL;
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|
|
#endif // HAVE_SSL
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void server_loop(void) {
|
|
int status, i, broken, min, i_min, i_conn, more_requests;
|
|
char str[1000], logbook[256], logbook_enc[256];
|
|
char *pend;
|
|
int lsock, len, flag, content_length, header_length;
|
|
struct sockaddr_in serv_addr, acc_addr;
|
|
struct hostent *phe;
|
|
fd_set readfds;
|
|
struct timeval timeout;
|
|
char *net_buffer = NULL;
|
|
int net_buffer_size;
|
|
#ifdef HAVE_SSL
|
|
SSL_CTX *ssl_ctx = NULL;
|
|
#endif
|
|
|
|
#ifdef OS_UNIX
|
|
/* sigaction structs */
|
|
struct sigaction ctrlc_handle;
|
|
struct sigaction ignore_handle;
|
|
struct sigaction hup_handle;
|
|
struct sigaction alarm_handle;
|
|
#endif
|
|
|
|
i_conn = content_length = 0;
|
|
net_buffer_size = 100000;
|
|
net_buffer = (char *)xmalloc(net_buffer_size);
|
|
return_buffer_size = 100000;
|
|
return_buffer = (char *)xmalloc(return_buffer_size);
|
|
pend = NULL;
|
|
|
|
/* determine logging level */
|
|
if (getcfg(NULL, "Logging Level", str, sizeof(str)))
|
|
_logging_level = atoi(str);
|
|
else
|
|
_logging_level = 2;
|
|
|
|
/* initialize SSL if requested */
|
|
_ssl_flag = 0;
|
|
if (getcfg("global", "SSL", str, sizeof(str)) && atoi(str) == 1) {
|
|
#ifdef HAVE_SSL
|
|
ssl_ctx = init_ssl();
|
|
if (ssl_ctx == NULL) {
|
|
eprintf("Cannot initialize SSL\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
_ssl_flag = 1;
|
|
#else
|
|
eprintf("SSL support not compiled into elogd\n");
|
|
exit(EXIT_FAILURE);
|
|
#endif
|
|
}
|
|
|
|
/* create a new socket */
|
|
lsock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (lsock == -1) {
|
|
eprintf("Cannot create socket\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* bind local node name and port to socket */
|
|
memset(&serv_addr, 0, sizeof(serv_addr));
|
|
serv_addr.sin_family = AF_INET;
|
|
/* if no hostname given with the -n flag, listen on any interface */
|
|
if (listen_interface[0] == 0)
|
|
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
else {
|
|
/* look up the given hostname. gethostbyname() will take a hostname
|
|
or an IP address */
|
|
phe = gethostbyname(listen_interface);
|
|
if (!phe) {
|
|
eprintf("Cannot find address for -n %s\n", listen_interface);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (phe->h_addrtype != AF_INET) {
|
|
eprintf("Non Internet address for -n %s\n", listen_interface);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
memcpy(&serv_addr.sin_addr.s_addr, phe->h_addr_list[0], phe->h_length);
|
|
}
|
|
|
|
serv_addr.sin_port = htons((short) elog_tcp_port);
|
|
|
|
/* switch on reuse of port */
|
|
flag = 1;
|
|
setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(int));
|
|
status = bind(lsock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
|
|
if (status < 0) {
|
|
eprintf("Cannot bind to port %d.\nProbably another instance of this program is already running.\n",
|
|
elog_tcp_port);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* get local host name */
|
|
if (getcfg("global", "URL", str, sizeof(str)))
|
|
split_url(str, host_name, NULL, NULL, NULL);
|
|
else {
|
|
gethostname(host_name, sizeof(host_name));
|
|
|
|
phe = gethostbyname(host_name);
|
|
/* if domain name is not in host name, hope to get it from phe */
|
|
if (strchr(host_name, '.') == NULL && phe != NULL && strchr(phe->h_name, '.') != NULL)
|
|
strcpy(host_name, phe->h_name);
|
|
}
|
|
|
|
/* open configuration file */
|
|
getcfg("dummy", "dummy", str, sizeof(str));
|
|
|
|
/* now, initiate daemon/service */
|
|
if (running_as_daemon) {
|
|
/* Redirect all messages handled with eprintf/efputs to syslog */
|
|
redirect_to_syslog();
|
|
|
|
#ifdef OS_UNIX
|
|
if (!ss_daemon_init()) {
|
|
eprintf("Couldn't initiate the daemon; aborting\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* about to entering the server loop, welcome user with a brief info */
|
|
eprintf("%s ", ELOGID);
|
|
strcpy(str, git_revision());
|
|
if (strchr(str, ' '))
|
|
*strchr(str, ' ') = 0;
|
|
eprintf("revision %s\n", str);
|
|
if (get_verbose() >= VERBOSE_INFO) {
|
|
getcwd(str, sizeof(str));
|
|
if (strchr(config_file, DIR_SEPARATOR) == NULL)
|
|
eprintf("Config file : %s%c%s\n", str, DIR_SEPARATOR, config_file);
|
|
else
|
|
eprintf("Config file : %s\n", config_file);
|
|
eprintf("Resource dir : %s\n", resource_dir[0] ? resource_dir : str);
|
|
if (logbook_dir[0] && logbook_dir[0] != DIR_SEPARATOR && logbook_dir[1] != ':')
|
|
eprintf("Logbook dir : %s%c%s\n", str, DIR_SEPARATOR, logbook_dir);
|
|
else
|
|
eprintf("Logbook dir : %s\n", logbook_dir[0] ? logbook_dir : str);
|
|
}
|
|
#ifdef OS_UNIX
|
|
/* create PID file if given as command line parameter or if running under root */
|
|
|
|
if (geteuid() == 0 || pidfile[0]) {
|
|
int fd;
|
|
char buf[20];
|
|
struct stat finfo;
|
|
|
|
if (pidfile[0] == 0)
|
|
strcpy(pidfile, PIDFILE);
|
|
|
|
/* check if file exists */
|
|
if (stat(pidfile, &finfo) >= 0) {
|
|
/* if it exists remove it */
|
|
eprintf("File \"%s\" exists, overwriting it.\n", pidfile);
|
|
remove(pidfile);
|
|
}
|
|
|
|
fd = open(pidfile, O_CREAT | O_RDWR, 0644);
|
|
if (fd < 0) {
|
|
sprintf(str, "Error creating pid file \"%s\"", pidfile);
|
|
eprintf("%s; %s\n", str, strerror(errno));
|
|
}
|
|
|
|
sprintf(buf, "%d\n", (int) getpid());
|
|
if (write(fd, buf, strlen(buf)) == -1) {
|
|
sprintf(str, "Error writing to pid file \"%s\"", pidfile);
|
|
eprintf("%s; %s\n", str, strerror(errno));
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
/* install signal handlers */
|
|
ctrlc_handle.sa_handler = ctrlc_handler;
|
|
sigemptyset(&ctrlc_handle.sa_mask);
|
|
ctrlc_handle.sa_flags = 0;
|
|
|
|
sigaction(SIGTERM, &ctrlc_handle, NULL);
|
|
sigaction(SIGINT, &ctrlc_handle, NULL);
|
|
|
|
ignore_handle.sa_handler = SIG_IGN;
|
|
ignore_handle.sa_flags = 0;
|
|
sigaction(SIGPIPE, &ignore_handle, NULL);
|
|
|
|
hup_handle.sa_handler = hup_handler;
|
|
sigemptyset(&hup_handle.sa_mask);
|
|
hup_handle.sa_flags = 0;
|
|
sigaction(SIGHUP, &hup_handle, NULL);
|
|
|
|
alarm_handle.sa_handler = alarm_handler;
|
|
sigemptyset(&alarm_handle.sa_mask);
|
|
alarm_handle.sa_flags = 0;
|
|
sigaction(SIGALRM, &alarm_handle, NULL);
|
|
|
|
#ifndef OS_WINNT
|
|
alarm(3); // prevents blocking send() operations
|
|
#endif
|
|
|
|
/* give up root privilege */
|
|
if (geteuid() == 0) {
|
|
if (!getcfg("global", "Grp", str, sizeof(str)) || setegroup(str) < 0) {
|
|
eprintf("Falling back to default group \"elog\"\n");
|
|
if (setegroup("elog") < 0) {
|
|
eprintf("Falling back to default group \"%s\"\n", DEFAULT_GROUP);
|
|
if (setegroup(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)) || seteuser(str) < 0) {
|
|
eprintf("Falling back to default user \"elog\"\n");
|
|
if (seteuser("elog") < 0) {
|
|
eprintf("Falling back to default user \"%s\"\n", DEFAULT_USER);
|
|
if (seteuser(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);
|
|
}
|
|
#endif
|
|
|
|
/* load initial configuration */
|
|
check_config();
|
|
|
|
/* check for CKedit */
|
|
strlcpy(str, resource_dir, sizeof(str));
|
|
strlcat(str, "scripts", sizeof(str));
|
|
strlcat(str, DIR_SEPARATOR_STR, sizeof(str));
|
|
strlcat(str, "ckeditor/ckeditor.js", sizeof(str));
|
|
ckedit_exist = exist_file(str);
|
|
if (ckedit_exist)
|
|
eprintf("CKeditor detected\n");
|
|
else
|
|
eprintf("CKeditor NOT detected\n");
|
|
|
|
/* check for ImageMagick */
|
|
strlcpy(_convert_cmd, "convert", sizeof(_convert_cmd));
|
|
strlcpy(_identify_cmd, "identify", sizeof(_convert_cmd));
|
|
sprintf(str, "%s -version", _convert_cmd);
|
|
my_shell(str, str, sizeof(str));
|
|
image_magick_exist = (strstr(str, "ImageMagick") != NULL);
|
|
if (!image_magick_exist) {
|
|
strlcpy(_convert_cmd, "/usr/bin/convert", sizeof(_convert_cmd));
|
|
strlcpy(_identify_cmd, "/usr/bin/identify", sizeof(_convert_cmd));
|
|
sprintf(str, "%s -version", _convert_cmd);
|
|
my_shell(str, str, sizeof(str));
|
|
image_magick_exist = (strstr(str, "ImageMagick") != NULL);
|
|
}
|
|
if (!image_magick_exist) {
|
|
strlcpy(_convert_cmd, "/usr/local/bin/convert", sizeof(_convert_cmd));
|
|
strlcpy(_identify_cmd, "/usr/local/bin/identify", sizeof(_convert_cmd));
|
|
sprintf(str, "%s -version", _convert_cmd);
|
|
my_shell(str, str, sizeof(str));
|
|
image_magick_exist = (strstr(str, "ImageMagick") != NULL);
|
|
}
|
|
if (!image_magick_exist) {
|
|
strlcpy(_convert_cmd, "/opt/local/bin/convert", sizeof(_convert_cmd));
|
|
strlcpy(_identify_cmd, "/opt/local/bin/identify", sizeof(_convert_cmd));
|
|
sprintf(str, "%s -version", _convert_cmd);
|
|
my_shell(str, str, sizeof(str));
|
|
image_magick_exist = (strstr(str, "ImageMagick") != NULL);
|
|
}
|
|
|
|
if (image_magick_exist)
|
|
eprintf("ImageMagick detected\n");
|
|
else
|
|
eprintf("ImageMagick NOT detected. Image scaling will not work.\n");
|
|
|
|
/* check for keepalive */
|
|
if (!use_keepalive)
|
|
eprintf("Keep-alive disabled\n");
|
|
|
|
/* build logbook indices */
|
|
if (get_verbose() == 0 && !running_as_daemon)
|
|
eprintf("Indexing logbooks ... ");
|
|
if (el_index_logbooks() != EL_SUCCESS)
|
|
exit(EXIT_FAILURE);
|
|
if (get_verbose() == 0 && !running_as_daemon)
|
|
eputs("done");
|
|
|
|
#ifndef HAVE_KRB5
|
|
/* check for Kerberos authentication */
|
|
getcfg("global", "Authentication", str, sizeof(str));
|
|
if (stristr(str, "Kerberos")) {
|
|
eprintf("Kerberos authentication not compiled into this version of elogd.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#endif
|
|
#ifndef HAVE_LDAP
|
|
/* check for LDAP authentication */
|
|
/* NPA change */
|
|
getcfg("global", "Authentication", str, sizeof(str));
|
|
if (stristr(str, "LDAP")) {
|
|
eprintf("LDAP authentication not compiled into this version of elogd.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#endif
|
|
/* listen for connection */
|
|
status = listen(lsock, SOMAXCONN);
|
|
if (status < 0) {
|
|
eprintf("Cannot listen\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (_ssl_flag)
|
|
sprintf(str, "SSLServer listening on port %d ...\n", elog_tcp_port);
|
|
else
|
|
sprintf(str, "Server listening on port %d ...\n", elog_tcp_port);
|
|
eprintf("%s", str);
|
|
if (_logging_level > 0)
|
|
write_logfile(NULL, str);
|
|
|
|
do {
|
|
FD_ZERO(&readfds);
|
|
FD_SET(lsock, &readfds);
|
|
for (i = 0; i < N_MAX_CONNECTION; i++)
|
|
if (ka_sock[i] > 0)
|
|
FD_SET(ka_sock[i], &readfds);
|
|
timeout.tv_sec = 1;
|
|
timeout.tv_usec = 0;
|
|
status = select(FD_SETSIZE, (fd_set *) &readfds, NULL, NULL, (struct timeval *) &timeout);
|
|
|
|
/* check UNIX signal flags */
|
|
if (_abort)
|
|
break;
|
|
|
|
/* call random number generator on each access to completely randomize it */
|
|
rand();
|
|
|
|
/* close old connections */
|
|
for (i = 0; i < N_MAX_CONNECTION; i++)
|
|
if (ka_sock[i] && (int) time(NULL) - ka_time[i] > 60) {
|
|
#ifdef HAVE_SSL
|
|
if (_ssl_flag) {
|
|
SSL_set_fd(ka_ssl_con[i], ka_sock[i]);
|
|
SSL_shutdown(ka_ssl_con[i]);
|
|
SSL_free(ka_ssl_con[i]);
|
|
ka_ssl_con[i] = NULL;
|
|
}
|
|
#endif
|
|
closesocket(ka_sock[i]);
|
|
ka_sock[i] = 0;
|
|
ka_time[i] = 0;
|
|
}
|
|
|
|
if (status != -1) { // if no HUP signal is received
|
|
if (FD_ISSET(lsock, &readfds)) {
|
|
len = sizeof(acc_addr);
|
|
_sock = accept(lsock, (struct sockaddr *) &acc_addr, (socklen_t *) &len);
|
|
|
|
#ifdef HAVE_SSL
|
|
if (_ssl_flag) {
|
|
_ssl_con = SSL_new(ssl_ctx);
|
|
SSL_set_fd(_ssl_con, _sock);
|
|
if (SSL_accept(_ssl_con) < 0) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("SSL_accept failed\n");
|
|
closesocket(_sock);
|
|
ka_sock[i_conn] = 0;
|
|
ka_ssl_con[i_conn] = NULL;
|
|
_ssl_con = NULL;
|
|
continue;
|
|
}
|
|
} else
|
|
_ssl_con = NULL;
|
|
|
|
#endif
|
|
|
|
/* find new entry in socket table */
|
|
for (i = 0; i < N_MAX_CONNECTION; i++)
|
|
if (ka_sock[i] == 0)
|
|
break;
|
|
/* recycle oldest connection */
|
|
if (i == N_MAX_CONNECTION) {
|
|
for (i = i_min = 0, min = ka_time[0]; i < N_MAX_CONNECTION; i++)
|
|
if (ka_time[i] < min) {
|
|
min = ka_time[i];
|
|
i_min = i;
|
|
}
|
|
#ifdef HAVE_SSL
|
|
if (_ssl_flag) {
|
|
SSL_set_fd(ka_ssl_con[i_min], ka_sock[i_min]);
|
|
SSL_shutdown(ka_ssl_con[i_min]);
|
|
SSL_free(ka_ssl_con[i_min]);
|
|
ka_ssl_con[i_min] = NULL;
|
|
}
|
|
#endif
|
|
closesocket(ka_sock[i_min]);
|
|
ka_sock[i_min] = 0;
|
|
ka_time[i_min] = 0;
|
|
i = i_min;
|
|
}
|
|
|
|
i_conn = i;
|
|
ka_sock[i_conn] = _sock;
|
|
ka_time[i_conn] = (int) time(NULL);
|
|
#ifdef HAVE_SSL
|
|
ka_ssl_con[i_conn] = _ssl_con;
|
|
#endif
|
|
/* save remote host address */
|
|
memcpy(&remote_addr[i_conn], &(acc_addr.sin_addr), sizeof(rem_addr));
|
|
memcpy(&rem_addr, &(acc_addr.sin_addr), sizeof(rem_addr));
|
|
|
|
if (getcfg("global", "Resolve host names", str, sizeof(str)) && atoi(str) == 1) {
|
|
phe = gethostbyaddr((char *) &rem_addr, 4, PF_INET);
|
|
if (phe != NULL)
|
|
strcpy(remote_host[i_conn], phe->h_name);
|
|
else
|
|
strcpy(remote_host[i_conn], (char *) inet_ntoa(rem_addr));
|
|
} else
|
|
strcpy(remote_host[i_conn], (char *) inet_ntoa(rem_addr));
|
|
|
|
strcpy(rem_host, remote_host[i_conn]);
|
|
|
|
if (get_verbose() == VERBOSE_URL)
|
|
eprintf("Open connection #%d on socket %d\n", i, _sock);
|
|
|
|
/* start over */
|
|
continue;
|
|
}
|
|
|
|
/* check if open connection received data */
|
|
for (i = 0; i < N_MAX_CONNECTION; i++)
|
|
if (ka_sock[i] > 0 && FD_ISSET(ka_sock[i], &readfds))
|
|
break;
|
|
if (i == N_MAX_CONNECTION) {
|
|
_sock = 0;
|
|
} else {
|
|
i_conn = i;
|
|
_sock = ka_sock[i_conn];
|
|
#ifdef HAVE_SSL
|
|
_ssl_con = ka_ssl_con[i_conn];
|
|
#endif
|
|
ka_time[i_conn] = (int) time(NULL);
|
|
memcpy(&rem_addr, &remote_addr[i_conn], sizeof(rem_addr));
|
|
strcpy(rem_host, remote_host[i_conn]);
|
|
}
|
|
|
|
/* turn off keep_alive by default */
|
|
keep_alive = FALSE;
|
|
|
|
/* receive data */
|
|
if (_sock > 0) {
|
|
|
|
memset(net_buffer, 0, net_buffer_size);
|
|
len = 0;
|
|
more_requests = 0;
|
|
|
|
do { /* pipleline loop */
|
|
header_length = 0;
|
|
broken = FALSE;
|
|
return_length = -1;
|
|
do {
|
|
|
|
if (!more_requests) {
|
|
do {
|
|
FD_ZERO(&readfds);
|
|
FD_SET(_sock, &readfds);
|
|
timeout.tv_sec = 6;
|
|
timeout.tv_usec = 0;
|
|
status = select(FD_SETSIZE, (fd_set *) &readfds, NULL, NULL, (struct timeval *) &timeout);
|
|
if (FD_ISSET(_sock, &readfds)) {
|
|
#ifdef HAVE_SSL
|
|
if (_ssl_flag) {
|
|
i = SSL_read(_ssl_con, net_buffer + len, net_buffer_size - len);
|
|
if (i <= 0) {
|
|
int ssl_error = SSL_get_error(_ssl_con, i);
|
|
if (ssl_error == SSL_ERROR_WANT_READ ||
|
|
ssl_error == SSL_ERROR_WANT_WRITE)
|
|
continue; // continue reading if SSL layer wants more data
|
|
}
|
|
} else
|
|
#endif
|
|
i = recv(_sock, net_buffer + len, net_buffer_size - len, 0);
|
|
|
|
if (get_verbose() == VERBOSE_URL)
|
|
eprintf("Connection #%d received %d bytes on socket %d\n", i_conn, i, _sock);
|
|
|
|
} else
|
|
break;
|
|
|
|
/* abort if connection got broken */
|
|
if (i < 0) {
|
|
broken = TRUE;
|
|
break;
|
|
}
|
|
/* abort if connection has been closed */
|
|
if (i == 0) {
|
|
broken = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (i > 0)
|
|
len += i;
|
|
|
|
/* check if net_buffer needs to be increased */
|
|
if (len == net_buffer_size) {
|
|
net_buffer = (char *)xrealloc(net_buffer, net_buffer_size + 100000);
|
|
if (net_buffer == NULL) {
|
|
sprintf(str,
|
|
"Error: Cannot increase net_buffer, out of memory, net_buffer_size = %d",
|
|
net_buffer_size);
|
|
show_error(str);
|
|
break;
|
|
}
|
|
|
|
if (net_buffer_size >= 1024*1024*1024) {
|
|
sprintf(str, "Error: Request extends 1 GB, dropped");
|
|
show_error(str);
|
|
break;
|
|
}
|
|
|
|
memset(net_buffer + net_buffer_size, 0, 100000);
|
|
net_buffer_size += 100000;
|
|
}
|
|
|
|
/* repeat until empty line received (fragmented TCP packets!) */
|
|
} while (strstr(net_buffer, "\r\n\r\n") == 0);
|
|
}
|
|
|
|
if (broken)
|
|
break;
|
|
|
|
/* if we are in pipelining mode, clear this flag now to force a new
|
|
recv if the request is not complete */
|
|
more_requests = 0;
|
|
|
|
pend = NULL;
|
|
if (strncmp(net_buffer, "GET", 3) == 0 && strncmp(net_buffer, "POST", 4) != 0) {
|
|
if (len > 4 && strstr(net_buffer, "\r\n\r\n") != NULL) {
|
|
pend = strstr(net_buffer, "\r\n\r\n") + 4;
|
|
break;
|
|
}
|
|
if (len > 6 && strstr(net_buffer, "\r\r\n\r\r\n") != NULL) {
|
|
pend = strstr(net_buffer, "\r\r\n\r\r\n") + 6;
|
|
break;
|
|
}
|
|
} else if (strncmp(net_buffer, "POST", 4) == 0) {
|
|
if (header_length == 0) {
|
|
/* extract logbook */
|
|
strlcpy(str, net_buffer + 6, sizeof(str));
|
|
if (strstr(str, "HTTP"))
|
|
*(strstr(str, "HTTP") - 1) = 0;
|
|
strlcpy(logbook, str, sizeof(logbook));
|
|
strlcpy(logbook_enc, str, sizeof(logbook));
|
|
url_decode(logbook);
|
|
|
|
/* extract content length */
|
|
if (strstr(net_buffer, "Content-Length:"))
|
|
content_length = atoi(strstr(net_buffer, "Content-Length:") + 15);
|
|
else if (strstr(net_buffer, "Content-length:"))
|
|
content_length = atoi(strstr(net_buffer, "Content-length:") + 15);
|
|
|
|
/* check for valid content-length */
|
|
if (content_length < 0) {
|
|
broken = TRUE;
|
|
break;
|
|
}
|
|
|
|
/* extract header length */
|
|
if (strstr(net_buffer, "\r\n\r\n"))
|
|
header_length = strstr(net_buffer, "\r\n\r\n") - net_buffer + 4;
|
|
if (strstr(net_buffer, "\r\r\n\r\r\n"))
|
|
header_length = strstr(net_buffer, "\r\r\n\r\r\n") - net_buffer + 6;
|
|
|
|
if (content_length > _max_content_length) {
|
|
|
|
/* drain socket connection */
|
|
do {
|
|
FD_ZERO(&readfds);
|
|
FD_SET(_sock, &readfds);
|
|
timeout.tv_sec = 6;
|
|
timeout.tv_usec = 0;
|
|
status = select(FD_SETSIZE, (fd_set *) &readfds, NULL, NULL, (struct timeval *) &timeout);
|
|
if (FD_ISSET(_sock, &readfds)) {
|
|
#ifdef HAVE_SSL
|
|
if (_ssl_flag)
|
|
i = SSL_read(_ssl_con, net_buffer, net_buffer_size);
|
|
else
|
|
#endif
|
|
i = recv(_sock, net_buffer, net_buffer_size, 0);
|
|
} else
|
|
break;
|
|
} while (i > 0);
|
|
|
|
/* return error */
|
|
memset(return_buffer, 0, return_buffer_size);
|
|
strlen_retbuf = 0;
|
|
return_length = 0;
|
|
|
|
sprintf(str,
|
|
loc("Error: Content length (%d) larger than maximum content length (%d)"),
|
|
content_length, _max_content_length);
|
|
strcat(str, "<br>");
|
|
strcat(str,
|
|
loc
|
|
("Please increase <b>\"Max content length\"</b> in [global] part of config file and restart elogd"));
|
|
keep_alive = FALSE;
|
|
show_error(str);
|
|
#ifdef HAVE_SSL
|
|
send_return(_sock, _ssl_con, net_buffer);
|
|
#else
|
|
send_return(_sock, net_buffer);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (header_length > 0 && len >= header_length + content_length) {
|
|
pend = net_buffer + header_length + content_length;
|
|
break;
|
|
}
|
|
|
|
} else if (strstr(net_buffer, "HEAD") != NULL) {
|
|
/* just return header */
|
|
rsprintf("HTTP/1.1 200 OK\r\n");
|
|
rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
|
|
rsprintf("Connection: close\r\n");
|
|
rsprintf("Content-Type: text/html\r\n\r\n");
|
|
keep_alive = FALSE;
|
|
return_length = strlen_retbuf + 1;
|
|
break;
|
|
} else if (strstr(net_buffer, "OPTIONS") != NULL) {
|
|
return_length = -1;
|
|
break;
|
|
} else {
|
|
if (strlen(net_buffer) > 0 && get_verbose() >= VERBOSE_INFO) {
|
|
strcpy(str, "Received unknown HTTP command: ");
|
|
strencode2(str, net_buffer, sizeof(str));
|
|
show_error(str);
|
|
}
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
|
|
if (broken) {
|
|
if (get_verbose() >= VERBOSE_URL)
|
|
eprintf("TCP connection #%d on socket %d closed\n", i_conn, _sock);
|
|
keep_alive = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (strncmp(net_buffer, "POST", 4) == 0 && len < header_length + content_length) {
|
|
if (get_verbose() >= VERBOSE_INFO)
|
|
eprintf("Incomplete POST request\n");
|
|
keep_alive = FALSE;
|
|
break;
|
|
}
|
|
|
|
/* now process HTTP request and put the result into the return_buffer */
|
|
if (process_http_request(net_buffer, i_conn)) {
|
|
|
|
/* send back the return_buffer to the browser */
|
|
#ifdef HAVE_SSL
|
|
send_return(_sock, _ssl_con, net_buffer);
|
|
#else
|
|
send_return(_sock, net_buffer);
|
|
#endif
|
|
}
|
|
|
|
/* check if the net_buffer contains more than one request (pipelining) */
|
|
if (pend && *pend) {
|
|
memmove(net_buffer, pend, strlen(pend) + 1);
|
|
more_requests = 1;
|
|
len -= (pend - net_buffer);
|
|
}
|
|
|
|
} while (more_requests);
|
|
|
|
if (!keep_alive) {
|
|
#ifdef HAVE_SSL
|
|
if (_ssl_flag) {
|
|
SSL_shutdown(_ssl_con);
|
|
SSL_free(_ssl_con);
|
|
}
|
|
#endif
|
|
|
|
closesocket(_sock);
|
|
ka_sock[i_conn] = 0;
|
|
#ifdef HAVE_SSL
|
|
ka_ssl_con[i_conn] = NULL;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#ifdef OS_WINNT
|
|
|
|
/* under windows, check if configuration changed (via stat()) once each access */
|
|
check_config();
|
|
|
|
#else
|
|
|
|
/* under unix, rely on "kill -HUP elogd" */
|
|
if (_hup) {
|
|
/* reload configuration */
|
|
check_config();
|
|
el_index_logbooks();
|
|
_hup = FALSE;
|
|
}
|
|
#endif
|
|
|
|
/* check for periodic tasks */
|
|
check_cron();
|
|
|
|
} while (!_abort);
|
|
|
|
eprintf("elogd server aborted.\n");
|
|
|
|
/* free all allocated memory */
|
|
for (i = 0; lb_list[i].name[0]; i++) {
|
|
if (lb_list[i].el_index) {
|
|
xfree(lb_list[i].el_index);
|
|
lb_list[i].el_index = NULL;
|
|
}
|
|
|
|
if (lb_list[i].n_el_index) {
|
|
xfree(lb_list[i].n_el_index);
|
|
lb_list[i].n_el_index = NULL;
|
|
}
|
|
}
|
|
|
|
xfree(net_buffer);
|
|
xfree(return_buffer);
|
|
free_config();
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int ss_getchar(BOOL reset)
|
|
/********************************************************************
|
|
Routine: ss_getchar
|
|
|
|
Purpose: Read a single character
|
|
|
|
Input:
|
|
BOOL reset Reset terminal to standard mode
|
|
|
|
Output:
|
|
<none>
|
|
|
|
Function value:
|
|
int 0 for no character available
|
|
n ASCII code for normal character
|
|
|
|
\********************************************************************/
|
|
{
|
|
#ifdef OS_UNIX
|
|
|
|
static BOOL init = FALSE;
|
|
static struct termios save_termios;
|
|
struct termios buf;
|
|
int i, fd;
|
|
char c[3];
|
|
|
|
fd = fileno(stdin);
|
|
|
|
if (reset) {
|
|
if (init)
|
|
tcsetattr(fd, TCSAFLUSH, &save_termios);
|
|
init = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
if (!init) {
|
|
tcgetattr(fd, &save_termios);
|
|
memcpy(&buf, &save_termios, sizeof(buf));
|
|
|
|
buf.c_lflag &= ~(ECHO | ICANON | IEXTEN);
|
|
|
|
buf.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON);
|
|
|
|
buf.c_cflag &= ~(CSIZE | PARENB);
|
|
buf.c_cflag |= CS8;
|
|
/* buf.c_oflag &= ~(OPOST); */
|
|
buf.c_cc[VMIN] = 0;
|
|
buf.c_cc[VTIME] = 0;
|
|
|
|
tcsetattr(fd, TCSAFLUSH, &buf);
|
|
init = TRUE;
|
|
}
|
|
|
|
memset(c, 0, 3);
|
|
i = my_read(fd, c, 1);
|
|
|
|
if (i == 0)
|
|
return 0;
|
|
|
|
/* BS/DEL -> BS */
|
|
if (c[0] == 127)
|
|
return 8;
|
|
|
|
return c[0];
|
|
|
|
#elif defined(OS_WINNT)
|
|
|
|
static BOOL init = FALSE;
|
|
static int repeat_count = 0;
|
|
static int repeat_char;
|
|
HANDLE hConsole;
|
|
DWORD nCharsRead;
|
|
INPUT_RECORD ir;
|
|
OSVERSIONINFO vi;
|
|
|
|
/* find out if we are under W95 */
|
|
vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
GetVersionEx(&vi);
|
|
|
|
if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
|
|
/* under W95, console doesn't work properly */
|
|
int c;
|
|
|
|
if (!kbhit())
|
|
return 0;
|
|
|
|
c = getch();
|
|
return c;
|
|
}
|
|
|
|
hConsole = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
if (reset) {
|
|
SetConsoleMode(hConsole, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT |
|
|
ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT);
|
|
init = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
if (!init) {
|
|
SetConsoleMode(hConsole, ENABLE_PROCESSED_INPUT);
|
|
init = TRUE;
|
|
}
|
|
|
|
if (repeat_count) {
|
|
repeat_count--;
|
|
return repeat_char;
|
|
}
|
|
|
|
PeekConsoleInput(hConsole, &ir, 1, &nCharsRead);
|
|
|
|
if (nCharsRead == 0)
|
|
return 0;
|
|
|
|
ReadConsoleInput(hConsole, &ir, 1, &nCharsRead);
|
|
|
|
if (ir.EventType != KEY_EVENT)
|
|
return ss_getchar(0);
|
|
|
|
if (!ir.Event.KeyEvent.bKeyDown)
|
|
return ss_getchar(0);
|
|
|
|
if (ir.Event.KeyEvent.wRepeatCount > 1) {
|
|
repeat_count = ir.Event.KeyEvent.wRepeatCount - 1;
|
|
repeat_char = ir.Event.KeyEvent.uChar.AsciiChar;
|
|
return repeat_char;
|
|
}
|
|
|
|
if (ir.Event.KeyEvent.uChar.AsciiChar)
|
|
return ir.Event.KeyEvent.uChar.AsciiChar;
|
|
|
|
if (ir.Event.KeyEvent.dwControlKeyState & (ENHANCED_KEY))
|
|
return ir.Event.KeyEvent.wVirtualKeyCode;
|
|
|
|
return ss_getchar(0);
|
|
#endif
|
|
}
|
|
|
|
int read_password(char *pwd, int size) {
|
|
int n;
|
|
char c, str[256];
|
|
|
|
n = 0;
|
|
do {
|
|
c = ss_getchar(0);
|
|
if (c == 13)
|
|
break;
|
|
|
|
if (c) {
|
|
|
|
if (c == 8) {
|
|
if (n > 0) {
|
|
str[--n] = 0;
|
|
eprintf("\b \b");
|
|
}
|
|
} else {
|
|
str[n++] = c;
|
|
eprintf("*");
|
|
}
|
|
#ifdef OS_WINNT
|
|
Sleep(10);
|
|
#endif
|
|
}
|
|
|
|
} while (1);
|
|
str[n] = 0;
|
|
|
|
ss_getchar(1); // reset
|
|
|
|
strlcpy(pwd, str, size);
|
|
return n;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
void create_password(const char *logbook, const char *name, const char *pwd) {
|
|
int fh, length, i;
|
|
char *cfgbuffer, str[256], *p;
|
|
|
|
fh = open(config_file, O_RDONLY);
|
|
if (fh < 0) {
|
|
/* create new file */
|
|
fh = open(config_file, O_CREAT | O_WRONLY, 0640);
|
|
if (fh < 0) {
|
|
eprintf("Cannot create \"%s\".\n", config_file);
|
|
return;
|
|
}
|
|
sprintf(str, "[%s]\n%s = %s\n", logbook, name, pwd);
|
|
write(fh, str, strlen(str));
|
|
close(fh);
|
|
eprintf("File \"%s\" created with password in logbook \"%s\".\n", config_file, logbook);
|
|
return;
|
|
}
|
|
|
|
/* read existing file and add password */
|
|
length = lseek(fh, 0, SEEK_END);
|
|
lseek(fh, 0, SEEK_SET);
|
|
cfgbuffer = (char *)xmalloc(length + 1);
|
|
length = my_read(fh, cfgbuffer, length);
|
|
cfgbuffer[length] = 0;
|
|
close(fh);
|
|
fh = open(config_file, O_TRUNC | O_WRONLY, 0640);
|
|
sprintf(str, "[%s]", logbook);
|
|
/* check if logbook exists already */
|
|
if (strstr(cfgbuffer, str)) {
|
|
p = strstr(cfgbuffer, str);
|
|
/* search password in current logbook */
|
|
do {
|
|
while (*p && *p != '\n')
|
|
p++;
|
|
if (*p && *p == '\n')
|
|
p++;
|
|
if (strncmp(p, name, strlen(name)) == 0) {
|
|
/* replace existing password */
|
|
i = (int) (p - cfgbuffer);
|
|
write(fh, cfgbuffer, i);
|
|
sprintf(str, "%s = %s\n", name, pwd);
|
|
write(fh, str, strlen(str));
|
|
eprintf("Password replaced in logbook \"%s\".\n", logbook);
|
|
while (*p && *p != '\n')
|
|
p++;
|
|
if (*p && *p == '\n')
|
|
p++;
|
|
/* write remainder of file */
|
|
write(fh, p, strlen(p));
|
|
xfree(cfgbuffer);
|
|
cfgbuffer = NULL;
|
|
close(fh);
|
|
return;
|
|
}
|
|
|
|
} while (*p && *p != '[');
|
|
if (!*p || *p == '[') {
|
|
/* enter password into current logbook */
|
|
p = strstr(cfgbuffer, str);
|
|
while (*p && *p != '\n')
|
|
p++;
|
|
if (*p && *p == '\n')
|
|
p++;
|
|
i = (int) (p - cfgbuffer);
|
|
write(fh, cfgbuffer, i);
|
|
sprintf(str, "%s = %s\n", name, pwd);
|
|
write(fh, str, strlen(str));
|
|
eprintf("Password added to logbook \"%s\".\n", logbook);
|
|
/* write remainder of file */
|
|
write(fh, p, strlen(p));
|
|
xfree(cfgbuffer);
|
|
cfgbuffer = NULL;
|
|
close(fh);
|
|
return;
|
|
}
|
|
} else { /* write new logbook entry */
|
|
|
|
write(fh, cfgbuffer, strlen(cfgbuffer));
|
|
sprintf(str, "\n[%s]\n%s = %s\n\n", logbook, name, pwd);
|
|
write(fh, str, strlen(str));
|
|
eprintf("Password added to new logbook \"%s\".\n", logbook);
|
|
}
|
|
|
|
xfree(cfgbuffer);
|
|
cfgbuffer = NULL;
|
|
close(fh);
|
|
}
|
|
|
|
void cleanup(void) {
|
|
#ifdef OS_UNIX
|
|
char str[1024];
|
|
struct stat finfo;
|
|
|
|
/* regain original uid */
|
|
if (setregid(-1, orig_gid) < 0 || setreuid(-1, orig_uid) < 0)
|
|
eprintf("Cannot restore original GID/UID.\n");
|
|
if (pidfile[0] && stat(pidfile, &finfo) >= 0) {
|
|
if (remove(pidfile) < 0) {
|
|
sprintf(str, "Cannot remove pidfile \"%s\"\n", pidfile);
|
|
eprintf("%s; %s\n", str, strerror(errno));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (running_as_daemon)
|
|
#ifdef OS_UNIX
|
|
closelog();
|
|
#else
|
|
DeregisterEventSource(hEventLog);
|
|
#endif
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
#ifdef OS_WINNT
|
|
|
|
/* Routines for Windows service management */
|
|
|
|
// Executable name
|
|
#define ELOGDAPPNAME "elogd"
|
|
|
|
// Internal service name
|
|
#define ELOGDSERVICENAME "elogd"
|
|
|
|
// Displayed service name
|
|
#define ELOGDSERVICEDISPLAYNAME "elogd"
|
|
|
|
SERVICE_STATUS serviceStatus;
|
|
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
|
|
|
|
int install_service(void)
|
|
{
|
|
OSVERSIONINFO vi;
|
|
char path[2048], dir[2048], cmd[2080];
|
|
SC_HANDLE hservice;
|
|
SC_HANDLE hsrvmanager;
|
|
|
|
/* check for Windows NT+ */
|
|
|
|
vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
GetVersionEx(&vi);
|
|
if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
|
|
eprintf("Can install service only under Windows NT/2k/XP.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (GetModuleFileName(NULL, path, sizeof(path)) == 0) {
|
|
eprintf("Cannot retrieve module file name.\n");
|
|
return -1;
|
|
}
|
|
|
|
strcpy(dir, path);
|
|
if (strrchr(dir, '\\'))
|
|
*(strrchr(dir, '\\') + 1) = 0;
|
|
|
|
sprintf(cmd, "\"%s\" -D -c \"%s%s\"", path, dir, CFGFILE);
|
|
|
|
/* Open the default, local Service Control Manager database */
|
|
hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
|
if (hsrvmanager == NULL) {
|
|
eprintf("Cannot connect to Service Control Manager.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Create an entry for the elogd service */
|
|
hservice = CreateService(hsrvmanager, // SCManager database
|
|
ELOGDSERVICENAME, // name of service
|
|
ELOGDSERVICEDISPLAYNAME, // name to display
|
|
SERVICE_ALL_ACCESS, // desired access
|
|
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
|
|
// service type
|
|
SERVICE_AUTO_START, // start type
|
|
SERVICE_ERROR_NORMAL, // error control type
|
|
cmd, // service's binary
|
|
NULL, // no load ordering group
|
|
NULL, // no tag identifier
|
|
"", // dependencies
|
|
NULL, // LocalSystem account
|
|
NULL); // no password
|
|
|
|
if (hservice == NULL) {
|
|
if (GetLastError() == ERROR_SERVICE_EXISTS)
|
|
eprintf("The elogd service is already registered.\n");
|
|
else
|
|
eprintf("The elogd service could not be registered. Error code %d.\n", GetLastError());
|
|
} else {
|
|
eprintf("The elogd service has been registered successfully.\n");
|
|
CloseServiceHandle(hservice);
|
|
}
|
|
|
|
/* Try to start the elogd service */
|
|
hservice = OpenService(hsrvmanager, ELOGDSERVICENAME, SERVICE_ALL_ACCESS);
|
|
|
|
if (hservice == NULL)
|
|
eprintf("The elogd service could not be accessed. Error code %d.\n", GetLastError());
|
|
else {
|
|
if (!StartService(hservice, 0, NULL))
|
|
eprintf("The elogd service could not be started. Error code %d.\n", GetLastError());
|
|
else
|
|
eprintf("The elogd service has been started successfully.\n");
|
|
|
|
CloseServiceHandle(hservice);
|
|
}
|
|
|
|
CloseServiceHandle(hsrvmanager);
|
|
return 1;
|
|
}
|
|
|
|
int remove_service(int silent)
|
|
{
|
|
SC_HANDLE hservice;
|
|
SC_HANDLE hsrvmanager;
|
|
SERVICE_STATUS status;
|
|
|
|
/* Open the SCM */
|
|
hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
|
if (hsrvmanager == NULL) {
|
|
if (!silent)
|
|
eprintf("Cannot connect to Service Control Manager.\n");
|
|
return -1;
|
|
}
|
|
|
|
hservice = OpenService(hsrvmanager, ELOGDSERVICENAME, SERVICE_ALL_ACCESS);
|
|
if (hservice == NULL) {
|
|
if (!silent)
|
|
eprintf("The elogd service could not be found.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Try to stop the elogd service */
|
|
if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
|
|
while (QueryServiceStatus(hservice, &status)) {
|
|
if (status.dwCurrentState == SERVICE_STOP_PENDING)
|
|
Sleep(100);
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (!silent) {
|
|
if (status.dwCurrentState != SERVICE_STOPPED) {
|
|
eprintf("The elogd service could not be stopped.\n");
|
|
} else
|
|
eprintf("elogd service stopped successfully.\n");
|
|
}
|
|
}
|
|
|
|
/* Now remove the service from the SCM */
|
|
if (!DeleteService(hservice)) {
|
|
if (!silent) {
|
|
if (GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE)
|
|
eprintf("The elogd service is already marked to be unregistered.\n");
|
|
else
|
|
eprintf("The elogd service could not be unregistered.\n");
|
|
}
|
|
} else if (!silent)
|
|
eprintf("The elogd service hass been unregistered successfully.\n");
|
|
|
|
CloseServiceHandle(hservice);
|
|
CloseServiceHandle(hsrvmanager);
|
|
return 1;
|
|
}
|
|
|
|
void WINAPI ServiceControlHandler(DWORD controlCode)
|
|
{
|
|
switch (controlCode) {
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
break;
|
|
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
case SERVICE_CONTROL_STOP:
|
|
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
_abort = TRUE;
|
|
|
|
return;
|
|
|
|
case SERVICE_CONTROL_PAUSE:
|
|
break;
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
break;
|
|
|
|
default:
|
|
if (controlCode >= 128 && controlCode <= 255)
|
|
// user defined control code
|
|
break;
|
|
else
|
|
// unrecognised control code
|
|
break;
|
|
}
|
|
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
}
|
|
|
|
void WINAPI ServiceMain(DWORD argc, LPSTR * argv)
|
|
{
|
|
// initialise service status
|
|
serviceStatus.dwServiceType = SERVICE_WIN32;
|
|
serviceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
serviceStatus.dwControlsAccepted = 0;
|
|
serviceStatus.dwWin32ExitCode = NO_ERROR;
|
|
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
|
|
serviceStatus.dwCheckPoint = 0;
|
|
serviceStatus.dwWaitHint = 0;
|
|
|
|
serviceStatusHandle = RegisterServiceCtrlHandler(ELOGDSERVICENAME, ServiceControlHandler);
|
|
|
|
if (serviceStatusHandle) {
|
|
// service is starting
|
|
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
// running
|
|
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
|
|
serviceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
/* avoid recursive calls to run_service */
|
|
running_as_daemon = FALSE;
|
|
|
|
/* Redirect all messages handled with eprintf/efputs to syslog */
|
|
redirect_to_syslog();
|
|
|
|
/* start main server, exit with "_abort = TRUE" */
|
|
server_loop();
|
|
|
|
// service was stopped
|
|
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
|
|
// service is now stopped
|
|
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
|
|
serviceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
SetServiceStatus(serviceStatusHandle, &serviceStatus);
|
|
}
|
|
}
|
|
|
|
int run_service(void)
|
|
{
|
|
SERVICE_TABLE_ENTRY serviceTable[] = {
|
|
{ELOGDSERVICENAME, ServiceMain},
|
|
{0, 0}
|
|
};
|
|
|
|
if (!StartServiceCtrlDispatcher(serviceTable))
|
|
return FAILURE;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int i, j, n, fh, tcp_port_cl, sync_flag, silent;
|
|
char smtp_pwd[80], str[256], logbook[256], clone_url[256], error_str[256], file_name[256];
|
|
time_t now;
|
|
struct tm *tms;
|
|
struct stat finfo;
|
|
|
|
#ifdef OS_UNIX
|
|
/* save gid/uid to regain later */
|
|
orig_gid = getegid();
|
|
orig_uid = geteuid();
|
|
pidfile[0] = 0;
|
|
#endif
|
|
|
|
/* register cleanup function */
|
|
atexit(cleanup);
|
|
|
|
tzset();
|
|
|
|
/* initialize variables */
|
|
smtp_pwd[0] = 0;
|
|
logbook_dir[0] = 0;
|
|
logbook[0] = clone_url[0] = resource_dir[0] = logbook_dir[0] = 0;
|
|
silent = tcp_port_cl = sync_flag = 0;
|
|
use_keepalive = TRUE;
|
|
running_as_daemon = FALSE;
|
|
|
|
/* initialize random number generator */
|
|
srand((unsigned) time(NULL));
|
|
|
|
/*
|
|
* Initially, redirect all messages handled with eprintf/efputs to stderr.
|
|
* Note that we should use eprintf/efputs wrappers for all logging purposes,
|
|
* but it is OK to use a printf for things like command line parsing till
|
|
* we switch to daemon mode (if required).
|
|
*/
|
|
redirect_to_stderr();
|
|
|
|
/* evaluate predefined files and directories */
|
|
#ifdef CONFIG_PATH
|
|
strcpy(config_file, CONFIG_PATH);
|
|
if (config_file[0] && config_file[strlen(config_file) - 1] != DIR_SEPARATOR)
|
|
strlcat(config_file, DIR_SEPARATOR_STR, sizeof(config_file));
|
|
#endif
|
|
/* default config file */
|
|
strlcat(config_file, CFGFILE, sizeof(config_file));
|
|
|
|
/* parse command line parameters */
|
|
for (i = 1; i < argc; i++) {
|
|
if (argv[i][0] == '-' && argv[i][1] == 'D')
|
|
running_as_daemon = TRUE;
|
|
else if (argv[i][0] == '-' && argv[i][1] == 'v') {
|
|
if (i < argc - 1 && atoi(argv[i + 1]) > 0) {
|
|
set_verbose(atoi(argv[i + 1]));
|
|
i++;
|
|
} else
|
|
set_verbose(VERBOSE_INFO);
|
|
} else if (argv[i][0] == '-' && argv[i][1] == 'k')
|
|
use_keepalive = FALSE;
|
|
else if (argv[i][0] == '-' && argv[i][1] == 'S')
|
|
silent = TRUE;
|
|
else if (argv[i][0] == '-' && argv[i][1] == 'x')
|
|
enable_execute = TRUE;
|
|
else if (argv[i][0] == '-' && argv[i][1] == 'm')
|
|
sync_flag = 1;
|
|
else if (argv[i][0] == '-' && argv[i][1] == 'M')
|
|
sync_flag = 2;
|
|
else if (argv[i][1] == 'T') {
|
|
time(&now);
|
|
tms = localtime(&now);
|
|
assert(tms);
|
|
printf("Actual date/time: %02d%02d%02d_%02d%02d%02d\n", tms->tm_year % 100, tms->tm_mon + 1,
|
|
tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
#ifdef OS_WINNT
|
|
else if (stricmp(argv[i], "-install") == 0) {
|
|
install_service();
|
|
if (!silent) {
|
|
printf("Please hit any key ...");
|
|
fgets(str, sizeof(str), stdin);
|
|
}
|
|
exit(EXIT_SUCCESS);
|
|
} else if (stricmp(argv[i], "-remove") == 0) {
|
|
remove_service(silent);
|
|
if (!silent) {
|
|
printf("Please hit any key ...");
|
|
fgets(str, sizeof(str), stdin);
|
|
}
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
#endif
|
|
|
|
else if (argv[i][0] == '-') {
|
|
if (argv[i][1] == 'C') {
|
|
if (i + 1 >= argc || argv[i + 1][0] == '-')
|
|
clone_url[0] = 1;
|
|
else
|
|
strlcpy(clone_url, argv[++i], sizeof(clone_url));
|
|
} else if (i + 1 >= argc || argv[i + 1][0] == '-')
|
|
goto usage;
|
|
else if (argv[i][1] == 'p')
|
|
tcp_port_cl = atoi(argv[++i]);
|
|
else if (argv[i][1] == 'c')
|
|
strlcpy(config_file, argv[++i], sizeof(config_file));
|
|
else if (argv[i][1] == 's')
|
|
strlcpy(resource_dir, argv[++i], sizeof(resource_dir));
|
|
else if (argv[i][1] == 'd')
|
|
strlcpy(logbook_dir, argv[++i], sizeof(logbook_dir));
|
|
else if (argv[i][1] == 't')
|
|
strlcpy(smtp_pwd, argv[++i], sizeof(smtp_pwd));
|
|
else if (argv[i][1] == 'l')
|
|
strlcpy(logbook, argv[++i], sizeof(logbook));
|
|
else if (argv[i][1] == 'n')
|
|
strlcpy(listen_interface, argv[++i], sizeof(listen_interface));
|
|
#ifdef OS_UNIX
|
|
else if (argv[i][1] == 'f')
|
|
strlcpy(pidfile, argv[++i], sizeof(pidfile));
|
|
#endif
|
|
else {
|
|
usage:
|
|
printf("%s\n", ELOGID);
|
|
printf("usage: elogd [-C <url>] [-c <file>] [-D] [-d <dir>] ");
|
|
printf("[-f <file>] [-h] [-k] [-l <logbook>] [-M] [-m] [-n <interface>] ");
|
|
printf("[-p <port>] [-S] [-s <dir>] [-t <pwd>] [-v] [-x]\n\n");
|
|
printf(" -C <url> clone remote elogd configuration\n");
|
|
printf(" -c <file> specify configuration file\n");
|
|
printf(" -M synchronize with removing deleted entries\n");
|
|
printf(" -m synchronize logbook(s) with remote server\n");
|
|
printf(" -D become a daemon\n");
|
|
printf(" -d <dir> specify logbook root directory\n");
|
|
#ifdef OS_UNIX
|
|
printf(" -f <file> PID file\n");
|
|
#endif
|
|
printf(" -h this help\n");
|
|
printf(" -k do not use keep-alive\n");
|
|
printf(" -l <logbook> specify logbook for -r, -w and -m commands\n");
|
|
printf(" -n hostname/IP interface to listen on\n");
|
|
printf(" -p <port> TCP/IP port\n");
|
|
printf(" -s <dir> specify resource directory (themes, icons, ...)\n");
|
|
printf(" -t <pwd> create/overwrite SMTP password in config file\n");
|
|
printf(" -v <n> verbose output (1:URL, 2:INFO, 3:DEBUG)\n");
|
|
printf(" -x enable execution of shell commands\n\n");
|
|
#ifdef OS_WINNT
|
|
printf("Windows service funtions:\n");
|
|
printf(" -install install elogd as service and start it\n");
|
|
printf(" -remove stop and remove elogd service\n");
|
|
#endif
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef OS_WINNT
|
|
{
|
|
WSADATA WSAData;
|
|
|
|
/* Start windows sockets */
|
|
if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef OS_WINNT
|
|
if (running_as_daemon) {
|
|
/* change to directory of executable */
|
|
strcpy(str, argv[0]);
|
|
for (i = strlen(str) - 1; i > 0; i--)
|
|
if (str[i] != '\\')
|
|
str[i] = 0;
|
|
else
|
|
break;
|
|
chdir(str);
|
|
}
|
|
#endif
|
|
|
|
/* clone remote elogd configuration */
|
|
if (clone_url[0]) {
|
|
|
|
/* check if local config file exists */
|
|
fh = open(config_file, (O_RDONLY | O_BINARY));
|
|
if (fh > 0) {
|
|
close(fh);
|
|
eprintf("Overwrite local \"%s\"? [y]/n: ", CFGFILE);
|
|
fgets(str, sizeof(str), stdin);
|
|
if (str[0] == 'n' || str[0] == 'N')
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* contact remote server */
|
|
receive_config(NULL, clone_url, error_str);
|
|
if (error_str[0]) {
|
|
eputs(error_str);
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
printf("\nRemote configuration successfully received.\n\n");
|
|
|
|
/* adjust config file */
|
|
adjust_config(clone_url);
|
|
|
|
/* receive logbook entries after set-up of direcories ... */
|
|
}
|
|
}
|
|
|
|
/* check for configuration file */
|
|
fh = open(config_file, (O_RDONLY | O_BINARY));
|
|
if (fh < 0) {
|
|
eprintf("Cannot open \"%s\": %s\n", config_file, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
close(fh);
|
|
|
|
/* parse contents of config file into internal structure */
|
|
check_config();
|
|
|
|
/* evaluate undefined directories from config file or compiled-in defaults */
|
|
if (!resource_dir[0])
|
|
if (getcfg("global", "Resource Dir", str, sizeof(str)))
|
|
strlcpy(resource_dir, str, sizeof(resource_dir));
|
|
#ifdef RESOURCE_DIR
|
|
else
|
|
strlcpy(resource_dir, RESOURCE_DIR, sizeof(resource_dir));
|
|
#endif
|
|
if (!logbook_dir[0])
|
|
if (getcfg("global", "Logbook Dir", str, sizeof(str)))
|
|
strlcpy(logbook_dir, str, sizeof(logbook_dir));
|
|
#ifdef LOGBOOK_DIR
|
|
else
|
|
strlcpy(logbook_dir, LOGBOOK_DIR, sizeof(logbook_dir));
|
|
#endif
|
|
|
|
/* extract resource directory from configuration file if not given */
|
|
if (config_file[0] && strchr(config_file, DIR_SEPARATOR) && !resource_dir[0]) {
|
|
strcpy(resource_dir, config_file);
|
|
for (i = strlen(resource_dir) - 1; i > 0; i--) {
|
|
if (resource_dir[i] == DIR_SEPARATOR) {
|
|
resource_dir[i] = 0;
|
|
break;
|
|
}
|
|
resource_dir[i] = 0;
|
|
}
|
|
}
|
|
|
|
/* do the same for the logbook dir */
|
|
if (config_file[0] && strchr(config_file, DIR_SEPARATOR) && !logbook_dir[0]) {
|
|
strcpy(logbook_dir, config_file);
|
|
for (i = strlen(logbook_dir) - 1; i > 0; i--) {
|
|
if (logbook_dir[i] == DIR_SEPARATOR)
|
|
break;
|
|
logbook_dir[i] = 0;
|
|
}
|
|
strlcat(logbook_dir, "logbooks", sizeof(logbook_dir));
|
|
}
|
|
|
|
/* set default logbook dir if not given */
|
|
if (!logbook_dir[0])
|
|
strcpy(logbook_dir, "logbooks");
|
|
|
|
/* strip trailing dir separator */
|
|
if (logbook_dir[strlen(logbook_dir) - 1] == DIR_SEPARATOR)
|
|
logbook_dir[strlen(logbook_dir) - 1] = 0;
|
|
|
|
/* check for directories */
|
|
if (logbook_dir[0] && stat(logbook_dir, &finfo) < 0) {
|
|
|
|
#ifdef OS_WINNT
|
|
if (mkdir(logbook_dir) == 0)
|
|
#else
|
|
if (mkdir(logbook_dir, 0755) == 0)
|
|
#endif
|
|
eprintf("Logbook directory \"%s\" successfully created.\n", logbook_dir);
|
|
else {
|
|
eprintf("Cannot create logbook directory \"%s\":%s.\n", logbook_dir, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if (resource_dir[0] && stat(resource_dir, &finfo) < 0) {
|
|
eprintf("Resource directory \"%s\" not found.\n", resource_dir);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* append '/' */
|
|
if (resource_dir[0] && resource_dir[strlen(resource_dir) - 1] != DIR_SEPARATOR)
|
|
strlcat(resource_dir, DIR_SEPARATOR_STR, sizeof(resource_dir));
|
|
if (logbook_dir[0] && logbook_dir[strlen(logbook_dir) - 1] != DIR_SEPARATOR)
|
|
strlcat(logbook_dir, DIR_SEPARATOR_STR, sizeof(logbook_dir));
|
|
|
|
if (sync_flag) {
|
|
el_index_logbooks();
|
|
|
|
if (sync_flag == 2)
|
|
setparam("confirm", "yes");
|
|
|
|
if (logbook[0]) {
|
|
for (i = 0; lb_list[i].name[0]; i++)
|
|
if (stricmp(lb_list[i].name, logbook) != 0)
|
|
break;
|
|
if (!lb_list[i].name[0]) {
|
|
eprintf("Logbook \"%s\" not defined in configuration file\n", logbook);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
synchronize(&lb_list[i], SYNC_CLONE);
|
|
} else
|
|
synchronize(NULL, SYNC_CLONE);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
if (clone_url[0]) {
|
|
|
|
/* force re-read of config file */
|
|
check_config_file(TRUE);
|
|
el_index_logbooks();
|
|
|
|
/* check for retrieving password files */
|
|
for (i = n = 0; lb_list[i].name[0]; i++)
|
|
if (getcfg(lb_list[i].name, "Password file", str, sizeof(str)))
|
|
n++;
|
|
|
|
if (n > 0) {
|
|
eprintf("\nRetrieve remote password files? [y]/n: ");
|
|
fgets(str, sizeof(str), stdin);
|
|
if (str[0] != 'n' && str[0] != 'N')
|
|
for (i = n = 0; lb_list[i].name[0]; i++) {
|
|
|
|
if (lb_list[i].top_group[0])
|
|
setcfg_topgroup(lb_list[i].top_group);
|
|
else
|
|
setcfg_topgroup("");
|
|
|
|
if (getcfg(lb_list[i].name, "Password file", file_name, sizeof(file_name))) {
|
|
|
|
/* check if this file has not already been retrieved */
|
|
for (j = 0; j < i; j++) {
|
|
if (lb_list[j].top_group[0])
|
|
setcfg_topgroup(lb_list[j].top_group);
|
|
else
|
|
setcfg_topgroup("");
|
|
|
|
if (getcfg(lb_list[j].name, "Password file", str, sizeof(str))
|
|
&& stricmp(file_name, str) == 0)
|
|
break;
|
|
}
|
|
|
|
if (lb_list[i].top_group[0])
|
|
setcfg_topgroup(lb_list[i].top_group);
|
|
else
|
|
setcfg_topgroup("");
|
|
|
|
if (j == i) {
|
|
receive_pwdfile(&lb_list[i], clone_url, error_str);
|
|
if (error_str[0]) {
|
|
eputs(error_str);
|
|
exit(EXIT_FAILURE);
|
|
} else
|
|
eprintf("File \"%s\" received successfully.\n", file_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
eprintf("\nRetrieve remote logbook entries? [y]/n: ");
|
|
fgets(str, sizeof(str), stdin);
|
|
if (str[0] != 'n' && str[0] != 'N')
|
|
/* synchronize all logbooks */
|
|
synchronize(NULL, SYNC_CLONE);
|
|
|
|
puts("\nCloning finished. Check " CFGFILE " and start the server normally.");
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
if (smtp_pwd[0]) {
|
|
do_crypt(smtp_pwd, str, sizeof(str));
|
|
create_password("global", "SMTP Password", str);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
/* get listen interface */
|
|
if (listen_interface[0] == 0)
|
|
if (getcfg("global", "interface", str, sizeof(str))) {
|
|
strlcpy(listen_interface, str, sizeof(listen_interface));
|
|
}
|
|
|
|
/* get default port */
|
|
if (getcfg("global", "SSL", str, sizeof(str)) && atoi(str) == 1)
|
|
elog_tcp_port = 443;
|
|
else
|
|
elog_tcp_port = 80;
|
|
|
|
/* get port from configuration file */
|
|
if (tcp_port_cl != 0)
|
|
elog_tcp_port = tcp_port_cl;
|
|
else {
|
|
if (getcfg("global", "Port", str, sizeof(str)))
|
|
elog_tcp_port = atoi(str);
|
|
}
|
|
|
|
/* get optional content length from configuration file */
|
|
if (getcfg("global", "Max content length", str, sizeof(str)))
|
|
_max_content_length = atoi(str);
|
|
|
|
#ifdef OS_WINNT
|
|
/* if running as a service, server_loop gets called from the service main routine */
|
|
if (running_as_daemon) {
|
|
redirect_to_syslog();
|
|
|
|
if (!run_service()) {
|
|
eprintf("Couldn't run the service; aborting\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else
|
|
server_loop();
|
|
#else
|
|
|
|
server_loop();
|
|
|
|
#endif
|
|
|
|
/* avoid compiler warning */
|
|
if (silent == 1)
|
|
silent = 0;
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|