/********************************************************************\ Name: elogd.c Created by: Stefan Ritt Contents: Web server program for Electronic Logbook ELOG $Id: elogd.c 1762 2006-11-28 11:20:58Z ritt $ \********************************************************************/ /* Version of ELOG */ #define VERSION "2.6.3" char svn_revision[] = "$Id: elogd.c 1762 2006-11-28 11:20:58Z ritt $"; /* ELOG identification */ static const char ELOGID[] = "elogd " VERSION " built " __DATE__ ", " __TIME__; #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include /* Default name of the configuration file. */ #ifndef CFGFILE #define CFGFILE "elogd.cfg" #endif /* Default TCP port for server. */ #ifndef DEFAULT_PORT #define DEFAULT_PORT 80 #endif #ifdef _MSC_VER #define OS_WINNT #define DIR_SEPARATOR '\\' #define DIR_SEPARATOR_STR "\\" #define snprintf _snprintf #include #include #include #include #include #include #else #define OS_UNIX #ifdef __APPLE__ #define OS_MACOSX #endif #define TRUE 1 #define FALSE 0 #ifndef O_TEXT #define O_TEXT 0 #define O_BINARY 0 #endif #define DIR_SEPARATOR '/' #define DIR_SEPARATOR_STR "/" #ifndef DEFAULT_USER #define DEFAULT_USER "nobody" #endif #ifndef DEFAULT_GROUP #define DEFAULT_GROUP "nogroup" #endif #ifndef PIDFILE #define PIDFILE "/var/run/elogd.pid" #endif #ifndef __USE_XOPEN #define __USE_XOPEN /* needed for crypt() */ #endif typedef int BOOL; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define closesocket(s) close(s) #ifndef stricmp #define stricmp(s1, s2) strcasecmp(s1, s2) #endif 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 /* OS_UNIX */ /* local includes */ #include "regex.h" #include "mxml.h" #include "strlcpy.h" BOOL running_as_daemon; /* Running as a daemon/service? */ int elog_tcp_port = (int) DEFAULT_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 */ #define SYSLOG_PRIORITY LOG_NOTICE /* Default priority for syslog facility */ #define TELL(fh) lseek(fh, 0, SEEK_CUR) #ifdef OS_WINNT #define TRUNCATE(fh) chsize(fh, TELL(fh)) #else #define TRUNCATE(fh) ftruncate(fh, TELL(fh)) #endif #define NAME_LENGTH 1500 #define DEFAULT_TIME_FORMAT "%c" #define DEFAULT_DATE_FORMAT "%x" #define DEFAULT_HTTP_CHARSET "ISO-8859-1" #define SUCCESS 1 #define FAILURE 0 #define EL_SUCCESS 1 #define EL_FIRST_MSG 2 #define EL_LAST_MSG 3 #define EL_NO_MSG 4 #define EL_FILE_ERROR 5 #define EL_UPGRADE 6 #define EL_EMPTY 7 #define EL_MEM_ERROR 8 #define EL_DUPLICATE 9 #define EL_INVAL_FILE 10 #define EL_FIRST 1 #define EL_LAST 2 #define EL_NEXT 3 #define EL_PREV 4 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]; #define MAX_GROUPS 32 #define MAX_PARAM 120 #define MAX_ATTACHMENTS 50 #define MAX_N_LIST 100 #define MAX_N_ATTR 100 #define MAX_REPLY_TO 100 #define CMD_SIZE 10000 #define TEXT_SIZE 250000 #define MAX_PATH_LENGTH 256 #define MAX_CONTENT_LENGTH 10*1024*1024 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 verbose, use_keepalive, enable_execute = FALSE; int _current_message_id; int _logging_level; char *mname[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; char type_list[MAX_N_LIST][NAME_LENGTH] = { "Routine", "Other" }; char category_list[MAX_N_LIST][NAME_LENGTH] = { "General", "Other", }; char author_list[MAX_N_LIST][NAME_LENGTH] = { "" }; /* attribute flags */ #define AF_REQUIRED (1<<0) #define AF_LOCKED (1<<1) #define AF_MULTI (1<<2) #define AF_FIXED_EDIT (1<<3) #define AF_FIXED_REPLY (1<<4) #define AF_ICON (1<<5) #define AF_RADIO (1<<6) #define AF_EXTENDABLE (1<<7) #define AF_DATE (1<<8) #define AF_DATETIME (1<<9) #define AF_TIME (1<<10) #define AF_NUMERIC (1<<11) #define AF_USERLIST (1<<12) #define AF_MUSERLIST (1<<13) #define AF_USEREMAIL (1<<14) #define AF_MUSEREMAIL (1<<15) #define AF_HIDDEN (1<<16) /* attribute format flags */ #define AFF_SAME_LINE 1 #define AFF_MULTI_LINE 2 #define AFF_DATE 4 #define AFF_EXTENDABLE 8 char attr_list[MAX_N_ATTR][NAME_LENGTH]; char attr_options[MAX_N_ATTR][MAX_N_LIST][NAME_LENGTH]; int attr_flags[MAX_N_ATTR]; char attr_list_default[][NAME_LENGTH] = { "Author", "Type", "Category", "Subject", "" }; char attr_options_default[][MAX_N_LIST][NAME_LENGTH] = { {""}, {"Routine", "Other"}, {"General", "Other"}, {""} }; int attr_flags_default[] = { AF_REQUIRED, 0, 0, 0 }; struct { char ext[32]; char type[32]; } 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"}, { ".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"}, { ".THUMB", "image/jpeg"}, { // hard-coded for now... "", ""},}; typedef struct { int message_id; char file_name[32]; time_t file_time; int offset; int in_reply_to; unsigned char md5_digest[16]; } EL_INDEX; typedef struct { char name[256]; char name_enc[256]; char data_dir[256]; char top_group[256]; EL_INDEX *el_index; int *n_el_index; int n_attr; PMXML_NODE pwd_xml_tree; } LOGBOOK; typedef struct { int message_id; unsigned char md5_digest[16]; } MD5_INDEX; typedef struct LBNODE *LBLIST; struct LBNODE { char name[256]; LBLIST *member; int n_members; int is_top; } LBNODE; typedef struct { LOGBOOK *lbs; int index; char string[256]; int in_reply_to; } MSG_LIST; LOGBOOK *lb_list = NULL; #ifdef OS_WINNT int run_service(void); #endif void show_error(char *error); BOOL enum_user_line(LOGBOOK * lbs, int n, char *user, int size); int get_user_line(LOGBOOK * lbs, char *user, char *password, char *full_name, char *email, BOOL email_notify[1000], time_t * last_access); int strbreak(char *str, char list[][NAME_LENGTH], int size, char *brk); int execute_shell(LOGBOOK * lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], char att_file[MAX_ATTACHMENTS][256], char *sh_cmd); BOOL isparam(char *param); char *getparam(char *param); void write_logfile(LOGBOOK * lbs, const char *str); BOOL check_login_user(LOGBOOK * lbs, char *user); LBLIST get_logbook_hierarchy(void); BOOL is_logbook_in_group(LBLIST pgrp, char *logbook); BOOL is_admin_user(char *logbook, char *user); BOOL is_admin_user_global(char *user); void free_logbook_hierarchy(LBLIST root); void show_top_text(LOGBOOK * lbs); void show_bottom_text(LOGBOOK * lbs); int set_attributes(LOGBOOK * lbs, char attributes[][NAME_LENGTH], int n); void show_elog_list(LOGBOOK * lbs, int past_n, int last_n, int page_n, BOOL default_page, char *info); int change_config_line(LOGBOOK * lbs, char *option, char *old_value, char *new_value); int read_password(char *pwd, int size); int getcfg(char *group, char *param, char *value, int vsize); int build_subst_list(LOGBOOK * lbs, char list[][NAME_LENGTH], char value[][NAME_LENGTH], char attrib[][NAME_LENGTH], BOOL format_date); void highlight_searchtext(regex_t * re_buf, char *src, char *dst, BOOL hidden); int parse_config_file(char *config_file); PMXML_NODE load_password_file(LOGBOOK * lbs, char *error, int error_size); int load_password_files(); void compose_base_url(LOGBOOK * lbs, char *base_url, int size); void show_elog_entry(LOGBOOK * lbs, char *dec_path, char *command); char *loc(char *orig); void strencode(char *text); int scan_attributes(char *logbook); int is_inline_attachment(int message_id, char *text, int i); int setgroup(char *str); int setuser(char *str); int setegroup(char *str); int seteuser(char *str); void strencode2(char *b, char *text, int size); /*---- 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) { char c1, c2, *ps, *pp; if (str == NULL || pattern == NULL) return NULL; while (*str) { ps = (char *) str; pp = (char *) pattern; c1 = *ps; c2 = *pp; if (my_toupper(c1) == my_toupper(c2)) { while (*pp) { c1 = *ps; c2 = *pp; if (my_toupper(c1) != my_toupper(c2)) break; ps++; pp++; } if (!*pp) return (char *) str; } str++; } 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; } /* 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, 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 MAX OSX */ long my_timezone() { #if defined(OS_MACOSX) || defined(__FreeBSD__) time_t tp; time(&tp); return -localtime(&tp)->tm_gmtoff; #else return timezone; #endif } /*---- Compose RFC2822 compliant date ---*/ void get_rfc2822_date(char *date, int size, time_t ltime) { time_t now; char buf[256]; int offset; struct tm *ts; /* switch locale temporarily back to english to comply with RFC2822 date format */ setlocale(LC_ALL, "C"); if (ltime == 0) time(&now); else now = ltime; ts = localtime(&now); assert(ts); strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", ts); offset = (-(int) my_timezone()); if (ts->tm_isdst) offset += 3600; snprintf(date, size - 1, "%s %+03d%02d", buf, (int) (offset / 3600), (int) ((abs((int) offset) / 60) % 60)); } /*---- Safe malloc wrappers with out of memory checking from GNU ---*/ static void memory_error_and_abort(char *func) { extern void eprintf(const char *, ...); 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) { void *temp; temp = malloc(bytes); if (temp == 0) memory_error_and_abort("xmalloc"); return (temp); } void *xcalloc(size_t count, size_t bytes) { void *temp; temp = calloc(count, bytes); if (temp == 0) memory_error_and_abort("xcalloc"); return (temp); } void *xrealloc(void *pointer, size_t bytes) { void *temp; temp = pointer ? realloc(pointer, bytes) : malloc(bytes); if (temp == 0) memory_error_and_abort("xrealloc"); return (temp); } void xfree(void *pointer) { if (pointer) free(pointer); } char *xstrdup(const char *string) { char *s; s = (char *) xmalloc(strlen(string) + 1); strcpy(s, string); return s; } /*----------------------- Message handling -------------------------*/ /* Have vasprintf? (seems that only libc6 based Linux has this) */ #ifdef __linux__ # define HAVE_VASPRintF #endif #ifndef HAVE_VASPRintF /* vasprintf implementation taken (and adapted) from GNU libiberty */ static int int_vasprintf(char **result, const char *format, va_list args) { const char *p = format; /* Add one to make sure that it is never zero, which might cause malloc to return NULL. */ int total_width = strlen(format) + 1; va_list ap; #ifdef va_copy va_copy(ap, args); #else memcpy(&ap, &args, sizeof(va_list)); #endif while (*p != '\0') { if (*p++ == '%') { while (strchr("-+ #0", *p)) ++p; if (*p == '*') { ++p; total_width += abs(va_arg(ap, int)); } else total_width += strtoul(p, (char **) &p, 10); if (*p == '.') { ++p; if (*p == '*') { ++p; total_width += abs(va_arg(ap, int)); } else total_width += strtoul(p, (char **) &p, 10); } while (strchr("hlL", *p)) ++p; /* * Should be big enough for any format specifier * except %s and floats. */ total_width += 30; switch (*p) { case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': case 'c': (void) va_arg(ap, int); break; case 'f': case 'e': case 'E': case 'g': case 'G': (void) va_arg(ap, double); /* * Since an ieee double can have an exponent of 307, we'll * make the buffer wide enough to cover the gross case. */ total_width += 307; break; case 's': total_width += strlen(va_arg(ap, char *)); break; case 'p': case 'n': (void) va_arg(ap, char *); break; } p++; } } #ifdef va_copy va_end(ap); #endif *result = (char *) malloc(total_width); if (*result != NULL) return vsprintf(*result, format, args); else return -1; } #if defined (_BSD_VA_LIST_) && defined (__FreeBSD__) int vasprintf(char **result, const char *format, _BSD_VA_LIST_ args) #else int vasprintf(char **result, const char *format, va_list args) #endif { return int_vasprintf(result, format, args); } #endif /* ! HAVE_VASPRintF */ /* Safe replacement for vasprintf (adapted code from Samba) */ int xvasprintf(char **ptr, const char *format, va_list ap) { int n; va_list save; #ifdef va_copy va_copy(save, ap); #else # ifdef __va_copy __va_copy(save, ap); # else save = ap; # endif #endif n = vasprintf(ptr, format, save); if (n == -1 || !*ptr) { printf("Not enough memory"); exit(EXIT_FAILURE); } return n; } /* Driver for printf_handler, drop-in replacement for printf */ void eprintf(const char *format, ...) { va_list ap; char *msg; va_start(ap, format); xvasprintf(&msg, format, ap); va_end(ap); (*printf_handler) (msg); free(msg); } /* Driver for fputs_handler, drop-in replacement for fputs(buf, fd) */ void efputs(const char *buf) { (*fputs_handler) (buf); } /* Dump with the newline, drop-in replacement for puts(buf) */ void eputs(const char *buf) { char *p; p = xmalloc(strlen(buf) + 2); strcpy(p, buf); strcat(p, "\n"); (*fputs_handler) (p); xfree(p); } /* Flush the current output stream */ void eflush(void) { /* Do this only for non-NULL streams (uninitiated stream or a syslog) */ if (current_output_stream != NULL) fflush(current_output_stream); } #ifdef OS_WINNT HANDLE hEventLog; #endif /* Print MSG to syslog */ void print_syslog(const char *msg) { char *p; /* strip trailing \r and \n */ p = xstrdup(msg); while (p[strlen(p) - 1] == '\r' || p[strlen(p) - 1] == '\n') p[strlen(p) - 1] = 0; #ifdef OS_UNIX syslog(SYSLOG_PRIORITY, "%s", p); #else ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, &p, NULL); #endif xfree(p); } /* Print MSG to stderr */ void print_stderr(const char *msg) { fprintf(stderr, "%s", msg); } /* Dump BUF to syslog */ void fputs_syslog(const char *buf) { char *p; /* strip trailing \r and \n */ p = xstrdup(buf); while (p[strlen(p) - 1] == '\r' || p[strlen(p) - 1] == '\n') p[strlen(p) - 1] = 0; #ifdef OS_UNIX syslog(SYSLOG_PRIORITY, "%s", p); #else ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, &p, NULL); #endif xfree(p); } /* Dump BUF to stderr */ void fputs_stderr(const char *buf) { fputs(buf, stderr); } /* Redirect all messages handled with eprintf/efputs to syslog (Unix) or event log (Windows) */ void redirect_to_syslog(void) { static int has_inited = 0; /* initiate syslog */ if (!has_inited) { #ifdef OS_UNIX setlogmask(LOG_UPTO(SYSLOG_PRIORITY)); openlog("elogd", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); #else hEventLog = RegisterEventSource(NULL, "ELOG"); #endif } has_inited = 1; printf_handler = print_syslog; fputs_handler = fputs_syslog; /* tells that the syslog facility is currently used as output */ current_output_stream = NULL; } /* Redirect all messages handled with eprintf/efputs to stderr */ void redirect_to_stderr(void) { printf_handler = print_stderr; fputs_handler = fputs_stderr; current_output_stream = stderr; } /*------------------------------------------------------------------*/ int subst_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; char str[256]; if ((child_pid = fork()) < 0) return 0; else if (child_pid > 0) { /* parent process waits for child */ waitpid(child_pid, &status, 0); /* read back result */ fh = open("/tmp/elog-shell", O_RDONLY); if (fh > 0) { read(fh, result, size); close(fh); } /* remove temporary file */ remove("/tmp/elog-shell"); /* 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 (setegid(orig_gid) < 0 || seteuid(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 (verbose) 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 (verbose) eprintf("Falling back to user \"%s\"\n", str); } /* execute shell with redirection to /tmp/elog-shell */ sprintf(str, "/bin/sh -c \"%s\" > /tmp/elog-shell 2>&1", cmd); 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; p = strchr(ps, '$'); if (p != NULL) { /* 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 subst_shell(str, result, sizeof(result)); strlcpy(pt, result, sizeof(tmp) - (pt - tmp)); pt += strlen(pt); } } /* copy remainder */ strlcpy(pt, ps, sizeof(tmp) - (pt - tmp)); /* return result */ strlcpy(string, tmp, size); } /*------------------------------------------------------------------*/ void strsubst(char *string, int size, char *pattern, char *subst) /* subsitute "pattern" with "subst" in "string" */ { char *tail, *p; int s; p = string; for (p = stristr(p, pattern); p != NULL; p = stristr(p, pattern)) { if (strlen(pattern) == strlen(subst)) { memcpy(p, subst, strlen(subst)); } else if (strlen(pattern) > strlen(subst)) { memcpy(p, subst, strlen(subst)); strcpy(p + strlen(subst), p + strlen(pattern)); } else { tail = 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(p, "%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 dtou(char *str) /* convert all dots to underscores in a string */ { int i; for (i = 0; i < (int) strlen(str); i++) if (str[i] == '.') str[i] = '_'; } /*-------------------------------------------------------------------*/ char *map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int cind(char c) { int i; if (c == '=') return 0; for (i = 0; i < 64; i++) if (map[i] == c) return i; return -1; } void base64_decode(char *s, char *d) { unsigned int t; while (s && *s) { t = cind(*s) << 18; s++; t |= cind(*s) << 12; s++; t |= cind(*s) << 6; s++; t |= cind(*s) << 0; s++; *(d + 2) = (char) (t & 0xFF); t >>= 8; *(d + 1) = (char) (t & 0xFF); t >>= 8; *d = (char) (t & 0xFF); d += 3; } *d = 0; } void base64_encode(unsigned char *s, unsigned char *d, int size) { unsigned int t, pad; unsigned char *p; pad = 3 - strlen((char *) s) % 3; if (pad == 3) pad = 0; p = d; while (*s) { t = (*s++) << 16; if (*s) t |= (*s++) << 8; if (*s) t |= (*s++) << 0; *(d + 3) = map[t & 63]; t >>= 6; *(d + 2) = map[t & 63]; t >>= 6; *(d + 1) = map[t & 63]; t >>= 6; *(d + 0) = map[t & 63]; d += 4; if (d - p >= size - 3) return; } *d = 0; while (pad--) *(--d) = '='; } void base64_bufenc(unsigned char *s, int len, char *d) { unsigned int t, pad; int i; pad = 3 - len % 3; if (pad == 3) pad = 0; for (i = 0; i < len;) { t = s[i++] << 16; if (i < len) t |= s[i++] << 8; if (i < len) t |= s[i++] << 0; *(d + 3) = map[t & 63]; t >>= 6; *(d + 2) = map[t & 63]; t >>= 6; *(d + 1) = map[t & 63]; t >>= 6; *(d + 0) = map[t & 63]; d += 4; } *d = 0; while (pad--) *(--d) = '='; } void do_crypt(char *s, char *d, int size) { #ifdef HAVE_CRYPT strlcpy(d, crypt(s, "el"), size); #else base64_encode((unsigned char *) s, (unsigned char *) d, size); #endif } /*------------------------------------------------------------------*\ MD5 Checksum Routines \*------------------------------------------------------------------*/ typedef struct { unsigned int state[4]; // state (ABCD) unsigned int count[2]; // number of bits, modulo 2^64 (lsb first) unsigned char buffer[64]; // input buffer } MD5_CONTEXT; /*------------------------------------------------------------------*/ /* prototypes of the support routines */ void _MD5_update(MD5_CONTEXT *, const void *, unsigned int); void _MD5_transform(unsigned int[4], unsigned char[64]); void _MD5_encode(unsigned char *, unsigned int *, unsigned int); void _MD5_decode(unsigned int *, unsigned char *, unsigned int); /* F, G, H and I are basic MD5 functions */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits */ #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ /* Rotation is separate from addition to prevent recomputation */ #define FF(a, b, c, d, x, s, ac) { \ (a) += F ((b), (c), (d)) + (x) + (unsigned int)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) { \ (a) += G ((b), (c), (d)) + (x) + (unsigned int)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) { \ (a) += H ((b), (c), (d)) + (x) + (unsigned int)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) { \ (a) += I ((b), (c), (d)) + (x) + (unsigned int)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } /*------------------------------------------------------------------*/ /* main MD5 checksum routine, returns digest from pdata buffer */ void MD5_checksum(const void *pdata, unsigned int len, unsigned char digest[16]) { MD5_CONTEXT ctx; unsigned char bits[8]; unsigned int i, padlen; /* to allow multithreading we have to locate the padding memory here */ unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; memset(&ctx, 0, sizeof(MD5_CONTEXT)); ctx.count[0] = ctx.count[1] = 0; /* load magic initialization constants */ ctx.state[0] = 0x67452301; ctx.state[1] = 0xefcdab89; ctx.state[2] = 0x98badcfe; ctx.state[3] = 0x10325476; _MD5_update(&ctx, pdata, len); // save number of bits _MD5_encode(bits, ctx.count, 8); // pad out to 56 mod 64 i = (unsigned int) ((ctx.count[0] >> 3) & 0x3f); padlen = (i < 56) ? (56 - i) : (120 - i); _MD5_update(&ctx, PADDING, padlen); // append length (before padding) _MD5_update(&ctx, bits, 8); // store state in digest _MD5_encode(digest, ctx.state, 16); } /*------------------------------------------------------------------*/ void _MD5_update(MD5_CONTEXT * pctx, const void *pdata, unsigned int len) { unsigned char *pin; unsigned int i, index, partlen; pin = (unsigned char *) pdata; // compute number of bytes mod 64 index = (unsigned int) ((pctx->count[0] >> 3) & 0x3F); // update number of bits if ((pctx->count[0] += ((unsigned int) len << 3)) < ((unsigned int) len << 3)) pctx->count[1]++; pctx->count[1] += ((unsigned int) len >> 29); partlen = 64 - index; // transform as many times as possible. if (len >= partlen) { memcpy(&pctx->buffer[index], pin, partlen); _MD5_transform(pctx->state, pctx->buffer); for (i = partlen; i + 63 < len; i += 64) _MD5_transform(pctx->state, &pin[i]); index = 0; } else i = 0; /* buffer remaining input */ memcpy(&pctx->buffer[index], &pin[i], len - i); } /*------------------------------------------------------------------*/ /* basic transformation, transforms state based on block */ void _MD5_transform(unsigned int state[4], unsigned char block[64]) { unsigned int lA = state[0], lB = state[1], lC = state[2], lD = state[3]; unsigned int x[16]; _MD5_decode(x, block, 64); /* round 1 */ FF(lA, lB, lC, lD, x[0], 7, 0xd76aa478); // 1 FF(lD, lA, lB, lC, x[1], 12, 0xe8c7b756); // 2 FF(lC, lD, lA, lB, x[2], 17, 0x242070db); // 3 FF(lB, lC, lD, lA, x[3], 22, 0xc1bdceee); // 4 FF(lA, lB, lC, lD, x[4], 7, 0xf57c0faf); // 5 FF(lD, lA, lB, lC, x[5], 12, 0x4787c62a); // 6 FF(lC, lD, lA, lB, x[6], 17, 0xa8304613); // 7 FF(lB, lC, lD, lA, x[7], 22, 0xfd469501); // 8 FF(lA, lB, lC, lD, x[8], 7, 0x698098d8); // 9 FF(lD, lA, lB, lC, x[9], 12, 0x8b44f7af); // 10 FF(lC, lD, lA, lB, x[10], 17, 0xffff5bb1); // 11 FF(lB, lC, lD, lA, x[11], 22, 0x895cd7be); // 12 FF(lA, lB, lC, lD, x[12], 7, 0x6b901122); // 13 FF(lD, lA, lB, lC, x[13], 12, 0xfd987193); // 14 FF(lC, lD, lA, lB, x[14], 17, 0xa679438e); // 15 FF(lB, lC, lD, lA, x[15], 22, 0x49b40821); // 16 /* round 2 */ GG(lA, lB, lC, lD, x[1], 5, 0xf61e2562); // 17 GG(lD, lA, lB, lC, x[6], 9, 0xc040b340); // 18 GG(lC, lD, lA, lB, x[11], 14, 0x265e5a51); // 19 GG(lB, lC, lD, lA, x[0], 20, 0xe9b6c7aa); // 20 GG(lA, lB, lC, lD, x[5], 5, 0xd62f105d); // 21 GG(lD, lA, lB, lC, x[10], 9, 0x2441453); // 22 GG(lC, lD, lA, lB, x[15], 14, 0xd8a1e681); // 23 GG(lB, lC, lD, lA, x[4], 20, 0xe7d3fbc8); // 24 GG(lA, lB, lC, lD, x[9], 5, 0x21e1cde6); // 25 GG(lD, lA, lB, lC, x[14], 9, 0xc33707d6); // 26 GG(lC, lD, lA, lB, x[3], 14, 0xf4d50d87); // 27 GG(lB, lC, lD, lA, x[8], 20, 0x455a14ed); // 28 GG(lA, lB, lC, lD, x[13], 5, 0xa9e3e905); // 29 GG(lD, lA, lB, lC, x[2], 9, 0xfcefa3f8); // 30 GG(lC, lD, lA, lB, x[7], 14, 0x676f02d9); // 31 GG(lB, lC, lD, lA, x[12], 20, 0x8d2a4c8a); // 32 /* round 3 */ HH(lA, lB, lC, lD, x[5], 4, 0xfffa3942); // 33 HH(lD, lA, lB, lC, x[8], 11, 0x8771f681); // 34 HH(lC, lD, lA, lB, x[11], 16, 0x6d9d6122); // 35 HH(lB, lC, lD, lA, x[14], 23, 0xfde5380c); // 36 HH(lA, lB, lC, lD, x[1], 4, 0xa4beea44); // 37 HH(lD, lA, lB, lC, x[4], 11, 0x4bdecfa9); // 38 HH(lC, lD, lA, lB, x[7], 16, 0xf6bb4b60); // 39 HH(lB, lC, lD, lA, x[10], 23, 0xbebfbc70); // 40 HH(lA, lB, lC, lD, x[13], 4, 0x289b7ec6); // 41 HH(lD, lA, lB, lC, x[0], 11, 0xeaa127fa); // 42 HH(lC, lD, lA, lB, x[3], 16, 0xd4ef3085); // 43 HH(lB, lC, lD, lA, x[6], 23, 0x4881d05); // 44 HH(lA, lB, lC, lD, x[9], 4, 0xd9d4d039); // 45 HH(lD, lA, lB, lC, x[12], 11, 0xe6db99e5); // 46 HH(lC, lD, lA, lB, x[15], 16, 0x1fa27cf8); // 47 HH(lB, lC, lD, lA, x[2], 23, 0xc4ac5665); // 48 /* round 4 */ II(lA, lB, lC, lD, x[0], 6, 0xf4292244); // 49 II(lD, lA, lB, lC, x[7], 10, 0x432aff97); // 50 II(lC, lD, lA, lB, x[14], 15, 0xab9423a7); // 51 II(lB, lC, lD, lA, x[5], 21, 0xfc93a039); // 52 II(lA, lB, lC, lD, x[12], 6, 0x655b59c3); // 53 II(lD, lA, lB, lC, x[3], 10, 0x8f0ccc92); // 54 II(lC, lD, lA, lB, x[10], 15, 0xffeff47d); // 55 II(lB, lC, lD, lA, x[1], 21, 0x85845dd1); // 56 II(lA, lB, lC, lD, x[8], 6, 0x6fa87e4f); // 57 II(lD, lA, lB, lC, x[15], 10, 0xfe2ce6e0); // 58 II(lC, lD, lA, lB, x[6], 15, 0xa3014314); // 59 II(lB, lC, lD, lA, x[13], 21, 0x4e0811a1); // 60 II(lA, lB, lC, lD, x[4], 6, 0xf7537e82); // 61 II(lD, lA, lB, lC, x[11], 10, 0xbd3af235); // 62 II(lC, lD, lA, lB, x[2], 15, 0x2ad7d2bb); // 63 II(lB, lC, lD, lA, x[9], 21, 0xeb86d391); // 64 state[0] += lA; state[1] += lB; state[2] += lC; state[3] += lD; /* lClear sensitive information */ memset(x, 0, sizeof(x)); } /*------------------------------------------------------------------*/ /* encodes input (unsigned int) into output (unsigned char), assumes that lLen is a multiple of 4 */ void _MD5_encode(unsigned char *pout, unsigned int *pin, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) { pout[j] = (unsigned char) (pin[i] & 0x0ff); pout[j + 1] = (unsigned char) ((pin[i] >> 8) & 0x0ff); pout[j + 2] = (unsigned char) ((pin[i] >> 16) & 0x0ff); pout[j + 3] = (unsigned char) ((pin[i] >> 24) & 0x0ff); } } /*------------------------------------------------------------------*/ /* encodes input (unsigned char) into output (unsigned int), assumes that lLen is a multiple of 4 */ void _MD5_decode(unsigned int *pout, unsigned char *pin, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) pout[i] = ((unsigned int) pin[j]) | (((unsigned int) pin[j + 1]) << 8) | (((unsigned int) pin[j + 2]) << 16) | (((unsigned int) pin[j + 3]) << 24); } /*------------------------------------------------------------------*/ BOOL file_exist(char *file_name) { int fh; fh = open(file_name, O_RDONLY); if (fh < 0) return FALSE; close(fh); return TRUE; } /*------------------------------------------------------------------*/ void serialdate2date(double days, int *day, int *month, int *year) /* convert days since 1.1.1900 to date */ { int i, j, l, n; l = (int) days + 68569 + 2415019; n = (int) ((4 * l) / 146097); l = l - (int) ((146097 * n + 3) / 4); i = (int) ((4000 * (l + 1)) / 1461001); l = l - (int) ((1461 * i) / 4) + 31; j = (int) ((80 * l) / 2447); *day = l - (int) ((2447 * j) / 80); l = (int) (j / 11); *month = j + 2 - (12 * l); *year = 100 * (n - 49) + i + l; } double date2serialdate(int day, int month, int year) /* convert date to days since 1.1.1900 */ { int serialdate; serialdate = (int) ((1461 * (year + 4800 + (int) ((month - 14) / 12))) / 4) + (int) ((367 * (month - 2 - 12 * ((month - 14) / 12))) / 12) - (int) ((3 * ((int) ((year + 4900 + (int) ((month - 14) / 12)) / 100))) / 4) + day - 2415019 - 32075; return serialdate; } /*------------------------------------------------------------------*/ /* Wrapper for setegid. */ int setegroup(char *str) { #ifdef OS_UNIX struct group *gr; gr = getgrnam(str); if (gr != NULL) if (setegid(gr->gr_gid) >= 0 && initgroups(gr->gr_name, gr->gr_gid) >= 0) return 0; else { eprintf("Cannot set effective GID to group \"%s\"\n", gr->gr_name); eprintf("setgroup: %s\n", strerror(errno)); } else eprintf("Group \"%s\" not found\n", str); return -1; #else return 0; #endif } /* Wrapper for seteuid. */ int seteuser(char *str) { #ifdef OS_UNIX struct passwd *pw; pw = getpwnam(str); if (pw != NULL) if (seteuid(pw->pw_uid) >= 0) return 0; else { eprintf("Cannot set effective UID to user \"%s\"\n", str); eprintf("setuser: %s\n", strerror(errno)); } else eprintf("User \"%s\" not found\n", str); return -1; #else return 0; #endif } /* Wrapper for setgid. */ int setgroup(char *str) { #ifdef OS_UNIX struct group *gr; gr = getgrnam(str); if (gr != NULL) if (setgid(gr->gr_gid) >= 0 && initgroups(gr->gr_name, gr->gr_gid) >= 0) return 0; else { eprintf("Cannot set effective GID to group \"%s\"\n", gr->gr_name); eprintf("setgroup: %s\n", strerror(errno)); } else eprintf("Group \"%s\" not found\n", str); return -1; #else return 0; #endif } /* Wrapper for setuid. */ int setuser(char *str) { #ifdef OS_UNIX struct passwd *pw; pw = getpwnam(str); if (pw != NULL) if (setuid(pw->pw_uid) >= 0) return 0; else { eprintf("Cannot set effective UID to user \"%s\"\n", str); eprintf("setuser: %s\n", strerror(errno)); } else eprintf("User \"%s\" not found\n", str); return -1; #else return 0; #endif } /*-------------------------------------------------------------------*/ int recv_string(int sock, char *buffer, int buffer_size, int millisec) { int i, n; fd_set readfds; struct timeval timeout; n = 0; memset(buffer, 0, buffer_size); do { if (millisec > 0) { FD_ZERO(&readfds); FD_SET(sock, &readfds); timeout.tv_sec = millisec / 1000; timeout.tv_usec = (millisec % 1000) * 1000; select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout); if (!FD_ISSET(sock, &readfds)) break; } i = recv(sock, buffer + n, 1, 0); if (i <= 0) break; n++; if (n >= buffer_size) break; } while (buffer[n - 1] && buffer[n - 1] != 10); return n - 1; } /*-------------------------------------------------------------------*/ void compose_email_header(LOGBOOK * lbs, char *subject, char *from, char *to, char *url, char *mail_text, int size, int mail_encoding, int n_attachments, char *multipart_boundary) { 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 (verbose) { 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); if (multipart) 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 (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, "Content-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, "Content-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, *p; char list[1024][NAME_LENGTH], buffer[10000], decoded[256]; memset(error, 0, error_size); if (verbose) 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; /* connect to remote node port 25 */ memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.sin_family = AF_INET; bind_addr.sin_port = htons((short) 25); phe = gethostbyname(smtp_host); if (phe == NULL) { if (error) strlcpy(error, loc("Cannot lookup server name"), error_size); return -1; } memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length); if (connect(s, (void *) &bind_addr, sizeof(bind_addr)) < 0) { closesocket(s); if (error) strlcpy(error, loc("Cannot connect to server"), error_size); return -1; } strsize = MAX_CONTENT_LENGTH + 1000; str = xmalloc(strsize); recv_string(s, str, strsize, 10000); if (verbose) efputs(str); write_logfile(lbs, str); /* drain server messages */ do { str[0] = 0; recv_string(s, str, strsize, 300); if (verbose) 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 (verbose) efputs(str); write_logfile(lbs, str); do { recv_string(s, str, strsize, 3000); if (verbose) efputs(str); write_logfile(lbs, str); if (!check_smtp_error(str, 250, error, error_size)) goto smtp_error; } while (stristr(str, " OK") == NULL); } else { snprintf(str, strsize - 1, "HELO %s\r\n", host_name); send(s, str, strlen(str), 0); if (verbose) efputs(str); write_logfile(lbs, str); recv_string(s, str, strsize, 3000); if (verbose) 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 (verbose) efputs(str); write_logfile(lbs, str); recv_string(s, str, strsize, 3000); if (strchr(str, '\r')) *strchr(str, '\r') = 0; if (verbose) efputs(decoded); write_logfile(lbs, str); base64_decode(str + 4, decoded); strcat(decoded, "\n"); if (verbose) 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, sizeof(str)); strcat(str, "\r\n"); send(s, str, strlen(str), 0); if (verbose) efputs(str); write_logfile(lbs, str); recv_string(s, str, strsize, 3000); if (strchr(str, '\r')) *strchr(str, '\r') = 0; base64_decode(str + 4, decoded); strcat(decoded, "\n"); if (verbose) 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 (verbose) efputs(str); write_logfile(lbs, str); recv_string(s, str, strsize, 3000); if (verbose) 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 (verbose) efputs(str); write_logfile(lbs, str); recv_string(s, str, strsize, 3000); if (verbose) 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, 1024, ","); for (i = 0; i < n; i++) { if (list[i] == 0 || strchr(list[i], '@') == NULL) continue; snprintf(str, strsize - 1, "RCPT TO: <%s>\r\n", list[i]); send(s, str, strlen(str), 0); if (verbose) efputs(str); write_logfile(lbs, str); /* increased timeout for SMTP servers with long alias lists */ recv_string(s, str, strsize, 30000); if (verbose) 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 (verbose) efputs(str); write_logfile(lbs, str); recv_string(s, str, strsize, 3000); if (verbose) efputs(str); write_logfile(lbs, str); if (!check_smtp_error(str, 354, error, error_size)) goto smtp_error; /* analyze text for "." at beginning of line */ p = text; str[0] = 0; while (strstr(p, "\r\n.\r\n")) { i = strstr(p, "\r\n.\r\n") - p + 1; strlcat(str, p, i); p += i + 4; strlcat(str, "\r\n..\r\n", strsize); } strlcat(str, p, strsize); /* add "." to signal end of message */ strlcat(str, ".\r\n", strsize); /* check if buffer exceeded */ if ((int) strlen(str) == strsize - 1) { strlcpy(error, loc("Entry size too large for email notification"), error_size); goto smtp_error; } send(s, str, strlen(str), 0); if (verbose) efputs(str); write_logfile(lbs, str); recv_string(s, str, strsize, 10000); if (verbose) 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 (verbose) efputs(str); write_logfile(lbs, str); recv_string(s, str, strsize, 3000); if (verbose) 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; } /*-------------------------------------------------------------------*/ void split_url(char *url, char *host, int *port, char *subdir, char *param) { char *p, str[256]; if (host) host[0] = 0; if (port) *port = 80; if (subdir) subdir[0] = 0; if (param) param[0] = 0; p = url; if (strncmp(url, "http://", 7) == 0) p += 7; if (strncmp(url, "https://", 8) == 0) p += 8; strncpy(str, p, sizeof(str)); if (strchr(str, '/')) { if (subdir) strncpy(subdir, strchr(str, '/'), 256); *strchr(str, '/') = 0; } if (strchr(str, '?')) { if (subdir) strncpy(subdir, strchr(str, '?'), 256); *strchr(str, '?') = 0; } if (strchr(str, ':')) { if (port) *port = atoi(strchr(str, ':') + 1); *strchr(str, ':') = 0; } if (host) strcpy(host, str); if (subdir) { if (strchr(subdir, '?')) { strncpy(param, strchr(subdir, '?'), 256); *strchr(subdir, '?') = 0; } if (subdir[0] == 0) strcpy(subdir, "/"); } } /*-------------------------------------------------------------------*/ int retrieve_url(char *url, char **buffer, char *rpwd) { struct sockaddr_in bind_addr; struct hostent *phe; char str[1000], unm[256], upwd[256], host[256], subdir[256], param[256], auth[256], pwd_enc[256]; int port, bufsize; int i, n; fd_set readfds; struct timeval timeout; static int sock, last_port; static char last_host[256]; *buffer = NULL; split_url(url, host, &port, subdir, param); if (sock && (strcmp(host, last_host) != 0 || port != last_port)) { closesocket(sock); sock = 0; } if (sock) { // keep-alive does not yet work, requires evaluation of Content-Length !!! closesocket(sock); sock = 0; } /* create a new socket for connecting to remote server */ if (!sock) { sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) return -1; /* connect to remote node */ memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.sin_family = AF_INET; bind_addr.sin_port = htons((short) port); phe = gethostbyname(host); if (phe == NULL) return -1; memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length); if (connect(sock, (void *) &bind_addr, sizeof(bind_addr)) < 0) { closesocket(sock); return -1; } } last_port = port; strcpy(last_host, host); /* 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 (isparam("unm") && isparam("upwd")) { strlcpy(unm, getparam("unm"), sizeof(unm)); strlcpy(upwd, getparam("upwd"), sizeof(upwd)); sprintf(str + strlen(str), "Cookie: unm=%s; upwd=%s\r\n", getparam("unm"), getparam("upwd")); } if (rpwd && rpwd[0]) { sprintf(auth, "anybody:%s", rpwd); base64_encode((unsigned char *) auth, (unsigned char *) pwd_enc, sizeof(pwd_enc)); sprintf(str + strlen(str), "Authorization: Basic %s\r\n", pwd_enc); } /* add host (RFC2616, Sec. 14) */ sprintf(str + strlen(str), "Host: %s:%d\r\n", host, port); /* add keepalive */ //sprintf(str + strlen(str), "Keep-Alive: 600\r\n"); //sprintf(str + strlen(str), "Connection: keep-alive\r\n"); strcat(str, "\r\n"); send(sock, str, strlen(str), 0); bufsize = TEXT_SIZE + 1000; *buffer = xmalloc(bufsize); memset(*buffer, 0, bufsize); n = 0; do { FD_ZERO(&readfds); FD_SET(sock, &readfds); timeout.tv_sec = 30; /* 30 sec. timeout */ timeout.tv_usec = 0; select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout); if (!FD_ISSET(sock, &readfds)) { closesocket(sock); sock = 0; xfree(*buffer); *buffer = NULL; return -1; } i = recv(sock, *buffer + n, bufsize - n, 0); if (i <= 0) break; n += i; if (n >= bufsize) { /* increase buffer size */ bufsize += 10000; *buffer = xrealloc(*buffer, bufsize); if (*buffer == NULL) { closesocket(sock); return -1; } } } while (1); return n; } /*-------------------------------------------------------------------*/ int ss_daemon_init() { #ifdef OS_UNIX /* only implemented for UNIX */ int i, fd, pid; if ((pid = fork()) < 0) return FAILURE; else if (pid != 0) _exit(EXIT_SUCCESS); /* parent finished, exit without atexit hook */ /* child continues here */ /* try and use up stdin, stdout and stderr, so other routines writing to stdout etc won't cause havoc. Copied from smbd */ for (i = 0; i < 3; i++) { close(i); fd = open("/dev/null", O_RDWR, 0); if (fd < 0) fd = open("/dev/null", O_WRONLY, 0); if (fd < 0) { eprintf("Can't open /dev/null"); return FAILURE; } if (fd != i) { eprintf("Did not get file descriptor"); return FAILURE; } } setsid(); /* become session leader */ chdir("/"); /* change working direcotry (not on NFS!) */ umask(0); /* clear our file mode creation mask */ #endif return SUCCESS; } /*------------------------------------------------------------------*/ /* Parameter extraction from configuration file similar to win.ini */ typedef struct { char *param; char *uparam; char *value; } CONFIG_PARAM; typedef struct { char *section_name; int n_params; CONFIG_PARAM *config_param; } LB_CONFIG; LB_CONFIG *lb_config = NULL; int n_lb_config = 0; char _topgroup[256]; char _condition[256]; time_t cfgfile_mtime = 0; /*-------------------------------------------------------------------*/ void check_config_file(BOOL force) { struct stat cfg_stat; if (force) { parse_config_file(config_file); return; } /* force re-read configuration file if changed */ if (stat(config_file, &cfg_stat) == 0) { if (cfgfile_mtime < cfg_stat.st_mtime) { cfgfile_mtime = cfg_stat.st_mtime; parse_config_file(config_file); } } else eprintf("Cannot stat() config file \"%s\": %s\n", config_file, strerror(errno)); } /*-------------------------------------------------------------------*/ void setcfg_topgroup(char *topgroup) { strcpy(_topgroup, topgroup); } char *getcfg_topgroup() { if (_topgroup[0]) return _topgroup; return NULL; } /*------------------------------------------------------------------*/ int is_logbook(char *logbook) { char str[256]; if (strieq(logbook, "global")) return 0; /* check for 'global group' or 'global_xxx' */ strlcpy(str, logbook, sizeof(str)); str[7] = 0; return !strieq(str, "global "); } /*-------------------------------------------------------------------*/ void set_condition(char *c) { strlcpy(_condition, c, sizeof(_condition)); } /*-------------------------------------------------------------------*/ void evaluate_conditions(LOGBOOK * lbs, char attrib[MAX_N_ATTR][NAME_LENGTH]) { char condition[256], str[256]; int index, i; condition[0] = 0; set_condition(""); for (index = 0; index < lbs->n_attr; index++) { for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) { if (strchr(attr_options[index][i], '{') && strchr(attr_options[index][i], '}')) { strlcpy(str, attr_options[index][i], sizeof(str)); *strchr(str, '{') = 0; if (strieq(str, attrib[index])) { strlcpy(str, strchr(attr_options[index][i], '{') + 1, sizeof(str)); if (*strchr(str, '}')) *strchr(str, '}') = 0; if (condition[0] == 0) strlcpy(condition, str, sizeof(condition)); else { strlcat(condition, ",", sizeof(condition)); strlcat(condition, str, sizeof(condition)); } set_condition(condition); scan_attributes(lbs->name); } } } } } /*-------------------------------------------------------------------*/ BOOL match_param(char *str, char *param, int conditional_only) { int ncl, npl, nand, i, j, k; char *p, pcond[256], clist[10][NAME_LENGTH], plist[10][NAME_LENGTH], alist[10][NAME_LENGTH]; if (conditional_only && str[0] != '{') return FALSE; if (!_condition[0] || str[0] != '{') return (strcmp(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, ","); ncl = strbreak(_condition, clist, 10, ","); for (i = 0; i < ncl; i++) for (j = 0; j < npl; j++) if (stricmp(clist[i], plist[j]) == 0) { /* condition matches */ return strcmp(p, param) == 0; } /* check and'ed conditions */ for (i = 0; i < npl; i++) if (strchr(plist[i], '&')) { nand = strbreak(plist[i], alist, 10, "&"); 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 strcmp(p, param) == 0; } return 0; } /*-------------------------------------------------------------------*/ int param_compare(const void *p1, const void *p2) { return stricmp(((CONFIG_PARAM *) p1)->uparam, ((CONFIG_PARAM *) p2)->uparam); } /*------------------------------------------------------------------*/ void free_config() { int i, j; for (i = 0; i < n_lb_config; i++) { for (j = 0; j < lb_config[i].n_params; j++) { xfree(lb_config[i].config_param[j].param); xfree(lb_config[i].config_param[j].uparam); xfree(lb_config[i].config_param[j].value); } if (lb_config[i].config_param) xfree(lb_config[i].config_param); xfree(lb_config[i].section_name); } xfree(lb_config); lb_config = NULL; n_lb_config = 0; } /*------------------------------------------------------------------*/ int parse_config_file(char *file_name) /* parse whole config file and store options in sorted list */ { char *str, *buffer, *p, *pstr; int index, i, j, fh, length; str = xmalloc(20000); /* open configuration file */ fh = open(file_name, O_RDONLY | O_BINARY); if (fh < 0) return 0; length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); buffer = xmalloc(length + 1); read(fh, buffer, length); buffer[length] = 0; close(fh); /* release previously allocated memory */ if (lb_config) free_config(); /* search group */ p = buffer; index = 0; do { if (*p == '#' || *p == ';') { /* skip comment */ while (*p && *p != '\n' && *p != '\r') p++; } else if (*p == '[') { p++; pstr = str; while (*p && *p != ']' && *p != '\n' && *p != '\r' && pstr - str < 10000) *pstr++ = *p++; *pstr = 0; /* allocate new group */ if (!lb_config) lb_config = xmalloc(sizeof(LB_CONFIG)); else lb_config = xrealloc(lb_config, sizeof(LB_CONFIG) * (n_lb_config + 1)); lb_config[n_lb_config].section_name = xmalloc(strlen(str) + 1); lb_config[n_lb_config].n_params = 0; lb_config[n_lb_config].config_param = NULL; strcpy(lb_config[n_lb_config].section_name, str); /* enumerate parameters */ i = 0; p = strchr(p, '\n'); if (p) p++; while (p && *p && *p != '[') { pstr = str; while (*p && *p != '=' && *p != '\n' && *p != '\r' && pstr - str < 10000) *pstr++ = *p++; *pstr-- = 0; while (pstr > str && (*pstr == ' ' || *pstr == '\t' || *pstr == '=')) *pstr-- = 0; if (*p == '=') { if (lb_config[n_lb_config].n_params == 0) lb_config[n_lb_config].config_param = xmalloc(sizeof(CONFIG_PARAM)); else lb_config[n_lb_config].config_param = xrealloc(lb_config[n_lb_config].config_param, sizeof(CONFIG_PARAM) * (lb_config[n_lb_config].n_params + 1)); lb_config[n_lb_config].config_param[i].param = xmalloc(strlen(str) + 1); lb_config[n_lb_config].config_param[i].uparam = xmalloc(strlen(str) + 1); strcpy(lb_config[n_lb_config].config_param[i].param, str); for (j = 0; j < (int) strlen(str); j++) lb_config[n_lb_config].config_param[i].uparam[j] = toupper(str[j]); lb_config[n_lb_config].config_param[i].uparam[j] = 0; p++; while (*p == ' ' || *p == '\t') p++; pstr = str; while (*p && *p != '\n' && *p != '\r' && pstr - str < 10000) *pstr++ = *p++; *pstr-- = 0; while (*pstr == ' ' || *pstr == '\t') *pstr-- = 0; lb_config[n_lb_config].config_param[i].value = xmalloc(strlen(str) + 1); strcpy(lb_config[n_lb_config].config_param[i].value, str); i++; lb_config[n_lb_config].n_params = i; } /* search for next line beginning */ while (*p && *p != '\r' && *p != '\n') p++; while (*p && (*p == '\r' || *p == '\n')) p++; } /* sort parameter */ qsort(lb_config[n_lb_config].config_param, lb_config[n_lb_config].n_params, sizeof(CONFIG_PARAM), param_compare); n_lb_config++; index++; } /* search for next line beginning */ while (*p && *p != '\r' && *p != '\n' && *p != '[') p++; while (*p && (*p == '\r' || *p == '\n')) p++; } while (*p); xfree(str); xfree(buffer); return 0; } /*-------------------------------------------------------------------*/ int getcfg_simple(char *group, char *param, char *value, int vsize, int conditional) { int i, j, status; char uparam[256]; if (strlen(param) >= sizeof(uparam)) return 0; for (i = 0; i < (int) strlen(param); i++) uparam[i] = toupper(param[i]); uparam[i] = 0; value[0] = 0; for (i = 0; i < n_lb_config; i++) if (strieq(lb_config[i].section_name, group)) break; if (i == n_lb_config) return 0; for (j = 0; j < lb_config[i].n_params; j++) if (match_param(lb_config[i].config_param[j].uparam, uparam, conditional)) { status = strchr(lb_config[i].config_param[j].uparam, '{') ? 2 : 1; strlcpy(value, lb_config[i].config_param[j].value, vsize); return status; } return 0; } /*-------------------------------------------------------------------*/ int enumgrp(int index, char *group) { if (index < n_lb_config) { strcpy(group, lb_config[index].section_name); return 1; } return 0; } /*-------------------------------------------------------------------*/ int getcfg(char *group, char *param, char *value, int vsize) /* Read parameter from configuration file. - if group == [global] and top group exists, read from [global ] - if parameter not in [global ], read from [global] - if group is logbook, read from logbook section - if parameter not in [], read from [global ] or [global] */ { char str[256]; int status; /* if group is [global] and top group exists, read from there */ if (strieq(group, "global") && getcfg_topgroup()) { sprintf(str, "global %s", getcfg_topgroup()); if (getcfg(str, param, value, vsize)) return 1; } /* first check if parameter is under condition */ if (_condition[0]) { status = getcfg_simple(group, param, value, vsize, TRUE); if (status) return status; } status = getcfg_simple(group, param, value, vsize, FALSE); if (status) return status; /* if parameter not found in logbook, look in [global] section */ if (!group || is_logbook(group)) return getcfg("global", param, value, vsize); return 0; } /*-------------------------------------------------------------------*/ char *find_param(char *buf, char *group, char *param) { char *str, *p, *pstr, *pstart; /* search group */ str = xmalloc(10000); p = buf; do { if (*p == '[') { p++; pstr = str; while (*p && *p != ']' && *p != '\n') *pstr++ = *p++; *pstr = 0; if (strieq(str, group)) { /* search parameter */ p = strchr(p, '\n'); if (p) p++; while (p && *p && *p != '[') { pstr = str; pstart = p; while (*p && *p != '=' && *p != '\n') *pstr++ = *p++; *pstr-- = 0; while (pstr > str && (*pstr == ' ' || *pstr == '=' || *pstr == '\t')) *pstr-- = 0; if (match_param(str, param, FALSE)) { xfree(str); return pstart; } if (p) p = strchr(p, '\n'); if (p) p++; } } } if (p) p = strchr(p, '\n'); if (p) p++; } while (p); xfree(str); /* now search if in [global] section */ if (!strieq(group, "global")) return find_param(buf, "global", param); return NULL; } /*-------------------------------------------------------------------*/ int is_group(char *group) { int i; for (i = 0; i < n_lb_config; i++) if (strieq(group, lb_config[i].section_name)) return 1; return 0; } /*------------------------------------------------------------------*/ int enumcfg(char *group, char *param, int psize, char *value, int vsize, int index) { int i; for (i = 0; i < n_lb_config; i++) if (strieq(group, lb_config[i].section_name)) { if (index < lb_config[i].n_params) { strlcpy(param, lb_config[i].config_param[index].param, psize); strlcpy(value, lb_config[i].config_param[index].value, vsize); return 1; } return 0; } return 0; } /*-------------------------------------------------------------------*/ int exist_top_group() { int i; char str[256]; for (i = 0;; i++) { if (!enumcfg("global", str, sizeof(str), NULL, 0, i)) break; str[9] = 0; if (strieq(str, "top group")) return 1; } return 0; } /*-------------------------------------------------------------------*/ char *_locbuffer = NULL; char **_porig, **_ptrans; time_t _locfile_mtime = 0; /* check if language file changed and if so reload it */ int check_language() { char language[256], file_name[256], *p; int fh, length, n; struct stat cfg_stat; getcfg("global", "Language", language, sizeof(language)); /* set locale for strftime */ if (language[0]) setlocale(LC_ALL, language); else setlocale(LC_ALL, "english"); /* force re-read configuration file if changed */ strlcpy(file_name, resource_dir, sizeof(file_name)); strlcat(file_name, "resources", sizeof(file_name)); strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name)); strlcat(file_name, "eloglang.", sizeof(file_name)); strlcat(file_name, language, sizeof(file_name)); if (stat(file_name, &cfg_stat) == 0) { if (_locfile_mtime != cfg_stat.st_mtime) { _locfile_mtime = cfg_stat.st_mtime; if (_locbuffer) { xfree(_locbuffer); _locbuffer = NULL; } } } if (strieq(language, "english") || language[0] == 0) { if (_locbuffer) { xfree(_locbuffer); _locbuffer = NULL; } } else { if (_locbuffer == NULL) { fh = open(file_name, O_RDONLY | O_BINARY); if (fh < 0) return -1; length = lseek(fh, 0, SEEK_END); lseek(fh, 0, SEEK_SET); _locbuffer = xmalloc(length + 1); read(fh, _locbuffer, length); _locbuffer[length] = 0; close(fh); /* scan lines, setup orig-translated pointers */ p = _locbuffer; n = 0; do { while (*p && (*p == '\r' || *p == '\n')) p++; if (*p && (*p == ';' || *p == '#' || *p == ' ' || *p == '\t')) { while (*p && *p != '\n' && *p != '\r') p++; continue; } if (n == 0) { _porig = xmalloc(sizeof(char *) * 2); _ptrans = xmalloc(sizeof(char *) * 2); } else { _porig = xrealloc(_porig, sizeof(char *) * (n + 2)); _ptrans = xrealloc(_ptrans, sizeof(char *) * (n + 2)); } _porig[n] = p; while (*p && (*p != '=' && *p != '\r' && *p != '\n')) p++; if (*p && *p != '=') continue; _ptrans[n] = p + 1; while (*_ptrans[n] == ' ' || *_ptrans[n] == '\t') _ptrans[n]++; /* remove '=' and surrounding blanks */ while (*p == '=' || *p == ' ' || *p == '\t') *p-- = 0; p = _ptrans[n]; while (*p && *p != '\n' && *p != '\r') p++; if (p) *p++ = 0; n++; } while (p && *p); _porig[n] = NULL; _ptrans[n] = NULL; } } return 0; } /*-------------------------------------------------------------------*/ /* localization support */ char *loc(char *orig) { int n; char language[256]; static char result[256]; if (!_locbuffer) return orig; /* search string and return translation */ for (n = 0; _porig[n]; n++) if (strcmp(orig, _porig[n]) == 0) { if (*_ptrans[n]) return _ptrans[n]; return orig; } /* special case: "Change %s" */ if (strstr(orig, "Change ") && strcmp(orig, "Change %s") != 0) { sprintf(result, loc("Change %s"), orig + 7); return result; } /* special case: some intrinsic commands */ if (strstr(orig, "GetPwdFile")) { strcpy(result, orig); return result; } getcfg("global", "Language", language, sizeof(language)); eprintf("Language error: string \"%s\" not found for language \"%s\"\n", orig, language); return orig; } /*-------------------------------------------------------------------*/ /* translate back from localized string to english */ char *unloc(char *orig) { int n; if (!_locbuffer) return orig; /* search string and return translation */ for (n = 0; _ptrans[n]; n++) if (strcmp(orig, _ptrans[n]) == 0) { if (*_porig[n]) return _porig[n]; return orig; } eprintf("Language error: string \"%s\" not found in English\n", orig); return orig; } /*-------------------------------------------------------------------*/ char *month_name(int m) /* return name of month in current locale, m=0..11 */ { struct tm ts; static char name[32]; memset(&ts, 0, sizeof(ts)); ts.tm_mon = m; ts.tm_mday = 15; ts.tm_year = 2000; mktime(&ts); strftime(name, sizeof(name), "%B", &ts); return name; } /*-------------------------------------------------------------------*/ time_t date_to_ltime(char *date) { struct tm tms; int i, date_zone, local_zone; time_t ltime; memset(&tms, 0, sizeof(struct tm)); if (strlen(date) > 25) { /* RFC2822 compliant date */ for (i = 0; i < 12; i++) if (strncmp(date + 8, mname[i], 3) == 0) break; tms.tm_mon = i; tms.tm_mday = atoi(date + 5); tms.tm_hour = atoi(date + 17); tms.tm_min = atoi(date + 20); tms.tm_sec = atoi(date + 23); tms.tm_year = atoi(date + 12) - 1900; tms.tm_isdst = -1; if (tms.tm_year < 90) tms.tm_year += 100; ltime = mktime(&tms); /* correct for difference between local time zone (used by mktime) and time zone of date */ date_zone = atoi(date + 26); /* correct for incorrect date_zone */ if (date_zone > 2400 || date_zone < -2400) date_zone = 0; date_zone = (abs(date_zone) % 100) * 60 + (date_zone) / 100 * 3600; local_zone = my_timezone(); if (tms.tm_isdst) local_zone -= 3600; ltime = ltime - local_zone - date_zone; } else { /* ctime() complient date */ for (i = 0; i < 12; i++) if (strncmp(date + 4, mname[i], 3) == 0) break; tms.tm_mon = i; tms.tm_mday = atoi(date + 8); tms.tm_hour = atoi(date + 11); tms.tm_min = atoi(date + 14); tms.tm_sec = atoi(date + 17); tms.tm_year = atoi(date + 20) - 1900; tms.tm_isdst = -1; if (tms.tm_year < 90) tms.tm_year += 100; ltime = mktime(&tms); } return ltime; } /*-------------------------------------------------------------------*/ void check_config() { check_config_file(FALSE); check_language(); } /*-------------------------------------------------------------------*/ void retrieve_email_from(LOGBOOK * lbs, char *ret, char *ret_name, char attrib[MAX_N_ATTR][NAME_LENGTH]) { char email_from[256], email_from_name[256], str[256], *p, login_name[256]; char slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH]; int i; if (!getcfg(lbs->name, "Use Email from", str, sizeof(str))) { if (isparam("full_name") && isparam("user_email")) { strlcpy(email_from_name, getparam("full_name"), sizeof(email_from_name)); strlcat(email_from_name, " <", sizeof(email_from_name)); strlcat(email_from_name, getparam("user_email"), sizeof(email_from_name)); strlcat(email_from_name, ">", sizeof(email_from_name)); strlcpy(email_from, getparam("user_email"), sizeof(email_from)); } else { sprintf(email_from_name, "ELog ", host_name); sprintf(email_from, "ELog@%s", host_name); } } else { strlcpy(email_from, str, sizeof(email_from)); strlcpy(email_from_name, str, sizeof(email_from)); } 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) strcpy(p, p + 7); if ((p = strstr(email_from, "mailto:")) != NULL) strcpy(p, p + 7); } /* 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); sprintf(email_from_name, "%s <%s>", login_name, email_from); if (is_admin_user(lbs->name, login_name) && strchr(email_from, '@')) break; } } if (ret) strcpy(ret, email_from); if (ret_name) strcpy(ret_name, email_from_name); } /*------------------------------------------------------------------*/ void el_decode(char *message, char *key, char *result) { char *pc, *ph; 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); while (*pc != '\n' && *pc != '\r') *result++ = *pc++; *result = 0; return; } pc = strchr(pc, '\n'); if (pc == NULL) return; while (*pc && (*pc == '\n' || *pc == '\r')) pc++; } } /*------------------------------------------------------------------*/ 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) { const char *stringstart; char c, test; for (stringstart = string;;) 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(char *path, char *pattern, char **plist) /********************************************************************\ Routine: ss_file_find Purpose: Return list of files matching 'pattern' from the 'path' location Input: char *path Name of a file in file system to check char *pattern pattern string (wildcard allowed) Output: char **plist pointer to the file list Function value: int Number of files matching request \********************************************************************/ { #ifdef OS_UNIX DIR *dir_pointer; struct dirent *dp; int i; if ((dir_pointer = opendir(path)) == NULL) return 0; *plist = (char *) xmalloc(MAX_PATH_LENGTH); i = 0; for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) { if (fnmatch1(pattern, dp->d_name) == 0) { *plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH); strncpy(*plist + (i * MAX_PATH_LENGTH), dp->d_name, strlen(dp->d_name)); *(*plist + (i * MAX_PATH_LENGTH) + strlen(dp->d_name)) = '\0'; i++; 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 } /*------------------------------------------------------------------*/ 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; } /*------------------------------------------------------------------*/ int el_build_index(LOGBOOK * lbs, BOOL rebuild) /* scan all ??????a.log files and build an index table in eli[] */ { char *file_list, str[256], date[256], dir[256], file_name[MAX_PATH_LENGTH], *buffer, *p, *pn, in_reply_to[80]; int index, n, length; int i, fh, len; if (rebuild) { xfree(lbs->el_index); xfree(lbs->n_el_index); } /* check if this logbook has same data dir as previous */ for (i = 0; lb_list[i].name[0] && &lb_list[i] != lbs; i++) if (strieq(lb_list[i].data_dir, lbs->data_dir)) break; if (strieq(lb_list[i].data_dir, lbs->data_dir) && &lb_list[i] != lbs) { if (verbose) eprintf("\n Same index as logbook %s\n", lb_list[i].name); lbs->el_index = lb_list[i].el_index; lbs->n_el_index = lb_list[i].n_el_index; return EL_SUCCESS; } lbs->n_el_index = xmalloc(sizeof(int)); *lbs->n_el_index = 0; lbs->el_index = xmalloc(0); /* get data directory */ strcpy(dir, lbs->data_dir); file_list = NULL; n = ss_file_find(dir, "??????a.log", &file_list); if (n == 0) { if (file_list) xfree(file_list); file_list = NULL; n = ss_file_find(dir, "??????.log", &file_list); if (n > 0) return EL_UPGRADE; return EL_EMPTY; } /* go through all files */ for (index = 0; index < n; index++) { strlcpy(file_name, dir, sizeof(file_name)); strlcat(file_name, file_list + index * MAX_PATH_LENGTH, sizeof(file_name)); fh = open(file_name, O_RDONLY | O_BINARY, 0644); if (fh < 0) { sprintf(str, "Cannot open file \"%s\"", file_name); eprintf("%s; %s\n", str, strerror(errno)); return EL_FILE_ERROR; } /* read file into buffer */ length = lseek(fh, 0, SEEK_END); if (length > 0) { buffer = xmalloc(length + 1); lseek(fh, 0, SEEK_SET); read(fh, buffer, length); buffer[length] = 0; close(fh); /* go through buffer */ p = buffer; do { p = strstr(p, "$@MID@$:"); if (p) { lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index + 1)); if (lbs->el_index == NULL) { eprintf("Not enough memory to allocate entry index\n"); return EL_MEM_ERROR; } strcpy(str, file_list + index * MAX_PATH_LENGTH); strcpy(lbs->el_index[*lbs->n_el_index].file_name, str); el_decode(p, "Date: ", date); el_decode(p, "In reply to: ", 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 (verbose) { if (*lbs->n_el_index == 0) eprintf("\n"); 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); } } xfree(file_list); /* sort entries according to date */ qsort(lbs->el_index, *lbs->n_el_index, sizeof(EL_INDEX), eli_compare); if (verbose) { 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); } return EL_SUCCESS; } /*------------------------------------------------------------------*/ int el_index_logbooks() { char str[256], data_dir[256], logbook[256], cwd[256], *p; int i, j, n, status; if (lb_list) { for (i = 0; lb_list[i].name[0]; i++) { if (lb_list[i].el_index != NULL) { xfree(lb_list[i].el_index); xfree(lb_list[i].n_el_index); /* check if other logbook uses same index */ for (j = i + 1; lb_list[j].name[0]; j++) { /* mark that logbook already freed */ if (lb_list[j].el_index == lb_list[i].el_index) lb_list[j].el_index = NULL; } } } xfree(lb_list); } /* count logbooks */ for (i = n = 0;; i++) { if (!enumgrp(i, str)) break; if (!is_logbook(str)) continue; n++; } lb_list = xcalloc(sizeof(LOGBOOK), n + 1); for (i = n = 0;; i++) { if (!enumgrp(i, logbook)) break; if (!is_logbook(logbook)) continue; /* check for duplicate name */ for (j = 0; j < i && lb_list[j].name[0]; j++) if (strieq(lb_list[j].name, logbook)) { eprintf("Error in configuration file: Duplicate logbook \"%s\"\n", logbook); return EL_DUPLICATE; } /* store logbook in list */ strcpy(lb_list[n].name, logbook); strcpy(lb_list[n].name_enc, logbook); url_encode(lb_list[n].name_enc, sizeof(lb_list[n].name_enc)); /* get data dir from configuration file (old method) */ if (getcfg(logbook, "Data dir", str, sizeof(str))) { if (str[0] == DIR_SEPARATOR || str[1] == ':') strlcpy(data_dir, str, sizeof(data_dir)); else { strlcpy(data_dir, resource_dir, sizeof(data_dir)); strlcat(data_dir, str, sizeof(data_dir)); } } else { /* use logbook_dir + "Subdir" (new method) */ strlcpy(data_dir, logbook_dir, sizeof(data_dir)); if (data_dir[strlen(data_dir) - 1] != DIR_SEPARATOR) strlcat(data_dir, DIR_SEPARATOR_STR, sizeof(data_dir)); if (getcfg(logbook, "Subdir", str, sizeof(str))) { if (str[0] == DIR_SEPARATOR) strlcpy(data_dir, str, sizeof(data_dir)); else strlcat(data_dir, str, sizeof(data_dir)); } else strlcat(data_dir, logbook, sizeof(data_dir)); /* use logbook name as default */ } if (data_dir[strlen(data_dir) - 1] != DIR_SEPARATOR) strlcat(data_dir, DIR_SEPARATOR_STR, sizeof(data_dir)); /* create data directory if not existing */ getcwd(cwd, sizeof(cwd)); j = chdir(data_dir); if (j < 0) { p = data_dir; if (*p == DIR_SEPARATOR) { chdir(DIR_SEPARATOR_STR); p++; } if (p[1] == ':') { strcpy(str, p); if (str[2] == DIR_SEPARATOR) str[3] = 0; else str[2] = 0; chdir(str); p += strlen(str); } do { if (strchr(p, DIR_SEPARATOR)) { strlcpy(str, p, sizeof(str)); *strchr(str, DIR_SEPARATOR) = 0; p = strchr(p, DIR_SEPARATOR) + 1; } else { strlcpy(str, p, sizeof(str)); p = NULL; } j = chdir(str); if (j < 0) { #ifdef OS_WINNT j = mkdir(str); #else j = mkdir(str, 0755); #endif if (j == 0) { if (verbose) 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; if (verbose) eprintf("Indexing logbook \"%s\" in \"%s\" ... ", logbook, lb_list[n].data_dir); eflush(); status = el_build_index(&lb_list[n], FALSE); if (verbose) if (status == EL_SUCCESS) eprintf("ok\n"); if (status == EL_EMPTY) { if (verbose) eprintf("Found empty logbook \"%s\"\n", logbook); } else if (status == EL_UPGRADE) { eprintf("Please upgrade data files in \"%s\" with the elconv program.\n", data_dir); return EL_UPGRADE; } 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) /********************************************************************\ 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 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[256], *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; sprintf(file_name, "%s%s", lbs->data_dir, 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); } message = malloc(TEXT_SIZE + 1000); lseek(fh, lbs->el_index[index].offset, SEEK_SET); i = my_read(fh, message, TEXT_SIZE + 1000 - 1); if (i <= 0) { free(message); close(fh); return EL_FILE_ERROR; } message[i] = 0; close(fh); if (strncmp(message, "$@MID@$:", 8) != 0) { free(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); } /* check for correct ID */ if (atoi(message + 8) != message_id) { free(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); if (reply_to) el_decode(message, "Reply to: ", reply_to); if (in_reply_to) el_decode(message, "In reply to: ", in_reply_to); 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]); } } el_decode(message, "Attachment: ", attachment_all); if (encoding) el_decode(message, "Encoding: ", encoding); if (attachment) { /* break apart attachements */ for (i = 0; i < MAX_ATTACHMENTS; i++) if (attachment[i] != NULL) attachment[i][0] = 0; for (i = 0; i < MAX_ATTACHMENTS; i++) { if (attachment[i] != NULL) { if (i == 0) p = strtok(attachment_all, ","); else p = strtok(NULL, ","); if (p != NULL) strcpy(attachment[i], p); else break; } } } if (locked_by) el_decode(message, "Locked by: ", locked_by); 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."); free(message); return EL_FILE_ERROR; } else { strlcpy(text, p, *textsize); /* strip CR at end */ if (text[strlen(text) - 1] == '\n') { text[strlen(text) - 1] = 0; if (text[strlen(text) - 1] == '\r') text[strlen(text) - 1] = 0; } *textsize = strlen(text); } } else { text[0] = 0; *textsize = 0; } } free(message); return EL_SUCCESS; } /*------------------------------------------------------------------*/ int el_submit_attachment(LOGBOOK * lbs, char *afilename, char *buffer, int buffer_size, char *full_name) { char file_name[MAX_PATH_LENGTH], ext_file_name[MAX_PATH_LENGTH + 100], str[MAX_PATH_LENGTH], *p; 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); } if (full_name) strlcpy(full_name, ext_file_name, MAX_PATH_LENGTH); strlcpy(str, lbs->data_dir, sizeof(str)); strlcat(str, ext_file_name, sizeof(str)); /* save attachment */ fh = open(str, O_CREAT | O_RDWR | O_BINARY, 0644); if (fh < 0) { strlcpy(file_name, str, sizeof(str) - 40); 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) { char str[MAX_PATH_LENGTH]; strlcpy(str, lbs->data_dir, sizeof(str)); strlcat(str, file_name, sizeof(str)); remove(str); strlcat(str, ".thumb", sizeof(str)); remove(str); } /*------------------------------------------------------------------*/ int el_retrieve_attachment(LOGBOOK * lbs, int message_id, int n, char name[MAX_PATH_LENGTH]) { int i, index, size, fh; char file_name[256], *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; sprintf(file_name, "%s%s", lbs->data_dir, 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); name[0] = 0; for (i = 0; i <= n; i++) { if (i == 0) p = strtok(attachment_all, ","); else p = strtok(NULL, ","); if (p == NULL) break; } if (p) strlcpy(name, p, MAX_PATH_LENGTH); return EL_SUCCESS; } /*------------------------------------------------------------------*/ int el_submit(LOGBOOK * lbs, int message_id, BOOL bedit, char *date, char attr_name[MAX_N_ATTR][NAME_LENGTH], char attr_value[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, char *in_reply_to, char *reply_to, char *encoding, char afilename[MAX_ATTACHMENTS][256], BOOL mark_original, char *locked_by) /********************************************************************\ 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 Function value: int New message ID \********************************************************************/ { int n, i, j, size, fh, index, tail_size, orig_size, delta, reply_id; char file_name[256], dir[256], str[NAME_LENGTH], date_str[256]; time_t ltime; char *message, *p, *buffer; char attachment_all[64 * MAX_ATTACHMENTS]; tail_size = orig_size = 0; buffer = NULL; message = xmalloc(TEXT_SIZE + 100); /* 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; } sprintf(file_name, "%s%s", lbs->data_dir, 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); message[i] = 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, attr_value, n_attr, text, in_reply_to, reply_to, encoding, afilename, mark_original, locked_by); } /* 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 (strieq(date, "")) el_decode(message, "Date: ", date_str); else strlcpy(date_str, date, sizeof(date_str)); if (strieq(reply_to, "")) el_decode(message, "Reply to: ", reply_to); if (strieq(in_reply_to, "")) el_decode(message, "In reply to: ", in_reply_to); el_decode(message, "Attachment: ", attachment_all); /* buffer tail of logfile */ lseek(fh, 0, SEEK_END); orig_size = size; tail_size = TELL(fh) - (lbs->el_index[index].offset + size); if (tail_size > 0) { buffer = xmalloc(tail_size); lseek(fh, lbs->el_index[index].offset + size, SEEK_SET); n = my_read(fh, buffer, tail_size); } lseek(fh, lbs->el_index[index].offset, SEEK_SET); } else { /* create new message */ if (!date[0]) get_rfc2822_date(date_str, sizeof(date_str), 0); else strlcpy(date_str, date, sizeof(date_str)); for (i = 0; i < 12; i++) if (strncmp(date_str + 8, mname[i], 3) == 0) break; ltime = date_to_ltime(date_str); sprintf(file_name, "%c%c%02d%c%ca.log", date_str[14], date_str[15], i + 1, date_str[5], date_str[6]); sprintf(str, "%s%s", dir, file_name); fh = open(str, O_CREAT | O_RDWR | O_BINARY, 0644); if (fh < 0) { xfree(message); return -1; } lseek(fh, 0, SEEK_END); /* new message id is old plus one */ if (message_id == 0) { message_id = 1; for (i = 0; i < *lbs->n_el_index; i++) if (lbs->el_index[i].message_id >= message_id) message_id = lbs->el_index[i].message_id + 1; } /* enter message in index */ index = *lbs->n_el_index; (*lbs->n_el_index)++; lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index)); lbs->el_index[index].message_id = message_id; strcpy(lbs->el_index[index].file_name, file_name); lbs->el_index[index].file_time = ltime; lbs->el_index[index].offset = TELL(fh); lbs->el_index[index].in_reply_to = atoi(in_reply_to); /* 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", date_str); if (reply_to[0]) sprintf(message + strlen(message), "Reply to: %s\n", reply_to); if (in_reply_to[0]) sprintf(message + strlen(message), "In reply to: %s\n", in_reply_to); for (i = 0; i < n_attr; i++) sprintf(message + strlen(message), "%s: %s\n", attr_name[i], attr_value[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", encoding); if (locked_by && locked_by[0]) sprintf(message + strlen(message), "Locked by: %s\n", locked_by); sprintf(message + strlen(message), "========================================\n"); strlcat(message, text, TEXT_SIZE + 100); strlcat(message, "\n", TEXT_SIZE + 100); 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 */ 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], lock[256]; reply_id = atoi(in_reply_to); /* 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); 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); } 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], *p, *ps, *message; int size, status; /* retrieve original message */ size = TEXT_SIZE + 1000; message = 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); 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); xfree(message); } /*------------------------------------------------------------------*/ int el_delete_message(LOGBOOK * lbs, int message_id, BOOL delete_attachments, char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], BOOL delete_bw_ref, BOOL delete_reply_to) /********************************************************************\ Routine: el_delete_message Purpose: Delete an ELog entry including attachments Input: LOGBOOK *lbs Pointer to logbook structure int message_id Message ID BOOL delete_attachments Delete attachments if TRUE char attachment Used to return attachments (on move) BOOL delete_bw_ref If true, delete backward references BOOL delete_reply_to If true, delete replies to this message Output: Function value: EL_SUCCESS Successful completion \********************************************************************/ { int i, index, n, size, fh, tail_size, old_offset; char str[MAX_PATH_LENGTH], file_name[MAX_PATH_LENGTH], reply_to[MAX_REPLY_TO * 10], in_reply_to[256]; char *buffer, *p; char *message, attachment_all[64 * MAX_ATTACHMENTS]; 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; sprintf(file_name, "%s%s", lbs->data_dir, lbs->el_index[index].file_name); fh = open(file_name, O_RDWR | O_BINARY, 0644); if (fh < 0) return EL_FILE_ERROR; message = xmalloc(TEXT_SIZE + 1000); lseek(fh, lbs->el_index[index].offset, SEEK_SET); i = my_read(fh, message, TEXT_SIZE + 1000 - 1); if (i <= 0) { xfree(message); close(fh); return EL_FILE_ERROR; } if (_logging_level > 1) { sprintf(str, "DELETE entry #%d", message_id); write_logfile(lbs, str); } message[i] = 0; if (strncmp(message, "$@MID@$:", 8) != 0) { close(fh); xfree(message); /* file might have been edited, rebuild index */ el_build_index(lbs, TRUE); return el_delete_message(lbs, message_id, delete_attachments, attachment, delete_bw_ref, delete_reply_to); } /* check for correct ID */ if (atoi(message + 8) != message_id) { close(fh); xfree(message); return EL_FILE_ERROR; } /* decode message size */ p = strstr(message + 8, "$@MID@$:"); if (p == NULL) size = strlen(message); else size = p - message; message[size] = 0; /* delete attachments */ el_decode(message, "Attachment: ", attachment_all); 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(message, "Reply to: ", reply_to); el_decode(message, "In reply to: ", in_reply_to); /* buffer tail of logfile */ lseek(fh, 0, SEEK_END); tail_size = TELL(fh) - (lbs->el_index[index].offset + size); buffer = NULL; if (tail_size > 0) { buffer = xmalloc(tail_size); lseek(fh, lbs->el_index[index].offset + size, SEEK_SET); n = my_read(fh, buffer, tail_size); } lseek(fh, lbs->el_index[index].offset, SEEK_SET); if (tail_size > 0) { n = write(fh, buffer, tail_size); xfree(buffer); } /* truncate file here */ TRUNCATE(fh); /* if file length gets zero, delete file */ tail_size = lseek(fh, 0, SEEK_END); close(fh); xfree(message); if (tail_size == 0) remove(file_name); /* remove message from index */ strcpy(str, lbs->el_index[index].file_name); old_offset = lbs->el_index[index].offset; for (i = index; i < *lbs->n_el_index - 1; i++) memcpy(&lbs->el_index[i], &lbs->el_index[i + 1], sizeof(EL_INDEX)); (*lbs->n_el_index)--; if (*lbs->n_el_index > 0) lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index)); /* correct all offsets after deleted message */ for (i = 0; i < *lbs->n_el_index; i++) if (strieq(lbs->el_index[i].file_name, str) && lbs->el_index[i].offset > old_offset) lbs->el_index[i].offset -= size; /* if other logbook has same index, update pointers */ for (i = 0; lb_list[i].name[0]; i++) if (&lb_list[i] != lbs && lb_list[i].n_el_index == lbs->n_el_index) lb_list[i].el_index = lbs->el_index; /* delete also replies to this message */ if (delete_reply_to && reply_to[0]) { p = reply_to; if (isdigit(*p)) do { if (atoi(p)) el_delete_message(lbs, atoi(p), TRUE, NULL, FALSE, TRUE); while (*p && isdigit(*p)) p++; while (*p && (*p == ',' || *p == ' ')) p++; } while (*p); } /* delete backward references */ if (in_reply_to[0] && delete_bw_ref) { p = in_reply_to; do { if (atoi(p)) remove_reference(lbs, atoi(p), message_id, TRUE); while (*p && isdigit(*p)) p++; while (*p && (*p == ',' || *p == ' ')) p++; } while (*p); } /* execute shell if requested */ if (getcfg(lbs->name, "Execute delete", str, sizeof(str))) execute_shell(lbs, message_id, NULL, 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]; char list[MAX_N_ATTR][NAME_LENGTH], list1[MAX_N_ATTR][NAME_LENGTH]; char *att_file; attrib = xmalloc(MAX_N_ATTR * NAME_LENGTH); text = xmalloc(TEXT_SIZE); att_file = xmalloc(MAX_ATTACHMENTS * 256); el_retrieve(lbs, new_id, date, attr_list, (void *) attrib, lbs->n_attr, NULL, 0, in_reply_to, reply_to, (void *) att_file, encoding, locked_by); /* go through in_reply_to list */ n = strbreak(in_reply_to, list, MAX_N_ATTR, ","); for (i = 0; i < n; i++) { size = TEXT_SIZE; el_retrieve(lbs, atoi(list[i]), date, attr_list, (void *) attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, (void *) att_file, encoding, locked_by); n1 = strbreak(reply_to, list1, MAX_N_ATTR, ","); 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, (void *) attrib, lbs->n_attr, text, in_reply_to, reply_to, encoding, (void *) att_file, TRUE, locked_by); } el_retrieve(lbs, new_id, date, attr_list, (void *) attrib, lbs->n_attr, NULL, 0, in_reply_to, reply_to, (void *) att_file, encoding, locked_by); /* go through reply_to list */ n = strbreak(reply_to, list, MAX_N_ATTR, ","); for (i = 0; i < n; i++) { size = sizeof(text); el_retrieve(lbs, atoi(list[i]), date, attr_list, (void *) attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, (void *) att_file, encoding, locked_by); n1 = strbreak(in_reply_to, list1, MAX_N_ATTR, ","); 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, (void *) attrib, lbs->n_attr, text, in_reply_to, reply_to, encoding, (void *) att_file, TRUE, locked_by); } 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]; char list[MAX_N_ATTR][NAME_LENGTH], str[256]; char att_file[MAX_ATTACHMENTS][256]; /* retrieve message */ text = xmalloc(TEXT_SIZE); size = TEXT_SIZE; el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, att_file, encoding, locked_by); /* 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); 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, ","); 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], att_file[MAX_ATTACHMENTS][256]; /* retrieve message */ text = xmalloc(TEXT_SIZE); size = TEXT_SIZE; status = el_retrieve(lbs, old_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to, att_file, encoding, locked_by); 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); 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) /* 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]; 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); /* submit message, unlocked if user==NULL */ el_submit(lbs, message_id, TRUE, date, attr_list, attrib, lbs->n_attr, text, in_reply_to, reply_to, encoding, att_file, FALSE, user); return EL_SUCCESS; } /*------------------------------------------------------------------*/ void write_logfile(LOGBOOK * lbs, const char *text) { char file_name[2000]; char str[10000], unm[256]; int fh; time_t now; char buf[10000]; if (lbs == NULL) { if (!getcfg("global", "logfile", str, sizeof(str))) return; } else if (!getcfg(lbs->name, "logfile", str, sizeof(str))) return; if (str[0] == DIR_SEPARATOR || str[1] == ':') strlcpy(file_name, str, sizeof(file_name)); else { strlcpy(file_name, logbook_dir, sizeof(file_name)); strlcat(file_name, str, sizeof(file_name)); } fh = open(file_name, O_RDWR | O_BINARY | O_CREAT | O_APPEND, 0644); if (fh < 0) return; now = time(0); strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", localtime(&now)); strcat(buf, " "); if (isparam("unm") && rem_host[0]) { strlcpy(unm, getparam("unm"), sizeof(unm)); if (rem_host_ip[0]) sprintf(buf + strlen(buf), "[%s@%s(%s)] ", unm, rem_host, rem_host_ip); else sprintf(buf + strlen(buf), "[%s@%s] ", unm, rem_host); } else if (rem_host[0]) { if (rem_host_ip[0]) sprintf(buf + strlen(buf), "[%s(%s)] ", rem_host, rem_host_ip); else sprintf(buf + strlen(buf), "[%s] ", rem_host); } else sprintf(buf + strlen(buf), "[%s] ", rem_host_ip); if (lbs) sprintf(buf + strlen(buf), "{%s} ", lbs->name); strlcat(buf, text, sizeof(buf) - 1); #ifdef OS_WINNT if (strlen(buf) > 0 && buf[strlen(buf) - 1] != '\n') strlcat(buf, "\r\n", sizeof(buf)); else if (strlen(buf) > 1 && buf[strlen(buf) - 2] != '\r') strlcpy(buf + strlen(buf) - 2, "\r\n", sizeof(buf) - (strlen(buf) - 2)); #else if (strlen(buf) > 1 && buf[strlen(buf) - 1] != '\n') strlcat(buf, "\n", sizeof(buf)); #endif write(fh, buf, strlen(buf)); close(fh); } /*------------------------------------------------------------------*/ /* void logd(const char *format, ...) { va_list argptr; char str[10000]; FILE *f; time_t now; char buf[1000]; va_start(argptr, format); vsprintf(str, (char *) format, argptr); va_end(argptr); f = fopen("c:\\tmp\\elogd.log", "a"); if (!f) return; now = time(0); strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", localtime(&now)); strcat(buf, " "); strlcat(buf, str, sizeof(buf)); if (buf[strlen(buf) - 1] != '\n') strlcat(buf, "\n", sizeof(buf)); fprintf(f, buf); fclose(f); } */ /*------------------------------------------------------------------*/ char *html_tags[] = { "", "", "

", "


", "" }; int is_html(char *s) { char *str, *p; int i; str = xstrdup(s); for (i = 0; i < (int) strlen(s); i++) str[i] = toupper(s[i]); str[i] = 0; for (i = 0; html_tags[i][0]; i++) { p = strstr(str, html_tags[i]); if (p && strchr(p, '>') && (p == str || (p > str && *(p - 1) != '\\'))) { xfree(str); return TRUE; } } xfree(str); return FALSE; } /*------------------------------------------------------------------*/ char *full_html_tags[] = { "", "", "", "" }; int is_full_html(char *file_name) { char *str, *p; int i, fh, length; unsigned char *buf; fh = open(file_name, O_RDONLY | O_BINARY); if (fh < 0) return FALSE; lseek(fh, 0, SEEK_END); length = TELL(fh); lseek(fh, 0, SEEK_SET); if (length > 1000) length = 1000; buf = xmalloc(length); read(fh, buf, length); close(fh); str = xstrdup(buf); for (i = 0; i < (int) strlen(buf); i++) str[i] = toupper(buf[i]); str[i] = 0; xfree(buf); for (i = 0; full_html_tags[i][0]; i++) { p = strstr(str, full_html_tags[i]); if (p && strchr(p, '>') && (p == str || (p > str && *(p - 1) != '\\'))) { xfree(str); return TRUE; } } xfree(str); return FALSE; } /*------------------------------------------------------------------*/ int is_ascii(char *file_name) { int i, fh, length; unsigned char *buf; fh = open(file_name, O_RDONLY | O_BINARY); if (fh < 0) return FALSE; lseek(fh, 0, SEEK_END); length = TELL(fh); lseek(fh, 0, SEEK_SET); if (length > 1000) length = 1000; buf = xmalloc(length); read(fh, buf, length); close(fh); for (i = 0; i < length; i++) { if (buf[i] < 32 && buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t') { xfree(buf); return FALSE; } } xfree(buf); return TRUE; } /*------------------------------------------------------------------*/ int is_image(char *att) { return (stristr(att, ".GIF") != NULL) || (stristr(att, ".JPG") != NULL) || (stristr(att, ".JPEG") != NULL) || (stristr(att, ".PNG") != NULL); } /*------------------------------------------------------------------*/ void strip_html(char *s) { char *p; while ((p = strchr(s, '<')) != NULL) { if (strchr(p, '>')) strcpy(p, strchr(p, '>') + 1); else *p = 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 rsputs(const char *str) { if (strlen_retbuf + (int) strlen(str) > return_buffer_size) { return_buffer = xrealloc(return_buffer, return_buffer_size + 100000); memset(return_buffer + return_buffer_size, 0, 100000); return_buffer_size += 100000; } strcpy(return_buffer + strlen_retbuf, str); strlen_retbuf += strlen(str); } /*------------------------------------------------------------------*/ char *key_list[] = { "http://", "https://", "ftp://", "mailto:", "elog:", "file://", "" }; void rsputs2(LOGBOOK * lbs, int absolute_link, const char *str) { int i, j, k, l, m, n; char *p, *pd, link[1000], link_text[1000], tmp[1000], base_url[256]; if (strlen_retbuf + (int) (2 * strlen(str) + 1000) >= return_buffer_size) { return_buffer = xrealloc(return_buffer, return_buffer_size + 100000); memset(return_buffer + return_buffer_size, 0, 100000); return_buffer_size += 100000; } j = strlen_retbuf; for (i = 0; i < (int) strlen(str); i++) { for (l = 0; key_list[l][0]; l++) { if (strncmp(str + i, key_list[l], strlen(key_list[l])) == 0) { /* check for escape character */ if (i > 0 && *(str + i - 1) == '\\') { j--; *(return_buffer + j) = 0; continue; } p = (char *) (str + i + strlen(key_list[l])); i += strlen(key_list[l]); for (k = 0; *p && strcspn(p, " \t\n\r({[)}]\"") && k < (int) sizeof(link); k++, i++) link[k] = *p++; link[k] = 0; i--; /* link may not end with a '.'/',' (like in a sentence) */ if (link[k - 1] == '.' || link[k - 1] == ',') { link[k - 1] = 0; k--; i--; } /* check if link contains coloring */ p = strchr(link, '\001'); if (p != NULL) { strlcpy(link_text, link, sizeof(link_text)); /* skip everything between '<' and '>' */ pd = p; while (*pd && *pd != '\002') *p = *pd++; strcpy(p, pd + 1); /* skip '' */ p = strchr(link, '\001'); if (p != NULL) { pd = p; while (*pd && *pd != '\002') *p = *pd++; strcpy(p, pd + 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) { 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)) { /* if link contains reference to other logbook, put logbook explicitly */ if (absolute_link) compose_base_url(NULL, base_url, sizeof(base_url)); else strcpy(base_url, "../"); sprintf(return_buffer + j, "
elog:%s", base_url, link, link_text); } else if (link[0] == '/') { if (absolute_link) compose_base_url(NULL, base_url, sizeof(base_url)); else base_url[0] = 0; sprintf(return_buffer + j, "elog:%s", base_url, lbs->name_enc, _current_message_id, link, link_text); } else { if (absolute_link) compose_base_url(lbs, base_url, sizeof(base_url)); else base_url[0] = 0; sprintf(return_buffer + j, "elog:%s", base_url, link, link_text); } } else if (strcmp(key_list[l], "mailto:") == 0) { sprintf(return_buffer + j, "%s", link, link_text); } else { sprintf(return_buffer + j, "%s", key_list[l]); j += strlen(return_buffer + j); strlen_retbuf = j; /* link_text can contain special characters */ rsputs2(lbs, absolute_link, link_text); j = strlen_retbuf; sprintf(return_buffer + j, ""); } j += strlen(return_buffer + j); break; } } if (!key_list[l][0]) { if (strncmp(str + i, "
", 4) == 0) { strcpy(return_buffer + j, "
"); j += 4; i += 3; } else switch (str[i]) { case '&': strcat(return_buffer, "&"); j += 5; break; case '<': strcat(return_buffer, "<"); j += 4; break; case '>': strcat(return_buffer, ">"); j += 4; break; /* suppress escape character '\' in front of HTML or ELCode tag */ case '\\': if (str[i + 1] != '<' && str[i + 1] != '[') return_buffer[j++] = str[i]; break; /* the translation for the search highliting */ case '\001': strcat(return_buffer, "<"); j++; break; case '\002': strcat(return_buffer, ">"); j++; break; case '\003': strcat(return_buffer, "\""); j++; break; case '\004': strcat(return_buffer, " "); j++; break; default: return_buffer[j++] = str[i]; } } } return_buffer[j] = 0; strlen_retbuf = j; } /*------------------------------------------------------------------*/ void rsputs3(const char *text) { int i; char str[2]; str[1] = 0; for (i = 0; i < (int) strlen(text); i++) { switch (text[i]) { case '<': rsputs("<"); break; case '>': rsputs(">"); break; case '&': rsputs("&"); break; case '\"': rsputs("""); break; default: str[0] = text[i]; rsputs(str); } } } /*------------------------------------------------------------------*/ typedef struct { char *pattern; char *subst; } PATTERN_LIST; PATTERN_LIST pattern_list[] = { /* smileys */ {":))", "\"Happy\""}, {":-))", "\"Happy\""}, {":)", "\"Smile\""}, {":-)", "\"Smile\""}, {":(", "\"Frown\""}, {":-(", "\"Frown\""}, {";)", "\"Wink\""}, {";-)", "\"Wink\""}, {":d", "\"Big"}, {"?-)", "\"Confused\""}, {";(", "\"Crying\""}, {";-(", "\"Crying\""}, {":]", "\"Pleased\""}, {":-]", "\"Pleased\""}, {":o", "\"Yawn\""}, {":-o", "\"Yawn\""}, {"8-)", "\"Cool\""}, {"8o", "\"Astonished\""}, {"x-(", "\"Mad\""}, {":p", "\"Tongue\""}, {":-p", "\"Tongue\""}, /* formatting */ {"[b]", ""}, {"[/b]", ""}, {"[u]", ""}, {"[/u]", ""}, {"[i]", ""}, {"[/i]", ""}, {"[center]", "
"}, {"[/center]", "
"}, {"[color=", ""}, {"[/color]", ""}, {"[size=", ""}, {"[/size]", ""}, {"[font=", ""}, {"[/font]", ""}, {"\r\n[code]", "
"},
   {"[code]", "
"},
   {"[/code]\r\n", "
"}, {"[/code]", "
"}, /* lists */ {"[list]\r", "
    "}, {"[list]", "
      "}, {"[*]", "
    • "}, {"[/list]\r", ""}, // either
    or {"[/list]", ""}, {"[list=", "
      "}, /* headings */ {"[h1]", "

      "}, {"[/h1]", "

      "}, {"[h2]", "

      "}, {"[/h2]", "

      "}, {"[h3]", "

      "}, {"[/h3]", "

      "}, /* URLs */ {"[url=", "%s"}, {"[url]", "%s"}, {"[/url]", ""}, {"[email]", "%s"}, {"[/email]", ""}, {"[img]", ""}, {"[/img]", ""}, /* quote */ {"[quote=", "
      %s:
      "}, {"[quote]", "
      %s:
      "}, {"[/quote]\r", "

      \r\n"}, {"[/quote]", "
      \r\n"}, {"", ""} }; char *email_quote_table = "\n\n"); /*---- entry form ----*/ /* table for two-column items */ rsprintf("\n"); subtable = 0; } else rsprintf(""); } /* if last attribute, close row or subtable */ if (aindex == n_disp_attr - 1) { if (subtable) { rsprintf("
      %s:
      "; void rsputs_elcode(LOGBOOK * lbs, BOOL email_notify, const char *str) { int i, j, k, l, m, interprete_elcode, escape_char, ordered_list; char *p, *pd, link[1000], link_text[1000], tmp[1000], attrib[1000], hattrib[1000], value[1000], subst[1000], base_url[256], param[256], *lstr; if (strlen_retbuf + (int) (2 * strlen(str) + 1000) >= return_buffer_size) { return_buffer = xrealloc(return_buffer, return_buffer_size + 100000); memset(return_buffer + return_buffer_size, 0, 100000); return_buffer_size += 100000; } interprete_elcode = TRUE; ordered_list = FALSE; escape_char = FALSE; j = strlen_retbuf; m = 0; /* make lower case copy of str */ lstr = xmalloc(strlen(str) + 1); for (pd = lstr, p = (char *) str; *p; p++, pd++) *pd = tolower(*p); *pd = 0; for (i = 0; i < (int) strlen(str); i++) { for (l = 0; key_list[l][0]; l++) { if (strncmp(lstr + i, key_list[l], strlen(key_list[l])) == 0) { /* check for escape character */ if (i > 0 && *(str + i - 1) == '\\') { j--; *(return_buffer + j) = 0; continue; } p = (char *) (str + i + strlen(key_list[l])); i += strlen(key_list[l]); for (k = 0; *p && strcspn(p, " \t\n\r({[)}]\"") && k < (int) sizeof(link); k++, i++) link[k] = *p++; link[k] = 0; i--; /* link may not end with a '.'/',' (like in a sentence) */ if (link[k - 1] == '.' || link[k - 1] == ',') { link[k - 1] = 0; k--; i--; } strlcpy(link_text, link, sizeof(link_text)); /* check if link contains coloring */ while ((p = strchr(link, '\001')) != NULL) { /* skip everything between '<' and '>' */ pd = p; while (*pd && *pd != '\002') *p = *pd++; strcpy(p, pd + 1); /* skip '' */ p = strchr(link, '\001'); if (p != NULL) { pd = p; while (*pd && *pd != '\002') *p = *pd++; strcpy(p, pd + 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)) { /* if link contains reference to other logbook, put logbook explicitly */ if (email_notify) compose_base_url(NULL, base_url, sizeof(base_url)); else strcpy(base_url, "../"); sprintf(return_buffer + j, "elog:%s", base_url, link, link_text); } else if (link[0] == '/') { if (email_notify) compose_base_url(NULL, base_url, sizeof(base_url)); else base_url[0] = 0; sprintf(return_buffer + j, "elog:%s", base_url, lbs->name_enc, _current_message_id, link, link_text); } else { if (email_notify) compose_base_url(lbs, base_url, sizeof(base_url)); else base_url[0] = 0; sprintf(return_buffer + j, "elog:%s", base_url, link, link_text); } } else if (strcmp(key_list[l], "mailto:") == 0) { sprintf(return_buffer + j, "%s", link, link_text); } else { sprintf(return_buffer + j, "%s", key_list[l]); j += strlen(return_buffer + j); strlen_retbuf = j; /* link_text can contain special characters */ rsputs2(lbs, email_notify, link_text); j = strlen_retbuf; sprintf(return_buffer + j, ""); } j += strlen(return_buffer + j); break; } } if (key_list[l][0]) continue; 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]")) interprete_elcode = TRUE; /* check for escape character */ if (i > 0 && str[i - 1] == '\\') { j--; 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... break; } if (interprete_elcode) { if (stristr(pattern_list[l].pattern, "[list=")) ordered_list = TRUE; 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] == '\"') strcpy(attrib, attrib + 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) /* add http:// if missing */ 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_email_from(lbs, link, NULL, NULL); p = strchr(attrib, '/'); if (p) m = atoi(p + 1) - 1; if (strchr(link, '@')) p = strchr(link, '@') + 1; else p = link; sprintf(hattrib, "cid:att%d@%s", m, p); } else { if (email_notify) compose_base_url(lbs, hattrib, sizeof(hattrib)); 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) && strstr(pattern_list[l].subst, "mailto") == NULL && strstr(pattern_list[l].subst, "img") == NULL) sprintf(hattrib, "http://%s", attrib); strlcpy(subst, pattern_list[l].subst, sizeof(subst)); *strchr(subst, '#') = 's'; sprintf(return_buffer + j, subst, hattrib, attrib); j += strlen(return_buffer + j); } } else if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] == '=') { /* extract sting after '=' and put it into '%s' of subst */ i += strlen(pattern_list[l].pattern); strextract(str + i, ']', attrib, sizeof(attrib)); i += strlen(attrib); sprintf(return_buffer + j, pattern_list[l].subst, attrib); j += strlen(return_buffer + j); } else if (strncmp(pattern_list[l].pattern, "[/list]", 7) == 0) { if (ordered_list) strcpy(subst, ""); else strcpy(subst, ""); ordered_list = FALSE; strcpy(return_buffer + j, subst); j += strlen(subst); i += strlen(pattern_list[l].pattern) - 1; // 1 gets added in for loop... } else { /* 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)); 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... } } // interprete_elcode if (stristr(pattern_list[l].pattern, "[code]")) interprete_elcode = FALSE; break; } } if ((interprete_elcode && pattern_list[l].pattern[0]) || stristr(pattern_list[l].pattern, "[code]")) continue; if (strnieq(str + i, "
      ", 4)) { strcpy(return_buffer + j, "
      "); j += 6; i += 3; } else switch (str[i]) { case '\r': if (interprete_elcode) { strcat(return_buffer, "
      \r\n"); j += 8; } else { strcat(return_buffer, "\r\n"); j += 2; } break; case '\n': break; case '&': strcat(return_buffer, "&"); j += 5; break; case '<': strcat(return_buffer, "<"); j += 4; break; case '>': strcat(return_buffer, ">"); j += 4; break; /* the translation for the search highliting */ case '\001': strcat(return_buffer, "<"); j++; break; case '\002': strcat(return_buffer, ">"); j++; break; case '\003': strcat(return_buffer, "\""); j++; break; case '\004': strcat(return_buffer, " "); j++; break; default: return_buffer[j++] = str[i]; } } xfree(lstr); return_buffer[j] = 0; strlen_retbuf = j; } /*------------------------------------------------------------------*/ void rsprintf(const char *format, ...) { va_list argptr; char str[10000]; va_start(argptr, format); vsprintf(str, (char *) format, argptr); va_end(argptr); if (strlen_retbuf + (int) strlen(str) > return_buffer_size) { return_buffer = xrealloc(return_buffer, return_buffer_size + 100000); memset(return_buffer + return_buffer_size, 0, 100000); return_buffer_size += 100000; } strcpy(return_buffer + strlen_retbuf, str); strlen_retbuf += strlen(str); } /*------------------------------------------------------------------*/ void flush_return_buffer() { send(_sock, return_buffer, strlen_retbuf, 0); memset(return_buffer, 0, return_buffer_size); strlen_retbuf = 0; } /*------------------------------------------------------------------*/ /* Parameter handling functions similar to setenv/getenv */ void initparam() { memset(_param, 0, sizeof(_param)); memset(_value, 0, sizeof(_value)); _mtext[0] = 0; _cmdline[0] = 0; } int setparam(char *param, char *value) { int i; char str[10000]; if (strieq(param, "text")) { if (strlen(value) >= TEXT_SIZE) { sprintf(str, "Error: Entry text too big (%lu bytes). Please increase TEXT_SIZE and recompile elogd\n", (unsigned long) strlen(value)); show_error(str); return 0; } strlcpy(_mtext, value, TEXT_SIZE); return 1; } if (strieq(param, "cmdline")) { if (strlen(value) >= CMD_SIZE) { sprintf(str, "Error: Command line too big (%lu bytes). Please increase CMD_SIZE and recompile elogd\n", (unsigned long) strlen(value)); show_error(str); return 0; } strlcpy(_cmdline, value, CMD_SIZE); return 1; } /* paremeters can be superseeded */ for (i = 0; i < MAX_PARAM; i++) if (_param[i][0] == 0 || strieq(param, _param[i])) break; if (i < MAX_PARAM) { if (strlen(param) >= NAME_LENGTH) { sprintf(str, "Error: Parameter name too big (%lu bytes).\n", (unsigned long) strlen(param)); show_error(str); return 0; } strlcpy(_param[i], param, NAME_LENGTH); if (strlen(value) >= NAME_LENGTH) { sprintf(str, "Error: Parameter value for parameter %s too big (%lu bytes). Please increase NAME_LENGTH and recompile elogd\n", param, (unsigned long) strlen(value)); show_error(str); return 0; } strlcpy(_value[i], value, NAME_LENGTH); } else { sprintf(str, "Error: Too many parameters (> %d). Cannot perform operation.\n", MAX_PARAM); show_error(str); return 0; } return 1; } char *getparam(char *param) { int i; if (strieq(param, "text")) return _mtext; if (strieq(param, "cmdline")) return _cmdline; for (i = 0; i < MAX_PARAM && _param[i][0]; i++) if (strieq(param, _param[i])) break; if (i < MAX_PARAM && _param[i][0]) return _value[i]; return NULL; } BOOL enumparam(int n, char *param, char *value) { param[0] = value[0] = 0; if (n >= MAX_PARAM) return FALSE; if (_param[n][0] == 0) return FALSE; strcpy(param, _param[n]); strcpy(value, _value[n]); return TRUE; } BOOL isparam(char *param) { int i; if (strieq(param, "text")) return _mtext[0] != 0; for (i = 0; i < MAX_PARAM && _param[i][0]; i++) if (strieq(param, _param[i])) break; if (i < MAX_PARAM && _param[i][0]) return TRUE; return FALSE; } void unsetparam(char *param) { int i; for (i = 0; i < MAX_PARAM; i++) if (strieq(param, _param[i])) break; if (i < MAX_PARAM) { for (; i < MAX_PARAM - 1; i++) { strlcpy(_param[i], _param[i + 1], NAME_LENGTH); strlcpy(_value[i], _value[i + 1], NAME_LENGTH); } _param[MAX_PARAM - 1][0] = 0; _value[MAX_PARAM - 1][0] = 0; } } /*------------------------------------------------------------------*/ void extract_path(char *str) { char *p, str2[256]; p = NULL; if (strstr(str, "http://")) p = str + 7; if (strstr(str, "https://")) p = str + 8; if (p) { while (*p && *p != '/') p++; if (*p == '/') p++; strcpy(str2, p); strcpy(str, str2); if (str[strlen(str) - 1] == '/') str[strlen(str) - 1] = 0; } } /*------------------------------------------------------------------*/ void extract_host(char *str) { char *p, *ph, str2[256]; p = NULL; if (strstr(str, "http://")) p = str + 7; else if (strstr(str, "https://")) p = str + 8; if (p) { ph = p; while (*p && *p != '/' && *p != ':') p++; *p = 0; strcpy(str2, ph); strcpy(str, str2); } } /*------------------------------------------------------------------*/ void compose_base_url(LOGBOOK * lbs, char *base_url, int size) /* return URL for elogd with optional logbook subdirectory */ { 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 { strcpy(base_url, "http://"); if (elog_tcp_port == 80) sprintf(base_url + strlen(base_url), "%s/", host_name); else sprintf(base_url + strlen(base_url), "%s:%d/", host_name, elog_tcp_port); if (lbs) { strlcat(base_url, lbs->name_enc, size); strlcat(base_url, "/", size); } } } else { if (base_url[strlen(base_url) - 1] != '/') strlcat(base_url, "/", size); if (lbs) { strlcat(base_url, lbs->name_enc, size); strlcat(base_url, "/", size); } } } /*------------------------------------------------------------------*/ void set_location(LOGBOOK * lbs, char *rel_path) { char str[NAME_LENGTH], group[NAME_LENGTH], list[NAME_LENGTH], *p; int i; 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 */ sprintf(group, "Group %s", p); if (getcfg("global", group, list, sizeof(list))) *p = 0; /* if last subdir equals any top group, strip it */ sprintf(group, "Top group %s", p); if (getcfg("global", group, list, sizeof(list))) *p = 0; } else { /* assemble absolute path from host name and port */ sprintf(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")) { strlcpy(str, "http://localhost", sizeof(str)); if (elog_tcp_port != 80) sprintf(str + strlen(str), ":%d", elog_tcp_port); strlcat(str, "/", sizeof(str)); } /* add trailing '/' if not present */ if (str[strlen(str) - 1] != '/') strlcat(str, "/", sizeof(str)); /* add top group if existing and not logbook */ if (!lbs && getcfg_topgroup()) { strlcat(str, getcfg_topgroup(), sizeof(str)); strlcat(str, "/", sizeof(str)); } if (strncmp(rel_path, "../", 3) == 0) strlcat(str, rel_path + 3, sizeof(str)); else if (strcmp(rel_path, ".") == 0) { if (lbs) strlcat(str, lbs->name_enc, sizeof(str)); } else if (rel_path[0] == '/') strlcat(str, rel_path + 1, sizeof(str)); else { if (lbs) { strlcat(str, lbs->name_enc, sizeof(str)); strlcat(str, "/", sizeof(str)); strlcat(str, rel_path, sizeof(str)); } else strlcat(str, rel_path, sizeof(str)); } rsputs("Location: "); rsputs(str); } } } rsprintf("\r\n\r\nredir\r\n"); } /*------------------------------------------------------------------*/ void set_redir(LOGBOOK * lbs, char *redir) { char str[NAME_LENGTH]; str[0] = 0; /* prepare relative path */ if (redir[0]) strlcpy(str, redir, sizeof(str)); else { if (lbs) sprintf(str, "../%s/", lbs->name_enc); else if (getcfg_topgroup()) sprintf(str, "."); } set_location(lbs, str); } /*------------------------------------------------------------------*/ void set_cookie(LOGBOOK * lbs, char *name, char *value, BOOL global, char *expiration) { char lb_name[256], str[NAME_LENGTH], format[80]; double exp; time_t now; struct tm *gmt; if (lbs) strcpy(lb_name, lbs->name); else strcpy(lb_name, "global"); if (value != NULL) rsprintf("Set-Cookie: %s=%s;", name, value); else rsprintf("Set-Cookie: %s;", name); /* add path */ if (global) { /* path for all logbooks */ if (getcfg(lb_name, "URL", str, sizeof(str))) { extract_path(str); url_encode(str, sizeof(str)); rsprintf(" path=/%s;", str); } else rsprintf(" path=/;"); } else { /* path for individual logbook */ if (getcfg(lb_name, "URL", str, sizeof(str))) { extract_path(str); url_encode(str, sizeof(str)); if (str[0]) rsprintf(" path=/%s/%s;", str, lbs->name_enc); else rsprintf(" path=/%s;", lbs->name_enc); } else rsprintf(" path=/%s;", lbs->name_enc); } exp = atof(expiration); /* to clear a cookie, set expiration date to yesterday */ if (value != NULL && value[0] == 0) exp = -24; /* add expriation date */ if (exp != 0 && exp < 100000) { time(&now); now += (int) (3600 * exp); gmt = gmtime(&now); strcpy(format, "%A, %d-%b-%y %H:%M:%S GMT"); strftime(str, sizeof(str), format, gmt); rsprintf(" expires=%s;", str); } rsprintf("\r\n"); } /*------------------------------------------------------------------*/ void redirect(LOGBOOK * lbs, char *rel_path) { /* redirect */ rsprintf("HTTP/1.1 302 Found\r\n"); rsprintf("Server: ELOG HTTP %s-%d\r\n", VERSION, atoi(svn_revision + 13)); if (use_keepalive) { rsprintf("Connection: Keep-Alive\r\n"); rsprintf("Keep-Alive: timeout=60, max=10\r\n"); } set_location(lbs, rel_path); } /*------------------------------------------------------------------*/ int strbreak(char *str, char list[][NAME_LENGTH], int size, char *brk) /* 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 == '"') { 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; 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], type[NAME_LENGTH], tmp_list[MAX_N_ATTR][NAME_LENGTH]; int i, j, n, m; 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, ","); /* get options lists for attributes */ memset(attr_options, 0, sizeof(attr_options)); for (i = 0; i < n; i++) { sprintf(str, "Options %s", attr_list[i]); if (getcfg(logbook, str, list, sizeof(list))) strbreak(list, attr_options[i], MAX_N_LIST, ","); sprintf(str, "MOptions %s", attr_list[i]); if (getcfg(logbook, str, list, sizeof(list))) { strbreak(list, attr_options[i], MAX_N_LIST, ","); attr_flags[i] |= AF_MULTI; } sprintf(str, "ROptions %s", attr_list[i]); if (getcfg(logbook, str, list, sizeof(list))) { strbreak(list, attr_options[i], MAX_N_LIST, ","); attr_flags[i] |= AF_RADIO; } sprintf(str, "IOptions %s", attr_list[i]); if (getcfg(logbook, str, list, sizeof(list))) { strbreak(list, attr_options[i], MAX_N_LIST, ","); attr_flags[i] |= AF_ICON; } } /* check if attribute required */ getcfg(logbook, "Required Attributes", list, sizeof(list)); m = strbreak(list, tmp_list, MAX_N_ATTR, ","); 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, ","); 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, ","); 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, ","); 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 if hidden attribute */ getcfg(logbook, "Hidden Attributes", list, sizeof(list)); m = strbreak(list, tmp_list, MAX_N_ATTR, ","); for (i = 0; i < m; i++) { for (j = 0; j < n; j++) if (strieq(attr_list[j], tmp_list[i])) attr_flags[j] |= AF_HIDDEN; } /* check for extendable options */ getcfg(logbook, "Extendable Options", list, sizeof(list)); m = strbreak(list, tmp_list, MAX_N_ATTR, ","); 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++) { sprintf(str, "Type %s", attr_list[i]); if (getcfg(logbook, str, type, sizeof(type))) { if (strieq(type, "date")) attr_flags[i] |= AF_DATE; if (strieq(type, "datetime")) attr_flags[i] |= AF_DATETIME; if (strieq(type, "time")) attr_flags[i] |= AF_TIME; if (strieq(type, "numeric")) attr_flags[i] |= AF_NUMERIC; if (strieq(type, "userlist")) attr_flags[i] |= AF_USERLIST; if (strieq(type, "muserlist")) attr_flags[i] |= AF_MUSERLIST; if (strieq(type, "useremail")) attr_flags[i] |= AF_USEREMAIL; if (strieq(type, "museremail")) attr_flags[i] |= AF_MUSEREMAIL; } } } else { memcpy(attr_list, attr_list_default, sizeof(attr_list_default)); memcpy(attr_options, attr_options_default, sizeof(attr_options_default)); memcpy(attr_flags, attr_flags_default, sizeof(attr_flags_default)); n = 4; } return n; } /*------------------------------------------------------------------*/ void show_http_header(LOGBOOK * lbs, BOOL expires, char *cookie) { char str[256]; rsprintf("HTTP/1.1 200 Document follows\r\n"); rsprintf("Server: ELOG HTTP %s-%d\r\n", VERSION, atoi(svn_revision + 13)); 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 (getcfg(lbs->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); rsprintf("\r\n"); } if (use_keepalive) { rsprintf("Connection: Keep-Alive\r\n"); rsprintf("Keep-Alive: timeout=60, max=10\r\n"); } if (expires) { rsprintf("Pragma: no-cache\r\n"); rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n"); } rsprintf("\r\n"); } void show_plain_header(int size, char *file_name) { /* header */ rsprintf("HTTP/1.1 200 Document follows\r\n"); rsprintf("Server: ELOG HTTP %s-%d\r\n", VERSION, atoi(svn_revision + 13)); rsprintf("Accept-Ranges: bytes\r\n"); if (use_keepalive) { rsprintf("Connection: Keep-Alive\r\n"); rsprintf("Keep-Alive: timeout=60, max=10\r\n"); } rsprintf("Pragma: no-cache\r\n"); rsprintf("Expires: Fri, 01 Jan 1983 00:00:00 GMT\r\n"); rsprintf("Content-Type: text/plain\r\n"); rsprintf("Content-disposition: attachment; filename=\"%s\"\r\n", file_name); if (size) rsprintf("Content-Length: %d\r\n", size); rsprintf("\r\n"); } void show_html_header(LOGBOOK * lbs, BOOL expires, char *title, BOOL close_head, BOOL rss_feed, char *cookie, int absolute_link) { char css[256], str[256]; show_http_header(lbs, expires, cookie); /* DOCTYPE */ rsprintf("\n"); /* page title */ rsprintf("\n"); rsprintf("\n"); rsprintf("%s\n", title); /* Cascading Style Sheet */ if (absolute_link) compose_base_url(lbs, css, sizeof(css)); else css[0] = 0; if (lbs != NULL && getcfg(lbs->name, "CSS", str, sizeof(str))) strlcat(css, str, sizeof(css)); else if (lbs == NULL && getcfg("global", "CSS", str, sizeof(str))) strlcat(css, str, sizeof(css)); else strlcat(css, "default.css", sizeof(css)); rsprintf("\n", css); rsprintf("\n"); rsprintf("\n"); if (rss_feed) { rsprintf("name); rsprintf("href=\"elog.rdf\">\n"); } if (close_head) rsprintf("\n"); } void show_standard_header(LOGBOOK * lbs, BOOL expires, char *title, char *path, BOOL rss_feed, char *cookie) { show_html_header(lbs, expires, title, TRUE, rss_feed, cookie, FALSE); rsprintf("\n"); show_top_text(lbs); if (path && path[0]) rsprintf("
      \n\n", path); else rsprintf("\n\n"); } /*------------------------------------------------------------------*/ void show_upgrade_page(LOGBOOK * lbs) { char str[1000]; show_html_header(lbs, FALSE, "ELOG Upgrade Information", TRUE, FALSE, NULL, FALSE); rsprintf("\n"); rsprintf("\n\n"); rsprintf("\n"); rsprintf("
      ELog Electronic Logbook Upgrade Information

      \n"); rsprintf("You probably use an %s configuration file for a ELOG version\n", CFGFILE); rsprintf("1.1.x, since it contains a \"Types = ...\" entry. From version\n"); rsprintf("1.2.0 on, the fixed attributes Type and Category have been\n"); rsprintf("replaced by arbitrary attributes. Please replace these two lines with the\n"); rsprintf("following entries:

      \n"); rsprintf("

      \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("
      \n"); rsprintf("

      \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 http://midas.psi.ch/elog/config.html\n"); rsprintf(".\n"); rsprintf("

      \n\n"); rsprintf("
      \n"); rsprintf("
      \n"); rsprintf("S. Ritt, 18 October 2001"); rsprintf("
      "); show_bottom_text(lbs); rsprintf("\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 = 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 = xmalloc(sizeof(void *)); else root->member = xrealloc(root->member, (n + 1) * sizeof(void *)); root->member[n] = 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, ","); root->member[n]->n_members = m; root->member[n]->member = xcalloc(sizeof(void *), m); root->member[n]->n_members = m; for (j = 0; j < m; j++) { root->member[n]->member[j] = 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 = xcalloc(n, sizeof(void *)); root->n_members = n; for (i = 0; i < n; i++) { root->member[i] = 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, 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, ","); 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, ","); 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(char *logbook, char *text, int printable) { char str[256], ref[256], sclass[32], comment[256], url[256]; int i, j, level; LBLIST phier, pnode, pnext, flb; if (printable) rsprintf("\n\n"); else rsprintf("
      \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("\n\n"); pnode = pnext; pnext = NULL; if (pnode == NULL || pnode->n_members == 0) break; } } free_logbook_hierarchy(phier); /*---- title row ----*/ rsprintf("\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 = 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 (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 = 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 ("
      ELOG V%s-%d
      ", loc("Goto ELOG home page"), VERSION, atoi(svn_revision + 13)); } /*------------------------------------------------------------------*/ 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 = 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 ("
      ELOG V%s-%d
      ", loc("Goto ELOG home page"), VERSION, atoi(svn_revision + 13)); } /*------------------------------------------------------------------*/ void show_error(char *error) { /* header */ show_html_header(NULL, FALSE, "ELOG error", TRUE, FALSE, NULL, FALSE); rsprintf("
      \n"); rsprintf("
      \n"); if (level == 0 && getcfg("global", "main tab", str, sizeof(str)) && !getcfg_topgroup()) { if (getcfg("global", "main tab url", url, sizeof(url))) rsprintf("%s\n", url, str); else rsprintf("%s\n", str); } if (level == 1 && getcfg("global", "main tab", str, sizeof(str)) && getcfg_topgroup()) { if (getcfg("global", "main tab url", url, sizeof(url))) rsprintf("%s\n", url, str); else rsprintf("%s\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("", sclass); if (comment[0]) { rsprintf(""); } else rsprintf("", 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("\n"); } } rsprintf("
      \n"); /* left cell */ rsprintf("\n"); /* middle cell */ if (isparam("full_name")) rsprintf("\n", loc("Logged in as"), getparam("full_name")); else if (getcfg(logbook, "Guest menu commands", str, sizeof(str))) rsprintf("\n", loc("Not logged in")); /* right cell */ rsprintf("\n"); rsprintf("
      "); /* 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(" %s \"%s\"%s"); if (getcfg(logbook, "Title image URL", str, sizeof(str))) rsprintf("\n", str); if (getcfg(logbook, "Title image", str, sizeof(str))) rsprintf("%s", str); else rsprintf("\"ELOG"); if (getcfg(logbook, "Title image URL", str, sizeof(str))) rsprintf("\n"); rsprintf("
      \n", error); rsprintf("\n
      %s
      "); /* rsprintf("\n"); rsprintf("\n"); */ rsprintf("
      \n"); rsprintf("\n"); } /*------------------------------------------------------------------*/ void set_login_cookies(LOGBOOK * lbs, char *user, char *enc_pwd) { char str[256], lb_name[256], exp[80]; BOOL global; int i; rsprintf("HTTP/1.1 302 Found\r\n"); rsprintf("Server: ELOG HTTP %s-%d\r\n", VERSION, atoi(svn_revision + 13)); if (use_keepalive) { 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(lbs->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)); /* two cookies for password and user name */ set_cookie(lbs, "unm", user, global, exp); set_cookie(lbs, "upwd", enc_pwd, global, exp); if (global &&user[0] == 0 && enc_pwd[0] == 0) { /* if logging out global, also delete possible non-global cookies */ for (i = 0; lb_list[i].name[0]; i++) { set_cookie(&lb_list[i], "unm", user, 0, exp); set_cookie(&lb_list[i], "upwd", enc_pwd, 0, exp); } } if (user[0]) { /* 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") : ""); } /*------------------------------------------------------------------*/ void remove_all_login_cookies(LOGBOOK * lbs) { int i; rsprintf("HTTP/1.1 302 Found\r\n"); rsprintf("Server: ELOG HTTP %s-%d\r\n", VERSION, atoi(svn_revision + 13)); if (use_keepalive) { rsprintf("Connection: Keep-Alive\r\n"); rsprintf("Keep-Alive: timeout=60, max=10\r\n"); } /* remove global cookies */ set_cookie(NULL, "unm", "", TRUE, ""); set_cookie(NULL, "upwd", "", TRUE, ""); for (i = 0; lb_list[i].name[0]; i++) { set_cookie(&lb_list[i], "unm", "", 0, ""); set_cookie(&lb_list[i], "upwd", "", 0, ""); } 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], charset[80], format[80]; time_t now; struct tm *gmt; 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-%d\r\n", VERSION, atoi(svn_revision + 13)); rsprintf("Accept-Ranges: bytes\r\n"); /* set expiration time to one day */ time(&now); now += (int) (3600 * 24); gmt = gmtime(&now); strcpy(format, "%A, %d-%b-%y %H:%M:%S GMT"); strftime(str, sizeof(str), format, gmt); rsprintf("Expires: %s\r\n", str); if (use_keepalive) { 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 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 */ if (length > return_buffer_size - (int) strlen(return_buffer)) { delta = length - (return_buffer_size - strlen(return_buffer)) + 1000; return_buffer = 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]; show_html_header(NULL, FALSE, "404 Not Found", TRUE, FALSE, NULL, FALSE); rsprintf("

      Not Found

      \r\n"); rsprintf("The requested file "); strencode2(encodedname, file_name, sizeof(encodedname)); rsprintf("%s", encodedname); rsprintf(" was not found on this server

      \r\n"); rsprintf("


      ELOG version %s
      \r\n\r\n", VERSION); return_length = strlen_retbuf; keep_alive = 0; } } /*------------------------------------------------------------------*/ void strencode(char *text) { int i; for (i = 0; i < (int) strlen(text); i++) { switch (text[i]) { case '\n': rsprintf("
      \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 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, 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, "
      \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) + 5 >= (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], format[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 | AF_DATETIME)) && format_date) { t = (time_t) atoi(attrib[i]); ts = localtime(&t); assert(ts); if (!getcfg(lbs->name, "Date format", format, sizeof(format))) strcpy(format, DEFAULT_DATE_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 user names */ strcpy(list[i], "short_name"); if (isparam("unm")) strlcpy(value[i++], getparam("unm"), NAME_LENGTH); else strlcpy(value[i++], loc("Anonymous"), NAME_LENGTH); strcpy(list[i], "long_name"); if (isparam("full_name")) strlcpy(value[i++], getparam("full_name"), NAME_LENGTH); else strlcpy(value[i++], loc("Anonymous"), NAME_LENGTH); /* add email */ if (isparam("user_email")) { strcpy(list[i], "user_email"); strcpy(value[i], "mailto:"); strlcat(value[i++], getparam("user_email"), NAME_LENGTH); } /* add logbook */ 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++], "%d", atoi(svn_revision + 13)); return i; } /*------------------------------------------------------------------*/ void add_subst_list(char list[][NAME_LENGTH], char value[][NAME_LENGTH], char *item, char *str, int *i) { strcpy(list[*i], item); strcpy(value[(*i)++], str); } void add_subst_time(LOGBOOK * lbs, char list[][NAME_LENGTH], char value[][NAME_LENGTH], char *item, char *date, int *i) { char format[80], str[256]; time_t ltime; struct tm *pts; 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; } /*------------------------------------------------------------------*/ BOOL change_pwd(LOGBOOK * lbs, char *user, char *pwd) { char str[256], file_name[256]; PMXML_NODE node; if (!lbs->pwd_xml_tree) return FALSE; sprintf(str, "/list/user[name=%s]/password", user); node = mxml_find_node(lbs->pwd_xml_tree, str); if (node == NULL) return FALSE; mxml_replace_node_value(node, pwd); if (get_password_file(lbs, file_name, sizeof(file_name))) mxml_write_tree(file_name, lbs->pwd_xml_tree); return TRUE; } /*------------------------------------------------------------------*/ void show_change_pwd_page(LOGBOOK * lbs) { char str[256], config[80], old_pwd[32], new_pwd[32], new_pwd2[32], act_pwd[32], user[80]; int wrong_pwd; old_pwd[0] = new_pwd[0] = new_pwd2[0] = 0; if (isparam("oldpwd")) do_crypt(getparam("oldpwd"), old_pwd, sizeof(old_pwd)); if (isparam("newpwd")) do_crypt(getparam("newpwd"), new_pwd, sizeof(new_pwd)); if (isparam("newpwd2")) do_crypt(getparam("newpwd2"), new_pwd2, sizeof(new_pwd2)); strlcpy(user, isparam("unm") ? getparam("unm") : "", sizeof(user)); if (isparam("config")) strlcpy(user, getparam("config"), sizeof(user)); wrong_pwd = FALSE; if (old_pwd[0] || new_pwd[0]) { if (user[0] && get_user_line(lbs, user, act_pwd, NULL, NULL, NULL, NULL)) { /* administrator does not have to supply old password if changing other user's password */ if (isparam("unm") && is_admin_user(lbs->name, getparam("unm")) && stricmp(getparam("unm"), user) != 0) wrong_pwd = 0; else { if (strcmp(old_pwd, act_pwd) != 0) wrong_pwd = 1; } if (strcmp(new_pwd, new_pwd2) != 0) wrong_pwd = 2; } if (new_pwd[0]) { /* replace password */ if (!wrong_pwd) change_pwd(lbs, user, new_pwd); if (!wrong_pwd && isparam("unm") && strcmp(user, getparam("unm")) == 0) { set_login_cookies(lbs, user, new_pwd); 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; } } } show_standard_header(lbs, TRUE, loc("ELOG change password"), NULL, FALSE, NULL); rsprintf(""); if (wrong_pwd == 1) rsprintf("\n", loc("Wrong password")); if (wrong_pwd == 2) rsprintf("\n", loc("New passwords do not match, please retype")); rsprintf("\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->name, getparam("unm")) || stricmp(getparam("unm"), user) == 0) { if (isparam("oldpwd") && !(wrong_pwd == 1)) rsprintf("\n"); } } } rsprintf("\n", loc("New password")); rsprintf("\n"); rsprintf("\n", loc("Retype new password")); rsprintf("\n"); rsprintf ("", loc("Submit")); rsprintf("
      %s!
      %s!
      \n"); rsprintf("", user); rsprintf("%s \"%s\"
      %s:\n", loc("Old password")); rsprintf("\n"); rsprintf("
      %s:
      %s:
      \n"); show_bottom_text(lbs); rsprintf("
      \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 str[NAME_LENGTH], attrib[MAX_N_ATTR][NAME_LENGTH], att[MAX_ATTACHMENTS][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)); if (strchr(retstr, '#') == NULL) 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 */ str[0] = 0; message_id = el_search_message(lbs, EL_LAST, 0, FALSE); if (!message_id) { /* start with 1 */ sprintf(retstr + loc, "%0*d", len, 1); return; } el_retrieve(lbs, message_id, NULL, attr_list, attrib, lbs->n_attr, NULL, 0, NULL, NULL, att, NULL, NULL); /* if date part changed, start over with inded */ if (strncmp(attrib[index], retstr, loc) != 0) old_index = 0; else /* retrieve old index */ old_index = atoi(attrib[index] + loc); /* 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], preset[NAME_LENGTH]; int i; /* check if current user is admin */ if (is_admin_user(lbs->name, getparam("unm"))) return TRUE; /* search attribute which contains short_name of author */ for (i = 0; i < lbs->n_attr; i++) { sprintf(str, "Preset %s", attr_list[i]); 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 */ for (i = 0; i < lbs->n_attr; i++) { sprintf(str, "Preset %s", attr_list[i]); if (getcfg(lbs->name, str, preset, sizeof(preset))) { if (strstr(preset, "$long_name")) { if (!isparam("full_name") || strstr(attrib[i], getparam("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 str[NAME_LENGTH], preset[NAME_LENGTH]; int i; /* search attribute which contains full_name of author */ for (i = 0; i < lbs->n_attr; i++) { sprintf(str, "Preset %s", attr_list[i]); 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++) { sprintf(str, "Preset %s", attr_list[i]); 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, char *index) { int i; rsprintf("\n"); rsprintf("\n"); if (year) rsprintf (" %s: ", loc("Year"), index, year); else rsprintf(" %s: ", loc("Year"), index); rsprintf("\n  \n"); } /*------------------------------------------------------------------*/ void show_time_selector(int hour, int min, int sec, char *index) { int i; rsprintf(" : \n"); rsprintf(" : \n"); rsprintf("\n"); rsprintf("\n\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], ua[NAME_LENGTH]; time_t ltime; struct tm ts; for (i = 0; i < n_attr; i++) { strcpy(ua, attr_list[i]); btou(ua); if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST)) { 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) { 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) { 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, "s%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 { strlcpy(attrib[i], isparam(ua) ? getparam(ua) : "", NAME_LENGTH); } } } /*------------------------------------------------------------------*/ void ricon(char *name, char *comment, char *onclick) { rsprintf("\"%s\""); } /*------------------------------------------------------------------*/ void rsicon(char *name, char *comment, char *elcode) { rsprintf("\"%s\""); } /*------------------------------------------------------------------*/ 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_smileys, show_text, n_moptions, display_inline, allowed_encoding; char str[2 * NAME_LENGTH], preset[2 * NAME_LENGTH], *p, *pend, star[80], comment[10000], reply_string[256], list[MAX_N_ATTR][NAME_LENGTH], file_name[256], *buffer, format[256], date[80], script[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[256], login_name[256], full_name[256], cookie[256], orig_author[256], attr_moptions[MAX_N_LIST][NAME_LENGTH], ref[256], file_enc[256], tooltip[256]; 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 = xmalloc(TEXT_SIZE); text[0] = 0; orig_author[0] = 0; encoding[0] = 0; date[0] = 0; /* 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)); } 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); get_author(lbs, attrib, orig_author); /* strip attachments on duplicate */ if (bduplicate) memset(att, 0, sizeof(att)); } } /* Determine encoding */ enc_selected = 0; /* Default is ELCode */ /* Overwrite from config file */ if (getcfg(lbs->name, "Default Encoding", str, sizeof(str))) enc_selected = atoi(str); /* detrmine if smiley bar should be displayed */ show_smileys = 0; cookie[0] = 0; if (isparam("hsm") && atoi(getparam("hsm")) == 1) /* cookie */ show_smileys = 0; if (isparam("smcmd") && strieq(getparam("smcmd"), "hsm")) { /* turn off */ show_smileys = 0; strcpy(cookie, "hsm=1"); } if (isparam("smcmd") && strieq(getparam("smcmd"), "ssm")) { /* turn on */ show_smileys = 1; strcpy(cookie, "hsm=0"); } /* 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 */ /* 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); /* check for index substitution */ if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) { /* get index */ get_auto_index(lbs, index, preset, str, sizeof(str)); strcpy(preset, str); } if (!strchr(preset, '%')) 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 */ /* 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); /* check for index substitution */ if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) { /* get index */ get_auto_index(lbs, index, preset, str, sizeof(str)); strcpy(preset, str); } if (!strchr(preset, '%')) 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 */ /* 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); /* check for index substitution */ if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) { /* get index */ get_auto_index(lbs, index, preset, str, sizeof(str)); strcpy(preset, str); } if (!strchr(preset, '%')) strcpy(attrib[index], preset); } } /* check for p */ 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) 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 */ /* 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); /* check for index substitution */ if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) { /* get index */ get_auto_index(lbs, index, preset, str, sizeof(str)); strcpy(preset, str); } if (!strchr(preset, '%')) 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 */ /* 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); /* check for index substitution */ if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) { /* get index */ get_auto_index(lbs, index, preset, str, sizeof(str)); strcpy(preset, str); } if (!strchr(preset, '%')) 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 */ /* 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); /* check for index substitution */ if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) { /* get index */ get_auto_index(lbs, index, preset, str, sizeof(str)); strcpy(preset, str); } if (!strchr(preset, '%')) 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 author */ if (bedit && getcfg(lbs->name, "Restrict edit", str, sizeof(str)) && atoi(str) == 1) { if (!is_author(lbs, attrib, owner)) { sprintf(str, loc("Only user %s can edit this entry"), owner); show_error(str); xfree(text); return; } } /* check for editing interval */ if (bedit && 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); 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("full_name")) strlcpy(str, getparam("full_name"), sizeof(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); } } /* remove attributes for replies */ if (breply) { getcfg(lbs->name, "Remove on reply", str, sizeof(str)); n = strbreak(str, list, MAX_N_ATTR, ","); for (i = 0; i < n; i++) for (j = 0; j < n_attr; j++) { if (strieq(attr_list[j], list[i])) attrib[j][0] = 0; } } /* subst attributes for replies */ if (breply) { for (index = 0; index < n_attr; index++) { sprintf(str, "Subst on reply %s", attr_list[index]); if (getcfg(lbs->name, str, preset, sizeof(preset))) { /* check if already second reply */ if (orig_tag[0] == 0) { /* do not format date for date attributes */ i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME)) == 0); sprintf(str, "%d", message_id); add_subst_list(slist, svalue, "message id", str, &i); add_subst_time(lbs, slist, svalue, "entry time", date, &i); strsubst_list(preset, sizeof(preset), slist, svalue, i); strcpy(attrib[index], preset); } } } } /* subst attributes for edits */ if (message_id && bedit && !breedit && !bupload) { for (index = 0; index < n_attr; index++) { sprintf(str, "Subst on edit %s", attr_list[index]); if (getcfg(lbs->name, str, preset, sizeof(preset))) { /* do not format date for date attributes */ i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME)) == 0); sprintf(str, "%d", message_id); add_subst_list(slist, svalue, "message id", str, &i); add_subst_time(lbs, slist, svalue, "entry time", date, &i); strsubst_list(preset, sizeof(preset), slist, svalue, i); if (strlen(preset) > NAME_LENGTH - 100) { if (strstr(preset + 100, "
      ")) { strlcpy(str, strstr(preset + 100, "
      "), sizeof(str)); } else strlcpy(str, preset + 100, sizeof(str)); strcpy(preset, "..."); strlcat(preset, str, sizeof(preset)); } if (strncmp(preset, "
      ", 4) == 0) strcpy(attrib[index], preset + 4); else strcpy(attrib[index], preset); } } } /* 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(str, sizeof(str), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, i); strip_html(str); } else sprintf(str, "ELOG %s", lbs->name); show_html_header(lbs, FALSE, str, FALSE, FALSE, cookie, FALSE); /* java script for checking required attributes and to check for cancelled edits */ rsprintf("\n"); /* optionally load ELCode JavaScript code */ if (enc_selected == 0) rsprintf("\n\n"); /* external script if requested */ if (isparam("js")) { rsprintf("\n\n"); } rsprintf("\n"); script[0] = 0; if (isparam("inlineatt") && *getparam("inlineatt")) strcpy(script, " OnLoad=\"document.form1.Text.focus();\""); if (getcfg(lbs->name, "Use Lock", str, sizeof(str)) && atoi(str) == 1) rsprintf("\n", script); else rsprintf("\n", script); show_top_text(lbs); rsprintf("
      \n"); /*---- add password in case cookie expires during edit ----*/ if (getcfg(lbs->name, "Write password", str, sizeof(str))) rsprintf("\n", str); if (getcfg(lbs->name, "Password file", str, sizeof(str)) && isparam("unm") && isparam("uwpd")) { rsprintf("\n", getparam("unm")); rsprintf("\n", getparam("upwd")); } rsprintf("\n"); rsprintf("\n"); rsprintf("\n"); /*---- title row ----*/ show_standard_title(lbs->name, "", 0); /*---- menu buttons ----*/ rsprintf("
      \n"); /* default cmd */ rsprintf("\n", loc("Update")); rsprintf ("\n", loc("Submit")); rsprintf ("\n", loc("Preview")); rsprintf ("\n", loc("Back")); rsprintf("
      "); /* 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 ("\n", loc("Fields marked with"), loc("are required")); break; } } time(&now); if (bedit) { 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))) my_strftime(str, sizeof(str), format, localtime(&now)); else strcpy(str, ctime(&now)); strcpy(date, ctime(&now)); date[24] = 0; } rsprintf("", loc("Entry time")); rsprintf("\n", date); if (_condition[0]) rsprintf("\n", _condition); /* retrieve attribute flags */ for (i = 0; i < n_attr; i++) { format_flags[i] = 0; sprintf(str, "Format %s", attr_list[i]); if (getcfg(lbs->name, str, format, sizeof(format))) { n = strbreak(format, fl, 8, ","); if (n > 0) format_flags[i] = atoi(fl[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, ","); 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; } /* display attributes */ for (aindex = 0; aindex < n_disp_attr; aindex++) { index = attr_index[aindex]; /* if attribute is hidden, skip it */ if (attr_flags[index] & AF_HIDDEN) continue; strcpy(class_name, "attribname"); strcpy(class_value, "attribvalue"); input_size = 80; input_maxlen = NAME_LENGTH; strcpy(ua, attr_list[index]); btou(ua); dtou(ua); sprintf(str, "Format %s", attr_list[index]); if (getcfg(lbs->name, str, format, sizeof(format))) { n = strbreak(format, fl, 8, ","); 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("\n"); } else { rsprintf("\n", title); /* display drop-down box */ rsprintf("\n"); if (is_cond_attr(index)) { /* show "update" button only of javascript is not enabled */ rsprintf("\n"); } if (attr_flags[index] & AF_EXTENDABLE) { sprintf(str, loc("Add %s"), attr_list[index]); rsprintf ("\n", index, str); } rsprintf("\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("
      %s * %s
      %s:%s\n", str); rsprintf("
      "); subtable = 1; } else /* for normal attribute, start new row */ rsprintf(""); strcpy(star, (attr_flags[index] & AF_REQUIRED) ? "*" : ""); /* 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("", title); /* 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("
      %s\n", comment); rsprintf("\n"); /* if attribute cannot be changed, just display text */ if ((attr_flags[index] & AF_LOCKED) || ((bedit && !breedit && !bupload) && (attr_flags[index] & AF_FIXED_EDIT)) || (message_id && !bedit && (attr_flags[index] & AF_FIXED_REPLY))) { if (attr_flags[index] & AF_DATE) { 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 strlcpy(str, attrib[index], sizeof(str)); rsprintf("\n", title); 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("\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); sprintf(str, "%s_%d", ua, i); if (strstr(attrib[index], full_name)) rsprintf("\n", str, full_name); } } 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("\n", str, attr_options[index][i]); } } else { strencode2(str, attrib[index], sizeof(str)); rsprintf("\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]); pts = localtime(<ime); assert(pts); year = pts->tm_year + 1900; month = pts->tm_mon + 1; day = pts->tm_mday; } rsprintf("", title); sprintf(str, "%d", index); show_date_selector(day, month, year, str); rsprintf("\n"); } else if (attr_flags[index] & AF_DATETIME) { year = month = day = 0; hour = min = sec = -1; if (attrib[index][0]) { ltime = atoi(attrib[index]); 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("", title); sprintf(str, "%d", index); show_date_selector(day, month, year, str); rsprintf("  "); show_time_selector(hour, min, sec, str); rsprintf("\n"); } else if (attr_flags[index] & AF_USERLIST) { rsprintf("\n", title); /* display drop-down box with list of users */ rsprintf("\n"); rsprintf("\n"); } else if (attr_flags[index] & AF_MUSERLIST) { /* display multiple check boxes with user names */ rsprintf("\n", title); n_moptions = strbreak(attrib[index], attr_moptions, MAX_N_LIST, "|"); 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); sprintf(str, "%s_%d", ua, i); rsprintf("\n"); for (j = 0; j < n_moptions; j++) if (strcmp(attr_moptions[j], full_name) == 0) break; if (j < n_moptions) rsprintf ("\n", str, str, full_name); else rsprintf ("\n", str, str, full_name); rsprintf("\n", str, full_name); rsprintf("\n"); if (format_flags[index] & AFF_MULTI_LINE) rsprintf("
      "); } rsprintf("\n"); } else if (attr_flags[index] & AF_USEREMAIL) { rsprintf("\n", title); /* display drop-down box with list of users */ rsprintf("\n"); rsprintf("\n"); } else { /* show normal edit field */ rsprintf("", title); strencode2(str, attrib[index], sizeof(str)); rsprintf ("\n", input_size, input_maxlen, ua, str); rsprintf("\n"); } } else { if (strieq(attr_options[index][0], "boolean")) { /* display checkbox */ if (atoi(attrib[index]) == 1) rsprintf ("\n", title, ua); else rsprintf ("\n", title, ua); } else { sprintf(str, "extend_%d", index); if (isparam(str)) { rsprintf("\n", title); rsprintf(""); rsprintf(loc("Add new option here"), attr_list[index]); rsprintf(" : \n"); if (attr_flags[index] & (AF_MULTI | AF_MUSERLIST)) rsprintf ("\n", input_maxlen, ua, attrib[index]); else rsprintf ("\n", input_maxlen, ua, attrib[index]); rsprintf("\n", index); rsprintf("\n"); } else if (attr_flags[index] & AF_MULTI) { /* display multiple check boxes */ rsprintf("\n", title); n_moptions = strbreak(attrib[index], attr_moptions, MAX_N_LIST, "|"); 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, "%s_%d", ua, i); rsprintf("\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 ("\n", str, str, attr_options[index][i]); else rsprintf ("\n", str, str, attr_options[index][i]); rsprintf("\n", str, attr_options[index][i]); rsprintf("\n"); if (format_flags[index] & AFF_MULTI_LINE) rsprintf("
      "); } if (attr_flags[index] & AF_EXTENDABLE) { sprintf(str, loc("Add %s"), attr_list[index]); rsprintf ("\n", index, str); } rsprintf("\n"); } else if (attr_flags[index] & AF_RADIO) { /* display radio buttons */ rsprintf("\n", title); for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) { strlcpy(str, attr_options[index][i], sizeof(str)); if (strchr(str, '{')) *strchr(str, '{') = 0; rsprintf("\n"); if (strstr(attrib[index], attr_options[index][i]) || strieq(str, attrib[index])) rsprintf ("\n", str, ua, str); else rsprintf ("\n", str, ua, str); rsprintf("\n", str, str); rsprintf("\n"); if (format_flags[index] & AFF_MULTI_LINE) rsprintf("
      "); } if (attr_flags[index] & AF_EXTENDABLE) { sprintf(str, loc("Add %s"), attr_list[index]); rsprintf ("\n", index, str); } rsprintf("\n"); } else if (attr_flags[index] & AF_ICON) { /* display icons */ rsprintf("\n", title); rsprintf("
      \n"); for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) { if (strstr(attrib[index], attr_options[index][i])) rsprintf ("\n"); if ((format_flags[index] & AFF_MULTI_LINE) && attr_options[index][i + 1][0]) { rsprintf("\n"); } } rsprintf("
      ", ua, attr_options[index][i]); else rsprintf ("", 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("\"%s\"\n", attr_options[index][i], comment, comment); else rsprintf("\"%s\"\n", attr_options[index][i], attr_options[index][i], attr_options[index][i]); rsprintf("
      \n"); subtable = 0; } else rsprintf(""); } } if (bpreview) { _current_message_id = message_id; rsprintf("\n"); if (strieq(encoding, "plain")) { rsputs("
      ");
               rsputs2(lbs, FALSE, text);
               rsputs("
      "); } else if (strieq(encoding, "ELCode")) rsputs_elcode(lbs, FALSE, text); else rsputs(text); rsprintf("\n"); } if (enc_selected == 0 && show_text) { rsprintf("\n"); ricon("bold", loc("bold text"), "elcode(document.form1.Text, 'B','')"); ricon("italic", loc("italics text"), "elcode(document.form1.Text, 'I','')"); ricon("underline", loc("underlined text"), "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"), str); rsprintf(" "); ricon("quote", loc("insert quote"), "elcode(document.form1.Text, 'QUOTE','')"); ricon("list", loc("insert list"), "elcode(document.form1.Text, 'LIST','')"); ricon("heading", loc("insert heading"), "queryHeading(document.form1.Text)"); rsprintf(" "); ricon("code", loc("insert code"), "elcode(document.form1.Text, 'CODE','')"); if (show_smileys) rsprintf (" \"%s\"\n"); rsprintf(" \n"); rsprintf(" \n"); rsprintf(" "); rsprintf("\n"); } /* main box for text box and icons */ rsprintf("\n"); if (enc_selected == 0) rsprintf("\n"); if (enc_selected == 0 && show_smileys) { rsprintf("\n"); } if (enc_selected == 0) rsprintf("
      \n"); rsicon("smile", loc("smiling"), ":)"); rsprintf("
      \n"); rsicon("happy", loc("happy"), ":))"); rsprintf("
      \n"); rsicon("wink", loc("winking"), ";)"); rsprintf("
      \n"); rsicon("biggrin", loc("big grin"), ":D"); rsprintf("
      \n"); rsicon("crying", loc("crying"), ";("); rsprintf("
      \n"); rsicon("cool", loc("cool"), "8-)"); rsprintf("
      \n"); rsicon("frown", loc("frowning"), ":("); rsprintf("
      \n"); rsicon("confused", loc("confused"), "?-)"); rsprintf("
      \n"); rsicon("astonished", loc("astonished"), "8o"); rsprintf("
      \n"); rsicon("mad", loc("mad"), "X-("); rsprintf("
      \n"); rsicon("pleased", loc("pleased"), ":]"); rsprintf("
      \n"); rsicon("tongue", loc("tongue"), ":P"); rsprintf("
      \n"); rsicon("yawn", loc("yawn"), ":O"); rsprintf("
      \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("\n", message_id); if (breedit || bupload) /* hidden text for original message */ if (isparam("reply_to")) rsprintf("\n", getparam("reply_to")); if (bedit && message_id) rsprintf("\n", message_id); if (getcfg(lbs->name, "Message comment", comment, sizeof(comment)) && !message_id) { rsputs(comment); rsputs("
      \n"); } if (getcfg(lbs->name, "Reply comment", comment, sizeof(comment)) && breply) { rsputs(comment); rsputs("
      \n"); } 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 (getcfg(lbs->name, "Fix text", str, sizeof(str)) && atoi(str) == 1) strcpy(str, " readonly"); else strcpy(str, ""); if (enc_selected == 1) /* use hard wrapping only for plain text */ rsprintf("
      \n"); /* Encoding radio buttons */ if (getcfg(lbs->name, "Allowed encoding", str, sizeof(str))) allowed_encoding = atoi(str); else allowed_encoding = 3; if (allowed_encoding < 1 || allowed_encoding > 7) { rsprintf ("

      Invalid \"Allowed encoding\" in configuration file, value must be between 1 and 7

      \n"); rsprintf("
      \n"); show_bottom_text(lbs); rsprintf("\r\n"); return; } if (allowed_encoding == 1) rsprintf("\n"); else if (allowed_encoding == 2) rsprintf("\n"); else if (allowed_encoding == 4) rsprintf("\n"); else { rsprintf("%s: ", loc("Encoding")); if (allowed_encoding & 2) { if (enc_selected == 0) rsprintf(""); else rsprintf (""); rsprintf ("\n"); } if (allowed_encoding & 1) { if (enc_selected == 1) rsprintf(""); else rsprintf (""); rsprintf("\n"); } if (allowed_encoding & 4) { if (enc_selected == 2) rsprintf(""); else rsprintf (""); rsprintf("\n"); } } rsprintf("
      \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(""); rsprintf("\n", loc("Suppress Email notification")); } else if (atoi(str) == 1) { rsprintf(""); rsprintf("\n", loc("Suppress Email notification")); } else if (atoi(str) == 2) { rsprintf(""); } else if (atoi(str) == 3) { rsprintf(""); } /* 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(""); rsprintf("\n", loc("Suppress shell execution")); } else if (atoi(str) == 1) { rsprintf("        \n"); rsprintf (""); rsprintf("\n", loc("Suppress shell execution")); } } else { rsprintf("        \n"); rsprintf(""); rsprintf("\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(""); rsprintf("\n", loc("Suppress shell execution")); } else if (atoi(str) == 1) { rsprintf("        \n"); rsprintf (""); rsprintf("\n", loc("Suppress shell execution")); } } else { rsprintf("        \n"); rsprintf(""); rsprintf("\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(""); rsprintf("