"Fossies" - the Fresh Open Source Software Archive

Member "elog-3.1.4-3/src/elogd.c" (19 Feb 2021, 1056262 Bytes) of package /linux/www/elog-3.1.4-3.tar.gz:


The requested HTML page contains a <FORM> tag that is unusable on "Fossies" in "automatic" (rendered) mode so that page is shown as HTML source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "elogd.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.1.4-2_vs_3.1.4-3.

    1 /********************************************************************
    2 
    3    Name:         elogd.c
    4    Created by:   Stefan Ritt
    5    Copyright 2000 + Stefan Ritt
    6 
    7    ELOG is free software: you can redistribute it and/or modify
    8    it under the terms of the GNU General Public License as published by
    9    the Free Software Foundation, either version 3 of the License, or
   10    (at your option) any later version.
   11 
   12    ELOG is distributed in the hope that it will be useful,
   13    but WITHOUT ANY WARRANTY; without even the implied warranty of
   14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15    GNU General Public License for more details.
   16 
   17    In addition, as a special exception, the copyright holders give
   18    permission to link the code of portions of this program with the
   19    OpenSSL library under certain conditions as described in each
   20    individual source file, and distribute linked combinations
   21    including the two.
   22    You must obey the GNU General Public License in all respects
   23    for all of the code used other than OpenSSL.  If you modify
   24    file(s) with this exception, you may extend this exception to your
   25    version of the file(s), but you are not obligated to do so.  If you
   26    do not wish to do so, delete this exception statement from your
   27    version.  If you delete this exception statement from all source
   28    files in the program, then also delete it here.
   29 
   30    You should have received a copy of the GNU General Public License
   31    along with ELOG.  If not, see <http://www.gnu.org/licenses/>.
   32 
   33 
   34    Contents:     Web server program for Electronic Logbook ELOG
   35 
   36 \********************************************************************/
   37 
   38 #include "elogd.h"
   39 #include "git-revision.h"
   40 
   41 const char *_git_revision = GIT_REVISION;
   42 
   43 BOOL running_as_daemon;         /* Running as a daemon/service? */
   44 int elog_tcp_port;              /* Server's TCP port            */
   45 
   46 static void (*printf_handler)(const char *);   /* Handler to printf for logging */
   47 static void (*fputs_handler)(const char *);    /* Handler to fputs for logging  */
   48 static FILE *current_output_stream = NULL;      /* Currently used output stream  */
   49 
   50 char *return_buffer;
   51 int return_buffer_size;
   52 int strlen_retbuf;
   53 int keep_alive;
   54 char header_buffer[20000];
   55 int return_length;
   56 char host_name[256];
   57 char referer[256];
   58 char browser[256];
   59 char config_file[256];
   60 char resource_dir[256];
   61 char logbook_dir[256];
   62 char listen_interface[256];
   63 char theme_name[80];
   64 char http_host[256];
   65 char http_user[256];
   66 
   67 char _param[MAX_PARAM][NAME_LENGTH];
   68 char _value[MAX_PARAM][NAME_LENGTH];
   69 char _mtext[TEXT_SIZE];
   70 char _cmdline[CMD_SIZE];
   71 char *_attachment_buffer;
   72 int _attachment_size;
   73 int _max_content_length = MAX_CONTENT_LENGTH;
   74 struct in_addr rem_addr;
   75 char rem_host[256];
   76 char rem_host_ip[256];
   77 int _sock;
   78 BOOL use_keepalive, enable_execute = FALSE;
   79 BOOL ckedit_exist, image_magick_exist;
   80 int _verbose_level, _current_message_id;
   81 int _logging_level, _ssl_flag;
   82 
   83 LOGBOOK *lb_list = NULL;
   84 
   85 #define VERBOSE_URL     1
   86 #define VERBOSE_INFO    2
   87 #define VERBOSE_DEBUG   3
   88 
   89 #ifdef HAVE_SSL
   90 SSL *_ssl_con;
   91 #endif
   92 
   93 char *mname[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September",
   94                  "October", "November", "December"
   95 };
   96 
   97 char attr_list[MAX_N_ATTR][NAME_LENGTH];
   98 char attr_options[MAX_N_ATTR][MAX_N_LIST][NAME_LENGTH];
   99 int attr_flags[MAX_N_ATTR];
  100 
  101 char attr_list_default[][NAME_LENGTH] = {"Author", "Type", "Category", "Subject", ""};
  102 
  103 char attr_options_default[][MAX_N_LIST][NAME_LENGTH] = {{""},
  104                                                         {"Routine", "Other"},
  105                                                         {"General", "Other"},
  106                                                         {""}
  107 };
  108 
  109 int attr_flags_default[] = {AF_REQUIRED, 0, 0, 0};
  110 
  111 struct {
  112    char ext[32];
  113    char type[80];
  114 } filetype[] = {
  115 
  116         {
  117                 ".AI",   "application/postscript"},
  118         {
  119                 ".ASC",  "text/plain"},
  120         {
  121                 ".BZ2",  "application/x-bzip2"},
  122         {
  123                 ".CFG",  "text/plain"},
  124         {
  125                 ".CHRT", "application/x-kchart"},
  126         {
  127                 ".CONF", "text/plain"},
  128         {
  129                 ".CSH",  "application/x-csh"},
  130         {
  131                 ".CSS",  "text/css"},
  132         {
  133                 ".DOC",  "application/msword"},
  134         {
  135                 ".DVI",  "application/x-dvi"},
  136         {
  137                 ".EPS",  "application/postscript"},
  138         {
  139                 ".GIF",  "image/gif"},
  140         {
  141                 ".GZ",   "application/x-gzip"},
  142         {
  143                 ".HTM",  "text/html"},
  144         {
  145                 ".HTML", "text/html"},
  146         {
  147                 ".ICO",  "image/x-icon"},
  148         {
  149                 ".JPEG", "image/jpeg"},
  150         {
  151                 ".JPG",  "image/jpeg"},
  152         {
  153                 ".JS",   "application/x-javascript"},
  154         {
  155                 ".KPR",  "application/x-kpresenter"},
  156         {
  157                 ".KSP",  "application/x-kspread"},
  158         {
  159                 ".KWD",  "application/x-kword"},
  160         {
  161                 ".MP3",  "audio/mpeg"},
  162         {
  163                 ".OGG",  "application/x-ogg"},
  164         {
  165                 ".PDF",  "application/pdf"},
  166         {
  167                 ".PNG",  "image/png"},
  168         {
  169                 ".PS",   "application/postscript"},
  170         {
  171                 ".RAM",  "audio/x-pn-realaudio"},
  172         {
  173                 ".RM",   "audio/x-pn-realaudio"},
  174         {
  175                 ".RM",   "audio/x-pn-realaudio"},
  176         {
  177                 ".RM",   "audio/x-pn-realaudio"},
  178         {
  179                 ".RPM",  "application/x-rpm"},
  180         {
  181                 ".RTF",  "application/rtf"},
  182         {
  183                 ".SH",   "application/x-sh"},
  184         {
  185                 ".SVG",  "image/svg+xml"},
  186         {
  187                 ".TAR",  "application/x-tar"},
  188         {
  189                 ".TCL",  "application/x-tcl"},
  190         {
  191                 ".TEX",  "application/x-tex"},
  192         {
  193                 ".TGZ",  "application/x-gzip"},
  194         {
  195                 ".TIF",  "image/tiff"},
  196         {
  197                 ".TIFF", "image/tiff"},
  198         {
  199                 ".TXT",  "text/plain"},
  200         {
  201                 ".WAV",  "audio/x-wav"},
  202         {
  203                 ".XLS",  "application/x-msexcel"},
  204         {
  205                 ".XML",  "text/xml"},
  206         {
  207                 ".XSL",  "text/xml"},
  208         {
  209                 ".ZIP",  "application/x-zip-compressed"},
  210         {
  211 
  212                 /* Open XML file types */
  213                 ".DOCM", "application/vnd.ms-word.document.macroEnabled.12"},
  214         {
  215                 ".DOCX", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
  216         {
  217                 ".DOTM", "application/vnd.ms-word.template.macroEnabled.12"},
  218         {
  219                 ".DOTX", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
  220         {
  221                 ".PPSM", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
  222         {
  223                 ".PPSX", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
  224         {
  225                 ".PPTM", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
  226         {
  227                 ".PPTX", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
  228         {
  229                 ".XLSB", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
  230         {
  231                 ".XLSM", "application/vnd.ms-excel.sheet.macroEnabled.12"},
  232         {
  233                 ".XLSX", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
  234         {
  235                 ".XPS",  "application/vnd.ms-xpsdocument"},
  236         {
  237 
  238                 "",      ""},};
  239 
  240 struct {
  241    char language[32];
  242    char abbrev[32];
  243 } lang_table[] = {
  244 
  245         {"brazilian",    "br"},
  246         {"bulgarian",    "bg"},
  247         {"czech",        "cz"},
  248         {"danish",       "dk"},
  249         {"dutch",        "nl"},
  250         {"french",       "fr"},
  251         {"german",       "de"},
  252         {"indonesia",    "id"},
  253         {"italian",      "it"},
  254         {"japanese",     "jp"},
  255         {"polish",       "pl"},
  256         {"ru_CP1251",    "ru"},
  257         {"slowak",       "sk"},
  258         {"spanish",      "es"},
  259         {"swedish",      "se"},
  260         {"turkish",      "tr"},
  261         {"zh_CN-GB2314", "zh"},
  262         {"zh_CN-UTF8",   "zh"},
  263         {"",             ""}
  264 };
  265 
  266 char _convert_cmd[256];
  267 char _identify_cmd[256];
  268 
  269 #ifdef OS_WINNT
  270 int run_service(void);
  271 #endif
  272 
  273 #ifdef OS_UNIX
  274 gid_t orig_gid;                 /* Original effective GID before dropping privilege */
  275 uid_t orig_uid;                 /* Original effective UID before dropping privilege */
  276 char pidfile[256];              /* Pidfile name                                     */
  277 #endif
  278 
  279 #ifdef __CYGWIN__               /* bug in cygwin, 'timezone' not linked automatically */
  280 long _timezone;
  281 #endif
  282 
  283 /*---- Funcions from the MIDAS library -----------------------------*/
  284 
  285 #define my_toupper(_c)    ( ((_c)>='a' && (_c)<='z') ? ((_c)-'a'+'A') : (_c) )
  286 #define my_tolower(_c)    ( ((_c)>='A' && (_c)<='Z') ? ((_c)-'A'+'a') : (_c) )
  287 
  288 BOOL strieq(const char *str1, const char *str2) {
  289    char c1, c2;
  290 
  291    if (str1 == NULL && str2 == NULL)
  292       return TRUE;
  293    if (str1 == NULL || str2 == NULL)
  294       return FALSE;
  295    if (strlen(str1) != strlen(str2))
  296       return FALSE;
  297 
  298    while (*str1) {
  299       c1 = *str1++;
  300       c2 = *str2++;
  301       if (my_toupper(c1) != my_toupper(c2))
  302          return FALSE;
  303    }
  304 
  305    if (*str2)
  306       return FALSE;
  307 
  308    return TRUE;
  309 }
  310 
  311 BOOL strnieq(const char *str1, const char *str2, int n) {
  312    char c1, c2;
  313    int i;
  314 
  315    if (str1 == NULL && str2 == NULL && n == 0)
  316       return TRUE;
  317    if (str1 == NULL || str2 == NULL)
  318       return FALSE;
  319    if ((int) strlen(str1) < n || (int) strlen(str2) < n)
  320       return FALSE;
  321 
  322    for (i = 0; i < n && *str1; i++) {
  323       c1 = *str1++;
  324       c2 = *str2++;
  325       if (my_toupper(c1) != my_toupper(c2))
  326          return FALSE;
  327    }
  328 
  329    if (i < n)
  330       return FALSE;
  331 
  332    return TRUE;
  333 }
  334 
  335 char *stristr(const char *str, const char *pattern) {
  336    size_t i;
  337 
  338    if (!*pattern)
  339       return (char *) str;
  340 
  341    for (; *str; str++) {
  342       if (toupper(*str) == toupper(*pattern)) {
  343          for (i = 1;; i++) {
  344             if (!pattern[i])
  345                return (char *) str;
  346             if (toupper(str[i]) != toupper(pattern[i]))
  347                break;
  348          }
  349       }
  350    }
  351    return NULL;
  352 }
  353 
  354 void strextract(const char *str, char delim, char *extr, int size)
  355 /* extract a substinr "extr" from "str" until "delim" is found */
  356 {
  357    int i;
  358 
  359    for (i = 0; str[i] != delim && i < size - 1; i++)
  360       extr[i] = str[i];
  361    extr[i] = 0;
  362 }
  363 
  364 static BOOL chkext(const char *str, const char *ext) {
  365    int extl, strl;
  366    char c1, c2;
  367 
  368    if (ext == NULL || str == NULL)
  369       return FALSE;
  370 
  371    extl = strlen(ext);
  372    strl = strlen(str);
  373    if (extl >= strl)
  374       return FALSE;
  375    str = str + strl - extl;
  376    while (*str) {
  377       c1 = *str++;
  378       c2 = *ext++;
  379       if (my_toupper(c1) != my_toupper(c2))
  380          return FALSE;
  381    }
  382    return TRUE;
  383 }
  384 
  385 int get_verbose(void) {
  386    return _verbose_level;
  387 }
  388 
  389 void set_verbose(int v) {
  390    _verbose_level = v;
  391 }
  392 
  393 /* workaround for some gcc versions bug for "%c" format (see strftime(3) */
  394 size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm) {
  395    return strftime(s, max, fmt, tm);
  396 }
  397 
  398 /* signal save read function */
  399 int my_read(int fh, void *buffer, unsigned int bytes) {
  400 #ifdef OS_UNIX
  401    int i, n = 0;
  402 
  403    do {
  404       i = read(fh, (char *) buffer + n, bytes - n);
  405 
  406       /* don't return if an alarm signal was cought */
  407       if (i == -1 && errno == EINTR)
  408          continue;
  409 
  410       if (i == -1)
  411          return -1;
  412 
  413       if (i == 0)
  414          return n;
  415 
  416       n += i;
  417 
  418    } while (n < (int) bytes);
  419 
  420    return n;
  421 #else
  422    return read(fh, buffer, bytes);
  423 #endif
  424 
  425    return 0;
  426 }
  427 
  428 /* workaround for wong timezone under MacOSX */
  429 long my_timezone() {
  430 #if defined(OS_MACOSX) || defined(__FreeBSD__) || defined(__OpenBSD__)
  431    time_t tp;
  432    time(&tp);
  433    return -localtime(&tp)->tm_gmtoff;
  434 #elif defined(OS_WINNT)
  435    return _timezone;
  436 #else
  437    return timezone;
  438 #endif
  439 }
  440 
  441 /*---- Compose RFC2822 compliant date ---*/
  442 
  443 void get_rfc2822_date(char *date, int size, time_t ltime) {
  444    time_t now;
  445    char buf[256];
  446    int offset;
  447    struct tm *ts;
  448 
  449    /* switch locale temporarily back to english to comply with RFC2822 date format */
  450    setlocale(LC_ALL, "C");
  451 
  452    if (ltime == 0)
  453       time(&now);
  454    else
  455       now = ltime;
  456    ts = localtime(&now);
  457    assert(ts);
  458    strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", ts);
  459    offset = (-(int) my_timezone());
  460    if (ts->tm_isdst)
  461       offset += 3600;
  462    snprintf(date, size - 1, "%s %+03d%02d", buf, (int) (offset / 3600),
  463             (int) ((abs((int) offset) / 60) % 60));
  464 }
  465 
  466 /*---- Safe malloc wrappers with out of memory checking from GNU ---*/
  467 
  468 static void memory_error_and_abort(char *func) {
  469    eprintf("%s: not enough memory\n", func);
  470    exit(EXIT_FAILURE);
  471 }
  472 
  473 /* Return a pointer to free()able block of memory large enough
  474  to hold BYTES number of bytes.  If the memory cannot be allocated,
  475  print an error message and abort. */
  476 void *xmalloc(size_t bytes) {
  477    char *temp;
  478 
  479    /* Align buffer on 4 byte boundery for HP UX and other 64 bit systems to prevent Bus error (core dump) */
  480    if (bytes & 3)
  481       bytes += 4 - (bytes & 3);
  482 
  483    temp = (char *) malloc(bytes + 12);
  484    if (temp == 0)
  485       memory_error_and_abort("xmalloc");
  486 
  487    /* put magic number around array and put array size */
  488    *(unsigned int *) (temp + 0) = bytes;
  489    *(unsigned int *) (temp + 4) = 0xdeadc0de;
  490    *(unsigned int *) (temp + bytes + 8) = 0xdeadc0de;
  491 
  492    return (temp + 8);
  493 }
  494 
  495 void *xcalloc(size_t count, size_t bytes) {
  496    char *temp;
  497 
  498    /* Align buffer on 4 byte boundery for HP UX and other 64 bit systems to prevent Bus error (core dump) */
  499    if (bytes & 3)
  500       bytes += 4 - (bytes & 3);
  501 
  502    temp = (char *) malloc(count * bytes + 12);
  503    if (temp == 0)
  504       memory_error_and_abort("xcalloc");
  505    memset(temp, 0, count * bytes + 12);
  506 
  507    /* put magic number around array */
  508    *(unsigned int *) (temp + 0) = count * bytes;
  509    *(unsigned int *) (temp + 4) = 0xdeadc0de;
  510    *(unsigned int *) (temp + count * bytes + 8) = 0xdeadc0de;
  511 
  512    return (temp + 8);
  513 }
  514 
  515 void *xrealloc(void *pointer, size_t bytes) {
  516    char *temp;
  517    int old_size;
  518 
  519    /* Align buffer on 4 byte boundery for HP UX and other 64 bit systems to prevent Bus error (core dump) */
  520    if (bytes & 3)
  521       bytes += 4 - (bytes & 3);
  522 
  523    if (pointer == NULL)
  524       return xmalloc(bytes);
  525 
  526    /* check old magic number */
  527    temp = pointer;
  528    assert(*((unsigned int *) (temp - 4)) == 0xdeadc0de);
  529    old_size = *((unsigned int *) (temp - 8));
  530    assert(*((unsigned int *) (temp + old_size)) == 0xdeadc0de);
  531 
  532    temp = (char *) realloc(temp - 8, bytes + 12);
  533 
  534    if (temp == 0)
  535       memory_error_and_abort("xrealloc");
  536 
  537    /* put magic number around array */
  538    *(unsigned int *) (temp + 0) = bytes;
  539    *(unsigned int *) (temp + 4) = 0xdeadc0de;
  540    *(unsigned int *) (temp + bytes + 8) = 0xdeadc0de;
  541 
  542    return (temp + 8);
  543 }
  544 
  545 void xfree(void *pointer) {
  546    char *temp;
  547    int old_size;
  548 
  549    if (!pointer)
  550       return;
  551 
  552    /* check for magic byte */
  553    temp = pointer;
  554    assert(*((unsigned int *) (temp - 4)) == 0xdeadc0de);
  555    old_size = *((unsigned int *) (temp - 8));
  556    assert(*((unsigned int *) (temp + old_size)) == 0xdeadc0de);
  557 
  558    free(temp - 8);
  559 }
  560 
  561 char *xstrdup(const char *string) {
  562    char *s;
  563 
  564    s = (char *) xmalloc(strlen(string) + 1);
  565    strcpy(s, string);
  566    return s;
  567 }
  568 
  569 /*----------------------- Message handling -------------------------*/
  570 
  571 /* Have vasprintf? (seems that only libc6 based Linux has this) */
  572 #ifdef __linux__
  573 #define HAVE_VASPRintF
  574 #endif
  575 
  576 #ifndef HAVE_VASPRintF
  577 
  578 /* vasprintf implementation taken (and adapted) from GNU libiberty */
  579 
  580 static int int_vasprintf(char **result, const char *format, va_list args) {
  581    const char *p = format;
  582    /* Add one to make sure that it is never zero, which might cause malloc
  583       to return NULL.  */
  584    int total_width = strlen(format) + 1;
  585    va_list ap;
  586 
  587 #ifdef va_copy
  588    va_copy(ap, args);
  589 #else
  590    memcpy(&ap, &args, sizeof(va_list));
  591 #endif
  592 
  593    while (*p != '\0') {
  594       if (*p++ == '%') {
  595          while (strchr("-+ #0", *p))
  596             ++p;
  597          if (*p == '*') {
  598             ++p;
  599             total_width += abs(va_arg(ap, int));
  600          } else
  601             total_width += strtoul(p, (char **) &p, 10);
  602          if (*p == '.') {
  603             ++p;
  604             if (*p == '*') {
  605                ++p;
  606                total_width += abs(va_arg(ap, int));
  607             } else
  608                total_width += strtoul(p, (char **) &p, 10);
  609          }
  610          while (strchr("hlL", *p))
  611             ++p;
  612          /*
  613           * Should be big enough for any format specifier
  614           * except %s and floats.
  615           */
  616          total_width += 30;
  617          switch (*p) {
  618             case 'd':
  619             case 'i':
  620             case 'o':
  621             case 'u':
  622             case 'x':
  623             case 'X':
  624             case 'c':
  625                (void) va_arg(ap, int);
  626                break;
  627             case 'f':
  628             case 'e':
  629             case 'E':
  630             case 'g':
  631             case 'G':
  632                (void) va_arg(ap, double);
  633                /*
  634              * Since an ieee double can have an exponent of 307, we'll
  635              * make the buffer wide enough to cover the gross case.
  636              */
  637                total_width += 307;
  638                break;
  639             case 's':
  640                total_width += strlen(va_arg(ap, char *));
  641                break;
  642             case 'p':
  643             case 'n':
  644                (void) va_arg(ap, char *);
  645                break;
  646          }
  647          p++;
  648       }
  649    }
  650 #ifdef va_copy
  651    va_end(ap);
  652 #endif
  653    *result = (char *) malloc(total_width);
  654    if (*result != NULL)
  655       return vsprintf(*result, format, args);
  656    else
  657       return -1;
  658 }
  659 
  660 #if defined (_BSD_VA_LIST_) && defined (__FreeBSD__)
  661 int vasprintf(char **result, const char *format, _BSD_VA_LIST_ args)
  662 #else
  663 
  664 int vasprintf(char **result, const char *format, va_list args)
  665 #endif
  666 {
  667    return int_vasprintf(result, format, args);
  668 }
  669 
  670 #endif                          /* ! HAVE_VASPRintF */
  671 
  672 /* Safe replacement for vasprintf (adapted code from Samba) */
  673 int xvasprintf(char **ptr, const char *format, va_list ap) {
  674    int n;
  675    va_list save;
  676 
  677 #ifdef va_copy
  678    va_copy(save, ap);
  679 #else
  680                                                                                                                            #ifdef __va_copy
  681    __va_copy(save, ap);
  682 #else
  683    save = ap;
  684 #endif
  685 #endif
  686 
  687    n = vasprintf(ptr, format, save);
  688 
  689    if (n == -1 || !*ptr) {
  690       printf("Not enough memory");
  691       exit(EXIT_FAILURE);
  692    }
  693 
  694    return n;
  695 }
  696 
  697 /* Driver for printf_handler, drop-in replacement for printf */
  698 void eprintf(const char *format, ...) {
  699    va_list ap;
  700    char *msg;
  701 
  702    va_start(ap, format);
  703    xvasprintf(&msg, format, ap);
  704    va_end(ap);
  705 
  706    (*printf_handler)(msg);
  707 
  708    free(msg);
  709 }
  710 
  711 /* Driver for fputs_handler, drop-in replacement for fputs(buf, fd) */
  712 void efputs(const char *buf) {
  713    (*fputs_handler)(buf);
  714 }
  715 
  716 /* Dump with the newline, drop-in replacement for puts(buf) */
  717 void eputs(const char *buf) {
  718    char *p;
  719 
  720    p = xmalloc(strlen(buf) + 2);
  721    strcpy(p, buf);
  722    strcat(p, "\n");
  723 
  724    (*fputs_handler)(p);
  725 
  726    xfree(p);
  727 }
  728 
  729 /* Flush the current output stream */
  730 void eflush(void) {
  731    /* Do this only for non-NULL streams (uninitiated stream or a syslog) */
  732    if (current_output_stream != NULL)
  733       fflush(current_output_stream);
  734 }
  735 
  736 #ifdef OS_WINNT
  737 HANDLE hEventLog;
  738 #endif
  739 
  740 /* Print MSG to syslog */
  741 void print_syslog(const char *msg) {
  742    char *p;
  743 
  744    /* strip trailing \r and \n */
  745    p = xstrdup(msg);
  746    while (p[strlen(p) - 1] == '\r' || p[strlen(p) - 1] == '\n')
  747       p[strlen(p) - 1] = 0;
  748 
  749 #ifdef OS_UNIX
  750    syslog(SYSLOG_PRIORITY, "%s", p);
  751 #else
  752    ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, &p, NULL);
  753 #endif
  754 
  755    xfree(p);
  756 }
  757 
  758 /* Print MSG to stderr */
  759 void print_stderr(const char *msg) {
  760    fprintf(stderr, "%s", msg);
  761 }
  762 
  763 /* Dump BUF to syslog */
  764 void fputs_syslog(const char *buf) {
  765    char *p;
  766 
  767    /* strip trailing \r and \n */
  768    p = xstrdup(buf);
  769    while (p[strlen(p) - 1] == '\r' || p[strlen(p) - 1] == '\n')
  770       p[strlen(p) - 1] = 0;
  771 
  772 #ifdef OS_UNIX
  773    syslog(SYSLOG_PRIORITY, "%s", p);
  774 #else
  775    ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, &p, NULL);
  776 #endif
  777 
  778    xfree(p);
  779 }
  780 
  781 /* Dump BUF to stderr */
  782 void fputs_stderr(const char *buf) {
  783    fputs(buf, stderr);
  784 }
  785 
  786 /* Redirect all messages handled with eprintf/efputs
  787  to syslog (Unix) or event log (Windows) */
  788 void redirect_to_syslog(void) {
  789    static int has_inited = 0;
  790 
  791    /* initiate syslog */
  792    if (!has_inited) {
  793 #ifdef OS_UNIX
  794       setlogmask(LOG_UPTO(SYSLOG_PRIORITY));
  795       openlog("elogd", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
  796 #else
  797       hEventLog = RegisterEventSource(NULL, "ELOG");
  798 #endif
  799    }
  800    has_inited = 1;
  801 
  802    printf_handler = print_syslog;
  803    fputs_handler = fputs_syslog;
  804 
  805    /* tells that the syslog facility is currently used as output */
  806    current_output_stream = NULL;
  807 }
  808 
  809 /* Redirect all messages handled with eprintf/efputs to stderr */
  810 void redirect_to_stderr(void) {
  811    printf_handler = print_stderr;
  812    fputs_handler = fputs_stderr;
  813 
  814    current_output_stream = stderr;
  815 }
  816 
  817 /*------------------------------------------------------------------*/
  818 
  819 int my_shell(char *cmd, char *result, int size) {
  820 #ifdef OS_WINNT
  821 
  822                                                                                                                            HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
  823        hChildStdoutRd, hChildStdoutWr, hChildStderrRd, hChildStderrWr, hSaveStdin, hSaveStdout, hSaveStderr;
  824 
  825    SECURITY_ATTRIBUTES saAttr;
  826    PROCESS_INFORMATION piProcInfo;
  827    STARTUPINFO siStartInfo;
  828    char buffer[10000];
  829    DWORD dwRead, dwAvail, i;
  830 
  831    /* Set the bInheritHandle flag so pipe handles are inherited. */
  832    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  833    saAttr.bInheritHandle = TRUE;
  834    saAttr.lpSecurityDescriptor = NULL;
  835 
  836    /* Save the handle to the current STDOUT. */
  837    hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
  838 
  839    /* Create a pipe for the child's STDOUT. */
  840    if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
  841       return 0;
  842 
  843    /* Set a write handle to the pipe to be STDOUT. */
  844    if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
  845       return 0;
  846 
  847    /* Save the handle to the current STDERR. */
  848    hSaveStderr = GetStdHandle(STD_ERROR_HANDLE);
  849 
  850    /* Create a pipe for the child's STDERR. */
  851    if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0))
  852       return 0;
  853 
  854    /* Set a read handle to the pipe to be STDERR. */
  855    if (!SetStdHandle(STD_ERROR_HANDLE, hChildStderrWr))
  856       return 0;
  857 
  858    /* Save the handle to the current STDIN. */
  859    hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
  860 
  861    /* Create a pipe for the child's STDIN. */
  862    if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
  863       return 0;
  864 
  865    /* Set a read handle to the pipe to be STDIN. */
  866    if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
  867       return 0;
  868 
  869    /* Duplicate the write handle to the pipe so it is not inherited. */
  870    if (!DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, FALSE,   /* not inherited */
  871                         DUPLICATE_SAME_ACCESS))
  872       return 0;
  873 
  874    CloseHandle(hChildStdinWr);
  875 
  876    /* Now create the child process. */
  877    memset(&siStartInfo, 0, sizeof(siStartInfo));
  878    siStartInfo.cb = sizeof(STARTUPINFO);
  879    siStartInfo.lpReserved = NULL;
  880    siStartInfo.lpReserved2 = NULL;
  881    siStartInfo.cbReserved2 = 0;
  882    siStartInfo.lpDesktop = NULL;
  883    siStartInfo.dwFlags = 0;
  884 
  885    /* command to execute */
  886    sprintf(buffer, "cmd /q /c %s", cmd);
  887 
  888    if (!CreateProcess(NULL, buffer,     /* command line */
  889                       NULL,     /* process security attributes */
  890                       NULL,     /* primary thread security attributes */
  891                       TRUE,     /* handles are inherited */
  892                       0,        /* creation flags */
  893                       NULL,     /* use parent's environment */
  894                       NULL,     /* use parent's current directory */
  895                       &siStartInfo,     /* STARTUPINFO pointer */
  896                       &piProcInfo))     /* receives PROCESS_INFORMATION */
  897       return 0;
  898 
  899    /* After process creation, restore the saved STDIN and STDOUT. */
  900    SetStdHandle(STD_INPUT_HANDLE, hSaveStdin);
  901    SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout);
  902    SetStdHandle(STD_ERROR_HANDLE, hSaveStderr);
  903 
  904    memset(result, 0, size);
  905 
  906    do {
  907       /* query stdout */
  908       do {
  909          if (!PeekNamedPipe(hChildStdoutRd, buffer, 256, &dwRead, &dwAvail, NULL))
  910             break;
  911          if (dwRead > 0) {
  912             ReadFile(hChildStdoutRd, buffer, 256, &dwRead, NULL);
  913             buffer[dwRead] = 0;
  914             strlcat(result, buffer, size);
  915          }
  916       } while (dwAvail > 0);
  917 
  918       /* query stderr */
  919       do {
  920          if (!PeekNamedPipe(hChildStderrRd, buffer, 256, &dwRead, &dwAvail, NULL))
  921             break;
  922          if (dwRead > 0) {
  923             ReadFile(hChildStderrRd, buffer, 256, &dwRead, NULL);
  924             buffer[dwRead] = 0;
  925             strlcat(result, buffer, size);
  926          }
  927       } while (dwAvail > 0);
  928 
  929       /* check if subprocess still alive */
  930       if (!GetExitCodeProcess(piProcInfo.hProcess, &i))
  931          break;
  932       if (i != STILL_ACTIVE)
  933          break;
  934 
  935       /* give some CPU to subprocess */
  936       Sleep(10);
  937 
  938    } while (TRUE);
  939 
  940    CloseHandle(hChildStdinWrDup);
  941    CloseHandle(hChildStdinRd);
  942    CloseHandle(hChildStderrRd);
  943    CloseHandle(hChildStdoutRd);
  944 
  945    /* strip trailing CR/LF */
  946    while (strlen(result) > 0 && (result[strlen(result) - 1] == '\r' || result[strlen(result) - 1] == '\n'))
  947       result[strlen(result) - 1] = 0;
  948 
  949    return 1;
  950 
  951 #endif                          /* OS_WINNT */
  952 
  953 #ifdef OS_UNIX
  954    pid_t child_pid;
  955    int fh, status, wait_status;
  956    char str[1024];
  957    char tmp_filename[1024];
  958 
  959    strlcpy(tmp_filename, "/tmp/elog_XXXXXX", sizeof(tmp_filename));
  960    fh = mkstemp(tmp_filename);
  961    if (fh == 0) {
  962       eprintf("Error getting TMP file name.\n");
  963       return 0;
  964    }
  965    close(fh);
  966 
  967    if ((child_pid = fork()) < 0)
  968       return 0;
  969    else if (child_pid > 0) {
  970       /* parent process waits for child */
  971       do {
  972          wait_status = waitpid(child_pid, &status, 0);
  973       } while (wait_status == -1 && errno == EINTR);
  974 
  975       /* read back result */
  976       memset(result, 0, size);
  977       fh = open(tmp_filename, O_RDONLY);
  978       if (fh > 0) {
  979          read(fh, result, size - 1);
  980          close(fh);
  981       }
  982 
  983       /* remove temporary file */
  984       remove(tmp_filename);
  985 
  986       /* strip trailing CR/LF */
  987       while (strlen(result) > 0 && (result[strlen(result) - 1] == '\r' || result[strlen(result) - 1] == '\n'))
  988          result[strlen(result) - 1] = 0;
  989 
  990    } else {
  991       /* child process */
  992 
  993       /* restore original UID/GID */
  994       if (setregid(-1, orig_gid) < 0 || setreuid(-1, orig_uid) < 0)
  995          eprintf("Cannot restore original GID/UID.\n");
  996 
  997       /* give up root privilege permanently */
  998       if (geteuid() == 0) {
  999          if (!getcfg("global", "Grp", str, sizeof(str)) || setgroup(str) < 0) {
 1000             eprintf("Falling back to default group \"elog\"\n");
 1001             if (setgroup("elog") < 0) {
 1002                eprintf("Falling back to default group \"%s\"\n", DEFAULT_GROUP);
 1003                if (setgroup(DEFAULT_GROUP) < 0) {
 1004                   eprintf("Refuse to run as setgid root.\n");
 1005                   eprintf("Please consider to define a Grp statement in configuration file\n");
 1006                   exit(EXIT_FAILURE);
 1007                }
 1008             }
 1009          } else if (get_verbose() >= VERBOSE_INFO)
 1010             eprintf("Falling back to group \"%s\"\n", str);
 1011 
 1012          if (!getcfg("global", "Usr", str, sizeof(str)) || setuser(str) < 0) {
 1013             eprintf("Falling back to default user \"elog\"\n");
 1014             if (setuser("elog") < 0) {
 1015                eprintf("Falling back to default user \"%s\"\n", DEFAULT_USER);
 1016                if (setuser(DEFAULT_USER) < 0) {
 1017                   eprintf("Refuse to run as setuid root.\n");
 1018                   eprintf("Please consider to define a Usr statement in configuration file\n");
 1019                   exit(EXIT_FAILURE);
 1020                }
 1021             }
 1022          } else if (get_verbose() >= VERBOSE_INFO)
 1023             eprintf("Falling back to user \"%s\"\n", str);
 1024       }
 1025 
 1026       /* execute shell with redirection to /tmp/elog-shell */
 1027       sprintf(str, "/bin/sh -c \"%s\" > %s 2>&1", cmd, tmp_filename);
 1028 
 1029       if (get_verbose() >= VERBOSE_INFO) {
 1030          efputs("Going to execute: ");
 1031          efputs(str);
 1032          efputs("\n");
 1033       }
 1034 
 1035       system(str);
 1036       _exit(0);
 1037    }
 1038 
 1039    return 1;
 1040 
 1041 #endif                          /* OS_UNIX */
 1042 }
 1043 
 1044 /*------------------------------------------------------------------*/
 1045 
 1046 void strsubst_list(char *string, int size, char name[][NAME_LENGTH], char value[][NAME_LENGTH], int n)
 1047 /* subsitute "$name" with value corresponding to name */
 1048 {
 1049    int i, j;
 1050    char tmp[2 * NAME_LENGTH], str[2 * NAME_LENGTH], uattr[2 * NAME_LENGTH], *ps, *pt, *p, result[10000];
 1051 
 1052    pt = tmp;
 1053    ps = string;
 1054    for (p = strchr(ps, '$'); p != NULL; p = strchr(ps, '$')) {
 1055 
 1056       /* copy leading characters */
 1057       j = (int) (p - ps);
 1058       if (j >= (int) sizeof(tmp))
 1059          return;
 1060       memcpy(pt, ps, j);
 1061       pt += j;
 1062       p++;
 1063 
 1064       /* extract name */
 1065       strlcpy(str, p, sizeof(str));
 1066       for (j = 0; j < (int) strlen(str); j++)
 1067          str[j] = toupper(str[j]);
 1068       /* do shell substituion at the end, so that shell parameter can
 1069          contain substituted attributes */
 1070       if (strncmp(str, "SHELL(", 6) == 0) {
 1071          strlcpy(pt, "$shell(", sizeof(tmp) - (pt - tmp));
 1072          ps += 7;
 1073          pt += 7;
 1074          continue;
 1075       }
 1076 
 1077       /* search name */
 1078       for (i = 0; i < n; i++) {
 1079          strlcpy(uattr, name[i], sizeof(uattr));
 1080          for (j = 0; j < (int) strlen(uattr); j++)
 1081             uattr[j] = toupper(uattr[j]);
 1082 
 1083          if (strncmp(str, uattr, strlen(uattr)) == 0)
 1084             break;
 1085       }
 1086 
 1087       /* copy value */
 1088       if (i < n) {
 1089          strlcpy(pt, value[i], sizeof(tmp) - (pt - tmp));
 1090          pt += strlen(pt);
 1091          ps = p + strlen(uattr);
 1092       } else {
 1093          *pt++ = '$';
 1094          ps = p;
 1095       }
 1096    }
 1097 
 1098    /* copy remainder */
 1099    strlcpy(pt, ps, sizeof(tmp) - (pt - tmp));
 1100    strlcpy(string, tmp, size);
 1101 
 1102    /* check for $shell() subsitution */
 1103    pt = tmp;
 1104    ps = string;
 1105    for (p = strchr(ps, '$'); p != NULL; p = strchr(ps, '$')) {
 1106 
 1107       /* copy leading characters */
 1108       j = (int) (p - ps);
 1109       if (j >= (int) sizeof(tmp))
 1110          return;
 1111       memcpy(pt, ps, j);
 1112       pt += j;
 1113       p++;
 1114 
 1115       /* extract name */
 1116       strlcpy(str, p, sizeof(str));
 1117       for (j = 0; j < (int) strlen(str); j++)
 1118          str[j] = toupper(str[j]);
 1119 
 1120       if (strncmp(str, "SHELL(", 6) == 0) {
 1121          ps += 7;
 1122          if (strrchr(p, '\"')) {
 1123             ps += strrchr(p, '\"') - p - 5;
 1124             if (strchr(ps, ')'))
 1125                ps = strchr(ps, ')') + 1;
 1126          } else {
 1127             if (strchr(ps, ')'))
 1128                ps = strchr(ps, ')') + 1;
 1129          }
 1130 
 1131          if (str[6] == '"') {
 1132             strcpy(str, p + 7);
 1133             if (strrchr(str, '\"'))
 1134                *strrchr(str, '\"') = 0;
 1135          } else {
 1136             strcpy(str, p + 6);
 1137             if (strrchr(str, ')'))
 1138                *strrchr(str, ')') = 0;
 1139          }
 1140 
 1141          if (!enable_execute) {
 1142             strlcpy(result, loc("Shell execution not enabled via -x flag"), sizeof(result));
 1143             eprintf("Shell execution not enabled via -x flag.\n");
 1144          } else
 1145             my_shell(str, result, sizeof(result));
 1146 
 1147          strlcpy(pt, result, sizeof(tmp) - (pt - tmp));
 1148          pt += strlen(pt);
 1149       } else {
 1150          *pt++ = '$';
 1151          ps = p;
 1152       }
 1153    }
 1154 
 1155    /* copy remainder */
 1156    strlcpy(pt, ps, sizeof(tmp) - (pt - tmp));
 1157 
 1158    /* return result */
 1159    strlcpy(string, tmp, size);
 1160 }
 1161 
 1162 /*------------------------------------------------------------------*/
 1163 
 1164 void strsubst(char *string, int size, char *pattern, char *subst)
 1165 /* subsitute "pattern" with "subst" in "string" */
 1166 {
 1167    char *tail, *p;
 1168    int s;
 1169 
 1170    p = string;
 1171    for (p = stristr(p, pattern); p != NULL; p = stristr(p, pattern)) {
 1172 
 1173       if (strlen(pattern) == strlen(subst)) {
 1174          memcpy(p, subst, strlen(subst));
 1175       } else if (strlen(pattern) > strlen(subst)) {
 1176          memcpy(p, subst, strlen(subst));
 1177          memmove(p + strlen(subst), p + strlen(pattern), strlen(p + strlen(pattern)) + 1);
 1178       } else {
 1179          tail = (char *) xmalloc(strlen(p) - strlen(pattern) + 1);
 1180          strcpy(tail, p + strlen(pattern));
 1181          s = size - (p - string);
 1182          strlcpy(p, subst, s);
 1183          strlcat(p, tail, s);
 1184          xfree(tail);
 1185       }
 1186 
 1187       p += strlen(subst);
 1188    }
 1189 }
 1190 
 1191 /*------------------------------------------------------------------*/
 1192 
 1193 void url_decode(char *p)
 1194 /********************************************************************\
 1195 Decode the given string in-place by expanding %XX escapes
 1196  \********************************************************************/
 1197 {
 1198    char *pD, str[3];
 1199    int i;
 1200 
 1201    pD = p;
 1202    while (*p) {
 1203       if (*p == '%') {
 1204          /* Escape: next 2 chars are hex representation of the actual character */
 1205          p++;
 1206          if (isxdigit(p[0]) && isxdigit(p[1])) {
 1207             str[0] = p[0];
 1208             str[1] = p[1];
 1209             str[2] = 0;
 1210             sscanf(str, "%02X", &i);
 1211 
 1212             *pD++ = (char) i;
 1213             p += 2;
 1214          } else
 1215             *pD++ = '%';
 1216       } else if (*p == '+') {
 1217          /* convert '+' to ' ' */
 1218          *pD++ = ' ';
 1219          p++;
 1220       } else {
 1221          *pD++ = *p++;
 1222       }
 1223    }
 1224    *pD = '\0';
 1225 }
 1226 
 1227 void url_encode(char *ps, int size)
 1228 /********************************************************************\
 1229 Encode the given string in-place by adding %XX escapes
 1230  \********************************************************************/
 1231 {
 1232    unsigned char *pd, *p;
 1233    unsigned char str[NAME_LENGTH];
 1234 
 1235    pd = str;
 1236    p = (unsigned char *) ps;
 1237    while (*p && pd < str + 250) {
 1238       if (strchr("%&=#?+<>", *p) || *p > 127) {
 1239          sprintf((char *) pd, "%%%02X", *p);
 1240          pd += 3;
 1241          p++;
 1242       } else if (*p == ' ') {
 1243          *pd++ = '+';
 1244          p++;
 1245       } else {
 1246          *pd++ = *p++;
 1247       }
 1248    }
 1249    *pd = '\0';
 1250    strlcpy(ps, (char *) str, size);
 1251 }
 1252 
 1253 void url_slash_encode(char *ps, int size)
 1254 /********************************************************************\
 1255 Do the same including '/' characters
 1256  \********************************************************************/
 1257 {
 1258    unsigned char *pd, *p;
 1259    unsigned char str[NAME_LENGTH];
 1260 
 1261    pd = str;
 1262    p = (unsigned char *) ps;
 1263    while (*p && pd < str + 250) {
 1264       if (strchr("%&=#?+<>/", *p) || *p > 127) {
 1265          sprintf((char *) pd, "%%%02X", *p);
 1266          pd += 3;
 1267          p++;
 1268       } else if (*p == ' ') {
 1269          *pd++ = '+';
 1270          p++;
 1271       } else {
 1272          *pd++ = *p++;
 1273       }
 1274    }
 1275    *pd = '\0';
 1276    strlcpy(ps, (char *) str, size);
 1277 }
 1278 
 1279 /*-------------------------------------------------------------------*/
 1280 
 1281 void str_escape(char *ps, int size)
 1282 /********************************************************************\
 1283 Encode the given string in-place by adding \\ escapes for `$"\
 1284 \********************************************************************/
 1285 {
 1286    unsigned char *pd, *p;
 1287    unsigned char str[NAME_LENGTH];
 1288 
 1289    pd = str;
 1290    p = (unsigned char *) ps;
 1291    while (*p && pd < str + 250) {
 1292       if (strchr("'$\"\\", *p)) {
 1293          *pd++ = '\\';
 1294          *pd++ = *p++;
 1295       } else {
 1296          *pd++ = *p++;
 1297       }
 1298    }
 1299    *pd = '\0';
 1300    strlcpy(ps, (char *) str, size);
 1301 }
 1302 
 1303 void btou(char *str)
 1304 /* convert all blanks to underscores in a string */
 1305 {
 1306    int i;
 1307 
 1308    for (i = 0; i < (int) strlen(str); i++)
 1309       if (str[i] == ' ')
 1310          str[i] = '_';
 1311 }
 1312 
 1313 /*-------------------------------------------------------------------*/
 1314 
 1315 void stou(char *str)
 1316 /* convert all special characters to underscores in a string */
 1317 {
 1318    int i;
 1319 
 1320    for (i = 0; i < (int) strlen(str); i++)
 1321       if (!isalnum(str[i]))
 1322          str[i] = '_';
 1323 }
 1324 
 1325 /*-------------------------------------------------------------------*/
 1326 
 1327 char *map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 1328 
 1329 int cind(char c) {
 1330    int i;
 1331 
 1332    if (c == '=')
 1333       return 0;
 1334 
 1335    for (i = 0; i < 64; i++)
 1336       if (map[i] == c)
 1337          return i;
 1338 
 1339    return -1;
 1340 }
 1341 
 1342 void base64_decode(char *s, char *d) {
 1343    unsigned int t;
 1344 
 1345    while (s && *s) {
 1346       t = cind(*s) << 18;
 1347       s++;
 1348       t |= cind(*s) << 12;
 1349       s++;
 1350       t |= cind(*s) << 6;
 1351       s++;
 1352       t |= cind(*s) << 0;
 1353       s++;
 1354 
 1355       *(d + 2) = (char) (t & 0xFF);
 1356       t >>= 8;
 1357       *(d + 1) = (char) (t & 0xFF);
 1358       t >>= 8;
 1359       *d = (char) (t & 0xFF);
 1360 
 1361       d += 3;
 1362    }
 1363    *d = 0;
 1364 }
 1365 
 1366 void base64_encode(unsigned char *s, unsigned char *d, int size) {
 1367    unsigned int t, pad;
 1368    unsigned char *p;
 1369 
 1370    pad = 3 - strlen((char *) s) % 3;
 1371    if (pad == 3)
 1372       pad = 0;
 1373    p = d;
 1374    while (*s) {
 1375       t = (*s++) << 16;
 1376       if (*s)
 1377          t |= (*s++) << 8;
 1378       if (*s)
 1379          t |= (*s++) << 0;
 1380 
 1381       *(d + 3) = map[t & 63];
 1382       t >>= 6;
 1383       *(d + 2) = map[t & 63];
 1384       t >>= 6;
 1385       *(d + 1) = map[t & 63];
 1386       t >>= 6;
 1387       *(d + 0) = map[t & 63];
 1388 
 1389       d += 4;
 1390       if (d - p >= size - 3)
 1391          return;
 1392    }
 1393    *d = 0;
 1394    while (pad--)
 1395       *(--d) = '=';
 1396 }
 1397 
 1398 void base64_bufenc(unsigned char *s, int len, char *d) {
 1399    unsigned int t, pad;
 1400    int i;
 1401 
 1402    pad = 3 - len % 3;
 1403    if (pad == 3)
 1404       pad = 0;
 1405    for (i = 0; i < len;) {
 1406       t = s[i++] << 16;
 1407       if (i < len)
 1408          t |= s[i++] << 8;
 1409       if (i < len)
 1410          t |= s[i++] << 0;
 1411 
 1412       *(d + 3) = map[t & 63];
 1413       t >>= 6;
 1414       *(d + 2) = map[t & 63];
 1415       t >>= 6;
 1416       *(d + 1) = map[t & 63];
 1417       t >>= 6;
 1418       *(d + 0) = map[t & 63];
 1419 
 1420       d += 4;
 1421    }
 1422    *d = 0;
 1423    while (pad--)
 1424       *(--d) = '=';
 1425 }
 1426 
 1427 char *sha256_crypt(const char *key, const char *salt);
 1428 
 1429 void do_crypt(const char *s, char *d, int size) {
 1430    strlcpy(d, sha256_crypt(s, "$5$") + 4, size);
 1431 }
 1432 
 1433 /*------------------------------------------------------------------*
 1434  MD5 Checksum Routines
 1435 
 1436  \*------------------------------------------------------------------*/
 1437 
 1438 typedef struct {
 1439    unsigned int state[4];       // state (ABCD)
 1440    unsigned int count[2];       // number of bits, modulo 2^64 (lsb first)
 1441    unsigned char buffer[64];    // input buffer
 1442 } MD5_CONTEXT;
 1443 
 1444 /*------------------------------------------------------------------*/
 1445 
 1446 /* prototypes of the support routines */
 1447 void _MD5_update(MD5_CONTEXT *, const void *, unsigned int);
 1448 
 1449 void _MD5_transform(unsigned int[4], unsigned char[64]);
 1450 
 1451 void _MD5_encode(unsigned char *, unsigned int *, unsigned int);
 1452 
 1453 void _MD5_decode(unsigned int *, unsigned char *, unsigned int);
 1454 
 1455 /* F, G, H and I are basic MD5 functions */
 1456 #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
 1457 #define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
 1458 #define H(x, y, z) ((x) ^ (y) ^ (z))
 1459 #define I(x, y, z) ((y) ^ ((x) | (~z)))
 1460 
 1461 /* ROTATE_LEFT rotates x left n bits */
 1462 #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
 1463 
 1464 /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
 1465 /* Rotation is separate from addition to prevent recomputation */
 1466 #define FF(a, b, c, d, x, s, ac) {                  \
 1467    (a) += F ((b), (c), (d)) + (x) + (unsigned int)(ac);  \
 1468    (a) = ROTATE_LEFT ((a), (s));                   \
 1469    (a) += (b);                                     \
 1470                                                    }
 1471 #define GG(a, b, c, d, x, s, ac) {                  \
 1472    (a) += G ((b), (c), (d)) + (x) + (unsigned int)(ac);  \
 1473    (a) = ROTATE_LEFT ((a), (s));                   \
 1474    (a) += (b);                                     \
 1475                                                    }
 1476 #define HH(a, b, c, d, x, s, ac) {                  \
 1477    (a) += H ((b), (c), (d)) + (x) + (unsigned int)(ac);  \
 1478    (a) = ROTATE_LEFT ((a), (s));                   \
 1479    (a) += (b);                                     \
 1480                                                    }
 1481 #define II(a, b, c, d, x, s, ac) {                  \
 1482    (a) += I ((b), (c), (d)) + (x) + (unsigned int)(ac);  \
 1483    (a) = ROTATE_LEFT ((a), (s));                   \
 1484    (a) += (b);                                     \
 1485                                                    }
 1486 
 1487 /*------------------------------------------------------------------*/
 1488 
 1489 /* main MD5 checksum routine, returns digest from pdata buffer */
 1490 
 1491 void MD5_checksum(const void *pdata, unsigned int len, unsigned char digest[16]) {
 1492    MD5_CONTEXT ctx;
 1493    unsigned char bits[8];
 1494    unsigned int i, padlen;
 1495 
 1496    /* to allow multithreading we have to locate the padding memory here */
 1497    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,
 1498                                 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,
 1499                                 0, 0, 0, 0,
 1500                                 0, 0, 0, 0, 0, 0, 0
 1501    };
 1502 
 1503    memset(&ctx, 0, sizeof(MD5_CONTEXT));
 1504    ctx.count[0] = ctx.count[1] = 0;
 1505 
 1506    /* load magic initialization constants */
 1507    ctx.state[0] = 0x67452301;
 1508    ctx.state[1] = 0xefcdab89;
 1509    ctx.state[2] = 0x98badcfe;
 1510    ctx.state[3] = 0x10325476;
 1511 
 1512    _MD5_update(&ctx, pdata, len);
 1513 
 1514    // save number of bits
 1515    _MD5_encode(bits, ctx.count, 8);
 1516 
 1517    // pad out to 56 mod 64
 1518    i = (unsigned int) ((ctx.count[0] >> 3) & 0x3f);
 1519    padlen = (i < 56) ? (56 - i) : (120 - i);
 1520    _MD5_update(&ctx, PADDING, padlen);
 1521 
 1522    // append length (before padding)
 1523    _MD5_update(&ctx, bits, 8);
 1524 
 1525    // store state in digest
 1526    _MD5_encode(digest, ctx.state, 16);
 1527 }
 1528 
 1529 /*------------------------------------------------------------------*/
 1530 
 1531 void _MD5_update(MD5_CONTEXT *pctx, const void *pdata, unsigned int len) {
 1532    unsigned char *pin;
 1533    unsigned int i, index, partlen;
 1534 
 1535    pin = (unsigned char *) pdata;
 1536 
 1537    // compute number of bytes mod 64
 1538    index = (unsigned int) ((pctx->count[0] >> 3) & 0x3F);
 1539 
 1540    // update number of bits
 1541    if ((pctx->count[0] += ((unsigned int) len << 3)) < ((unsigned int) len << 3))
 1542       pctx->count[1]++;
 1543    pctx->count[1] += ((unsigned int) len >> 29);
 1544 
 1545    partlen = 64 - index;
 1546 
 1547    // transform as many times as possible.
 1548    if (len >= partlen) {
 1549       memcpy(&pctx->buffer[index], pin, partlen);
 1550       _MD5_transform(pctx->state, pctx->buffer);
 1551 
 1552       for (i = partlen; i + 63 < len; i += 64)
 1553          _MD5_transform(pctx->state, &pin[i]);
 1554 
 1555       index = 0;
 1556    } else
 1557       i = 0;
 1558 
 1559    /* buffer remaining input */
 1560    memcpy(&pctx->buffer[index], &pin[i], len - i);
 1561 }
 1562 
 1563 /*------------------------------------------------------------------*/
 1564 
 1565 /* basic transformation, transforms state based on block */
 1566 void _MD5_transform(unsigned int state[4], unsigned char block[64]) {
 1567    unsigned int lA = state[0], lB = state[1], lC = state[2], lD = state[3];
 1568    unsigned int x[16];
 1569 
 1570    _MD5_decode(x, block, 64);
 1571 
 1572    /* round 1 */
 1573    FF(lA, lB, lC, lD, x[0], 7, 0xd76aa478);     // 1
 1574    FF(lD, lA, lB, lC, x[1], 12, 0xe8c7b756);    // 2
 1575    FF(lC, lD, lA, lB, x[2], 17, 0x242070db);    // 3
 1576    FF(lB, lC, lD, lA, x[3], 22, 0xc1bdceee);    // 4
 1577    FF(lA, lB, lC, lD, x[4], 7, 0xf57c0faf);     // 5
 1578    FF(lD, lA, lB, lC, x[5], 12, 0x4787c62a);    // 6
 1579    FF(lC, lD, lA, lB, x[6], 17, 0xa8304613);    // 7
 1580    FF(lB, lC, lD, lA, x[7], 22, 0xfd469501);    // 8
 1581    FF(lA, lB, lC, lD, x[8], 7, 0x698098d8);     // 9
 1582    FF(lD, lA, lB, lC, x[9], 12, 0x8b44f7af);    // 10
 1583    FF(lC, lD, lA, lB, x[10], 17, 0xffff5bb1);   // 11
 1584    FF(lB, lC, lD, lA, x[11], 22, 0x895cd7be);   // 12
 1585    FF(lA, lB, lC, lD, x[12], 7, 0x6b901122);    // 13
 1586    FF(lD, lA, lB, lC, x[13], 12, 0xfd987193);   // 14
 1587    FF(lC, lD, lA, lB, x[14], 17, 0xa679438e);   // 15
 1588    FF(lB, lC, lD, lA, x[15], 22, 0x49b40821);   // 16
 1589 
 1590    /* round 2 */
 1591    GG(lA, lB, lC, lD, x[1], 5, 0xf61e2562);     // 17
 1592    GG(lD, lA, lB, lC, x[6], 9, 0xc040b340);     // 18
 1593    GG(lC, lD, lA, lB, x[11], 14, 0x265e5a51);   // 19
 1594    GG(lB, lC, lD, lA, x[0], 20, 0xe9b6c7aa);    // 20
 1595    GG(lA, lB, lC, lD, x[5], 5, 0xd62f105d);     // 21
 1596    GG(lD, lA, lB, lC, x[10], 9, 0x2441453);     // 22
 1597    GG(lC, lD, lA, lB, x[15], 14, 0xd8a1e681);   // 23
 1598    GG(lB, lC, lD, lA, x[4], 20, 0xe7d3fbc8);    // 24
 1599    GG(lA, lB, lC, lD, x[9], 5, 0x21e1cde6);     // 25
 1600    GG(lD, lA, lB, lC, x[14], 9, 0xc33707d6);    // 26
 1601    GG(lC, lD, lA, lB, x[3], 14, 0xf4d50d87);    // 27
 1602    GG(lB, lC, lD, lA, x[8], 20, 0x455a14ed);    // 28
 1603    GG(lA, lB, lC, lD, x[13], 5, 0xa9e3e905);    // 29
 1604    GG(lD, lA, lB, lC, x[2], 9, 0xfcefa3f8);     // 30
 1605    GG(lC, lD, lA, lB, x[7], 14, 0x676f02d9);    // 31
 1606    GG(lB, lC, lD, lA, x[12], 20, 0x8d2a4c8a);   // 32
 1607 
 1608    /* round 3 */
 1609    HH(lA, lB, lC, lD, x[5], 4, 0xfffa3942);     // 33
 1610    HH(lD, lA, lB, lC, x[8], 11, 0x8771f681);    // 34
 1611    HH(lC, lD, lA, lB, x[11], 16, 0x6d9d6122);   // 35
 1612    HH(lB, lC, lD, lA, x[14], 23, 0xfde5380c);   // 36
 1613    HH(lA, lB, lC, lD, x[1], 4, 0xa4beea44);     // 37
 1614    HH(lD, lA, lB, lC, x[4], 11, 0x4bdecfa9);    // 38
 1615    HH(lC, lD, lA, lB, x[7], 16, 0xf6bb4b60);    // 39
 1616    HH(lB, lC, lD, lA, x[10], 23, 0xbebfbc70);   // 40
 1617    HH(lA, lB, lC, lD, x[13], 4, 0x289b7ec6);    // 41
 1618    HH(lD, lA, lB, lC, x[0], 11, 0xeaa127fa);    // 42
 1619    HH(lC, lD, lA, lB, x[3], 16, 0xd4ef3085);    // 43
 1620    HH(lB, lC, lD, lA, x[6], 23, 0x4881d05);     // 44
 1621    HH(lA, lB, lC, lD, x[9], 4, 0xd9d4d039);     // 45
 1622    HH(lD, lA, lB, lC, x[12], 11, 0xe6db99e5);   // 46
 1623    HH(lC, lD, lA, lB, x[15], 16, 0x1fa27cf8);   // 47
 1624    HH(lB, lC, lD, lA, x[2], 23, 0xc4ac5665);    // 48
 1625 
 1626    /* round 4 */
 1627    II(lA, lB, lC, lD, x[0], 6, 0xf4292244);     // 49
 1628    II(lD, lA, lB, lC, x[7], 10, 0x432aff97);    // 50
 1629    II(lC, lD, lA, lB, x[14], 15, 0xab9423a7);   // 51
 1630    II(lB, lC, lD, lA, x[5], 21, 0xfc93a039);    // 52
 1631    II(lA, lB, lC, lD, x[12], 6, 0x655b59c3);    // 53
 1632    II(lD, lA, lB, lC, x[3], 10, 0x8f0ccc92);    // 54
 1633    II(lC, lD, lA, lB, x[10], 15, 0xffeff47d);   // 55
 1634    II(lB, lC, lD, lA, x[1], 21, 0x85845dd1);    // 56
 1635    II(lA, lB, lC, lD, x[8], 6, 0x6fa87e4f);     // 57
 1636    II(lD, lA, lB, lC, x[15], 10, 0xfe2ce6e0);   // 58
 1637    II(lC, lD, lA, lB, x[6], 15, 0xa3014314);    // 59
 1638    II(lB, lC, lD, lA, x[13], 21, 0x4e0811a1);   // 60
 1639    II(lA, lB, lC, lD, x[4], 6, 0xf7537e82);     // 61
 1640    II(lD, lA, lB, lC, x[11], 10, 0xbd3af235);   // 62
 1641    II(lC, lD, lA, lB, x[2], 15, 0x2ad7d2bb);    // 63
 1642    II(lB, lC, lD, lA, x[9], 21, 0xeb86d391);    // 64
 1643 
 1644    state[0] += lA;
 1645    state[1] += lB;
 1646    state[2] += lC;
 1647    state[3] += lD;
 1648 
 1649    /* lClear sensitive information */
 1650    memset(x, 0, sizeof(x));
 1651 }
 1652 
 1653 /*------------------------------------------------------------------*/
 1654 
 1655 /* encodes input (unsigned int) into output (unsigned char),
 1656  assumes that lLen is a multiple of 4 */
 1657 void _MD5_encode(unsigned char *pout, unsigned int *pin, unsigned int len) {
 1658    unsigned int i, j;
 1659 
 1660    for (i = 0, j = 0; j < len; i++, j += 4) {
 1661       pout[j] = (unsigned char) (pin[i] & 0x0ff);
 1662       pout[j + 1] = (unsigned char) ((pin[i] >> 8) & 0x0ff);
 1663       pout[j + 2] = (unsigned char) ((pin[i] >> 16) & 0x0ff);
 1664       pout[j + 3] = (unsigned char) ((pin[i] >> 24) & 0x0ff);
 1665    }
 1666 }
 1667 
 1668 /*------------------------------------------------------------------*/
 1669 
 1670 /* encodes input (unsigned char) into output (unsigned int),
 1671  assumes that lLen is a multiple of 4 */
 1672 void _MD5_decode(unsigned int *pout, unsigned char *pin, unsigned int len) {
 1673    unsigned int i, j;
 1674 
 1675    for (i = 0, j = 0; j < len; i++, j += 4)
 1676       pout[i] = ((unsigned int) pin[j]) | (((unsigned int) pin[j + 1]) << 8) | (((unsigned int) pin[j + 2])
 1677               << 16) | (((unsigned int)
 1678               pin[j + 3]) << 24);
 1679 }
 1680 
 1681 /*------------------------------------------------------------------*/
 1682 
 1683 BOOL file_exist(char *file_name) {
 1684    int fh;
 1685 
 1686    fh = open(file_name, O_RDONLY);
 1687    if (fh < 0)
 1688       return FALSE;
 1689 
 1690    close(fh);
 1691    return TRUE;
 1692 }
 1693 
 1694 /*------------------------------------------------------------------*/
 1695 
 1696 void serialdate2date(double days, int *day, int *month, int *year)
 1697 /* convert days since 1.1.1900 to date */
 1698 {
 1699    int i, j, l, n;
 1700 
 1701    l = (int) days + 68569 + 2415019;
 1702    n = (int) ((4 * l) / 146097);
 1703    l = l - (int) ((146097 * n + 3) / 4);
 1704    i = (int) ((4000 * (l + 1)) / 1461001);
 1705    l = l - (int) ((1461 * i) / 4) + 31;
 1706    j = (int) ((80 * l) / 2447);
 1707    *day = l - (int) ((2447 * j) / 80);
 1708    l = (int) (j / 11);
 1709    *month = j + 2 - (12 * l);
 1710    *year = 100 * (n - 49) + i + l;
 1711 }
 1712 
 1713 double date2serialdate(int day, int month, int year)
 1714 /* convert date to days since 1.1.1900 */
 1715 {
 1716    int serialdate;
 1717 
 1718    serialdate = (int) ((1461 * (year + 4800 + (int) ((month - 14) / 12))) / 4) + (int) ((367 * (month - 2
 1719                                                                                                 -
 1720                                                                                                 12 *
 1721                                                                                                 ((month -
 1722                                                                                                   14) /
 1723                                                                                                  12))) / 12) -
 1724                 (int) ((3 * ((int) ((year + 4900 + (int) ((month - 14) / 12))
 1725                                     / 100))) / 4) + day - 2415019 - 32075;
 1726 
 1727    return serialdate;
 1728 }
 1729 
 1730 /*------------------------------------------------------------------*/
 1731 
 1732 /* Wrapper for setegid. */
 1733 int setegroup(char *str) {
 1734 #ifdef OS_UNIX
 1735    struct group *gr;
 1736 
 1737    gr = getgrnam(str);
 1738 
 1739    if (gr != NULL) {
 1740       chown(logbook_dir, -1, gr->gr_gid);
 1741       if (setregid(-1, gr->gr_gid) >= 0 && initgroups(gr->gr_name, gr->gr_gid) >= 0)
 1742          return 0;
 1743       else {
 1744          eprintf("Cannot set effective GID to group \"%s\"\n", gr->gr_name);
 1745          eprintf("setgroup: %s\n", strerror(errno));
 1746       }
 1747    } else
 1748       eprintf("Group \"%s\" not found\n", str);
 1749 
 1750    return -1;
 1751 #else
 1752    return 0;
 1753 #endif
 1754 }
 1755 
 1756 /* Wrapper for seteuid. */
 1757 int seteuser(char *str) {
 1758 #ifdef OS_UNIX
 1759    struct passwd *pw;
 1760 
 1761    pw = getpwnam(str);
 1762 
 1763    if (pw != NULL) {
 1764       chown(logbook_dir, pw->pw_uid, -1);
 1765       if (setreuid(-1, pw->pw_uid) >= 0) {
 1766          return 0;
 1767       } else {
 1768          eprintf("Cannot set effective UID to user \"%s\"\n", str);
 1769          eprintf("setuser: %s\n", strerror(errno));
 1770       }
 1771    } else
 1772       eprintf("User \"%s\" not found\n", str);
 1773 
 1774    return -1;
 1775 #else
 1776    return 0;
 1777 #endif
 1778 }
 1779 
 1780 /* Wrapper for setgid. */
 1781 int setgroup(char *str) {
 1782 #ifdef OS_UNIX
 1783    struct group *gr;
 1784 
 1785    gr = getgrnam(str);
 1786 
 1787    if (gr != NULL)
 1788       if (setgid(gr->gr_gid) >= 0 && initgroups(gr->gr_name, gr->gr_gid) >= 0)
 1789          return 0;
 1790       else {
 1791          eprintf("Cannot set effective GID to group \"%s\"\n", gr->gr_name);
 1792          eprintf("setgroup: %s\n", strerror(errno));
 1793       }
 1794    else
 1795       eprintf("Group \"%s\" not found\n", str);
 1796 
 1797    return -1;
 1798 #else
 1799    return 0;
 1800 #endif
 1801 }
 1802 
 1803 /* Wrapper for setuid. */
 1804 int setuser(char *str) {
 1805 #ifdef OS_UNIX
 1806    struct passwd *pw;
 1807 
 1808    pw = getpwnam(str);
 1809 
 1810    if (pw != NULL)
 1811       if (setuid(pw->pw_uid) >= 0)
 1812          return 0;
 1813       else {
 1814          eprintf("Cannot set effective UID to user \"%s\"\n", str);
 1815          eprintf("setuser: %s\n", strerror(errno));
 1816       }
 1817    else
 1818       eprintf("User \"%s\" not found\n", str);
 1819 
 1820    return -1;
 1821 #else
 1822    return 0;
 1823 #endif
 1824 }
 1825 
 1826 /*-------------------------------------------------------------------*/
 1827 
 1828 int send_with_timeout(void *p, int sock, char *buf, int buf_size) {
 1829    int status, sent, send_size, send_packet;
 1830    time_t start, now;
 1831    char *pbuf;
 1832 #ifdef HAVE_SSL
 1833    SSL *ssl;
 1834 #endif
 1835 
 1836    time(&start);
 1837    sent = 0;
 1838    send_size = buf_size;
 1839    pbuf = p; // fix compiler warning
 1840    pbuf = buf;
 1841 
 1842    do {
 1843       if (send_size > 65536)
 1844          send_packet = 65536;
 1845       else
 1846          send_packet = send_size;
 1847 
 1848 #ifdef HAVE_SSL
 1849       ssl = (SSL *) p;
 1850       if (ssl)
 1851          status = SSL_write(ssl, pbuf, send_packet);
 1852       else
 1853 #endif
 1854          status = send(sock, pbuf, send_packet, 0);
 1855 
 1856       // abort after 30 seconds
 1857       time(&now);
 1858       if (now > start + 30) {
 1859          printf("Timeout after 30 seconds\n");
 1860          break;
 1861       }
 1862 
 1863       // repeat if we were interrupted by alarm() signal
 1864       if (status == -1 && errno == EINTR) {
 1865          continue;
 1866       }
 1867 
 1868       if (status == -1)
 1869          break;
 1870 
 1871       if (status > 0)
 1872          sent += status;
 1873 
 1874       if (status > 0 && sent < buf_size) {
 1875          pbuf += status;
 1876          send_size -= status;
 1877       }
 1878 
 1879    } while (sent < buf_size);
 1880 
 1881    return sent;
 1882 }
 1883 
 1884 /*-------------------------------------------------------------------*/
 1885 
 1886 int recv_string(int sock, char *buffer, int buffer_size, int millisec) {
 1887    int i, n;
 1888    fd_set readfds;
 1889    struct timeval timeout;
 1890 
 1891    n = 0;
 1892    memset(buffer, 0, buffer_size);
 1893 
 1894    do {
 1895       if (millisec > 0) {
 1896          FD_ZERO(&readfds);
 1897          FD_SET(sock, &readfds);
 1898 
 1899          timeout.tv_sec = millisec / 1000;
 1900          timeout.tv_usec = (millisec % 1000) * 1000;
 1901 
 1902          select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout);
 1903 
 1904          if (!FD_ISSET(sock, &readfds))
 1905             break;
 1906       }
 1907 
 1908       i = recv(sock, buffer + n, 1, 0);
 1909 
 1910       if (i <= 0)
 1911          break;
 1912 
 1913       n++;
 1914 
 1915       if (n >= buffer_size)
 1916          break;
 1917 
 1918    } while (buffer[n - 1] && buffer[n - 1] != 10);
 1919 
 1920    return n - 1;
 1921 }
 1922 
 1923 /*-------------------------------------------------------------------*/
 1924 
 1925 SESSION_ID *_sid = NULL;
 1926 int _n_sid;
 1927 
 1928 int sid_new(LOGBOOK *lbs, const char *user, const char *host, char *sid) {
 1929    double exp;
 1930    time_t now;
 1931    int i, new_i;
 1932    char str[256];
 1933 
 1934    time(&now);
 1935    new_i = 0;
 1936 
 1937    if (_sid == NULL) {
 1938       _sid = (SESSION_ID *) malloc(sizeof(SESSION_ID));
 1939       _n_sid = 1;
 1940       new_i = 0;
 1941    } else {
 1942 
 1943       exp = 24;
 1944       str[0] = 0;
 1945       if (lbs == NULL)
 1946          getcfg("global", "Login expiration", str, sizeof(str));
 1947       else
 1948          getcfg(lbs->name, "Login expiration", str, sizeof(str));
 1949 
 1950       if (atof(str) > 0)
 1951          exp = atof(str);
 1952       if (exp < 24)
 1953          exp = 24;              /* one day minimum for dangling edit pages */
 1954 
 1955       /* search for expired sid */
 1956       for (i = 0; i < _n_sid; i++) {
 1957          if (_sid[i].time + exp * 3600 < now) {
 1958             new_i = i;
 1959             break;
 1960          }
 1961       }
 1962 
 1963       if (i == _n_sid) {
 1964          _sid = (SESSION_ID *) realloc(_sid, sizeof(SESSION_ID) * (_n_sid + 1));
 1965          new_i = _n_sid;
 1966          _n_sid++;
 1967       }
 1968    }
 1969 
 1970    strlcpy(_sid[new_i].user_name, user, sizeof(_sid[0].user_name));
 1971    strlcpy(_sid[new_i].host_ip, host, sizeof(_sid[0].host_ip));
 1972    for (i = 0; i < 4; i++)
 1973       sprintf(sid + i * 4, "%04X", rand() % 0x10000);
 1974    sid[16] = 0;
 1975    strlcpy(_sid[new_i].session_id, sid, sizeof(_sid[0].session_id));
 1976    _sid[new_i].time = now;
 1977 
 1978    return 1;
 1979 }
 1980 
 1981 /*-------------------------------------------------------------------*/
 1982 
 1983 int sid_check(char *sid, char *user_name) {
 1984    int i;
 1985    time_t now;
 1986 
 1987    if (sid == NULL)
 1988       return FALSE;
 1989 
 1990    time(&now);
 1991    for (i = 0; i < _n_sid; i++) {
 1992       if (strcmp(_sid[i].host_ip, (char *) inet_ntoa(rem_addr)) == 0 && strcmp(_sid[i].session_id, sid) == 0) {
 1993          strcpy(user_name, _sid[i].user_name);
 1994          _sid[i].time = now;
 1995          return TRUE;
 1996       }
 1997    }
 1998 
 1999    return FALSE;
 2000 }
 2001 
 2002 /*-------------------------------------------------------------------*/
 2003 
 2004 int sid_remove(char *sid) {
 2005    int i;
 2006 
 2007    if (sid == NULL)
 2008       return FALSE;
 2009 
 2010    for (i = 0; i < _n_sid; i++) {
 2011       if (strcmp(_sid[i].session_id, sid) == 0) {
 2012          memset(&_sid[i], 0, sizeof(SESSION_ID));
 2013          return TRUE;
 2014       }
 2015    }
 2016 
 2017    return FALSE;
 2018 }
 2019 
 2020 /*-------------------------------------------------------------------*/
 2021 
 2022 void compose_email_header(LOGBOOK *lbs, char *subject, char *from, char *to, char *url, char *mail_text,
 2023                           int size, int mail_encoding, int n_attachments, char *multipart_boundary,
 2024                           int message_id, int reply_id) {
 2025    char buffer[256], charset[256], subject_enc[5000];
 2026    char buf[80], str[256];
 2027    int i, offset, multipart;
 2028    time_t now;
 2029    struct tm *ts;
 2030 
 2031    i = 0;
 2032    if (mail_encoding & 1)
 2033       i++;
 2034    if (mail_encoding & 2)
 2035       i++;
 2036    if (mail_encoding & 4)
 2037       i++;
 2038    multipart = i > 1;
 2039 
 2040    if (!getcfg("global", "charset", charset, sizeof(charset)))
 2041       strcpy(charset, DEFAULT_HTTP_CHARSET);
 2042 
 2043    /* switch locale temporarily back to english to comply with RFC2822 date format */
 2044    setlocale(LC_ALL, "C");
 2045 
 2046    time(&now);
 2047    ts = localtime(&now);
 2048    assert(ts);
 2049    strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", ts);
 2050    offset = (-(int) my_timezone());
 2051    if (ts->tm_isdst)
 2052       offset += 3600;
 2053    if (get_verbose() >= VERBOSE_INFO) {
 2054       sprintf(str, "timezone: %d, offset: %d\n", (int) my_timezone(), (int) offset);
 2055       efputs(str);
 2056    }
 2057    snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Date: %s %+03d%02d\r\n", buf,
 2058             (int) (offset / 3600), (int) ((abs((int) offset) / 60) % 60));
 2059 
 2060    getcfg("global", "Language", str, sizeof(str));
 2061    if (str[0])
 2062       setlocale(LC_ALL, str);
 2063 
 2064    strlcat(mail_text, "To: ", size);
 2065 
 2066    if (getcfg(lbs->name, "Omit Email to", str, sizeof(str)) && atoi(str) == 1)
 2067       strlcat(mail_text, "ELOG", size);
 2068    else
 2069       strlcat(mail_text, to, size);
 2070 
 2071    strlcat(mail_text, "\r\n", size);
 2072 
 2073    snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "From: %s\r\n", from);
 2074    snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "User-Agent: Elog Version %s\r\n",
 2075             VERSION);
 2076 
 2077    strlcat(mail_text, "MIME-Version: 1.0\r\n", size);
 2078 
 2079    memset(subject_enc, 0, sizeof(subject_enc));
 2080 
 2081    for (i = 0; i < (int) strlen(subject); i++)
 2082       if (subject[i] < 0)
 2083          break;
 2084 
 2085    if (i < (int) strlen(subject)) {
 2086       /* subject contains local characters, so encode it using charset */
 2087       for (i = 0; i < (int) strlen(subject); i += 40) {
 2088          strlcpy(buffer, subject + i, sizeof(buffer));
 2089          buffer[40] = 0;
 2090          strlcat(subject_enc, "=?", sizeof(subject_enc));
 2091          strlcat(subject_enc, charset, sizeof(subject_enc));
 2092          strlcat(subject_enc, "?B?", sizeof(subject_enc));
 2093          base64_encode((unsigned char *) buffer, (unsigned char *) (subject_enc + strlen(subject_enc)),
 2094                        sizeof(subject_enc) - strlen(subject_enc));
 2095          strlcat(subject_enc, "?=", sizeof(subject_enc));
 2096          if (strlen(subject + i) < 40)
 2097             break;
 2098 
 2099          strlcat(subject_enc, "\r\n ", sizeof(subject_enc));    // another encoded-word
 2100       }
 2101    } else
 2102       strlcpy(subject_enc, subject, sizeof(subject_enc));
 2103 
 2104    snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Subject: %s\r\n", subject_enc);
 2105 
 2106    if (strchr(from, '@')) {
 2107       strlcpy(str, strchr(from, '@') + 1, sizeof(str));
 2108       if (strchr(str, '>'))
 2109          *strchr(str, '>') = 0;
 2110    } else
 2111       strlcpy(str, "elog.org", sizeof(str));
 2112 
 2113    if (message_id)
 2114       snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Message-ID: <%s-%d@%s>\r\n",
 2115                lbs->name_enc, message_id, str);
 2116    if (reply_id)
 2117       snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "In-Reply-To: <%s-%d@%s>\r\n",
 2118                lbs->name_enc, reply_id, str);
 2119 
 2120    if (url)
 2121       snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "X-Elog-URL: %s\r\n", url);
 2122 
 2123    strlcat(mail_text, "X-Elog-submit-type: web|elog\r\n", size);
 2124 
 2125    if (multipart) {
 2126 
 2127       sprintf(multipart_boundary, "------------%04X%04X%04X", rand(), rand(), rand());
 2128       snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
 2129                "MIME-Version: 1.0\r\nContent-Type: multipart/alternative;\r\n boundary=\"%s\"\r\n\r\n",
 2130                multipart_boundary);
 2131 
 2132       strlcat(mail_text, "This is a multi-part message in MIME format.\r\n", size);
 2133    } else {
 2134       if (n_attachments) {
 2135          sprintf(multipart_boundary, "------------%04X%04X%04X", rand(), rand(), rand());
 2136          snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
 2137                   "MIME-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n boundary=\"%s\"\r\n\r\n",
 2138                   multipart_boundary);
 2139 
 2140          strlcat(mail_text, "This is a multi-part message in MIME format.\r\n", size);
 2141       } else {
 2142          if (multipart_boundary)
 2143             multipart_boundary[0] = 0;
 2144       }
 2145    }
 2146 }
 2147 
 2148 /*-------------------------------------------------------------------*/
 2149 
 2150 int check_smtp_error(char *str, int expected, char *error, int error_size) {
 2151    if (atoi(str) != expected) {
 2152       if (error)
 2153          strlcpy(error, str + 4, error_size);
 2154       return 0;
 2155    }
 2156 
 2157    return 1;
 2158 }
 2159 
 2160 /*-------------------------------------------------------------------*/
 2161 
 2162 int sendmail(LOGBOOK *lbs, char *smtp_host, char *from, char *to, char *text, char *error, int error_size) {
 2163    struct sockaddr_in bind_addr;
 2164    struct hostent *phe;
 2165    int i, n, s, strsize;
 2166    char *str;
 2167    char list[MAX_N_EMAIL][NAME_LENGTH], buffer[10000], decoded[256];
 2168 
 2169    memset(error, 0, error_size);
 2170 
 2171    if (get_verbose() >= VERBOSE_INFO)
 2172       eprintf("\n\nEmail from %s to %s, SMTP host %s:\n", from, to, smtp_host);
 2173    sprintf(buffer, "Email from %s to ", from);
 2174    strlcat(buffer, to, sizeof(buffer));
 2175    strlcat(buffer, ", SMTP host ", sizeof(buffer));
 2176    strlcat(buffer, smtp_host, sizeof(buffer));
 2177    strlcat(buffer, ":\n", sizeof(buffer));
 2178    write_logfile(lbs, buffer);
 2179 
 2180    /* create a new socket for connecting to remote server */
 2181    s = socket(AF_INET, SOCK_STREAM, 0);
 2182    if (s == -1)
 2183       return -1;
 2184 
 2185    strsize = MAX_CONTENT_LENGTH + 1000;
 2186    str = xmalloc(strsize);
 2187 
 2188    /* connect to remote node port on SMTP port */
 2189    int smtp_port = 25;
 2190    if (getcfg(lbs->name, "SMTP port", str, strsize))
 2191       smtp_port = atoi(str);
 2192 
 2193    memset(&bind_addr, 0, sizeof(bind_addr));
 2194    bind_addr.sin_family = AF_INET;
 2195    bind_addr.sin_port = htons((short) smtp_port);
 2196 
 2197    phe = gethostbyname(smtp_host);
 2198    if (phe == NULL) {
 2199       if (error)
 2200          strlcpy(error, loc("Cannot lookup server name"), error_size);
 2201       return -1;
 2202    }
 2203    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
 2204 
 2205    if (connect(s, (void *) &bind_addr, sizeof(bind_addr)) < 0) {
 2206       closesocket(s);
 2207       if (error)
 2208          strlcpy(error, loc("Cannot connect to server"), error_size);
 2209       return -1;
 2210    }
 2211 
 2212    recv_string(s, str, strsize, 10000);
 2213    if (get_verbose() >= VERBOSE_INFO)
 2214       efputs(str);
 2215    write_logfile(lbs, str);
 2216 
 2217    /* drain server messages */
 2218    do {
 2219       str[0] = 0;
 2220       recv_string(s, str, strsize, 300);
 2221       if (get_verbose() >= VERBOSE_INFO)
 2222          efputs(str);
 2223       write_logfile(lbs, str);
 2224    } while (str[0]);
 2225 
 2226    if (getcfg(lbs->name, "SMTP username", str, strsize)) {
 2227 
 2228       snprintf(str, strsize - 1, "EHLO %s\r\n", host_name);
 2229       send(s, str, strlen(str), 0);
 2230       if (get_verbose() >= VERBOSE_INFO)
 2231          efputs(str);
 2232       write_logfile(lbs, str);
 2233 
 2234       do {
 2235          recv_string(s, str, strsize, 3000);
 2236          if (get_verbose() >= VERBOSE_INFO)
 2237             efputs(str);
 2238          write_logfile(lbs, str);
 2239          if (!check_smtp_error(str, 250, error, error_size))
 2240             goto smtp_error;
 2241       } while (stristr(str, "250 ") == NULL);
 2242 
 2243    } else {
 2244 
 2245       snprintf(str, strsize - 1, "HELO %s\r\n", host_name);
 2246 
 2247       send(s, str, strlen(str), 0);
 2248       if (get_verbose() >= VERBOSE_INFO)
 2249          efputs(str);
 2250       write_logfile(lbs, str);
 2251       recv_string(s, str, strsize, 3000);
 2252       if (get_verbose() >= VERBOSE_INFO)
 2253          efputs(str);
 2254       write_logfile(lbs, str);
 2255       if (!check_smtp_error(str, 250, error, error_size))
 2256          goto smtp_error;
 2257    }
 2258 
 2259    /* optional authentication */
 2260    if (getcfg(lbs->name, "SMTP username", str, strsize)) {
 2261 
 2262       snprintf(str, strsize - 1, "AUTH LOGIN\r\n");
 2263       send(s, str, strlen(str), 0);
 2264       if (get_verbose() >= VERBOSE_INFO)
 2265          efputs(str);
 2266       write_logfile(lbs, str);
 2267       recv_string(s, str, strsize, 3000);
 2268       if (strchr(str, '\r'))
 2269          *strchr(str, '\r') = 0;
 2270       if (atoi(str) != 334) {
 2271          strcat(str, "\n");
 2272          if (get_verbose() >= VERBOSE_INFO)
 2273             efputs(str);
 2274          write_logfile(lbs, str);
 2275       } else {
 2276          base64_decode(str + 4, decoded);
 2277          strcat(decoded, "\n");
 2278          if (get_verbose() >= VERBOSE_INFO)
 2279             efputs(decoded);
 2280          write_logfile(lbs, decoded);
 2281       }
 2282       if (!check_smtp_error(str, 334, error, error_size))
 2283          goto smtp_error;
 2284 
 2285       getcfg(lbs->name, "SMTP username", decoded, sizeof(decoded));
 2286       base64_encode((unsigned char *) decoded, (unsigned char *) str, strsize);
 2287       strcat(str, "\r\n");
 2288       send(s, str, strlen(str), 0);
 2289       if (get_verbose() >= VERBOSE_INFO)
 2290          efputs(decoded);
 2291       write_logfile(lbs, decoded);
 2292       recv_string(s, str, strsize, 3000);
 2293       if (strchr(str, '\r'))
 2294          *strchr(str, '\r') = 0;
 2295       base64_decode(str + 4, decoded);
 2296       strcat(decoded, "\n");
 2297       if (get_verbose() >= VERBOSE_INFO)
 2298          efputs(decoded);
 2299       write_logfile(lbs, decoded);
 2300       if (!check_smtp_error(str, 334, error, error_size))
 2301          goto smtp_error;
 2302 
 2303       getcfg(lbs->name, "SMTP password", str, strsize);
 2304       strcat(str, "\r\n");
 2305       send(s, str, strlen(str), 0);
 2306       if (get_verbose() >= VERBOSE_INFO)
 2307          efputs(str);
 2308       write_logfile(lbs, str);
 2309       recv_string(s, str, strsize, 3000);
 2310       if (get_verbose() >= VERBOSE_INFO)
 2311          efputs(str);
 2312       write_logfile(lbs, str);
 2313       if (!check_smtp_error(str, 235, error, error_size))
 2314          goto smtp_error;
 2315    }
 2316 
 2317    snprintf(str, strsize - 1, "MAIL FROM: %s\r\n", from);
 2318    send(s, str, strlen(str), 0);
 2319    if (get_verbose() >= VERBOSE_INFO)
 2320       efputs(str);
 2321    write_logfile(lbs, str);
 2322    recv_string(s, str, strsize, 3000);
 2323    if (get_verbose() >= VERBOSE_INFO)
 2324       efputs(str);
 2325    write_logfile(lbs, str);
 2326 
 2327    if (!check_smtp_error(str, 250, error, error_size))
 2328       goto smtp_error;
 2329 
 2330    /* break recipients into list */
 2331    n = strbreak(to, list, MAX_N_EMAIL, ",", FALSE);
 2332 
 2333    for (i = 0; i < n; i++) {
 2334       if (list[i] == 0 || strchr(list[i], '@') == NULL)
 2335          continue;
 2336 
 2337       snprintf(str, strsize - 1, "RCPT TO: <%s>\r\n", list[i]);
 2338       send(s, str, strlen(str), 0);
 2339       if (get_verbose() >= VERBOSE_INFO)
 2340          efputs(str);
 2341       write_logfile(lbs, str);
 2342 
 2343       /* increased timeout for SMTP servers with long alias lists */
 2344       recv_string(s, str, strsize, 30000);
 2345       if (get_verbose() >= VERBOSE_INFO)
 2346          efputs(str);
 2347       write_logfile(lbs, str);
 2348 
 2349       if (!check_smtp_error(str, 250, error, error_size))
 2350          goto smtp_error;
 2351    }
 2352 
 2353    snprintf(str, strsize - 1, "DATA\r\n");
 2354    send(s, str, strlen(str), 0);
 2355    if (get_verbose() >= VERBOSE_INFO)
 2356       efputs(str);
 2357    write_logfile(lbs, str);
 2358    recv_string(s, str, strsize, 3000);
 2359    if (get_verbose() >= VERBOSE_INFO)
 2360       efputs(str);
 2361    write_logfile(lbs, str);
 2362    if (!check_smtp_error(str, 354, error, error_size))
 2363       goto smtp_error;
 2364 
 2365    /* replace "." at beginning of line by ".." */
 2366    strlcpy(str, text, strsize);
 2367    strsubst(str, strsize, "\n.", "\n..");
 2368 
 2369    /* add ".<CR>" to signal end of message */
 2370    strlcat(str, ".\r\n", strsize);
 2371 
 2372    /* check if buffer exceeded */
 2373    if ((int) strlen(str) == strsize - 1) {
 2374       strlcpy(error, loc("Entry size too large for email notification"), error_size);
 2375       goto smtp_error;
 2376    }
 2377 
 2378    send(s, str, strlen(str), 0);
 2379    if (get_verbose() >= VERBOSE_INFO)
 2380       efputs(str);
 2381    write_logfile(lbs, str);
 2382    recv_string(s, str, strsize, 10000);
 2383    if (get_verbose() >= VERBOSE_INFO)
 2384       efputs(str);
 2385    write_logfile(lbs, str);
 2386    if (!check_smtp_error(str, 250, error, error_size))
 2387       goto smtp_error;
 2388 
 2389    snprintf(str, strsize - 1, "QUIT\r\n");
 2390    send(s, str, strlen(str), 0);
 2391    if (get_verbose() >= VERBOSE_INFO)
 2392       efputs(str);
 2393    write_logfile(lbs, str);
 2394    recv_string(s, str, strsize, 3000);
 2395    if (get_verbose() >= VERBOSE_INFO)
 2396       efputs(str);
 2397    write_logfile(lbs, str);
 2398    if (!check_smtp_error(str, 221, error, error_size))
 2399       goto smtp_error;
 2400 
 2401    closesocket(s);
 2402    xfree(str);
 2403    return 1;
 2404 
 2405    smtp_error:
 2406 
 2407    closesocket(s);
 2408    xfree(str);
 2409 
 2410    return -1;
 2411 }
 2412 
 2413 /*------------------------------------------------------------------*/
 2414 
 2415 int elog_connect(char *host, int port) {
 2416    int status, sock;
 2417    struct hostent *phe;
 2418    struct sockaddr_in bind_addr;
 2419 
 2420    /* create socket */
 2421    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
 2422       perror("cannot create socket");
 2423       return -1;
 2424    }
 2425 
 2426    /* compose remote address */
 2427    memset(&bind_addr, 0, sizeof(bind_addr));
 2428    bind_addr.sin_family = AF_INET;
 2429    bind_addr.sin_addr.s_addr = 0;
 2430    bind_addr.sin_port = htons((unsigned short) port);
 2431 
 2432    phe = gethostbyname(host);
 2433    if (phe == NULL) {
 2434       perror("cannot get host name");
 2435       return -1;
 2436    }
 2437    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
 2438 
 2439    /* connect to server */
 2440    status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
 2441    if (status != 0)
 2442       return -1;
 2443 
 2444    return sock;
 2445 }
 2446 
 2447 /*------------------------------------------------------------------*/
 2448 
 2449 #ifdef HAVE_SSL
 2450 
 2451 int ssl_connect(int sock, SSL **ssl_con) {
 2452    SSL_METHOD *meth;
 2453    SSL_CTX *ctx;
 2454    X509 *cert = NULL;
 2455    int i;
 2456 
 2457    SSL_library_init();
 2458    SSL_load_error_strings();
 2459 
 2460 #if OPENSSL_VERSION_NUMBER > 0x1010000fL
 2461    meth = (SSL_METHOD *) TLS_method();
 2462 #else
 2463    meth = (SSL_METHOD *) TLSv1_2_method();
 2464 #endif
 2465    ctx = SSL_CTX_new(meth);
 2466 
 2467    *ssl_con = SSL_new(ctx);
 2468    SSL_set_fd(*ssl_con, sock);
 2469    if (SSL_connect(*ssl_con) <= 0)
 2470       return -1;
 2471 
 2472    cert = SSL_get_peer_certificate(*ssl_con);
 2473    if (cert == NULL)
 2474       return -1;
 2475 
 2476    i = SSL_get_verify_result(*ssl_con);
 2477    if (i != X509_V_OK)
 2478       printf("Possibly invalid certificate, continue on your own risk!\n");
 2479 
 2480    return 0;
 2481 }
 2482 
 2483 #endif
 2484 
 2485 /*-------------------------------------------------------------------*/
 2486 
 2487 void split_url(const char *url, char *host, int *port, char *subdir, char *param) {
 2488    const char *p;
 2489    char str[256];
 2490 
 2491    if (host)
 2492       host[0] = 0;
 2493    if (port)
 2494       *port = 80;
 2495    if (subdir)
 2496       subdir[0] = 0;
 2497    if (param)
 2498       param[0] = 0;
 2499 
 2500    p = url;
 2501    if (strncmp(url, "http://", 7) == 0)
 2502       p += 7;
 2503    if (strncmp(url, "https://", 8) == 0)
 2504       p += 8;
 2505 
 2506    strncpy(str, p, sizeof(str));
 2507    if (strchr(str, '/')) {
 2508       if (subdir)
 2509          strncpy(subdir, strchr(str, '/'), 256);
 2510       *strchr(str, '/') = 0;
 2511    }
 2512 
 2513    if (strchr(str, '?')) {
 2514       if (subdir)
 2515          strncpy(subdir, strchr(str, '?'), 256);
 2516       *strchr(str, '?') = 0;
 2517    }
 2518 
 2519    if (strchr(str, ':')) {
 2520       if (port)
 2521          *port = atoi(strchr(str, ':') + 1);
 2522       *strchr(str, ':') = 0;
 2523    }
 2524 
 2525    if (host)
 2526       strcpy(host, str);
 2527 
 2528    if (subdir) {
 2529       if (strchr(subdir, '?')) {
 2530          strncpy(param, strchr(subdir, '?'), 256);
 2531          *strchr(subdir, '?') = 0;
 2532       }
 2533 
 2534       if (subdir[0] == 0)
 2535          strcpy(subdir, "/");
 2536    }
 2537 }
 2538 
 2539 /*-------------------------------------------------------------------*/
 2540 
 2541 int retrieve_url(LOGBOOK *lbs, const char *url, int ssl, char **buffer, BOOL send_unm) {
 2542    char str[1000], unm[256], upwd[256], host[256], subdir[256], param[256];
 2543    int port, bufsize;
 2544    int i, n;
 2545    fd_set readfds;
 2546    struct timeval timeout;
 2547 
 2548    UNUSED(ssl);
 2549 
 2550 #ifdef HAVE_SSL
 2551    SSL *ssl_con = NULL;
 2552 #else
 2553    void *ssl_con = NULL;
 2554 #endif
 2555    int sock;
 2556 
 2557    *buffer = NULL;
 2558    split_url(url, host, &port, subdir, param);
 2559 
 2560    /* create a new socket for connecting to remote server */
 2561    sock = elog_connect(host, port);
 2562    if (sock == -1)
 2563       return -1;
 2564 #ifdef HAVE_SSL
 2565    if (ssl)
 2566       if (ssl_connect(sock, &ssl_con) < 0) {
 2567          SSL_free(ssl_con);
 2568          printf("Error initiating SSL connection\n");
 2569          return -1;
 2570       }
 2571 #endif
 2572 
 2573    /* compose GET request, avoid chunked data in HTTP/1.1 protocol */
 2574    sprintf(str, "GET %s%s HTTP/1.0\r\nConnection: Close\r\n", subdir, param);
 2575 
 2576    /* add local username/password */
 2577    if (send_unm) {
 2578       if (isparam("unm")) {
 2579          strlcpy(unm, getparam("unm"), sizeof(unm));
 2580          if (isparam("upwd"))
 2581             strlcpy(upwd, getparam("upwd"), sizeof(upwd));
 2582          else
 2583             get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL);
 2584 
 2585          sprintf(str + strlen(str), "Cookie: unm=%s; upwd=%s\r\n", unm, upwd);
 2586       }
 2587    }
 2588 
 2589    /* add host (RFC2616, Sec. 14) */
 2590    sprintf(str + strlen(str), "Host: %s:%d\r\n", host, port);
 2591 
 2592    strcat(str, "\r\n");
 2593 
 2594    send_with_timeout(ssl_con, sock, (char *) str, strlen(str));
 2595 
 2596    bufsize = TEXT_SIZE + 1000;
 2597    *buffer = xmalloc(bufsize);
 2598    memset(*buffer, 0, bufsize);
 2599 
 2600    n = 0;
 2601 
 2602    do {
 2603       FD_ZERO(&readfds);
 2604       FD_SET(sock, &readfds);
 2605 
 2606       timeout.tv_sec = 30;      /* 30 sec. timeout */
 2607       timeout.tv_usec = 0;
 2608 
 2609       select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout);
 2610 
 2611       if (!FD_ISSET(sock, &readfds)) {
 2612          closesocket(sock);
 2613          sock = 0;
 2614          xfree(*buffer);
 2615          *buffer = NULL;
 2616          return -1;
 2617       }
 2618 #ifdef HAVE_SSL
 2619       if (ssl)
 2620          i = SSL_read(ssl_con, *buffer + n, bufsize - n);
 2621       else
 2622 #endif
 2623          i = recv(sock, *buffer + n, bufsize - n, 0);
 2624 
 2625       if (i <= 0)
 2626          break;
 2627 
 2628       n += i;
 2629 
 2630       if (n >= bufsize) {
 2631          /* increase buffer size */
 2632          bufsize += 10000;
 2633          *buffer = (char *) xrealloc(*buffer, bufsize);
 2634          if (*buffer == NULL) {
 2635             closesocket(sock);
 2636             return -1;
 2637          }
 2638       }
 2639 
 2640    } while (1);
 2641 
 2642 #ifdef HAVE_SSL
 2643    if (ssl)
 2644       SSL_free(ssl_con);
 2645 #endif
 2646 
 2647    return n;
 2648 }
 2649 
 2650 /*-------------------------------------------------------------------*/
 2651 
 2652 int ss_daemon_init() {
 2653 #ifdef OS_UNIX
 2654 
 2655    /* only implemented for UNIX */
 2656    int i, fd, pid;
 2657 
 2658    if ((pid = fork()) < 0)
 2659       return FAILURE;
 2660    else if (pid != 0)
 2661       _exit(EXIT_SUCCESS);      /* parent finished, exit without atexit hook */
 2662 
 2663    /* child continues here */
 2664 
 2665    /* try and use up stdin, stdout and stderr, so other
 2666       routines writing to stdout etc won't cause havoc. Copied from smbd */
 2667    for (i = 0; i < 3; i++) {
 2668       close(i);
 2669       fd = open("/dev/null", O_RDWR, 0);
 2670       if (fd < 0)
 2671          fd = open("/dev/null", O_WRONLY, 0);
 2672       if (fd < 0) {
 2673          eprintf("Can't open /dev/null");
 2674          return FAILURE;
 2675       }
 2676       if (fd != i) {
 2677          eprintf("Did not get file descriptor");
 2678          return FAILURE;
 2679       }
 2680    }
 2681 
 2682    setsid();                    /* become session leader */
 2683    chdir("/");                  /* change working direcotry (not on NFS!) */
 2684    umask(0);                    /* clear our file mode creation mask */
 2685 
 2686 #endif
 2687 
 2688    return SUCCESS;
 2689 }
 2690 
 2691 /*------------------------------------------------------------------*/
 2692 
 2693 /* Parameter extraction from configuration file similar to win.ini */
 2694 
 2695 typedef struct {
 2696    char *param;
 2697    char *uparam;
 2698    char *value;
 2699 } CONFIG_PARAM;
 2700 
 2701 typedef struct {
 2702    char *section_name;
 2703    int n_params;
 2704    CONFIG_PARAM *config_param;
 2705 } LB_CONFIG;
 2706 
 2707 LB_CONFIG *lb_config = NULL;
 2708 int n_lb_config = 0;
 2709 
 2710 char _topgroup[256];
 2711 char _condition[256];
 2712 time_t cfgfile_mtime = 0;
 2713 
 2714 /*-------------------------------------------------------------------*/
 2715 
 2716 void check_config_file(BOOL force) {
 2717    struct stat cfg_stat;
 2718 
 2719    if (force) {
 2720       parse_config_file(config_file);
 2721       return;
 2722    }
 2723 
 2724    /* force re-read configuration file if changed */
 2725    if (stat(config_file, &cfg_stat) == 0) {
 2726       if (cfgfile_mtime < cfg_stat.st_mtime) {
 2727          cfgfile_mtime = cfg_stat.st_mtime;
 2728          parse_config_file(config_file);
 2729       }
 2730    } else
 2731       eprintf("Cannot stat() config file \"%s\": %s\n", config_file, strerror(errno));
 2732 }
 2733 
 2734 /*-------------------------------------------------------------------*/
 2735 
 2736 void setcfg_topgroup(char *topgroup) {
 2737    strcpy(_topgroup, topgroup);
 2738 }
 2739 
 2740 char *getcfg_topgroup() {
 2741    if (_topgroup[0])
 2742       return _topgroup;
 2743 
 2744    return NULL;
 2745 }
 2746 
 2747 /*------------------------------------------------------------------*/
 2748 
 2749 int is_logbook(char *logbook) {
 2750    char str[256];
 2751 
 2752    if (strieq(logbook, "global"))
 2753       return 0;
 2754 
 2755    /* check for 'global group' or 'global_xxx' */
 2756    strlcpy(str, logbook, sizeof(str));
 2757    str[7] = 0;
 2758    return !strieq(str, "global ");
 2759 }
 2760 
 2761 /*-------------------------------------------------------------------*/
 2762 
 2763 void set_condition(char *c) {
 2764    strlcpy(_condition, c, sizeof(_condition));
 2765 }
 2766 
 2767 /*-------------------------------------------------------------------*/
 2768 
 2769 void evaluate_conditions(LOGBOOK *lbs, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
 2770    char condition[256], str[256];
 2771    int index, i;
 2772 
 2773    condition[0] = 0;
 2774    set_condition("");
 2775    for (index = 0; index < lbs->n_attr; index++) {
 2776       for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
 2777 
 2778          if (strchr(attr_options[index][i], '{') && strchr(attr_options[index][i], '}')) {
 2779 
 2780             strlcpy(str, attr_options[index][i], sizeof(str));
 2781             *strchr(str, '{') = 0;
 2782 
 2783             if (strieq(str, attrib[index])) {
 2784                strlcpy(str, strchr(attr_options[index][i], '{') + 1, sizeof(str));
 2785                if (*strchr(str, '}'))
 2786                   *strchr(str, '}') = 0;
 2787 
 2788                if (condition[0] == 0)
 2789                   strlcpy(condition, str, sizeof(condition));
 2790                else {
 2791                   strlcat(condition, ",", sizeof(condition));
 2792                   strlcat(condition, str, sizeof(condition));
 2793                }
 2794 
 2795                set_condition(condition);
 2796                scan_attributes(lbs->name);
 2797             }
 2798          }
 2799       }
 2800    }
 2801 }
 2802 
 2803 /*-------------------------------------------------------------------*/
 2804 
 2805 BOOL match_param(char *str, char *param, int conditional_only) {
 2806    int ncl, npl, nand, i, j, k;
 2807    char *p, pcond[256], clist[10][NAME_LENGTH], plist[10][NAME_LENGTH], alist[10][NAME_LENGTH];
 2808 
 2809    if (conditional_only && str[0] != '{')
 2810       return FALSE;
 2811 
 2812    if (!_condition[0] || str[0] != '{')
 2813       return (stricmp(str, param) == 0);
 2814 
 2815    p = str;
 2816    if (strchr(p, '}'))
 2817       p = strchr(p, '}') + 1;
 2818    while (*p == ' ')
 2819       p++;
 2820 
 2821    strlcpy(pcond, str, sizeof(pcond));
 2822    if (strchr(pcond, '}'))
 2823       *strchr(pcond, '}') = 0;
 2824    if (strchr(pcond, '{'))
 2825       *strchr(pcond, '{') = ' ';
 2826 
 2827    npl = strbreak(pcond, plist, 10, ",", FALSE);
 2828    ncl = strbreak(_condition, clist, 10, ",", FALSE);
 2829 
 2830    for (i = 0; i < ncl; i++)
 2831       for (j = 0; j < npl; j++)
 2832          if (stricmp(clist[i], plist[j]) == 0) {
 2833             /* condition matches */
 2834             return stricmp(p, param) == 0;
 2835          }
 2836 
 2837    /* check and'ed conditions */
 2838    for (i = 0; i < npl; i++)
 2839       if (strchr(plist[i], '&')) {
 2840          nand = strbreak(plist[i], alist, 10, "&", FALSE);
 2841          for (j = 0; j < nand; j++) {
 2842             for (k = 0; k < ncl; k++)
 2843                if (stricmp(clist[k], alist[j]) == 0)
 2844                   break;
 2845 
 2846             if (k == ncl)
 2847                return FALSE;
 2848          }
 2849 
 2850          if (j == nand)
 2851             return stricmp(p, param) == 0;
 2852       }
 2853 
 2854    return 0;
 2855 }
 2856 
 2857 /*-------------------------------------------------------------------*/
 2858 
 2859 int param_compare(const void *p1, const void *p2) {
 2860    return stricmp(((CONFIG_PARAM *) p1)->uparam, ((CONFIG_PARAM *) p2)->uparam);
 2861 }
 2862 
 2863 /*------------------------------------------------------------------*/
 2864 
 2865 void free_config() {
 2866    int i, j;
 2867 
 2868    for (i = 0; i < n_lb_config; i++) {
 2869       for (j = 0; j < lb_config[i].n_params; j++) {
 2870          xfree(lb_config[i].config_param[j].param);
 2871          xfree(lb_config[i].config_param[j].uparam);
 2872          xfree(lb_config[i].config_param[j].value);
 2873       }
 2874       if (lb_config[i].config_param)
 2875          xfree(lb_config[i].config_param);
 2876       xfree(lb_config[i].section_name);
 2877    }
 2878    xfree(lb_config);
 2879    lb_config = NULL;
 2880    n_lb_config = 0;
 2881 }
 2882 
 2883 /*------------------------------------------------------------------*/
 2884 
 2885 int parse_config_file(char *file_name)
 2886 /* parse whole config file and store options in sorted list */
 2887 {
 2888    char *str, *buffer, *p, *pstr;
 2889    int index, i, j, fh, length;
 2890 
 2891    str = xmalloc(20000);
 2892 
 2893    /* open configuration file */
 2894    fh = open(file_name, O_RDONLY | O_BINARY);
 2895    if (fh < 0)
 2896       return 0;
 2897    length = lseek(fh, 0, SEEK_END);
 2898    lseek(fh, 0, SEEK_SET);
 2899    buffer = xmalloc(length + 1);
 2900    read(fh, buffer, length);
 2901    buffer[length] = 0;
 2902    close(fh);
 2903 
 2904    /* release previously allocated memory */
 2905    if (lb_config)
 2906       free_config();
 2907 
 2908    /* search group */
 2909    p = buffer;
 2910    index = 0;
 2911    do {
 2912       if (*p == '#' || *p == ';') {
 2913          /* skip comment */
 2914          while (*p && *p != '\n' && *p != '\r')
 2915             p++;
 2916       } else if (*p == '[') {
 2917          p++;
 2918          pstr = str;
 2919          while (*p && *p != ']' && *p != '\n' && *p != '\r' && pstr - str < 10000)
 2920             *pstr++ = *p++;
 2921          *pstr = 0;
 2922 
 2923          /* allocate new group */
 2924          if (!lb_config)
 2925             lb_config = xmalloc(sizeof(LB_CONFIG));
 2926          else
 2927             lb_config = xrealloc(lb_config, sizeof(LB_CONFIG) * (n_lb_config + 1));
 2928          lb_config[n_lb_config].section_name = xmalloc(strlen(str) + 1);
 2929          lb_config[n_lb_config].n_params = 0;
 2930          lb_config[n_lb_config].config_param = NULL;
 2931          strcpy(lb_config[n_lb_config].section_name, str);
 2932 
 2933          /* enumerate parameters */
 2934          i = 0;
 2935          p = strchr(p, '\n');
 2936          if (p)
 2937             p++;
 2938          while (p && *p && *p != '[') {
 2939             pstr = str;
 2940             while (*p && *p != '=' && *p != '\n' && *p != '\r' && pstr - str < 10000)
 2941                *pstr++ = *p++;
 2942             *pstr-- = 0;
 2943             while (pstr > str && (*pstr == ' ' || *pstr == '\t' || *pstr == '='))
 2944                *pstr-- = 0;
 2945 
 2946             if (*p == '=') {
 2947 
 2948                if (lb_config[n_lb_config].n_params == 0)
 2949                   lb_config[n_lb_config].config_param = xmalloc(sizeof(CONFIG_PARAM));
 2950                else
 2951                   lb_config[n_lb_config].config_param = xrealloc(lb_config[n_lb_config].config_param,
 2952                                                                  sizeof(CONFIG_PARAM) *
 2953                                                                  (lb_config[n_lb_config].n_params + 1));
 2954                lb_config[n_lb_config].config_param[i].param = xmalloc(strlen(str) + 1);
 2955                lb_config[n_lb_config].config_param[i].uparam = xmalloc(strlen(str) + 1);
 2956 
 2957                strcpy(lb_config[n_lb_config].config_param[i].param, str);
 2958                for (j = 0; j < (int) strlen(str); j++)
 2959                   lb_config[n_lb_config].config_param[i].uparam[j] = toupper(str[j]);
 2960                lb_config[n_lb_config].config_param[i].uparam[j] = 0;
 2961 
 2962                p++;
 2963                while (*p == ' ' || *p == '\t')
 2964                   p++;
 2965                pstr = str;
 2966                while (*p && *p != '\n' && *p != '\r' && pstr - str < 10000)
 2967                   *pstr++ = *p++;
 2968                *pstr-- = 0;
 2969                while (*pstr == ' ' || *pstr == '\t')
 2970                   *pstr-- = 0;
 2971 
 2972                lb_config[n_lb_config].config_param[i].value = xmalloc(strlen(str) + 1);
 2973                strcpy(lb_config[n_lb_config].config_param[i].value, str);
 2974 
 2975                i++;
 2976                lb_config[n_lb_config].n_params = i;
 2977             }
 2978 
 2979             /* search for next line beginning */
 2980             while (*p && *p != '\r' && *p != '\n')
 2981                p++;
 2982             while (*p && (*p == '\r' || *p == '\n'))
 2983                p++;
 2984          }
 2985 
 2986          /* sort parameter */
 2987          // outcommented: not needed, might screw up group ordering
 2988          //qsort(lb_config[n_lb_config].config_param, lb_config[n_lb_config].n_params, sizeof(CONFIG_PARAM),
 2989          //      param_compare);
 2990 
 2991          n_lb_config++;
 2992          index++;
 2993       }
 2994 
 2995       /* search for next line beginning */
 2996       while (*p && *p != '\r' && *p != '\n' && *p != '[')
 2997          p++;
 2998       while (*p && (*p == '\r' || *p == '\n'))
 2999          p++;
 3000 
 3001    } while (*p);
 3002 
 3003    xfree(str);
 3004    xfree(buffer);
 3005    return 0;
 3006 }
 3007 
 3008 /*-------------------------------------------------------------------*/
 3009 
 3010 int getcfg_simple(char *group, char *param, char *value, int vsize, int conditional) {
 3011    int i, j, status;
 3012    char uparam[256];
 3013 
 3014    if (strlen(param) >= sizeof(uparam))
 3015       return 0;
 3016 
 3017    for (i = 0; i < (int) strlen(param); i++)
 3018       uparam[i] = toupper(param[i]);
 3019    uparam[i] = 0;
 3020    value[0] = 0;
 3021 
 3022    for (i = 0; i < n_lb_config; i++)
 3023       if (strieq(lb_config[i].section_name, group))
 3024          break;
 3025 
 3026    if (i == n_lb_config)
 3027       return 0;
 3028 
 3029    for (j = 0; j < lb_config[i].n_params; j++)
 3030       if (match_param(lb_config[i].config_param[j].uparam, uparam, conditional)) {
 3031          status = strchr(lb_config[i].config_param[j].uparam, '{') ? 2 : 1;
 3032          strlcpy(value, lb_config[i].config_param[j].value, vsize);
 3033          return status;
 3034       }
 3035 
 3036    return 0;
 3037 }
 3038 
 3039 /*-------------------------------------------------------------------*/
 3040 
 3041 int enumgrp(int index, char *group) {
 3042    if (index < n_lb_config) {
 3043       strcpy(group, lb_config[index].section_name);
 3044       return 1;
 3045    }
 3046 
 3047    return 0;
 3048 }
 3049 
 3050 /*-------------------------------------------------------------------*/
 3051 
 3052 int getcfg(char *group, char *param, char *value, int vsize)
 3053 /*
 3054  Read parameter from configuration file.
 3055 
 3056  - if group == [global] and top group exists, read
 3057  from [global <top group>]
 3058 
 3059  - if parameter not in [global <top group>], read from [global]
 3060 
 3061  - if group is logbook, read from logbook section
 3062 
 3063  - if parameter not in [<logbook>], read from [global <top group>]
 3064  or [global]
 3065  */
 3066 {
 3067    char str[256];
 3068    int status;
 3069 
 3070    /* if group is [global] and top group exists, read from there */
 3071    if (strieq(group, "global") && getcfg_topgroup()) {
 3072       sprintf(str, "global %s", getcfg_topgroup());
 3073       status = getcfg(str, param, value, vsize);
 3074       if (status)
 3075          return status;
 3076    }
 3077 
 3078    /* first check if parameter is under condition */
 3079    if (_condition[0]) {
 3080       status = getcfg_simple(group, param, value, vsize, TRUE);
 3081       if (status)
 3082          return status;
 3083    }
 3084 
 3085    status = getcfg_simple(group, param, value, vsize, FALSE);
 3086    if (status)
 3087       return status;
 3088 
 3089    /* if parameter not found in logbook, look in [global] section */
 3090    if (!group || is_logbook(group))
 3091       return getcfg("global", param, value, vsize);
 3092 
 3093    return 0;
 3094 }
 3095 
 3096 /*-------------------------------------------------------------------*/
 3097 
 3098 char *find_param(char *buf, char *group, char *param) {
 3099    char *str, *p, *pstr, *pstart;
 3100 
 3101    /* search group */
 3102    str = xmalloc(10000);
 3103    p = buf;
 3104    do {
 3105       if (*p == '[') {
 3106          p++;
 3107          pstr = str;
 3108          while (*p && *p != ']' && *p != '\n')
 3109             *pstr++ = *p++;
 3110          *pstr = 0;
 3111          if (strieq(str, group)) {
 3112             /* search parameter */
 3113             p = strchr(p, '\n');
 3114             if (p)
 3115                p++;
 3116             while (p && *p && *p != '[') {
 3117                pstr = str;
 3118                pstart = p;
 3119                while (*p && *p != '=' && *p != '\n')
 3120                   *pstr++ = *p++;
 3121                *pstr-- = 0;
 3122                while (pstr > str && (*pstr == ' ' || *pstr == '=' || *pstr == '\t'))
 3123                   *pstr-- = 0;
 3124 
 3125                if (match_param(str, param, FALSE)) {
 3126                   xfree(str);
 3127                   return pstart;
 3128                }
 3129 
 3130                if (p)
 3131                   p = strchr(p, '\n');
 3132                if (p)
 3133                   p++;
 3134             }
 3135          }
 3136       }
 3137       if (p)
 3138          p = strchr(p, '\n');
 3139       if (p)
 3140          p++;
 3141    } while (p);
 3142 
 3143    xfree(str);
 3144 
 3145    /* now search if in [global] section */
 3146    if (!strieq(group, "global"))
 3147       return find_param(buf, "global", param);
 3148 
 3149    return NULL;
 3150 }
 3151 
 3152 /*-------------------------------------------------------------------*/
 3153 
 3154 int is_group(char *group) {
 3155    int i;
 3156 
 3157    for (i = 0; i < n_lb_config; i++)
 3158       if (strieq(group, lb_config[i].section_name))
 3159          return 1;
 3160    return 0;
 3161 }
 3162 
 3163 /*------------------------------------------------------------------*/
 3164 
 3165 int enumcfg(char *group, char *param, int psize, char *value, int vsize, int index) {
 3166    int i;
 3167 
 3168    for (i = 0; i < n_lb_config; i++)
 3169       if (strieq(group, lb_config[i].section_name)) {
 3170 
 3171          if (index < lb_config[i].n_params) {
 3172             strlcpy(param, lb_config[i].config_param[index].param, psize);
 3173             strlcpy(value, lb_config[i].config_param[index].value, vsize);
 3174             return 1;
 3175          }
 3176 
 3177          return 0;
 3178 
 3179       }
 3180 
 3181    return 0;
 3182 }
 3183 
 3184 /*-------------------------------------------------------------------*/
 3185 
 3186 int exist_top_group() {
 3187    int i;
 3188    char str[256];
 3189 
 3190    for (i = 0;; i++) {
 3191       if (!enumcfg("global", str, sizeof(str), NULL, 0, i))
 3192          break;
 3193       str[9] = 0;
 3194       if (strieq(str, "top group"))
 3195          return 1;
 3196    }
 3197 
 3198    return 0;
 3199 }
 3200 
 3201 /*-------------------------------------------------------------------*/
 3202 
 3203 char *_locbuffer = NULL;
 3204 char **_porig, **_ptrans;
 3205 time_t _locfile_mtime = 0;
 3206 
 3207 /* check if language file changed and if so reload it */
 3208 int check_language() {
 3209    char language[256], file_name[256], *p;
 3210    int fh, length, n;
 3211    struct stat cfg_stat;
 3212 
 3213    getcfg("global", "Language", language, sizeof(language));
 3214 
 3215    /* set locale for strftime */
 3216    if (language[0])
 3217       setlocale(LC_ALL, language);
 3218    else
 3219       setlocale(LC_ALL, "english");
 3220 
 3221    /* force re-read configuration file if changed */
 3222    strlcpy(file_name, resource_dir, sizeof(file_name));
 3223    strlcat(file_name, "resources", sizeof(file_name));
 3224    strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
 3225    strlcat(file_name, "eloglang.", sizeof(file_name));
 3226    strlcat(file_name, language, sizeof(file_name));
 3227 
 3228    if (stat(file_name, &cfg_stat) == 0) {
 3229       if (_locfile_mtime != cfg_stat.st_mtime) {
 3230          _locfile_mtime = cfg_stat.st_mtime;
 3231 
 3232          if (_locbuffer) {
 3233             xfree(_locbuffer);
 3234             _locbuffer = NULL;
 3235          }
 3236       }
 3237    }
 3238 
 3239    if (strieq(language, "english") || language[0] == 0) {
 3240       if (_locbuffer) {
 3241          xfree(_locbuffer);
 3242          _locbuffer = NULL;
 3243       }
 3244    } else {
 3245       if (_locbuffer == NULL) {
 3246          fh = open(file_name, O_RDONLY | O_BINARY);
 3247          if (fh < 0)
 3248             return -1;
 3249 
 3250          length = lseek(fh, 0, SEEK_END);
 3251          lseek(fh, 0, SEEK_SET);
 3252          _locbuffer = xmalloc(length + 1);
 3253          read(fh, _locbuffer, length);
 3254          _locbuffer[length] = 0;
 3255          close(fh);
 3256 
 3257          /* scan lines, setup orig-translated pointers */
 3258          p = _locbuffer;
 3259          n = 0;
 3260          do {
 3261             while (*p && (*p == '\r' || *p == '\n'))
 3262                p++;
 3263 
 3264             if (*p && (*p == ';' || *p == '#' || *p == ' ' || *p == '\t')) {
 3265                while (*p && *p != '\n' && *p != '\r')
 3266                   p++;
 3267                continue;
 3268             }
 3269 
 3270             if (n == 0) {
 3271                _porig = xmalloc(sizeof(char *) * 2);
 3272                _ptrans = xmalloc(sizeof(char *) * 2);
 3273             } else {
 3274                _porig = xrealloc(_porig, sizeof(char *) * (n + 2));
 3275                _ptrans = xrealloc(_ptrans, sizeof(char *) * (n + 2));
 3276             }
 3277 
 3278             _porig[n] = p;
 3279             while (*p && (*p != '=' && *p != '\r' && *p != '\n'))
 3280                p++;
 3281 
 3282             if (*p && *p != '=')
 3283                continue;
 3284 
 3285             _ptrans[n] = p + 1;
 3286             while (*_ptrans[n] == ' ' || *_ptrans[n] == '\t')
 3287                _ptrans[n]++;
 3288 
 3289             /* remove '=' and surrounding blanks */
 3290             while (*p == '=' || *p == ' ' || *p == '\t')
 3291                *p-- = 0;
 3292 
 3293             p = _ptrans[n];
 3294             while (*p && *p != '\n' && *p != '\r')
 3295                p++;
 3296 
 3297             if (p)
 3298                *p++ = 0;
 3299 
 3300             n++;
 3301          } while (p && *p);
 3302 
 3303          _porig[n] = NULL;
 3304          _ptrans[n] = NULL;
 3305       }
 3306    }
 3307 
 3308    return 0;
 3309 }
 3310 
 3311 /*-------------------------------------------------------------------*/
 3312 
 3313 /* localization support */
 3314 char *loc(char *orig) {
 3315    int n;
 3316    char language[256];
 3317    static char result[256];
 3318 
 3319    if (!_locbuffer)
 3320       return orig;
 3321 
 3322    /* search string and return translation */
 3323    for (n = 0; _porig[n]; n++)
 3324       if (strcmp(orig, _porig[n]) == 0) {
 3325          if (*_ptrans[n])
 3326             return _ptrans[n];
 3327          return orig;
 3328       }
 3329 
 3330    /* special case: "Change %s" */
 3331    if (strstr(orig, "Change ") && strcmp(orig, "Change %s") != 0) {
 3332       sprintf(result, loc("Change %s"), orig + 7);
 3333       return result;
 3334    }
 3335 
 3336    /* special case: some intrinsic commands */
 3337    if (strstr(orig, "GetPwdFile")) {
 3338       strcpy(result, orig);
 3339       return result;
 3340    }
 3341 
 3342    getcfg("global", "Language", language, sizeof(language));
 3343    eprintf("Language error: string \"%s\" not found for language \"%s\"\n", orig, language);
 3344 
 3345    return orig;
 3346 }
 3347 
 3348 /*-------------------------------------------------------------------*/
 3349 
 3350 char *month_name(int m)
 3351 /* return name of month in current locale, m=0..11 */
 3352 {
 3353    struct tm ts;
 3354    static char name[32];
 3355 
 3356    memset(&ts, 0, sizeof(ts));
 3357    ts.tm_mon = m;
 3358    ts.tm_mday = 15;
 3359    ts.tm_year = 2000;
 3360 
 3361    mktime(&ts);
 3362    strftime(name, sizeof(name), "%B", &ts);
 3363    return name;
 3364 }
 3365 
 3366 /*-------------------------------------------------------------------*/
 3367 
 3368 time_t date_to_ltime(char *date) {
 3369    struct tm tms;
 3370    int i, date_zone, local_zone;
 3371    time_t ltime;
 3372 
 3373    memset(&tms, 0, sizeof(struct tm));
 3374 
 3375    if (strlen(date) > 25) {
 3376 
 3377       /* RFC2822 compliant date */
 3378       for (i = 0; i < 12; i++)
 3379          if (strncmp(date + 8, mname[i], 3) == 0)
 3380             break;
 3381       tms.tm_mon = i;
 3382 
 3383       tms.tm_mday = atoi(date + 5);
 3384       tms.tm_hour = atoi(date + 17);
 3385       tms.tm_min = atoi(date + 20);
 3386       tms.tm_sec = atoi(date + 23);
 3387       tms.tm_year = atoi(date + 12) - 1900;
 3388       tms.tm_isdst = -1;
 3389 
 3390       if (tms.tm_year < 90)
 3391          tms.tm_year += 100;
 3392 
 3393       ltime = mktime(&tms);
 3394 
 3395       /* correct for difference between local time zone (used by mktime) and time zone of date */
 3396       date_zone = atoi(date + 26);
 3397 
 3398       /* correct for incorrect date_zone */
 3399       if (date_zone > 2400 || date_zone < -2400)
 3400          date_zone = 0;
 3401       date_zone = (abs(date_zone) % 100) * 60 + (date_zone) / 100 * 3600;
 3402 
 3403       local_zone = my_timezone();
 3404       if (tms.tm_isdst)
 3405          local_zone -= 3600;
 3406 
 3407       ltime = ltime - local_zone - date_zone;
 3408 
 3409    } else {
 3410 
 3411       /* ctime() complient date */
 3412       for (i = 0; i < 12; i++)
 3413          if (strncmp(date + 4, mname[i], 3) == 0)
 3414             break;
 3415       tms.tm_mon = i;
 3416 
 3417       tms.tm_mday = atoi(date + 8);
 3418       tms.tm_hour = atoi(date + 11);
 3419       tms.tm_min = atoi(date + 14);
 3420       tms.tm_sec = atoi(date + 17);
 3421       tms.tm_year = atoi(date + 20) - 1900;
 3422       tms.tm_isdst = -1;
 3423 
 3424       if (tms.tm_year < 90)
 3425          tms.tm_year += 100;
 3426 
 3427       ltime = mktime(&tms);
 3428    }
 3429 
 3430    return ltime;
 3431 }
 3432 
 3433 /*-------------------------------------------------------------------*/
 3434 
 3435 void check_config() {
 3436    check_config_file(FALSE);
 3437    check_language();
 3438 }
 3439 
 3440 /*-------------------------------------------------------------------*/
 3441 
 3442 void retrieve_domain(char *ret, int size) {
 3443    char smtp_host[80];
 3444 
 3445    strlcpy(ret, "tmp.org", size);
 3446    if (getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) {
 3447       if (strchr(smtp_host, '.'))
 3448          strlcpy(ret, strchr(smtp_host, '.') + 1, size);
 3449    }
 3450 }
 3451 
 3452 /*-------------------------------------------------------------------*/
 3453 
 3454 void retrieve_email_from(LOGBOOK *lbs, char *ret, char *ret_name, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
 3455    char email_from[256], email_from_name[256], str[256], *p, login_name[256],
 3456            slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH],
 3457            full_name[256], user_email[256];
 3458    int i;
 3459 
 3460    if (getcfg(lbs->name, "Use Email from", str, sizeof(str))) {
 3461       if (str[0] != '<') {
 3462          strlcpy(email_from, "<", sizeof(email_from));
 3463          strlcat(email_from, str, sizeof(email_from));
 3464          strlcat(email_from, ">", sizeof(email_from));
 3465       } else
 3466          strlcpy(email_from, str, sizeof(email_from));
 3467       strlcpy(email_from_name, str, sizeof(email_from));
 3468    } else if (isparam("unm")) {
 3469       get_user_line(lbs, getparam("unm"), NULL, full_name, user_email, NULL, NULL, NULL);
 3470       strlcpy(email_from_name, full_name, sizeof(email_from_name));
 3471       strlcat(email_from_name, " <", sizeof(email_from_name));
 3472       strlcat(email_from_name, user_email, sizeof(email_from_name));
 3473       strlcat(email_from_name, ">", sizeof(email_from_name));
 3474       strlcpy(email_from, "<", sizeof(email_from));
 3475       strlcat(email_from, user_email, sizeof(email_from));
 3476       strlcat(email_from, ">", sizeof(email_from));
 3477    } else if (getcfg(lbs->name, "Default Email from", str, sizeof(str))) {
 3478       if (str[0] != '<') {
 3479          strlcpy(email_from, "<", sizeof(email_from));
 3480          strlcat(email_from, str, sizeof(email_from));
 3481          strlcat(email_from, ">", sizeof(email_from));
 3482       } else
 3483          strlcpy(email_from, str, sizeof(email_from));
 3484       strlcpy(email_from_name, str, sizeof(email_from));
 3485    } else {
 3486       sprintf(email_from_name, "ELog <ELog@%s>", host_name);
 3487       sprintf(email_from, "<ELog@%s>", host_name);
 3488    }
 3489 
 3490    if (attrib) {
 3491       i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
 3492       strsubst_list(email_from_name, sizeof(email_from_name), slist, svalue, i);
 3493       strsubst_list(email_from, sizeof(email_from), slist, svalue, i);
 3494 
 3495       /* remove possible 'mailto:' */
 3496       if ((p = strstr(email_from_name, "mailto:")) != NULL)
 3497          memmove(p, p + 7, strlen(p + 7) + 1);
 3498       if ((p = strstr(email_from, "mailto:")) != NULL)
 3499          memmove(p, p + 7, strlen(p + 7) + 1);
 3500    }
 3501 
 3502    /* if nothing available, figure out email from an administrator */
 3503    if (strchr(email_from, '@') == NULL) {
 3504       for (i = 0;; i++) {
 3505          if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
 3506             break;
 3507          get_user_line(lbs, login_name, NULL, NULL, email_from, NULL, NULL, NULL);
 3508          sprintf(email_from_name, "%s <%s>", login_name, email_from);
 3509          if (is_admin_user(lbs, login_name) && strchr(email_from, '@'))
 3510             break;
 3511       }
 3512    }
 3513 
 3514    if (ret)
 3515       strcpy(ret, email_from);
 3516    if (ret_name)
 3517       strcpy(ret_name, email_from_name);
 3518 }
 3519 
 3520 /*------------------------------------------------------------------*/
 3521 
 3522 void el_decode(char *message, char *key, char *result, int size) {
 3523    char *pc, *ph;
 3524    int i;
 3525 
 3526    if (result == NULL)
 3527       return;
 3528 
 3529    *result = 0;
 3530 
 3531    ph = strstr(message, "========================================");
 3532    if (ph == NULL)
 3533       return;
 3534 
 3535    do {
 3536       if (ph[40] == '\r' || ph[40] == '\n')
 3537          break;
 3538       ph = strstr(ph + 40, "========================================");
 3539       if (ph == NULL)
 3540          return;
 3541 
 3542    } while (1);
 3543 
 3544    /* go through all lines */
 3545    for (pc = message; pc < ph;) {
 3546 
 3547       if (strncmp(pc, key, strlen(key)) == 0) {
 3548          pc += strlen(key);
 3549          for (i = 0; *pc != '\n' && *pc != '\r' && i < size - 1; i++)
 3550             result[i] = *pc++;
 3551          result[i] = 0;
 3552          return;
 3553       }
 3554 
 3555       pc = strchr(pc, '\n');
 3556       if (pc == NULL)
 3557          return;
 3558       while (*pc && (*pc == '\n' || *pc == '\r'))
 3559          pc++;
 3560    }
 3561 }
 3562 
 3563 /*------------------------------------------------------------------*/
 3564 
 3565 void el_decode_int(char *message, char *key, char *result, int size) {
 3566    char str[80];
 3567 
 3568    if (result == NULL)
 3569       return;
 3570 
 3571    *result = 0;
 3572    el_decode(message, key, str, sizeof(str));
 3573    if (str[0])
 3574       sprintf(str, "%d", atoi(str));
 3575    strlcpy(result, str, size);
 3576 }
 3577 
 3578 /*------------------------------------------------------------------*/
 3579 
 3580 void el_decode_intlist(char *message, char *key, char *result, int size) {
 3581    int i;
 3582 
 3583    if (result == NULL)
 3584       return;
 3585 
 3586    *result = 0;
 3587    el_decode(message, key, result, size);
 3588 
 3589    /* remove any non allowed characters */
 3590    for (i = 0; i < size && i < (int) strlen(result); i++)
 3591       if (!isdigit(result[i]) && result[i] != ' ' && result[i] != ',')
 3592          result[i] = ' ';
 3593 }
 3594 
 3595 /*------------------------------------------------------------------*/
 3596 
 3597 void el_enum_attr(char *message, int n, char *attr_name, char *attr_value) {
 3598    char *p, str[NAME_LENGTH], tmp[NAME_LENGTH];
 3599    int i;
 3600 
 3601    p = message;
 3602    for (i = 0; i <= n; i++) {
 3603       strlcpy(str, p, sizeof(str));
 3604       if (strchr(str, '\n'))
 3605          *strchr(str, '\n') = 0;
 3606       if (strchr(str, '\r'))
 3607          *strchr(str, '\r') = 0;
 3608 
 3609       if (strcmp(str, "========================================") == 0)
 3610          break;
 3611 
 3612       p = strchr(p, '\n');
 3613       if (!p) {
 3614          str[0] = 0;            /* not a valid line */
 3615          break;
 3616       }
 3617       while (*p == '\n' || *p == '\r')
 3618          p++;
 3619 
 3620       if (strchr(str, ':')) {
 3621          strcpy(tmp, str);
 3622          *strchr(tmp, ':') = 0;
 3623 
 3624          if (strieq(tmp, "$@MID@$") || strieq(tmp, "Date") || strieq(tmp, "Attachment") || strieq(tmp,
 3625                                                                                                   "Reply To")
 3626              || strieq(tmp, "In Reply To") || strieq(tmp, "Encoding") || strieq(tmp, "Locked by"))
 3627             i--;
 3628       }
 3629    }
 3630 
 3631    attr_name[0] = 0;
 3632    attr_value[0] = 0;
 3633    if (strchr(str, ':')) {
 3634       strlcpy(attr_name, str, NAME_LENGTH);
 3635       *strchr(attr_name, ':') = 0;
 3636       strlcpy(attr_value, strchr(str, ':') + 2, NAME_LENGTH);
 3637    }
 3638 
 3639 }
 3640 
 3641 /*------------------------------------------------------------------*/
 3642 
 3643 /* Simplified copy of fnmatch() for Cygwin where fnmatch is not defined */
 3644 
 3645 #define EOS '\0'
 3646 
 3647 int fnmatch1(const char *pattern, const char *string) {
 3648    char c, test;
 3649 
 3650    for (;;)
 3651       switch (c = *pattern++) {
 3652          case EOS:
 3653             return (*string == EOS ? 0 : 1);
 3654          case '?':
 3655             if (*string == EOS)
 3656                return (1);
 3657             ++string;
 3658             break;
 3659          case '*':
 3660             c = *pattern;
 3661             /* Collapse multiple stars. */
 3662             while (c == '*')
 3663                c = *++pattern;
 3664 
 3665             /* Optimize for pattern with * at end or before /. */
 3666             if (c == EOS)
 3667                return (0);
 3668 
 3669             /* General case, use recursion. */
 3670             while ((test = *string) != EOS) {
 3671                if (!fnmatch1(pattern, string))
 3672                   return (0);
 3673                ++string;
 3674             }
 3675             return (1);
 3676             /* FALLTHROUGH */
 3677          default:
 3678             if (c != *string)
 3679                return (1);
 3680             string++;
 3681             break;
 3682       }
 3683 }
 3684 
 3685 /*------------------------------------------------------------------*/
 3686 
 3687 int ss_file_find(const char *path, char *pattern, char **plist)
 3688 /********************************************************************
 3689  Routine: ss_file_find
 3690 
 3691  Purpose: Return list of files matching 'pattern' from the 'path' location
 3692 
 3693  Input:
 3694  char  *path             Name of a file in file system to check
 3695  char  *pattern          pattern string (wildcard allowed)
 3696 
 3697  Output:
 3698  char  **plist           pointer to the file list
 3699 
 3700  Function value:
 3701  int                     Number of files matching request
 3702 
 3703  \********************************************************************/
 3704 {
 3705 #ifdef OS_UNIX
 3706    DIR *dir_pointer;
 3707    struct dirent *dp;
 3708    int i;
 3709 
 3710    if ((dir_pointer = opendir(path)) == NULL)
 3711       return 0;
 3712    *plist = (char *) xmalloc(MAX_PATH_LENGTH);
 3713    i = 0;
 3714    for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) {
 3715       if (fnmatch1(pattern, dp->d_name) == 0) {
 3716          *plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH);
 3717          strncpy(*plist + (i * MAX_PATH_LENGTH), dp->d_name, strlen(dp->d_name));
 3718          *(*plist + (i * MAX_PATH_LENGTH) + strlen(dp->d_name)) = '\0';
 3719          i++;
 3720       }
 3721    }
 3722    closedir(dir_pointer);
 3723    return i;
 3724 #endif
 3725 
 3726 #ifdef OS_WINNT
 3727                                                                                                                            HANDLE pffile;
 3728    LPWIN32_FIND_DATA lpfdata;
 3729    char str[255];
 3730    int i, first;
 3731 
 3732    strlcpy(str, path, sizeof(str));
 3733    strlcat(str, "\\", sizeof(str));
 3734    strlcat(str, pattern, sizeof(str));
 3735    first = 1;
 3736    i = 0;
 3737    lpfdata = xmalloc(sizeof(WIN32_FIND_DATA));
 3738    *plist = (char *) xmalloc(MAX_PATH_LENGTH);
 3739    pffile = FindFirstFile(str, lpfdata);
 3740    if (pffile == INVALID_HANDLE_VALUE)
 3741       return 0;
 3742    first = 0;
 3743    *plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH);
 3744    strncpy(*plist + (i * MAX_PATH_LENGTH), lpfdata->cFileName, strlen(lpfdata->cFileName));
 3745    *(*plist + (i * MAX_PATH_LENGTH) + strlen(lpfdata->cFileName)) = '\0';
 3746    i++;
 3747    while (FindNextFile(pffile, lpfdata)) {
 3748       *plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH);
 3749       strncpy(*plist + (i * MAX_PATH_LENGTH), lpfdata->cFileName, strlen(lpfdata->cFileName));
 3750       *(*plist + (i * MAX_PATH_LENGTH) + strlen(lpfdata->cFileName)) = '\0';
 3751       i++;
 3752    }
 3753    xfree(lpfdata);
 3754    return i;
 3755 #endif
 3756 }
 3757 
 3758 /*------------------------------------------------------------------*/
 3759 
 3760 int eli_compare(const void *e1, const void *e2) {
 3761 
 3762    if (((EL_INDEX *) e1)->file_time < ((EL_INDEX *) e2)->file_time)
 3763       return -1;
 3764    if (((EL_INDEX *) e1)->file_time >= ((EL_INDEX *) e2)->file_time)
 3765       return 1;
 3766    return 0;
 3767 }
 3768 
 3769 /*------------------------------------------------------------------*/
 3770 
 3771 void generate_subdir_name(char *file_name, char *subdir, int size) {
 3772    char fn[MAX_PATH_LENGTH], path[MAX_PATH_LENGTH];
 3773    int year;
 3774 
 3775    // extract path from file_name
 3776    strlcpy(path, file_name, size);
 3777    if (strrchr(path, DIR_SEPARATOR))
 3778       *(strrchr(path, DIR_SEPARATOR) + 1) = 0;
 3779 
 3780    // extract file name
 3781    if (strrchr(file_name, DIR_SEPARATOR))
 3782       strlcpy(fn, strrchr(file_name, DIR_SEPARATOR) + 1, sizeof(fn));
 3783    else
 3784       strlcpy(fn, file_name, sizeof(fn));
 3785 
 3786    // create subdir from name
 3787    year = (fn[0] - '0') * 10 + (fn[1] - '0');
 3788    // month = (fn[2]-'0')*10+(fn[3]-'0');
 3789    if (year < 80)
 3790       sprintf(subdir, "20%02d", year);
 3791    else
 3792       sprintf(subdir, "19%02d", year);
 3793 
 3794    strlcat(subdir, DIR_SEPARATOR_STR, size);
 3795 }
 3796 
 3797 /*------------------------------------------------------------------*/
 3798 
 3799 int restructure_dir(char *dir) {
 3800    char *file_list;
 3801    int n1, n2, index, status;
 3802    char old_path[MAX_PATH_LENGTH], new_path[MAX_PATH_LENGTH],
 3803            subdir[MAX_PATH_LENGTH];
 3804    static int first = TRUE;
 3805 
 3806    /* go through all entry files */
 3807    n1 = ss_file_find(dir, "??????a.log", &file_list);
 3808    for (index = 0; index < n1; index++) {
 3809       generate_subdir_name(file_list + index * MAX_PATH_LENGTH, subdir, sizeof(subdir));
 3810 
 3811       // create new subdir
 3812       strlcpy(new_path, dir, MAX_PATH_LENGTH);
 3813       strlcat(new_path, subdir, MAX_PATH_LENGTH);
 3814 
 3815 #ifdef OS_WINNT
 3816       status = mkdir(new_path);
 3817 #else
 3818       status = mkdir(new_path, 0755);
 3819 #endif
 3820 
 3821       if (status == 0) {
 3822          if (first) {
 3823             eprintf("\nFound old directory structure. Creating subdirectories and moving files...\n");
 3824             first = FALSE;
 3825          }
 3826          eprintf("Created directory \"%s\"\n", new_path);
 3827       } else {
 3828          if (errno != EEXIST) {
 3829             eprintf("generate_subdir_name: %s\n", strerror(errno));
 3830             eprintf("Cannot create directory \"%s\"\n", new_path);
 3831          }
 3832       }
 3833 
 3834       strlcpy(old_path, dir, sizeof(old_path));
 3835       strlcat(old_path, file_list + index * MAX_PATH_LENGTH, sizeof(old_path));
 3836       strlcpy(new_path, dir, sizeof(new_path));
 3837       strlcat(new_path, subdir, sizeof(new_path));
 3838       strlcat(new_path, file_list + index * MAX_PATH_LENGTH, sizeof(new_path));
 3839 
 3840       rename(old_path, new_path);
 3841    }
 3842    if (file_list)
 3843       xfree(file_list);
 3844 
 3845    /* go through all attachment files */
 3846    n2 = ss_file_find(dir, "??????_??????_*", &file_list);
 3847    for (index = 0; index < n2; index++) {
 3848       generate_subdir_name(file_list + index * MAX_PATH_LENGTH, subdir, sizeof(subdir));
 3849 
 3850       // create new subdir
 3851       strlcpy(new_path, dir, MAX_PATH_LENGTH);
 3852       strlcat(new_path, subdir, MAX_PATH_LENGTH);
 3853 
 3854 #ifdef OS_WINNT
 3855       status = mkdir(new_path);
 3856 #else
 3857       status = mkdir(new_path, 0755);
 3858 #endif
 3859 
 3860       strlcpy(old_path, dir, sizeof(old_path));
 3861       strlcat(old_path, file_list + index * MAX_PATH_LENGTH, sizeof(old_path));
 3862       strlcpy(new_path, dir, sizeof(new_path));
 3863       strlcat(new_path, subdir, sizeof(new_path));
 3864       strlcat(new_path, file_list + index * MAX_PATH_LENGTH, sizeof(new_path));
 3865 
 3866       rename(old_path, new_path);
 3867    }
 3868    if (file_list)
 3869       xfree(file_list);
 3870 
 3871    return n1 + n2;
 3872 }
 3873 
 3874 /*------------------------------------------------------------------*/
 3875 
 3876 int parse_file(LOGBOOK *lbs, char *file_name) {
 3877    char str[256], date[256], *buffer, *p, *pn, in_reply_to[80];
 3878    int length, i, fh, len;
 3879 
 3880    fh = open(file_name, O_RDONLY | O_BINARY, 0644);
 3881 
 3882    if (fh < 0) {
 3883       sprintf(str, "Cannot open file \"%s\"", file_name);
 3884       eprintf("%s; %s\n", str, strerror(errno));
 3885       return EL_FILE_ERROR;
 3886    }
 3887 
 3888    /* read file into buffer */
 3889    length = lseek(fh, 0, SEEK_END);
 3890 
 3891    if (length > 0) {
 3892       buffer = xmalloc(length + 1);
 3893       lseek(fh, 0, SEEK_SET);
 3894       read(fh, buffer, length);
 3895       buffer[length] = 0;
 3896       close(fh);
 3897 
 3898       /* go through buffer */
 3899       p = buffer;
 3900 
 3901       do {
 3902          p = strstr(p, "$@MID@$:");
 3903 
 3904          if (p) {
 3905             lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index + 1));
 3906             if (lbs->el_index == NULL) {
 3907                eprintf("Not enough memory to allocate entry index\n");
 3908                return EL_MEM_ERROR;
 3909             }
 3910 
 3911             strlcpy(lbs->el_index[*lbs->n_el_index].subdir, file_name + strlen(lbs->data_dir), 256);
 3912             if (strrchr(lbs->el_index[*lbs->n_el_index].subdir, DIR_SEPARATOR))
 3913                *(strrchr(lbs->el_index[*lbs->n_el_index].subdir, DIR_SEPARATOR) + 1) = 0;
 3914 
 3915             if (strrchr(file_name, DIR_SEPARATOR))
 3916                strlcpy(str, strrchr(file_name, DIR_SEPARATOR) + 1, sizeof(str));
 3917             else
 3918                strlcpy(str, file_name, sizeof(str));
 3919             strcpy(lbs->el_index[*lbs->n_el_index].file_name, str);
 3920 
 3921             el_decode(p, "Date: ", date, sizeof(date));
 3922             el_decode_int(p, "In reply to: ", in_reply_to, sizeof(in_reply_to));
 3923 
 3924             lbs->el_index[*lbs->n_el_index].file_time = date_to_ltime(date);
 3925 
 3926             lbs->el_index[*lbs->n_el_index].message_id = atoi(p + 8);
 3927             lbs->el_index[*lbs->n_el_index].offset = p - buffer;
 3928             lbs->el_index[*lbs->n_el_index].in_reply_to = atoi(in_reply_to);
 3929 
 3930             pn = strstr(p + 8, "$@MID@$:");
 3931             if (pn)
 3932                len = pn - p;
 3933             else
 3934                len = strlen(p);
 3935 
 3936             MD5_checksum(p, len, lbs->el_index[*lbs->n_el_index].md5_digest);
 3937 
 3938             if (lbs->el_index[*lbs->n_el_index].message_id > 0) {
 3939                if (get_verbose() == VERBOSE_DEBUG) {
 3940                   eprintf("  ID %3d, %s, ofs %5d, %s, MD5=", lbs->el_index[*lbs->n_el_index].message_id,
 3941                           str, lbs->el_index[*lbs->n_el_index].offset,
 3942                           lbs->el_index[*lbs->n_el_index].in_reply_to ? "reply" : "thead");
 3943 
 3944                   for (i = 0; i < 16; i++)
 3945                      eprintf("%02X", lbs->el_index[*lbs->n_el_index].md5_digest[i]);
 3946                   eprintf("\n");
 3947                }
 3948 
 3949                /* valid ID */
 3950                (*lbs->n_el_index)++;
 3951             }
 3952 
 3953             p += 8;
 3954          }
 3955 
 3956       } while (p);
 3957 
 3958       xfree(buffer);
 3959    }
 3960 
 3961    return SUCCESS;
 3962 }
 3963 
 3964 /*------------------------------------------------------------------*/
 3965 
 3966 int scan_dir_tree(LOGBOOK *lbs, const char *dir, char **file_list, int *n) {
 3967    int index, n_files;
 3968    char str[MAX_PATH_LENGTH];
 3969    char *fl, *p;
 3970 
 3971    fl = NULL;
 3972    n_files = ss_file_find(dir, "*", &fl);
 3973    if (n_files == 0) {
 3974       if (fl)
 3975          xfree(fl);
 3976       return 0;
 3977    }
 3978 
 3979    if (*file_list == NULL)
 3980       *file_list = (char *) xmalloc(n_files * MAX_PATH_LENGTH);
 3981    else
 3982       *file_list = (char *) xrealloc(*file_list, ((*n) + n_files) * MAX_PATH_LENGTH);
 3983 
 3984    /* go through all files */
 3985    for (index = 0; index < n_files; index++) {
 3986       if (fnmatch1("??????a.log", &fl[index * MAX_PATH_LENGTH]) == 0) {
 3987          p = *file_list + ((*n) * MAX_PATH_LENGTH);
 3988          strlcpy(p, dir, MAX_PATH_LENGTH);
 3989          if (p[strlen(p) - 1] != DIR_SEPARATOR)
 3990             strlcat(p, DIR_SEPARATOR_STR, MAX_PATH_LENGTH);
 3991          strlcat(p, fl + index * MAX_PATH_LENGTH, MAX_PATH_LENGTH);
 3992          (*n)++;
 3993       }
 3994    }
 3995 
 3996    /* go through all sub-directories */
 3997    for (index = 0; index < n_files; index++) {
 3998       if (fnmatch1("????", &fl[index * MAX_PATH_LENGTH]) == 0 ||
 3999           fnmatch1("??", &fl[index * MAX_PATH_LENGTH]) == 0) {
 4000          if (strieq(fl + index * MAX_PATH_LENGTH, ".."))
 4001             continue;
 4002          strlcpy(str, dir, sizeof(str));
 4003          if (str[strlen(str) - 1] != DIR_SEPARATOR)
 4004             strlcat(str, DIR_SEPARATOR_STR, sizeof(str));
 4005          strlcat(str, fl + index * MAX_PATH_LENGTH, sizeof(str));
 4006          scan_dir_tree(lbs, str, file_list, n);
 4007       }
 4008    }
 4009 
 4010    if (fl)
 4011       xfree(fl);
 4012 
 4013    return *n;
 4014 }
 4015 
 4016 /*------------------------------------------------------------------*/
 4017 
 4018 int el_build_index(LOGBOOK *lbs, BOOL rebuild)
 4019 /* scan all ??????a.log files and build an index table in eli[] */
 4020 {
 4021    char *file_list, error_str[256], base_dir[256], *buffer;
 4022    int index, n;
 4023    int i, status;
 4024    unsigned char digest[16];
 4025 
 4026    if (rebuild) {
 4027       xfree(lbs->el_index);
 4028       xfree(lbs->n_el_index);
 4029    }
 4030 
 4031    lbs->n_el_index = xmalloc(sizeof(int));
 4032    *lbs->n_el_index = 0;
 4033    lbs->el_index = xmalloc(0);
 4034 
 4035    /* get data directory */
 4036    strcpy(base_dir, lbs->data_dir);
 4037 
 4038    if (get_verbose() >= VERBOSE_DEBUG) {
 4039       /* show MD5 from config file */
 4040 
 4041       load_config_section(lbs->name, &buffer, error_str);
 4042       if (error_str[0])
 4043          eprintf(error_str);
 4044       else {
 4045          remove_crlf(buffer);
 4046          MD5_checksum(buffer, strlen(buffer), digest);
 4047          eprintf("\n\nConfig [%s],                           MD5=", lbs->name);
 4048 
 4049          for (i = 0; i < 16; i++)
 4050             eprintf("%02X", digest[i]);
 4051          eprintf("\n\n");
 4052       }
 4053       if (buffer)
 4054          xfree(buffer);
 4055    }
 4056 
 4057    if (get_verbose() >= VERBOSE_DEBUG)
 4058       eprintf("Entries:\n");
 4059 
 4060    // move files to directories if (new layout to reduce number of files per directory)
 4061    restructure_dir(base_dir);
 4062 
 4063    file_list = NULL;
 4064    n = 0;
 4065    scan_dir_tree(lbs, base_dir, &file_list, &n);
 4066 
 4067    /* go through all files */
 4068    for (index = 0; index < n; index++) {
 4069       status = parse_file(lbs, file_list + index * MAX_PATH_LENGTH);
 4070       if (status != SUCCESS) {
 4071          if (file_list)
 4072             xfree(file_list);
 4073          return status;
 4074       }
 4075    }
 4076 
 4077    if (file_list)
 4078       xfree(file_list);
 4079 
 4080    /* sort entries according to date */
 4081    qsort(lbs->el_index, *lbs->n_el_index, sizeof(EL_INDEX), eli_compare);
 4082 
 4083    if (get_verbose() >= VERBOSE_DEBUG) {
 4084       eprintf("After sort:\n");
 4085       for (i = 0; i < *lbs->n_el_index; i++)
 4086          eprintf("  ID %3d, %s, ofs %5d\n", lbs->el_index[i].message_id, lbs->el_index[i].file_name,
 4087                  lbs->el_index[i].offset);
 4088    }
 4089 
 4090    if (rebuild && n == 0) {
 4091       eprintf("Logbook files seem to have disappeared, aborting program.\n");
 4092       assert(rebuild && n > 0);
 4093    }
 4094 
 4095    return EL_SUCCESS;
 4096 }
 4097 
 4098 /*------------------------------------------------------------------*/
 4099 
 4100 int el_index_logbooks() {
 4101    char str[256], data_dir[256], logbook[256], cwd[256], *p;
 4102    int i, j, n, status = 0;
 4103 
 4104    if (lb_list) {
 4105       for (i = 0; lb_list[i].name[0]; i++) {
 4106          if (lb_list[i].el_index != NULL) {
 4107             xfree(lb_list[i].el_index);
 4108             xfree(lb_list[i].n_el_index);
 4109 
 4110             /* check if other logbook uses same index */
 4111             for (j = i + 1; lb_list[j].name[0]; j++) {
 4112                /* mark that logbook already freed */
 4113                if (lb_list[j].el_index == lb_list[i].el_index)
 4114                   lb_list[j].el_index = NULL;
 4115             }
 4116          }
 4117       }
 4118       xfree(lb_list);
 4119    }
 4120 
 4121    /* count logbooks */
 4122    for (i = n = 0;; i++) {
 4123       if (!enumgrp(i, str))
 4124          break;
 4125 
 4126       if (!is_logbook(str))
 4127          continue;
 4128 
 4129       n++;
 4130    }
 4131 
 4132    lb_list = xcalloc(sizeof(LOGBOOK), n + 1);
 4133    for (i = n = 0;; i++) {
 4134       if (!enumgrp(i, logbook))
 4135          break;
 4136 
 4137       if (!is_logbook(logbook))
 4138          continue;
 4139 
 4140       /* check for duplicate name */
 4141       for (j = 0; j < i && lb_list[j].name[0]; j++)
 4142          if (strieq(lb_list[j].name, logbook)) {
 4143             eprintf("Error in configuration file: Duplicate logbook \"%s\"\n", logbook);
 4144             return EL_DUPLICATE;
 4145          }
 4146 
 4147       /* store logbook in list */
 4148       strcpy(lb_list[n].name, logbook);
 4149       strcpy(lb_list[n].name_enc, logbook);
 4150       url_encode(lb_list[n].name_enc, sizeof(lb_list[n].name_enc));
 4151 
 4152       /* get data dir from configuration file (old method) */
 4153       if (getcfg(logbook, "Data dir", str, sizeof(str))) {
 4154          if (str[0] == DIR_SEPARATOR || str[1] == ':')
 4155             strlcpy(data_dir, str, sizeof(data_dir));
 4156          else {
 4157             strlcpy(data_dir, resource_dir, sizeof(data_dir));
 4158             strlcat(data_dir, str, sizeof(data_dir));
 4159          }
 4160       } else {
 4161          /* use logbook_dir + "Subdir" (new method) */
 4162          strlcpy(data_dir, logbook_dir, sizeof(data_dir));
 4163          if (data_dir[strlen(data_dir) - 1] != DIR_SEPARATOR)
 4164             strlcat(data_dir, DIR_SEPARATOR_STR, sizeof(data_dir));
 4165 
 4166          if (getcfg(logbook, "Subdir", str, sizeof(str))) {
 4167             if (str[0] == DIR_SEPARATOR)
 4168                strlcpy(data_dir, str, sizeof(data_dir));
 4169             else
 4170                strlcat(data_dir, str, sizeof(data_dir));
 4171          } else
 4172             strlcat(data_dir, logbook, sizeof(data_dir));       /* use logbook name as default */
 4173       }
 4174 
 4175       if (data_dir[strlen(data_dir) - 1] != DIR_SEPARATOR)
 4176          strlcat(data_dir, DIR_SEPARATOR_STR, sizeof(data_dir));
 4177 
 4178       /* create data directory if not existing */
 4179       getcwd(cwd, sizeof(cwd));
 4180 
 4181       j = chdir(data_dir);
 4182       if (j < 0) {
 4183          p = data_dir;
 4184          if (*p == DIR_SEPARATOR) {
 4185             chdir(DIR_SEPARATOR_STR);
 4186             p++;
 4187          }
 4188          if (p[1] == ':') {
 4189             strcpy(str, p);
 4190             if (str[2] == DIR_SEPARATOR)
 4191                str[3] = 0;
 4192             else
 4193                str[2] = 0;
 4194 
 4195             chdir(str);
 4196             p += strlen(str);
 4197          }
 4198 
 4199          do {
 4200             if (strchr(p, DIR_SEPARATOR)) {
 4201                strlcpy(str, p, sizeof(str));
 4202                *strchr(str, DIR_SEPARATOR) = 0;
 4203                p = strchr(p, DIR_SEPARATOR) + 1;
 4204             } else {
 4205                strlcpy(str, p, sizeof(str));
 4206                p = NULL;
 4207             }
 4208 
 4209             j = chdir(str);
 4210             if (j < 0) {
 4211 
 4212 #ifdef OS_WINNT
 4213                j = mkdir(str);
 4214 #else
 4215                j = mkdir(str, 0755);
 4216 #endif
 4217 
 4218                if (j == 0) {
 4219                   if (get_verbose() >= VERBOSE_INFO)
 4220                      eprintf("Created directory \"%s\"\n", str);
 4221                } else {
 4222                   eprintf("el_index_logbooks: %s\n", strerror(errno));
 4223                   eprintf("Cannot create directory \"%s\"\n", str);
 4224                }
 4225 
 4226                chdir(str);
 4227             }
 4228 
 4229          } while (p && *p);
 4230       }
 4231 
 4232       chdir(cwd);
 4233       strcpy(lb_list[n].data_dir, data_dir);
 4234       lb_list[n].el_index = NULL;
 4235 
 4236       /* check if other logbook uses the same directory */
 4237       for (j = 0; j < n; j++)
 4238          if (strcmp(lb_list[j].data_dir, lb_list[n].data_dir) == 0) {
 4239             if (get_verbose() >= VERBOSE_INFO)
 4240                eprintf("Logbook \"%s\" uses same directory as logbook \"%s\"\n", logbook, lb_list[j].name);
 4241             lb_list[n].el_index = lb_list[j].el_index;
 4242             lb_list[n].n_el_index = lb_list[j].n_el_index;
 4243             break;
 4244          }
 4245 
 4246       if (j == n) {
 4247          if (get_verbose() >= VERBOSE_INFO)
 4248             eprintf("Indexing logbook \"%s\" in \"%s\" ... ", logbook, lb_list[n].data_dir);
 4249          eflush();
 4250          status = el_build_index(&lb_list[n], FALSE);
 4251          if (get_verbose() >= VERBOSE_INFO)
 4252             if (status == EL_SUCCESS)
 4253                eprintf("ok\n");
 4254       }
 4255 
 4256       if (status == EL_EMPTY) {
 4257          if (get_verbose() >= VERBOSE_INFO)
 4258             eprintf("Found empty logbook \"%s\"\n", logbook);
 4259       } else if (status != EL_SUCCESS) {
 4260          eprintf("Error generating index.\n");
 4261          return status;
 4262       }
 4263 
 4264       n++;
 4265    }
 4266 
 4267    /* if top groups defined, set top group in logbook */
 4268    if (exist_top_group()) {
 4269       LBLIST phier;
 4270 
 4271       phier = get_logbook_hierarchy();
 4272       for (i = 0; i < phier->n_members; i++)
 4273          if (phier->member[i]->is_top)
 4274             for (j = 0; lb_list[j].name[0]; j++)
 4275                if (is_logbook_in_group(phier->member[i], lb_list[j].name))
 4276                   strcpy(lb_list[j].top_group, phier->member[i]->name);
 4277 
 4278       free_logbook_hierarchy(phier);
 4279    }
 4280 
 4281    if (!load_password_files())
 4282       return EL_INVAL_FILE;
 4283 
 4284    return EL_SUCCESS;
 4285 }
 4286 
 4287 /*------------------------------------------------------------------*/
 4288 
 4289 int el_search_message(LOGBOOK *lbs, int mode, int message_id, BOOL head_only)
 4290 /********************************************************************
 4291  Routine: el_search_message
 4292 
 4293  Purpose: Search for a specific message in a logbook
 4294 
 4295  Input:
 4296  int   mode              Search mode, EL_FIRST, EL_LAST, EL_NEXT, EL_PREV
 4297  int   message_id        Message id for EL_NEXT and EL_PREV
 4298 
 4299  Function value:
 4300  int                     New message id
 4301 
 4302  \********************************************************************/
 4303 {
 4304    int i;
 4305 
 4306    if (lbs->n_el_index == 0)
 4307       return 0;
 4308 
 4309    if (mode == EL_FIRST) {
 4310       if (head_only) {
 4311          for (i = 0; i < *lbs->n_el_index; i++)
 4312             if (lbs->el_index[i].in_reply_to == 0)
 4313                return lbs->el_index[i].message_id;
 4314 
 4315          return 0;
 4316       }
 4317       if (*lbs->n_el_index == 0)
 4318          return 0;
 4319       return lbs->el_index[0].message_id;
 4320    }
 4321 
 4322    if (mode == EL_LAST) {
 4323       if (head_only) {
 4324          for (i = *lbs->n_el_index - 1; i >= 0; i--)
 4325             if (lbs->el_index[i].in_reply_to == 0)
 4326                return lbs->el_index[i].message_id;
 4327 
 4328          return 0;
 4329       }
 4330       if (*lbs->n_el_index == 0)
 4331          return 0;
 4332       return lbs->el_index[*lbs->n_el_index - 1].message_id;
 4333    }
 4334 
 4335    if (mode == EL_NEXT) {
 4336       for (i = 0; i < *lbs->n_el_index; i++)
 4337          if (lbs->el_index[i].message_id == message_id)
 4338             break;
 4339 
 4340       if (i == *lbs->n_el_index)
 4341          return 0;              // message not found
 4342 
 4343       if (i == *lbs->n_el_index - 1)
 4344          return 0;              // last message
 4345 
 4346       if (head_only) {
 4347          for (i++; i < *lbs->n_el_index; i++)
 4348             if (lbs->el_index[i].in_reply_to == 0)
 4349                return lbs->el_index[i].message_id;
 4350 
 4351          return 0;
 4352       }
 4353 
 4354       return lbs->el_index[i + 1].message_id;
 4355    }
 4356 
 4357    if (mode == EL_PREV) {
 4358       for (i = 0; i < *lbs->n_el_index; i++)
 4359          if (lbs->el_index[i].message_id == message_id)
 4360             break;
 4361 
 4362       if (i == *lbs->n_el_index)
 4363          return 0;              // message not found
 4364 
 4365       if (i == 0)
 4366          return 0;              // first message
 4367 
 4368       if (head_only) {
 4369          for (i--; i >= 0; i--)
 4370             if (lbs->el_index[i].in_reply_to == 0)
 4371                return lbs->el_index[i].message_id;
 4372 
 4373          return 0;
 4374       }
 4375 
 4376       return lbs->el_index[i - 1].message_id;
 4377    }
 4378 
 4379    return 0;
 4380 }
 4381 
 4382 /*------------------------------------------------------------------*/
 4383 
 4384 int el_retrieve(LOGBOOK *lbs, int message_id, char *date, char attr_list[MAX_N_ATTR][NAME_LENGTH],
 4385                 char attrib[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, int *textsize,
 4386                 char *in_reply_to, char *reply_to, char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH],
 4387                 char *encoding, char *locked_by, char *draft)
 4388 /********************************************************************
 4389  Routine: el_retrieve
 4390 
 4391  Purpose: Retrieve an ELog entry by its message tab
 4392 
 4393  Input:
 4394  LOGBOOK lbs             Logbook structure
 4395  int    message_id       Message ID to retrieve
 4396  int    *size            Size of text buffer
 4397 
 4398  Output:
 4399  char   *tag             tag of retrieved message
 4400  char   *date            Date/time of message recording
 4401  char   attr_list        Names of attributes
 4402  char   attrib           Values of attributes
 4403  int    n_attr           Number of attributes
 4404  char   *text            Message text
 4405  char   *in_reply_to     Original message if this one is a reply
 4406  char   *reply_to        Replies for current message
 4407  char   *attachment[]    File attachments
 4408  char   *encoding        Encoding of message
 4409  char   *locked_by       User/Host if locked for editing
 4410  char   *draft           User who drafted that message
 4411  int    *size            Actual message text size
 4412 
 4413  Function value:
 4414  EL_SUCCESS              Successful completion
 4415  EL_EMPTY                Logbook is empty
 4416  EL_NO_MSG               Message doesn't exist
 4417  EL_FILE_ERROR           Internal error
 4418 
 4419  \********************************************************************/
 4420 {
 4421    int i, index, size, fh;
 4422    char str[NAME_LENGTH], file_name[MAX_PATH_LENGTH * 3], *p;
 4423    char *message, attachment_all[64 * MAX_ATTACHMENTS];
 4424 
 4425    if (message_id == 0)
 4426       /* open most recent message */
 4427       message_id = el_search_message(lbs, EL_LAST, 0, FALSE);
 4428 
 4429    if (message_id == 0)
 4430       return EL_EMPTY;
 4431 
 4432    for (index = 0; index < *lbs->n_el_index; index++)
 4433       if (lbs->el_index[index].message_id == message_id)
 4434          break;
 4435 
 4436    if (index == *lbs->n_el_index)
 4437       return EL_NO_MSG;
 4438 
 4439    snprintf(file_name, sizeof(file_name), "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir,
 4440             lbs->el_index[index].file_name);
 4441    fh = open(file_name, O_RDONLY | O_BINARY, 0644);
 4442    if (fh < 0) {
 4443       /* file might have been deleted, rebuild index */
 4444       el_build_index(lbs, TRUE);
 4445       return el_retrieve(lbs, message_id, date, attr_list, attrib, n_attr, text, textsize, in_reply_to,
 4446                          reply_to, attachment, encoding, locked_by, draft);
 4447    }
 4448 
 4449    message = xmalloc(TEXT_SIZE + 1000);
 4450 
 4451    lseek(fh, lbs->el_index[index].offset, SEEK_SET);
 4452    i = my_read(fh, message, TEXT_SIZE + 1000 - 1);
 4453    if (i <= 0) {
 4454       xfree(message);
 4455       close(fh);
 4456       return EL_FILE_ERROR;
 4457    }
 4458 
 4459    message[i] = 0;
 4460    close(fh);
 4461 
 4462    if (strncmp(message, "$@MID@$:", 8) != 0) {
 4463       xfree(message);
 4464       /* file might have been edited, rebuild index */
 4465       el_build_index(lbs, TRUE);
 4466       return el_retrieve(lbs, message_id, date, attr_list, attrib, n_attr, text, textsize, in_reply_to,
 4467                          reply_to, attachment, encoding, locked_by, draft);
 4468    }
 4469 
 4470    /* check for correct ID */
 4471    if (atoi(message + 8) != message_id) {
 4472       xfree(message);
 4473       return EL_FILE_ERROR;
 4474    }
 4475 
 4476    /* decode message size */
 4477    p = strstr(message + 8, "$@MID@$:");
 4478    if (p == NULL)
 4479       size = strlen(message);
 4480    else
 4481       size = p - message;
 4482 
 4483    message[size] = 0;
 4484 
 4485    /* decode message */
 4486    if (date)
 4487       el_decode(message, "Date: ", date, 80);
 4488    if (reply_to)
 4489       el_decode_intlist(message, "Reply to: ", reply_to, MAX_REPLY_TO * 10);
 4490    if (in_reply_to)
 4491       el_decode_int(message, "In reply to: ", in_reply_to, 80);
 4492 
 4493    if (n_attr == -1) {
 4494       /* derive attribute names from message */
 4495       for (i = 0;; i++) {
 4496          el_enum_attr(message, i, attr_list[i], attrib[i]);
 4497          if (!attr_list[i][0])
 4498             break;
 4499       }
 4500       n_attr = i;
 4501 
 4502    } else {
 4503       if (attrib)
 4504          for (i = 0; i < n_attr; i++) {
 4505             sprintf(str, "%s: ", attr_list[i]);
 4506             el_decode(message, str, attrib[i], NAME_LENGTH);
 4507          }
 4508    }
 4509 
 4510    el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
 4511    if (encoding)
 4512       el_decode(message, "Encoding: ", encoding, 80);
 4513 
 4514    if (attachment) {
 4515       /* break apart attachements */
 4516       for (i = 0; i < MAX_ATTACHMENTS; i++)
 4517          if (attachment[i] != NULL)
 4518             attachment[i][0] = 0;
 4519 
 4520       for (i = 0; i < MAX_ATTACHMENTS; i++) {
 4521          if (attachment[i] != NULL) {
 4522             if (i == 0)
 4523                p = strtok(attachment_all, ",");
 4524             else
 4525                p = strtok(NULL, ",");
 4526 
 4527             if (p != NULL)
 4528                strcpy(attachment[i], p);
 4529             else
 4530                break;
 4531          }
 4532       }
 4533    }
 4534 
 4535    if (locked_by)
 4536       el_decode(message, "Locked by: ", locked_by, 80);
 4537 
 4538    if (draft)
 4539       el_decode(message, "Draft: ", draft, 80);
 4540 
 4541    p = strstr(message, "========================================\n");
 4542 
 4543    /* check for \n -> \r conversion (e.g. zipping/unzipping) */
 4544    if (p == NULL)
 4545       p = strstr(message, "========================================\r");
 4546 
 4547    if (text) {
 4548       if (p != NULL) {
 4549          p += 41;
 4550          if ((int) strlen(p) >= *textsize) {
 4551             strlcpy(text, p, *textsize);
 4552             show_error("Entry too long to display. Please increase TEXT_SIZE and recompile elogd.");
 4553             xfree(message);
 4554             return EL_FILE_ERROR;
 4555          } else {
 4556             strlcpy(text, p, *textsize);
 4557 
 4558             /* strip CR at end */
 4559             if (text[strlen(text) - 1] == '\n') {
 4560                text[strlen(text) - 1] = 0;
 4561                if (text[strlen(text) - 1] == '\r')
 4562                   text[strlen(text) - 1] = 0;
 4563             }
 4564 
 4565             *textsize = strlen(text);
 4566          }
 4567       } else {
 4568          text[0] = 0;
 4569          *textsize = 0;
 4570       }
 4571    }
 4572 
 4573    xfree(message);
 4574    return EL_SUCCESS;
 4575 }
 4576 
 4577 /*------------------------------------------------------------------*/
 4578 
 4579 int el_submit_attachment(LOGBOOK *lbs, const char *afilename, const char *buffer, int buffer_size,
 4580                          char *full_name) {
 4581    char file_name[MAX_PATH_LENGTH], ext_file_name[MAX_PATH_LENGTH + 100], str[MAX_PATH_LENGTH],
 4582       *p, subdir[MAX_PATH_LENGTH], path_name[MAX_PATH_LENGTH];
 4583    int fh;
 4584    time_t now;
 4585    struct tm tms;
 4586 
 4587    /* strip directory, add date and time to filename */
 4588    strlcpy(str, afilename, sizeof(str));
 4589    p = str;
 4590    while (strchr(p, ':'))
 4591       p = strchr(p, ':') + 1;
 4592    while (strchr(p, '\\'))
 4593       p = strchr(p, '\\') + 1;  /* NT */
 4594    while (strchr(p, '/'))
 4595       p = strchr(p, '/') + 1;   /* Unix */
 4596    strlcpy(file_name, p, sizeof(file_name));
 4597 
 4598    /* assemble ELog filename */
 4599    if (file_name[0]) {
 4600 
 4601       if (file_name[6] == '_' && file_name[13] == '_' && isdigit(file_name[0]) && isdigit(file_name[1]))
 4602          strlcpy(ext_file_name, file_name, sizeof(ext_file_name));
 4603       else {
 4604          time(&now);
 4605          memcpy(&tms, localtime(&now), sizeof(struct tm));
 4606 
 4607          sprintf(ext_file_name, "%02d%02d%02d_%02d%02d%02d_%s", tms.tm_year % 100, tms.tm_mon + 1,
 4608                  tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec, file_name);
 4609       }
 4610 
 4611       strlcpy(path_name, lbs->data_dir, sizeof(str));
 4612       generate_subdir_name(ext_file_name, subdir, sizeof(subdir));
 4613       strlcat(path_name, subdir, sizeof(str));
 4614       if (strlen(path_name) > 0 && path_name[strlen(path_name) - 1] == DIR_SEPARATOR)
 4615          path_name[strlen(path_name) - 1] = 0;
 4616 
 4617 #ifdef OS_WINNT
 4618       mkdir(path_name);
 4619 #else
 4620       mkdir(path_name, 0755);
 4621 #endif
 4622 
 4623       strlcat(path_name, DIR_SEPARATOR_STR, sizeof(path_name));
 4624 
 4625 
 4626       /* test if file exists */
 4627       do {
 4628          strlcpy(str, path_name, sizeof(str));
 4629          strlcat(str, ext_file_name, sizeof(path_name));
 4630 
 4631          fh = open(str, O_RDONLY, 0644);
 4632          if (fh > 0) {
 4633             close(fh);
 4634             strlcpy(str, ext_file_name, sizeof(str));
 4635             if (strchr(str, '.')) {
 4636                *strchr(str, '.') = 0;
 4637                strlcat(str, "_1", sizeof(str));
 4638                strlcat(str, strchr(ext_file_name, '.'), sizeof(str));
 4639                strlcpy(ext_file_name, str, sizeof(ext_file_name));
 4640             }
 4641          }
 4642       } while (fh > 0);
 4643 
 4644       if (full_name)
 4645          strlcpy(full_name, ext_file_name, MAX_PATH_LENGTH);
 4646 
 4647       strlcpy(str, path_name, sizeof(str));
 4648       strlcat(str, ext_file_name, sizeof(path_name));
 4649 
 4650       /* save attachment */
 4651       fh = open(str, O_CREAT | O_RDWR | O_BINARY, 0644);
 4652       if (fh < 0) {
 4653          strencode2(file_name, str, sizeof(file_name));
 4654          sprintf(str, "Cannot write attachment file \"%s\"", file_name);
 4655          show_error(str);
 4656          return -1;
 4657       } else {
 4658          write(fh, buffer, buffer_size);
 4659          close(fh);
 4660       }
 4661    }
 4662 
 4663    return 0;
 4664 }
 4665 
 4666 /*------------------------------------------------------------------*/
 4667 
 4668 void el_delete_attachment(LOGBOOK *lbs, char *file_name) {
 4669    int i;
 4670    char str[MAX_PATH_LENGTH], subdir[MAX_PATH_LENGTH];
 4671 
 4672    strlcpy(str, lbs->data_dir, sizeof(str));
 4673    generate_subdir_name(file_name, subdir, sizeof(subdir));
 4674    strlcat(str, subdir, sizeof(str));
 4675    strlcat(str, file_name, sizeof(str));
 4676    remove(str);
 4677    strlcat(str, ".png", sizeof(str));
 4678    remove(str);
 4679    for (i = 0;; i++) {
 4680       strlcpy(str, lbs->data_dir, sizeof(str));
 4681       strlcat(str, subdir, sizeof(str));
 4682       strlcat(str, file_name, sizeof(str));
 4683       sprintf(str + strlen(str), "-%d.png", i);
 4684       if (file_exist(str)) {
 4685          remove(str);
 4686          continue;
 4687       }
 4688 
 4689       strlcpy(str, lbs->data_dir, sizeof(str));
 4690       strlcat(str, subdir, sizeof(str));
 4691       strlcat(str, file_name, sizeof(str));
 4692       if (strrchr(str, '.'))
 4693          *strrchr(str, '.') = 0;
 4694       sprintf(str + strlen(str), "-%d.png", i);
 4695       if (file_exist(str)) {
 4696          remove(str);
 4697          continue;
 4698       }
 4699       break;
 4700    }
 4701 }
 4702 
 4703 /*------------------------------------------------------------------*/
 4704 
 4705 int el_retrieve_attachment(LOGBOOK *lbs, int message_id, int n, char name[MAX_PATH_LENGTH]) {
 4706    int i, index, size, fh;
 4707    char file_name[MAX_PATH_LENGTH * 3], *p;
 4708    char message[TEXT_SIZE + 1000], attachment_all[64 * MAX_ATTACHMENTS];
 4709 
 4710    if (message_id == 0)
 4711       return EL_EMPTY;
 4712 
 4713    for (index = 0; index < *lbs->n_el_index; index++)
 4714       if (lbs->el_index[index].message_id == message_id)
 4715          break;
 4716 
 4717    if (index == *lbs->n_el_index)
 4718       return EL_NO_MSG;
 4719 
 4720    snprintf(file_name, sizeof(file_name), "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir,
 4721             lbs->el_index[index].file_name);
 4722    fh = open(file_name, O_RDONLY | O_BINARY, 0644);
 4723    if (fh < 0) {
 4724       /* file might have been deleted, rebuild index */
 4725       el_build_index(lbs, TRUE);
 4726       return el_retrieve_attachment(lbs, message_id, n, name);
 4727    }
 4728 
 4729    lseek(fh, lbs->el_index[index].offset, SEEK_SET);
 4730    i = my_read(fh, message, sizeof(message) - 1);
 4731    if (i <= 0) {
 4732       close(fh);
 4733       return EL_FILE_ERROR;
 4734    }
 4735 
 4736    message[i] = 0;
 4737    close(fh);
 4738 
 4739    if (strncmp(message, "$@MID@$:", 8) != 0) {
 4740       /* file might have been edited, rebuild index */
 4741       el_build_index(lbs, TRUE);
 4742       return el_retrieve_attachment(lbs, message_id, n, name);
 4743    }
 4744 
 4745    /* check for correct ID */
 4746    if (atoi(message + 8) != message_id)
 4747       return EL_FILE_ERROR;
 4748 
 4749    /* decode message size */
 4750    p = strstr(message + 8, "$@MID@$:");
 4751    if (p == NULL)
 4752       size = strlen(message);
 4753    else
 4754       size = p - message;
 4755 
 4756    message[size] = 0;
 4757 
 4758    el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
 4759 
 4760    name[0] = 0;
 4761 
 4762    for (i = 0; i <= n; i++) {
 4763       if (i == 0)
 4764          p = strtok(attachment_all, ",");
 4765       else
 4766          p = strtok(NULL, ",");
 4767 
 4768       if (p == NULL)
 4769          break;
 4770    }
 4771 
 4772    if (p)
 4773       strlcpy(name, p, MAX_PATH_LENGTH);
 4774 
 4775    return EL_SUCCESS;
 4776 }
 4777 
 4778 /*------------------------------------------------------------------*/
 4779 
 4780 int el_submit(LOGBOOK *lbs, int message_id, BOOL bedit, char *date, char attr_name[MAX_N_ATTR][NAME_LENGTH],
 4781               char attr_value[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, char *in_reply_to,
 4782               char *reply_to, char *encoding, char afilename[MAX_ATTACHMENTS][256], BOOL mark_original,
 4783               char *locked_by, char *draft)
 4784 /********************************************************************
 4785  Routine: el_submit
 4786 
 4787  Purpose: Submit an ELog entry
 4788 
 4789  Input:
 4790  LOGBOOK lbs             Logbook structure
 4791  int    message_id       Message id
 4792  BOOL   bedit            TRUE for existing message, FALSE for new message
 4793  char   *date            Message date
 4794  char   attr_name[][]    Name of attributes
 4795  char   attr_value[][]   Value of attributes
 4796  int    n_attr           Number of attributes
 4797 
 4798  char   *text            Message text
 4799  char   *in_reply_to     In reply to this message
 4800  char   *reply_to        Replie(s) to this message
 4801  char   *encoding        Text encoding, either HTML or plain
 4802 
 4803  char   *afilename[]     File name of attachments
 4804  char   *tag             If given, edit existing message
 4805  int    *tag_size        Maximum size of tag
 4806  BOOL   mark_original    Tag original message for replies
 4807  char   *locked_by       User/Host which locked message for edit