Commit message: Serve SVG files as attachments only to avoid XSS vulnerabilities
CVE: CVE-2019-20376
CWE: CWE-79
Vulnerability description: A cross-site scripting (XSS) vulnerability in Electronic Logbook (ELOG) 3.1.4 allows remote attackers to inject arbitrary web script or HTML via a crafted SVG document to elogd.c.

Project: elog

void show_elog_entry(LOGBOOK *lbs, char *dec_path, char *command) {
   int size, i, j, k, n, n_log, status, fh, length, message_error, index, n_hidden, message_id,
           orig_message_id, format_flags[MAX_N_ATTR], att_hide[MAX_ATTACHMENTS], att_inline[MAX_ATTACHMENTS],
           n_attachments, n_lines, n_disp_attr, attr_index[MAX_N_ATTR], thumb_status, max_n_lines;
   char str[2 * NAME_LENGTH], str2[NAME_LENGTH], ref[256], file_enc[256], attrib[MAX_N_ATTR][NAME_LENGTH];
   char date[80], text[TEXT_SIZE], menu_str[1000], cmd[256], script[256], orig_tag[80],
           reply_tag[MAX_REPLY_TO * 10], display[NAME_LENGTH], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH],
           encoding[80], locked_by[256], att[256], lattr[256], mid[80], menu_item[MAX_N_LIST][NAME_LENGTH],
           format[80], slist[MAX_N_ATTR + 10][NAME_LENGTH], file_name[MAX_PATH_LENGTH],
           gattr[MAX_N_ATTR][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], *p,
           lbk_list[MAX_N_LIST][NAME_LENGTH], comment[256], class_name[80], class_value[80],
           fl[8][NAME_LENGTH], list[MAX_N_ATTR][NAME_LENGTH], domain[256], subdir[256], draft[256];
   FILE *f;
   BOOL first, show_text, display_inline, subtable, email, att_links;
   struct tm *pts;
   struct tm ts;
   struct stat st;
   time_t ltime, entry_ltime;

   message_id = atoi(dec_path);
   message_error = EL_SUCCESS;
   _current_message_id = message_id;
   email = (strstr(command, "email") != NULL);
   att_links = (strstr(command, "att-links") != NULL);

   /* check for custom form to display entry */
   if (getcfg(lbs->name, "Custom display form", str, sizeof(str))) {
      /* check if file starts with an absolute directory */
      if (str[0] == DIR_SEPARATOR || str[1] == ':')
         strcpy(file_name, str);
      else {
         strlcpy(file_name, logbook_dir, sizeof(file_name));
         strlcat(file_name, str, sizeof(file_name));
      }
      send_file_direct(str);
      return;
   }

   /* check for guest access */
   if (!getcfg(lbs->name, "Guest Menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs))
      getcfg(lbs->name, "Menu commands", menu_str, sizeof(menu_str));

   /* default menu commands */
   if (menu_str[0] == 0) {
      strcpy(menu_str, "List, New, Edit, Delete, Reply, Duplicate, Find, ");

      if (getcfg(lbs->name, "Password file", str, sizeof(str))) {
         strcat(menu_str, "Config, Logout, ");
      } else {
         strcat(menu_str, "Config, ");
      }

      strcat(menu_str, "Help");
   } else {
      /* check for admin command */
      n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE);
      menu_str[0] = 0;
      for (i = 0; i < n; i++) {
         if (strcmp(menu_item[i], "Admin") == 0) {
            if (!is_admin_user(lbs, getparam("unm")))
               continue;
         }
         strcat(menu_str, menu_item[i]);
         if (i < n - 1)
            strcat(menu_str, ", ");
      }
   }

   /*---- check next/previous message -------------------------------*/

   if (strieq(command, loc("Next")) || strieq(command, loc("Previous")) || strieq(command, loc("Last"))
       || strieq(command, loc("First"))) {
      orig_message_id = message_id;

      if (strieq(command, loc("Last")))
         message_id = el_search_message(lbs, EL_LAST, 0, FALSE);

      if (strieq(command, loc("First")))
         message_id = el_search_message(lbs, EL_FIRST, 0, FALSE);

      /* avoid display of "invalid id '0'", if "start page = 0?cmd=Last" */
      if (!message_id)
         dec_path[0] = 0;

      first = TRUE;
      do {
         if (strieq(command, loc("Next")))
            message_id = el_search_message(lbs, EL_NEXT, message_id, FALSE);

         if (strieq(command, loc("Previous")))
            message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);

         if (!first) {
            if (strieq(command, loc("First")))
               message_id = el_search_message(lbs, EL_NEXT, message_id, FALSE);

            if (strieq(command, loc("Last")))
               message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);
         } else
            first = FALSE;

         if (message_id == 0) {
            if (strieq(command, loc("Next")))
               message_error = EL_LAST_MSG;
            else
               message_error = EL_FIRST_MSG;

            message_id = orig_message_id;
            break;
         }

         size = sizeof(text);
         el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, orig_tag, reply_tag,
                     attachment, encoding, locked_by, draft);

         /* check for locked attributes */
         for (i = 0; i < lbs->n_attr; i++) {
            sprintf(lattr, "l%s", attr_list[i]);
            if (isparam(lattr) == '1'
                && !(isparam(attr_list[i]) && strieq(getparam(attr_list[i]), attrib[i])))
               break;
         }
         if (i < lbs->n_attr)
            continue;

         /* check for attribute filter if not browsing */
         if (!isparam("browsing")) {
            for (i = 0; i < lbs->n_attr; i++) {
               if (isparam(attr_list[i]) && !(isparam(attr_list[i]) && strieq(getparam(attr_list[i]),
                                                                              attrib[i])))
                  break;
            }
            if (i < lbs->n_attr)
               continue;
         }

         sprintf(str, "%d", message_id);

         for (i = 0; i < lbs->n_attr; i++) {
            sprintf(lattr, "l%s", attr_list[i]);
            if (isparam(lattr) == '1') {
               if (strchr(str, '?') == NULL)
                  sprintf(str + strlen(str), "?%s=1", lattr);
               else
                  sprintf(str + strlen(str), "&amp;%s=1", lattr);
            }
         }

         redirect(lbs, str);
         return;

      } while (TRUE);
   }

   /*---- check for valid URL ---------------------------------------*/

   if (dec_path[0] && atoi(dec_path) == 0) {
      strencode2(str2, dec_path, sizeof(str2));
      sprintf(str, "%s: <b>%s</b>", loc("Invalid URL"), str2);
      show_error(str);
      return;
   }

   /*---- get current message ---------------------------------------*/

   if (message_id == 0)
      message_id = el_search_message(lbs, EL_LAST, 0, FALSE);

   status = 0;
   reply_tag[0] = orig_tag[0] = 0;
   if (message_id) {
      size = sizeof(text);
      status = el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, orig_tag,
                           reply_tag, attachment, encoding, locked_by, draft);

      if (status != EL_SUCCESS)
         message_error = status;
      else {
         if (_logging_level > 2) {
            sprintf(str, "READ entry #%d", message_id);
            write_logfile(lbs, str);
         }
      }
   } else
      message_error = EL_EMPTY;

   /*---- check for conditional attribute ----*/

   evaluate_conditions(lbs, attrib);

   /*---- header ----*/

   /* header */
   if (status == EL_SUCCESS && message_error != EL_EMPTY) {
      str[0] = 0;

      if (getcfg(lbs->name, "Page Title", str, sizeof(str))) {
         i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
         sprintf(mid, "%d", message_id);
         add_subst_list(slist, svalue, "message id", mid, &i);
         add_subst_time(lbs, slist, svalue, "entry time", date, &i, 0);
         strsubst_list(str, sizeof(str), slist, svalue, i);
         strip_html(str);
      } else
         strcpy(str, "ELOG");

      if (email) {
         /* embed CSS */
         show_html_header(lbs, FALSE, str, TRUE, FALSE, NULL, TRUE, 0);
         rsprintf("<body>\n");
      } else {
         sprintf(ref, "%d", message_id);
         strlcpy(script, "OnLoad=\"document.onkeypress=browse;\"", sizeof(script));
         if (str[0])
            show_standard_header(lbs, TRUE, str, ref, FALSE, NULL, script, 0);
         else
            show_standard_header(lbs, TRUE, lbs->name, ref, FALSE, NULL, script, 0);
      }
   } else
      show_standard_header(lbs, TRUE, "", "", FALSE, NULL, NULL, 0);

   /*---- title ----*/

   if (email)
      rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\">\n");
   else
      show_standard_title(lbs, "", 0);

   /*---- menu buttons ----*/

   if (!email) {

      rsprintf("<tr><td class=\"menuframe\">\n");
      rsprintf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
      rsprintf("<tr>\n");

      /*---- next/previous buttons ----*/

      if (!getcfg(lbs->name, "Enable browsing", str, sizeof(str)) || atoi(str) == 1) {
         rsprintf("<td class=\"menu1a\">\n");

         /* check if first.png exists, just put link there if not */
         strlcpy(file_name, resource_dir, sizeof(file_name));
         if (file_name[0] && file_name[strlen(file_name) - 1] != DIR_SEPARATOR)
            strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
         strlcat(file_name, "themes", sizeof(file_name));
         strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
         if (theme_name[0]) {
            strlcat(file_name, theme_name, sizeof(file_name));
            strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
         }
         strlcat(file_name, "first.png", sizeof(file_name));
         if (stat(file_name, &st) >= 0) {
            rsprintf("<input type=image name=cmd_first alt=\"%s\"  title=\"%s\" src=\"first.png\">\n",
                     loc("First entry, Ctrl-Home"), loc("First entry, Ctrl-Home"));
            rsprintf("<input type=image name=cmd_previous alt=\"%s\"  title=\"%s\" src=\"previous.png\">\n",
                     loc("Previous entry, Ctrl-PgUp"), loc("Previous entry, Ctrl-PgUp"));
            rsprintf("<input type=image name=cmd_next alt=\"%s\" title=\"%s\" src=\"next.png\">\n",
                     loc("Next entry, Ctrl-PgDn"), loc("Next entry, Ctrl-PgDn"));
            rsprintf("<input type=image name=cmd_last alt=\"%s\" title=\"%s\" src=\"last.png\">\n",
                     loc("Last entry, Ctrl-End"), loc("Last entry, Ctrl-End"));
         } else {
            rsprintf("<a href=\"%d?cmd=%s\">|&lt;</a>&nbsp;\n", message_id, loc("First"));
            rsprintf("<a href=\"%d?cmd=%s\">&lt;</a>&nbsp;\n", message_id, loc("Previous"));
            rsprintf("<a href=\"%d?cmd=%s\">&gt;</a>&nbsp;\n", message_id, loc("Next"));
            rsprintf("<a href=\"%d?cmd=%s\">&gt;|</a>&nbsp;\n", message_id, loc("Last"));
         }

         rsprintf("</td>\n");
      }

      n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE);

      rsprintf("<td class=\"menu1\">\n");

      for (i = 0; i < n; i++) {
         /* display menu item */
         strcpy(cmd, menu_item[i]);

         /* only display allowed commands */
         if (!is_user_allowed(lbs, cmd))
            continue;

         if (strieq(cmd, "Copy to") || strieq(cmd, "Move to")) {
            rsprintf("&nbsp;<input type=submit name=cmd value=\"%s\">\n", loc(cmd));
            if (strieq(cmd, "Copy to"))
               rsprintf("<select name=destc>\n");
            else
               rsprintf("<select name=destm>\n");

            if (getcfg(lbs->name, cmd, str, sizeof(str))) {
               n_log = strbreak(str, lbk_list, MAX_N_LIST, ",", FALSE);

               for (j = 0; j < n_log; j++)
                  rsprintf("<option value=\"%s\">%s\n", lbk_list[j], lbk_list[j]);
            } else {
               for (j = 0;; j++) {
                  if (!enumgrp(j, str))
                     break;

                  if (!is_logbook(str))
                     continue;

                  if (strieq(str, lbs->name))
                     continue;

                  rsprintf("<option value=\"%s\">%s\n", str, str);
               }
            }
            rsprintf("</select>\n");

            if (i < n - 1)
               rsprintf("&nbsp|\n");
         } else {
            strlcpy(str, loc(menu_item[i]), sizeof(str));
            url_encode(str, sizeof(str));

            if (strieq(menu_item[i], "list")) {
               if (getcfg(lbs->name, "Back to main", str, sizeof(str)) && atoi(str) == 1)
                  rsprintf("&nbsp;<a href=\"../\">%s</a>&nbsp;\n", loc(menu_item[i]));
               else
                  rsprintf("&nbsp;<a href=\".?id=%d\">%s</a>&nbsp;\n", message_id, loc(menu_item[i]));
            } else
               rsprintf("&nbsp;<a href=\"%d?cmd=%s\">%s</a>&nbsp;\n", message_id, str, loc(menu_item[i]));

            if (i < n - 1)
               rsprintf("|\n");
            else
               rsprintf("\n");
         }
      }

      rsprintf("</td>\n\n");

      rsprintf("</table></td></tr>\n\n");

      /*---- menu text ----*/

      if (getcfg(lbs->name, "menu text", str, sizeof(str))) {
         FILE *file;
         char filename[256], *buf;

         rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");

         /* check if file starts with an absolute directory */
         if (str[0] == DIR_SEPARATOR || str[1] == ':')
            strcpy(filename, str);
         else {
            strlcpy(filename, logbook_dir, sizeof(filename));
            strlcat(filename, str, sizeof(filename));
         }

         file = fopen(filename, "rb");
         if (file != NULL) {
            fseek(file, 0, SEEK_END);
            size = TELL(fileno(file));
            fseek(file, 0, SEEK_SET);

            buf = xmalloc(size + 1);
            fread(buf, 1, size, file);
            buf[size] = 0;
            fclose(file);

            rsputs(buf);

         } else
            rsprintf("<center><b>Error: file <i>\"%s\"</i> not found</b></center>", filename);

         rsprintf("</span></td></tr>");
      }
   }                            // if (!email)

   /*---- message ----*/

   if (reply_tag[0] || orig_tag[0])
      show_elog_thread(lbs, message_id, email, 0);

   if (message_error == EL_EMPTY)
      rsprintf("<tr><td class=\"errormsg\" colspan=2>%s</td></tr>\n", loc("Logbook is empty"));
   else if (message_error == EL_NO_MSG)
      rsprintf("<tr><td class=\"errormsg\" colspan=2>%s</td></tr>\n", loc("This entry has been deleted"));
   else {
      /* overall message table */
      rsprintf("<tr><td><table class=\"listframe\" width=\"100%%\" cellspacing=\"0\" cellpadding=\"0\">\n");

      /* check for locked attributes */
      for (i = 0; i < lbs->n_attr; i++) {
         sprintf(lattr, "l%s", attr_list[i]);
         if (isparam(lattr) == '1')
            break;
      }
      if (i < lbs->n_attr) {
         if (isparam(attr_list[i]))
            sprintf(str, " %s <i>\"%s = %s\"</i>", loc("with"), attr_list[i], getparam(attr_list[i]));
      } else
         str[0] = 0;

      if (message_error == EL_LAST_MSG)
         rsprintf("<tr><td class=\"notifymsg\" colspan=2>%s %s</td></tr>\n", loc("This is the last entry"),
                  str);

      if (message_error == EL_FIRST_MSG)
         rsprintf("<tr><td class=\"notifymsg\" colspan=2>%s %s</td></tr>\n", loc("This is the first entry"),
                  str);

      /* check for mail submissions */
      if (isparam("suppress")) {
         rsprintf("<tr><td class=\"notifymsg\" colspan=2>%s</td></tr>\n",
                  loc("Email notification suppressed"));
      } else if (isparam("error")) {
         strencode2(str, getparam("error"), sizeof(str));
         rsprintf("<tr><td class=\"errormsg\" colspan=2>%s</td></tr>\n", str);
      } else {
         for (i = 0;; i++) {
            sprintf(str, "mail%d", i);
            if (isparam(str)) {
               if (i == 0)
                  rsprintf("<tr><td class=\"notifymsg\" colspan=2>");
               strencode2(str, getparam(str), sizeof(str));
               rsprintf("%s <b>%s</b><br>\n", loc("Email sent to"), str);
            } else
               break;
         }
         if (i > 0)
            rsprintf("</tr>\n");
      }

      /*---- display message ID ----*/

      _current_message_id = message_id;

      if (email) {
         rsprintf("<tr><td class=\"title1\">\n");
         if (strstr(command, "oldemail") != NULL)
            rsprintf("%s:", loc("An old ELOG entry has been updated"));
         else
            rsprintf("%s:", loc("A new ELOG entry has been submitted"));
         rsprintf("</td></tr>\n");
      }

      if (locked_by[0]) {
         sprintf(str, "%s %s", loc("Entry is currently edited by"), locked_by);
         rsprintf
                 ("<tr><td nowrap colspan=2 class=\"errormsg\"><img src=\"stop.png\" alt=\"%s\"  title=\"%s\">\n",
                  loc("stop"), loc("stop"));
         rsprintf("%s.<br>%s.<br>%s.</td></tr>\n", str,
                  loc("You can \"steal\" the lock by editing this entry"),
                  loc("You might however then overwrite each other's modifications"));
      } else {
         if (draft[0]) {
            rsprintf("<tr><td nowrap colspan=2 class=\"errormsg\">%s\n",
                     loc("This is a draft message, edit and submit it to make it permanent"));
            rsprintf("&nbsp;<input type=button value=\"%s\" onClick=\"window.location.href='%d?cmd=%s';\">",
                     loc("Edit"), message_id, loc("Edit"));
            rsprintf("</td></tr>\n");
         }
      }

      rsprintf("<tr><td class=\"attribhead\">\n");

      for (i = 0; i < lbs->n_attr; i++) {
         strencode2(str, attrib[i], sizeof(str));
         rsprintf("<input type=hidden name=\"%s\" value=\"%s\">\n", attr_list[i], str);
      }

      /* browsing flag to distinguish "/../<attr>=<value>" from browsing */
      rsprintf("<input type=hidden name=browsing value=1>\n");

      if (getcfg(lbs->name, "ID display", display, sizeof(display))) {
         j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib,
                              TRUE);
         sprintf(str, "%d", message_id);
         add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &j);
         add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time",
                        date, &j, 0);

         strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist,
                       (char (*)[NAME_LENGTH]) svalue, j);

      } else
         sprintf(display, "%d", message_id);

      if (email) {
         compose_base_url(lbs, str, sizeof(str), TRUE);
         sprintf(str + strlen(str), "%d", message_id);
         rsprintf("%s:&nbsp;<b>%s</b>&nbsp;&nbsp;", loc("Logbook"), lbs->name);
         rsprintf("%s:&nbsp;<a href=\"%s\"><b>%d</b></a>", loc("Message ID"), str, message_id);
      } else
         rsprintf("%s:&nbsp;<b>%s</b>\n", loc("Message ID"), display);

      /*---- display date ----*/

      if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
         strcpy(format, DEFAULT_TIME_FORMAT);

      entry_ltime = date_to_ltime(date);
      pts = localtime(&entry_ltime);
      assert(pts);
      my_strftime(str, sizeof(str), format, pts);

      rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;%s:&nbsp;<b>%s</b>\n", loc("Entry time"), str);

      /*---- link to original message or reply ----*/

      if (message_error != EL_FILE_ERROR && (reply_tag[0] || orig_tag[0])) {

         if (orig_tag[0]) {
            if (email)
               compose_base_url(lbs, ref, sizeof(ref), TRUE);
            else
               ref[0] = 0;
            sprintf(ref + strlen(ref), "%s", orig_tag);
            rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;%s:&nbsp;", loc("In reply to"));
            rsprintf("<b><a href=\"%s\">%s</a></b>\n", ref, orig_tag);
         }
         if (reply_tag[0]) {
            rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;%s:&nbsp;<b>", loc("Reply to this"));

            p = strtok(reply_tag, ",");
            do {
               if (email)
                  compose_base_url(lbs, ref, sizeof(ref), TRUE);
               else
                  ref[0] = 0;
               sprintf(ref + strlen(ref), "%s", p);
               rsprintf("<a href=\"%s\">%s</a>\n", ref, p);

               p = strtok(NULL, ",");

               if (p)
                  rsprintf("&nbsp; \n");

            } while (p);

            rsprintf("</b>\n");
         }
      }

      rsprintf("</td></tr>\n");

      /*---- display attributes ----*/

      /* retrieve attribute flags */
      for (i = 0; i < lbs->n_attr; i++) {
         format_flags[i] = 0;
         sprintf(str, "Format %s", attr_list[i]);
         if (getcfg(lbs->name, str, format, sizeof(format))) {
            n = strbreak(format, fl, 8, ",", FALSE);
            if (n > 0)
               format_flags[i] = atoi(fl[0]);
         }
      }

      /* 2 column table for all attributes */
      rsprintf("<tr><td><table width=\"100%%\" cellpadding=\"0\" cellspacing=\"0\">");
      subtable = 0;

      /* generate list of attributes to show */
      if (getcfg(lbs->name, "Show attributes", str, sizeof(str))) {
         n_disp_attr = strbreak(str, list, MAX_N_ATTR, ",", FALSE);
         for (i = 0; i < n_disp_attr; i++) {
            for (j = 0; j < lbs->n_attr; j++)
               if (strieq(attr_list[j], list[i]))
                  break;
            if (!strieq(attr_list[j], list[i]))
               /* attribute not found */
               j = 0;
            attr_index[i] = j;
         }
      } else {
         for (i = 0; i < lbs->n_attr; i++)
            attr_index[i] = i;
         n_disp_attr = lbs->n_attr;
      }

      for (j = 0; j < n_disp_attr; j++) {

         i = attr_index[j];
         if (getcfg(lbs->name, "Password file", str, sizeof(str)) && getcfg(lbs->name, "Guest display", str,
                                                                            sizeof(str)) && !isparam("unm")) {

            n = strbreak(str, gattr, MAX_N_ATTR, ",", FALSE);
            for (k = 0; k < n; k++)
               if (strieq(gattr[k], attr_list[i]))
                  break;

            if (k == n)
               continue;
         }

         strcpy(class_name, "attribname");
         strcpy(class_value, "attribvalue");

         sprintf(str, "Format %s", attr_list[i]);
         if (getcfg(lbs->name, str, format, sizeof(format))) {
            n = strbreak(format, fl, 8, ",", FALSE);
            if (n > 1)
               strlcpy(class_name, fl[1], sizeof(class_name));
            if (n > 2)
               strlcpy(class_value, fl[2], sizeof(class_value));
         }

         if (format_flags[i] & AFF_SAME_LINE)
            /* if attribute on same line, do nothing */
            rsprintf("");
         else if (i < lbs->n_attr - 1 && (format_flags[i + 1] & AFF_SAME_LINE)) {
            /* if next attribute on same line, start a new subtable */
            rsprintf("<tr><td colspan=2><table width=\"100%%\" cellpadding=\"0\" cellspacing=\"0\"><tr>");
            subtable = 1;
         } else
            /* for normal attribute, start new row */
            rsprintf("<tr>");

         sprintf(lattr, "l%s", attr_list[i]);

         /* display cell with optional tooltip */
         sprintf(str, "Tooltip %s", attr_list[i]);
         if (getcfg(lbs->name, str, comment, sizeof(comment)))
            rsprintf("<td nowrap title=\"%s\" class=\"%s\">", comment, class_name);
         else
            rsprintf("<td nowrap class=\"%s\">", class_name);

         if (getcfg(lbs->name, "Filtered browsing", str, sizeof(str)) && atoi(str) == 1) {
            if (isparam(lattr) == '1')
               rsprintf("<input type=\"checkbox\" checked name=\"%s\" value=\"1\">&nbsp;", lattr);
            else
               rsprintf("<input alt=\"text\" title=\"text\"type=\"checkbox\" name=\"%s\" value=\"1\">&nbsp;",
                        lattr);
         }

         /* display checkbox for boolean attributes */
         if (strieq(attr_options[i][0], "boolean")) {
            if (atoi(attrib[i]) == 1)
               rsprintf("%s:</td><td class=\"%s\"><input type=checkbox checked disabled></td>\n",
                        attr_list[i], class_value);
            else
               rsprintf("%s:</td><td class=\"%s\"><input type=checkbox disabled></td>\n", attr_list[i],
                        class_value);
         }
            /* display image for icon */
         else if (attr_flags[i] & AF_ICON) {
            rsprintf("%s:</td><td class=\"%s\">\n", attr_list[i], class_value);
            if (attrib[i][0]) {
               sprintf(str, "Icon comment %s", attrib[i]);
               getcfg(lbs->name, str, comment, sizeof(comment));

               if (comment[0])
                  rsprintf("<img src=\"icons/%s\" alt=\"%s\" title=\"%s\">", attrib[i], comment, comment);
               else
                  rsprintf("<img src=\"icons/%s\" alt=\"%s\" title=\"%s\">", attrib[i], attrib[i], attrib[i]);
            }
            rsprintf("&nbsp;</td>\n");
         } else if ((attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) && (format_flags[i]
                                                                                    & AFF_MULTI_LINE)) {
            rsprintf("%s:</td><td class=\"%s\">\n", attr_list[i], class_value);

            /* separate options into individual lines */
            strlcpy(str, attrib[i], sizeof(str));
            p = strtok(str, "|");
            while (p) {
               while (*p == ' ')
                  p++;

               rsputs2(lbs, email, p);

               p = strtok(NULL, "|");

               if (p)
                  rsprintf("<br>");
            }

            rsprintf("</td>\n");
         } else if (attr_flags[i] & AF_DATE) {

            sprintf(str, "Date format %s", attr_list[i]);
            if (!getcfg(lbs->name, str, format, sizeof(format)))
               if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
                  strcpy(format, DEFAULT_DATE_FORMAT);

            ltime = atoi(attrib[i]);
            pts = localtime(&ltime);
            assert(pts);
            if (ltime == 0)
               strcpy(str, "-");
            else
               my_strftime(str, sizeof(str), format, pts);

            rsprintf("%s:</td><td class=\"%s\">%s&nbsp;</td>\n", attr_list[i], class_value, str);

         } else if (attr_flags[i] & AF_DATETIME) {

            sprintf(str, "Time format %s", attr_list[i]);
            if (!getcfg(lbs->name, str, format, sizeof(format)))
               if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
                  strcpy(format, DEFAULT_TIME_FORMAT);

            ltime = atoi(attrib[i]);
            pts = localtime(&ltime);
            assert(pts);
            if (ltime == 0)
               strcpy(str, "-");
            else
               my_strftime(str, sizeof(str), format, pts);

            rsprintf("%s:</td><td class=\"%s\">%s&nbsp;</td>\n", attr_list[i], class_value, str);

         } else {
            rsprintf("%s:</td><td class=\"%s\">\n", attr_list[i], class_value);

            sprintf(str, "Change %s", attr_list[i]);
            if (getcfg(lbs->name, str, display, sizeof(display))) {
               k = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                                    attrib, TRUE);
               sprintf(str, "%d", message_id);
               add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id",
                              str, &k);
               add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                              "entry time", date, &k, attr_flags[i]);

               strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist,
                             (char (*)[NAME_LENGTH]) svalue, k);

            } else
               strcpy(display, attrib[i]);

            if (is_html(display) && !is_script(display) && html_allowed(lbs))
               rsputs(display);
            else
               rsputs2(lbs, email, display);

            rsprintf("&nbsp;</td>\n");
         }

         if (i < lbs->n_attr - 1 && (format_flags[i + 1] & AFF_SAME_LINE) == 0) {
            /* if next attribute not on same line, close row or subtable */
            if (subtable) {
               rsprintf("</table></td></tr>\n");
               subtable = 0;
            } else
               rsprintf("</tr>");
         }

         /* if last attribute, close row or subtable */
         if (i == lbs->n_attr - 1) {
            if (subtable) {
               rsprintf("</table></td></tr>\n");
               subtable = 0;
            } else
               rsprintf("</tr>");
         }
      }

      rsputs("</table></td></tr>\n");   // 2 column table

      rsputs("</table><!-- listframe -->\n");

      rsputs("</td></tr>\n");

      /*---- message text ----*/

      show_text = !getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1;

      if (getcfg(lbs->name, "Password file", str, sizeof(str)) && getcfg(lbs->name, "Guest display", str,
                                                                         sizeof(str)) && !isparam("unm")) {

         n = strbreak(str, gattr, MAX_N_ATTR, ",", FALSE);
         for (j = 0; j < n; j++)
            if (strieq(gattr[j], "text"))
               break;

         if (j == n)
            show_text = FALSE;
      }

      if (show_text) {
         rsprintf("<tr><td class=\"messageframe\">");

         if (strieq(encoding, "html")) {
            if (email)
               replace_inline_img(lbs, text);
            rsputs(text);
         } else if (strieq(encoding, "ELCode")) {
            rsputs_elcode(lbs, email, text);
         } else {
            rsputs("<pre class=\"messagepre\">");
            rsputs2(lbs, email, text);
            rsputs("</pre>");
         }

         rsputs("</td></tr>\n");

         n_hidden = 0;
         for (i = 0, n_attachments = 0; i < MAX_ATTACHMENTS; i++) {
            att_inline[i] = 0;
            att_hide[i] = getcfg(lbs->name, "Show attachments", str, sizeof(str)) && atoi(str) == 0;

            if (is_inline_attachment(encoding, message_id, text, i, attachment[i]))
               att_inline[i] = 1;

            if (attachment[i][0])
               n_attachments++;
         }

         if (isparam("hide")) {
            strlcpy(str, getparam("hide"), sizeof(str));
            p = strtok(str, ",");
            while (p != NULL) {
               if (atoi(p) < MAX_ATTACHMENTS) {
                  att_hide[atoi(p)] = 1;
                  n_hidden++;
               }
               p = strtok(NULL, ",");
            }
         }
         if (isparam("show")) {
            strlcpy(str, getparam("show"), sizeof(str));
            p = strtok(str, ",");
            while (p != NULL) {
               if (atoi(p) < MAX_ATTACHMENTS) {
                  att_hide[atoi(p)] = 0;
               }
               p = strtok(NULL, ",");
            }
         }

         for (index = 0; index < MAX_ATTACHMENTS; index++) {
            if (attachment[index][0] && strlen(attachment[index]) > 14 && !att_inline[index]) {
               for (i = 0; i < (int) strlen(attachment[index]); i++)
                  att[i] = toupper(attachment[index][i]);
               att[i] = 0;

               /* determine size of attachment */
               strlcpy(file_name, lbs->data_dir, sizeof(file_name));
               generate_subdir_name(attachment[index], subdir, sizeof(subdir));
               strlcat(file_name, subdir, sizeof(file_name));
               strlcat(file_name, attachment[index], sizeof(file_name));
               thumb_status = create_thumbnail(lbs, file_name);

               length = 0;
               fh = open(file_name, O_RDONLY | O_BINARY);
               if (fh > 0) {
                  lseek(fh, 0, SEEK_END);
                  length = TELL(fh);
                  close(fh);
               }

               strlcpy(str, attachment[index], sizeof(str));
               str[13] = 0;
               strcpy(file_enc, attachment[index] + 14);
               url_encode(file_enc, sizeof(file_enc));  /* for file names with special characters like "+" */
               if (email && !att_links) {
                  retrieve_domain(domain, sizeof(domain));
                  sprintf(ref, "cid:att%d@%s", index, domain);
               } else if (email) {
                  compose_base_url(lbs, ref, sizeof(ref), TRUE);
                  sprintf(ref + strlen(ref), "%s", str);
                  sprintf(ref + strlen(ref), "/%s", file_enc);
               } else {
                  sprintf(ref, "%s/%s", str, file_enc);
               }

               /* overall table */
               rsprintf("<tr><td><table class=\"listframe\" width=\"100%%\" cellspacing=0>\n");

               rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s %d:</td>\n",
                        loc("Attachment"), index + 1);

               if (email && !att_links)
                  rsprintf("<td class=\"attribvalue\">%s\n", attachment[index] + 14);
               else
                  rsprintf("<td class=\"attribvalue\"><a href=\"%s\" target=\"_blank\">%s</a>\n", ref,
                           attachment[index] + 14);

               rsprintf("&nbsp;<span class=\"bytes\">");

               if (length < 1024)
                  rsprintf("%d Bytes", length);
               else if (length < 1024 * 1024)
                  rsprintf("%d kB", length / 1024);
               else
                  rsprintf("%1.3lf MB", length / 1024.0 / 1024.0);

               rsprintf("</span>\n");

               /* retrieve submission date */
               memset(&ts, 0, sizeof(ts));
               ts.tm_mon = (attachment[index][2] - '0') * 10 + attachment[index][3] - '0' - 1;
               ts.tm_mday = (attachment[index][4] - '0') * 10 + attachment[index][5] - '0';
               ts.tm_year = (attachment[index][0] - '0') * 10 + attachment[index][1] - '0';

               ts.tm_hour = (attachment[index][7] - '0') * 10 + attachment[index][8] - '0';
               ts.tm_min = (attachment[index][9] - '0') * 10 + attachment[index][10] - '0';
               ts.tm_sec = (attachment[index][11] - '0') * 10 + attachment[index][12] - '0';

               if (ts.tm_year < 90)
                  ts.tm_year += 100;
               ltime = mktime(&ts);

               /* show upload date/time only if different from entry date/time */
               if (abs((int) (ltime - entry_ltime)) > 3600) {
                  if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
                     strcpy(format, DEFAULT_TIME_FORMAT);

                  my_strftime(str, sizeof(str), format, &ts);

                  rsprintf("&nbsp;<span class=\"uploaded\">");
                  rsprintf("Uploaded %s", str);
                  rsprintf("</span>\n");
               }

               /* determine if displayed inline */
               display_inline = is_image(file_name) || is_ascii(file_name);
               if (chkext(att, ".PS") || chkext(att, ".PDF"))
                  display_inline = 0;
               if ((chkext(att, ".HTM") || chkext(att, ".HTML")) && is_full_html(file_name))
                  display_inline = 0;
               if (thumb_status)
                  display_inline = 1;

               if (display_inline) {
                  /* hide this / show this */
                  if (!email) {
                     rsprintf("<span class=\"bytes\">");

                     rsprintf("&nbsp;|&nbsp;");
                     if (att_hide[index]) {
                        rsprintf("<a href=\"%d?hide=", message_id);
                        for (i = 0; i < MAX_ATTACHMENTS; i++)
                           if (att_hide[i] && i != index) {
                              rsprintf("%d,", i);
                           }
                        rsprintf("&amp;show=%d", index);
                        rsprintf("\">%s</a>", loc("Show"));
                     } else {
                        rsprintf("<a href=\"%d?hide=", message_id);
                        for (i = 0; i < MAX_ATTACHMENTS; i++)
                           if (att_hide[i] || i == index) {
                              rsprintf("%d,", i);
                           }
                        rsprintf("\">%s</a>", loc("Hide"));
                     }

                     /* hide all */
                     if (n_hidden < n_attachments) {
                        rsprintf("&nbsp;|&nbsp;<a href=\"%d?hide=", message_id);
                        for (i = 0; i < MAX_ATTACHMENTS; i++)
                           if (attachment[i][0]) {
                              rsprintf("%d,", i);
                           }
                        rsprintf("\">%s</a>", loc("Hide all"));
                     }

                     /* show all */
                     if (n_hidden > 0) {
                        for (i = 0; i < MAX_ATTACHMENTS; i++)
                           if (att_hide[i])
                              break;
                        if (i < MAX_ATTACHMENTS) {
                           rsprintf("&nbsp;|&nbsp;<a href=\"%d?show=", message_id);
                           for (i = 0; i < MAX_ATTACHMENTS; i++)
                              if (att_hide[i])
                                 rsprintf("%d,", i);
                           rsprintf("\">%s</a>", loc("Show all"));
                        }
                     }

                     rsprintf("</span>\n");
                  }
               }

               rsprintf("</td></tr></table></td></tr>\n");
               strlcpy(file_name, lbs->data_dir, sizeof(file_name));
               generate_subdir_name(attachment[index], subdir, sizeof(subdir));
               strlcat(file_name, subdir, sizeof(file_name));
               strlcat(file_name, attachment[index], sizeof(file_name));

               if (!att_hide[index] && display_inline) {

                  if (thumb_status) {
                     rsprintf("<tr><td class=\"attachmentframe\">\n");

                     if (thumb_status == 3) {
                        rsprintf("<font color=red><b>%s</b></font>\n",
                                 loc("Cannot create thumbnail, please check ImageMagick installation"));
                     } else {
                        if (thumb_status == 2 && !email) {
                           for (i = 0;; i++) {
                              strlcpy(str, file_name, sizeof(str));
                              if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
                                 if (strrchr(str, '.'))
                                    *strrchr(str, '.') = 0;
                              sprintf(str + strlen(str), "-%d.png", i);
                              if (file_exist(str)) {
                                 strlcpy(str, ref, sizeof(str));
                                 if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
                                    if (strrchr(str, '.'))
                                       *strrchr(str, '.') = 0;
                                 sprintf(str + strlen(str), "-%d.png", i);
                                 rsprintf("<a name=\"att%d\" href=\"%s\">\n", index + 1, ref);
                                 rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\"></a>\n", str,
                                          attachment[index] + 14, attachment[index] + 14);
                              } else
                                 break;
                           }
                        } else {
                           if (!email) {
                              rsprintf("<a name=\"att%d\" href=\"%s\">\n", index + 1, ref);
                              strlcpy(str, ref, sizeof(str));
                              if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
                                 if (strrchr(str, '.'))
                                    *strrchr(str, '.') = 0;
                              strlcat(str, ".png", sizeof(str));
                              rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\"></a>\n", str,
                                       attachment[index] + 14, attachment[index] + 14);
                           }
                        }
                     }
                     rsprintf("</td></tr>\n\n");
                  } else if (is_image(att)) {
                     if (!email) {
                        rsprintf("<tr><td class=\"attachmentframe\">\n");
                        rsprintf("<a name=\"att%d\"></a>\n", index + 1);
                        rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\">\n", ref, attachment[index] + 14,
                                 attachment[index] + 14);
                        rsprintf("</td></tr>\n\n");
                     }
                  } else {
                     if (is_ascii(file_name)) {
                        /* display attachment */
                        rsprintf("<tr><td class=\"messageframe\">\n");

                        /* anchor for references */
                        rsprintf("<a name=\"att%d\"></a>\n", index + 1);

                        if (!chkext(att, ".HTML"))
                           rsprintf("<pre class=\"messagepre\">");

                        f = fopen(file_name, "rt");
                        n_lines = 0;
                        if (getcfg(lbs->name, "Attachment lines", str, sizeof(str)))
                           max_n_lines = atoi(str);
                        else
                           max_n_lines = 300;

                        if (f != NULL) {
                           while (!feof(f)) {
                              str[0] = 0;
                              fgets(str, sizeof(str), f);

                              if (n_lines < max_n_lines) {
                                 if (!chkext(att, ".HTML"))
                                    rsputs2(lbs, email, str);
                                 else
                                    rsputs(str);
                              }
                              n_lines++;
                           }
                           fclose(f);
                        }

                        if (!chkext(att, ".HTML"))
                           rsprintf("</pre>");
                        rsprintf("\n");
                        if (max_n_lines == 0)
                           rsprintf("<i><b>%d lines</b></i>\n", n_lines);
                        else if (n_lines > max_n_lines)
                           rsprintf("<i><b>... %d more lines ...</b></i>\n", n_lines - max_n_lines);

                        rsprintf("</td></tr>\n");
                     }
                  }
               }
            }
         }
      }
   }

   /* overall table (class "frame" from show_standard_header) */
   rsprintf("\r\n</table><!-- show_standard_title -->\r\n");

   show_bottom_text(lbs);
   if (!email)
      rsprintf("</form>\n");
   rsprintf("</body></html>\r\n");
}

Project: elog

int el_lock_message(LOGBOOK *lbs, int message_id, char *user, BOOL lock)
/* lock message for editing */
{
   int size;
   char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], text[TEXT_SIZE], in_reply_to[80],
           reply_to[MAX_REPLY_TO * 10], encoding[80], locked_by[256], draft[256];
   char att_file[MAX_ATTACHMENTS][256];

   /* retrieve message */
   size = sizeof(text);
   el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to, reply_to,
               att_file, encoding, locked_by, draft);

   /* submit message, unlocked if block == FALSE */
   el_submit(lbs, message_id, TRUE, date, attr_list, attrib, lbs->n_attr, text, in_reply_to, reply_to,
             encoding, att_file, FALSE, lock ? user : NULL, draft);

   return EL_SUCCESS;
}

Project: elog

void strip_html(char *s) {
   char *p;

   while ((p = strchr(s, '<')) != NULL) {
      if (strchr(p, '>'))
         memmove(p, strchr(p, '>') + 1, strlen(strchr(p, '>') + 1) + 1);
      else
         *p = 0;
   }
}

Project: elog

void format_email_attachments(LOGBOOK *lbs, int message_id, int attachment_type,
                              char att_file[MAX_ATTACHMENTS][256], char *mail_text, int size,
                              char *multipart_boundary, int content_id) {
   int i, index, n_att, fh, n, is_inline, length;
   char str[256], file_name[256], buffer[256], domain[256], subdir[256];

   /* count attachments */
   for (n_att = 0; att_file[n_att][0] && n_att < MAX_ATTACHMENTS; n_att++);

   for (index = 0; index < MAX_ATTACHMENTS; index++) {

      if (att_file[index][0] == 0)
         continue;

      is_inline = is_inline_attachment(getparam("encoding"), message_id,
                                       getparam("text"), index, att_file[index]);
      if (attachment_type == 1 && is_inline)
         continue;
      if (attachment_type == 2 && !is_inline)
         continue;

      snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "\r\n--%s\r\n",
               multipart_boundary);

      /* return proper Content-Type for file type */
      for (i = 0; i < (int) strlen(att_file[index]) && i < (int) sizeof(str) - 1; i++)
         str[i] = toupper(att_file[index][i]);
      str[i] = 0;

      for (i = 0; filetype[i].ext[0]; i++)
         if (strstr(str, filetype[i].ext))
            break;

      if (filetype[i].ext[0])
         snprintf(str, sizeof(str), "Content-Type: %s; name=\"%s\"\r\n", filetype[i].type, att_file[index]
                                                                                           + 14);
      else if (strchr(str, '.') == NULL)
         snprintf(str, sizeof(str), "Content-Type: text/plain; name=\"%s\"\r\n", att_file[index] + 14);
      else
         snprintf(str, sizeof(str), "Content-Type: application/octet-stream; name=\"%s\"\r\n",
                  att_file[index] + 14);

      strlcat(mail_text, str, size);

      strlcat(mail_text, "Content-Transfer-Encoding: BASE64\r\n", size);

      if (content_id) {
         retrieve_domain(domain, sizeof(domain));
         snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Content-ID: <att%d@%s>\r\n",
                  index, domain);
         snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
                  "Content-Disposition: inline; filename=\"%s\"\r\n\r\n", att_file[index] + 14);
      } else
         snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
                  "Content-Disposition: attachment; filename=\"%s\"\r\n\r\n", att_file[index] + 14);

      /* encode file */
      strlcpy(file_name, lbs->data_dir, sizeof(file_name));
      generate_subdir_name(att_file[index], subdir, sizeof(subdir));
      strlcat(file_name, subdir, sizeof(file_name));
      strlcat(file_name, att_file[index], sizeof(file_name));
      if (is_image(file_name)) {
         get_thumb_name(file_name, str, sizeof(str), 0);
         if (str[0])
            strlcpy(file_name, str, sizeof(file_name));
      }

      fh = open(file_name, O_RDONLY | O_BINARY);
      length = strlen(mail_text);
      if (fh > 0) {
         do {
            n = my_read(fh, buffer, 45);
            if (n <= 0)
               break;

            base64_bufenc((unsigned char *) buffer, n, str);

            if (length + (int) strlen(str) + 2 < size) {
               strcpy(mail_text + length, str);
               length += strlen(str);
               strcpy(mail_text + length, "\r\n");
               length += 2;
            }
         } while (1);

         close(fh);
      }
   }
}

Project: elog

void synchronize(LOGBOOK *lbs, int mode) {
   int i;
   char str[256], pwd[256];

   if (mode == SYNC_HTML) {
      show_html_header(NULL, FALSE, loc("Synchronization"), TRUE, FALSE, NULL, FALSE, 0);
      rsprintf("<body>\n");
   }

   if (lbs == NULL) {
      for (i = 0; lb_list[i].name[0]; i++)
         if (getcfg(lb_list[i].name, "mirror server", str, sizeof(str))) {

            if (exist_top_group() && getcfg_topgroup())
               if (lb_list[i].top_group[0] && !strieq(lb_list[i].top_group, getcfg_topgroup()))
                  continue;

            /* skip if excluded */
            if (getcfg(lb_list[i].name, "Mirror exclude", str, sizeof(str)) && atoi(str) == 1)
               continue;

            /* if called by cron, set user name and password */
            if (mode == SYNC_CRON && getcfg(lb_list[i].name, "mirror user", str, sizeof(str))) {
               if (get_user_line(&lb_list[i], str, pwd, NULL, NULL, NULL, NULL, NULL) == EL_SUCCESS) {
                  setparam("unm", str);
                  setparam("upwd", pwd);
               }
            }

            synchronize_logbook(&lb_list[i], mode, TRUE);
         }
   } else
      synchronize_logbook(lbs, mode, FALSE);

   if (mode == SYNC_HTML) {
      rsprintf("<table width=\"100%%\" cellpadding=\"1\" cellspacing=\"0\"");
      rsprintf("<tr><td class=\"seltitle\"><a href=\".\">%s</a></td></tr>\n", loc("Back"));
      rsprintf("</table><p>\n");

      rsprintf("</body></html>\n");
      flush_return_buffer();
      keep_alive = FALSE;
   }
}

Project: elog

int ss_daemon_init() {
#ifdef OS_UNIX

   /* only implemented for UNIX */
   int i, fd, pid;

   if ((pid = fork()) < 0)
      return FAILURE;
   else if (pid != 0)
      _exit(EXIT_SUCCESS);      /* parent finished, exit without atexit hook */

   /* child continues here */

   /* try and use up stdin, stdout and stderr, so other
      routines writing to stdout etc won't cause havoc. Copied from smbd */
   for (i = 0; i < 3; i++) {
      close(i);
      fd = open("/dev/null", O_RDWR, 0);
      if (fd < 0)
         fd = open("/dev/null", O_WRONLY, 0);
      if (fd < 0) {
         eprintf("Can't open /dev/null");
         return FAILURE;
      }
      if (fd != i) {
         eprintf("Did not get file descriptor");
         return FAILURE;
      }
   }

   setsid();                    /* become session leader */
   chdir("/");                  /* change working direcotry (not on NFS!) */
   umask(0);                    /* clear our file mode creation mask */

#endif

   return SUCCESS;
}

Project: elog

void show_bottom_text_login(LOGBOOK *lbs) {
   char str[NAME_LENGTH], slist[20][NAME_LENGTH], svalue[20][NAME_LENGTH];
   int i, size;

   if (getcfg(lbs->name, "bottom text login", str, sizeof(str))) {
      FILE *f;
      char file_name[256], *buf;

      if (str[0]) {
         /* check if file starts with an absolute directory */
         if (str[0] == DIR_SEPARATOR || str[1] == ':')
            strcpy(file_name, str);
         else {
            strlcpy(file_name, logbook_dir, sizeof(file_name));
            strlcat(file_name, str, sizeof(file_name));
         }

         f = fopen(file_name, "rb");
         if (f != NULL) {
            fseek(f, 0, SEEK_END);
            size = TELL(fileno(f));
            fseek(f, 0, SEEK_SET);

            buf = xmalloc(size + 100);
            fread(buf, 1, size, f);
            buf[size] = 0;
            fclose(f);

            if (lbs != NULL) {
               i = build_subst_list(lbs, slist, svalue, NULL, TRUE);
               strsubst_list(buf, size + 100, slist, svalue, i);
            }

            rsputs(buf);
            xfree(buf);
         } else {
            if (lbs != NULL) {
               i = build_subst_list(lbs, slist, svalue, NULL, TRUE);
               strsubst_list(str, sizeof(str), slist, svalue, i);
            }

            rsputs(str);
         }
      }
   } else
      /* add little logo */
      rsprintf
              ("<center><a class=\"bottomlink\" title=\"%s\" href=\"https://elog.psi.ch/elog/\">ELOG V%s-%s</a></center>",
               loc("Goto ELOG home page"), VERSION, git_revision());
}

Project: elog

LBLIST *get_subgroup(LBLIST pgrp, char *logbook)
/* retrieve parent of group member "logbook" (which might be group by itself) */
{
   int i;

   for (i = 0; i < pgrp->n_members; i++) {
      /* check if logbook is current member */
      if (strieq(logbook, pgrp->member[i]->name))
         return &(pgrp->member[i]);

      /* check if logbook is in subgroup of current member */
      if (pgrp->member[i]->n_members > 0 && get_subgroup(pgrp->member[i], logbook))
         return get_subgroup(pgrp->member[i], logbook);
   }

   return NULL;
}

Project: elog

void show_elog_thread(LOGBOOK *lbs, int message_id, int absolute_links, int highlight_mid) {
   int size, head_id, n_display, n_attr_disp;
   char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
           attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], encoding[80], locked_by[256], draft[256],
           disp_attr[MAX_N_ATTR + 4][NAME_LENGTH];
   char *p;

   text = (char *) xmalloc(TEXT_SIZE);

   /* retrieve message */
   size = TEXT_SIZE;
   el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to,
               reply_to, attachment, encoding, locked_by, draft);

   /* find message head */
   if (atoi(in_reply_to))
      head_id = find_thread_head(lbs, atoi(in_reply_to));
   else
      head_id = message_id;

   n_attr_disp = lbs->n_attr + 2;
   strcpy(disp_attr[0], loc("ID"));
   strcpy(disp_attr[1], loc("Date"));
   memcpy(disp_attr + 2, attr_list, sizeof(attr_list));

   size = TEXT_SIZE;
   el_retrieve(lbs, head_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to,
               reply_to, attachment, encoding, locked_by, draft);

   rsprintf("<tr><td><table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");

   display_line(lbs, head_id, 0, "Threaded", 1, 0, FALSE, 0,
                FALSE, FALSE, date, in_reply_to, reply_to, n_attr_disp, disp_attr, NULL, attrib, lbs->n_attr,
                text, FALSE, attachment, encoding, 0, &n_display, locked_by, message_id, NULL, highlight_mid,
                absolute_links, draft);

   if (reply_to[0]) {
      p = reply_to;
      do {
         display_reply(lbs, atoi(p), FALSE, 1, 0, n_attr_disp, disp_attr, FALSE, 1, message_id, NULL,
                       highlight_mid, absolute_links);

         while (*p && isdigit(*p))
            p++;
         while (*p && (*p == ',' || *p == ' '))
            p++;
      } while (*p);
   }

   rsprintf("</table>\n");
   rsprintf("</td></tr>\n");

   xfree(text);
}

Project: elog

void compare_attributes(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], int *n) {
   int status, i, n_reply;
   char reply_to[MAX_REPLY_TO * 10], *attr, *list;

   attr = xmalloc(MAX_N_ATTR * NAME_LENGTH);
   assert(attr);
   status = el_retrieve(lbs, message_id, NULL, attr_list, (char (*)[NAME_LENGTH]) attr, lbs->n_attr,
                        NULL, NULL, NULL, reply_to, NULL, NULL, NULL, NULL);
   if (status != EL_SUCCESS) {
      xfree(attr);
      return;
   }

   if (*n == 0)
      memcpy(attrib, attr, sizeof(MAX_N_ATTR * NAME_LENGTH));
   else {
      for (i = 0; i < lbs->n_attr; i++)
         if (!strieq(attrib[i], attr + i * NAME_LENGTH))
            sprintf(attrib[i], "- %s -", loc("keep original values"));
   }
   (*n)++;
   if (isparam("elmode") && strieq(getparam("elmode"), "threaded")) {

      list = xmalloc(MAX_N_ATTR * NAME_LENGTH);
      assert(list);

      // go through all replies in threaded mode
      n_reply = strbreak(reply_to, (char (*)[NAME_LENGTH]) list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < n_reply; i++) {
         compare_attributes(lbs, atoi(list + i * NAME_LENGTH), attrib, n);

         xfree(list);
      }
   }

   xfree(attr);
}

Project: elog

void split_url(const char *url, char *host, int *port, char *subdir, char *param) {
   const char *p;
   char str[256];

   if (host)
      host[0] = 0;
   if (port)
      *port = 80;
   if (subdir)
      subdir[0] = 0;
   if (param)
      param[0] = 0;

   p = url;
   if (strncmp(url, "http://", 7) == 0)
      p += 7;
   if (strncmp(url, "https://", 8) == 0)
      p += 8;

   strncpy(str, p, sizeof(str));
   if (strchr(str, '/')) {
      if (subdir)
         strncpy(subdir, strchr(str, '/'), 256);
      *strchr(str, '/') = 0;
   }

   if (strchr(str, '?')) {
      if (subdir)
         strncpy(subdir, strchr(str, '?'), 256);
      *strchr(str, '?') = 0;
   }

   if (strchr(str, ':')) {
      if (port)
         *port = atoi(strchr(str, ':') + 1);
      *strchr(str, ':') = 0;
   }

   if (host)
      strcpy(host, str);

   if (subdir) {
      if (strchr(subdir, '?')) {
         strncpy(param, strchr(subdir, '?'), 256);
         *strchr(subdir, '?') = 0;
      }

      if (subdir[0] == 0)
         strcpy(subdir, "/");
   }
}

Project: elog

void el_decode_intlist(char *message, char *key, char *result, int size) {
   int i;

   if (result == NULL)
      return;

   *result = 0;
   el_decode(message, key, result, size);

   /* remove any non allowed characters */
   for (i = 0; i < size && i < (int) strlen(result); i++)
      if (!isdigit(result[i]) && result[i] != ' ' && result[i] != ',')
         result[i] = ' ';
}

Project: elog

int is_group(char *group) {
   int i;

   for (i = 0; i < n_lb_config; i++)
      if (strieq(group, lb_config[i].section_name))
         return 1;
   return 0;
}

Project: elog

void rsicon(char *name, char *comment, char *elcode) {
   rsprintf("<img align=\"middle\" name=\"%s\" src=\"icons/elc_%s.png\" alt=\"%s\" title=\"%s\" border=\"0\"",
            name, name, comment, comment);
   rsprintf(" onclick=\"elcode(document.form1.Text, '','%s')\"", elcode);
   rsprintf(" onmousedown=\"document.images.%s.src='icons/eld_%s.png'\"", name, name);
   rsprintf(" onmouseup=\"document.images.%s.src='icons/elc_%s.png'\"", name, name);
   rsprintf(" onmouseover=\"this.style.cursor='pointer';\" />");
}

Project: elog

int show_md5_page(LOGBOOK *lbs) {
   int i, j;
   char *buffer, error_str[256];
   unsigned char digest[16];

   /* header */
   rsprintf("HTTP/1.1 200 Document follows\r\n");
   rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
   rsprintf("Accept-Ranges: bytes\r\n");
   rsprintf("Connection: close\r\n");
   rsprintf("Content-Type: text/plain;charset=%s\r\n", DEFAULT_HTTP_CHARSET);
   rsprintf("Pragma: no-cache\r\n");
   rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n\r\n");

   /* calculate MD5 for logbook section in config file */
   load_config_section(lbs->name, &buffer, error_str);
   if (error_str[0])
      rsprintf("<h2>%s</h2>", error_str);
   else {
      rsprintf("ID: %6d MD5:", 0);

      remove_crlf(buffer);
      MD5_checksum(buffer, strlen(buffer), digest);

      for (i = 0; i < 16; i++)
         rsprintf("%02X", digest[i]);
      rsprintf("\n");
   }
   xfree(buffer);

   /* show MD5's of logbook entries */
   for (i = 0; i < *lbs->n_el_index; i++) {
      rsprintf("ID: %6d MD5:", lbs->el_index[i].message_id);
      for (j = 0; j < 16; j++)
         rsprintf("%02X", lbs->el_index[i].md5_digest[j]);
      rsprintf("\n");
   }

   keep_alive = FALSE;

   return EL_SUCCESS;
}

Project: elog

void show_page_navigation(LOGBOOK *lbs, int n_msg, int page_n, int n_page) {
   int i, num_pages, skip, max_n_msg;
   char ref[256], str[256];

   if (!page_n || n_msg <= n_page)
      return;

   rsprintf("<tr><td class=\"menuframe\"><span class=\"menu3\">\n");

   rsprintf("%s \n", loc("Goto page"));

   if (page_n > 1) {
      sprintf(ref, "page%d", page_n - 1);
      build_ref(ref, sizeof(ref), "", "", "", "");

      rsprintf("<a href=\"%s\">%s</a>&nbsp;&nbsp;", ref, loc("Previous"));
   }

   if (page_n && n_msg > n_page) {
      /* number of pages */
      num_pages = (n_msg - 1) / n_page + 1;
      skip = FALSE;

      for (i = 1; i <= num_pages; i++) {
         sprintf(ref, "page%d", i);
         build_ref(ref, sizeof(ref), "", "", "", "");

         if (i <= 3 || (i >= page_n - 1 && i <= page_n + 1) || i >= num_pages - 2) {
            if (i > 1 && !skip)
               rsprintf(", \n");

            skip = FALSE;
         } else {
            if (!skip)
               rsprintf("&nbsp;...&nbsp;");

            skip = TRUE;
         }

         if (skip)
            continue;

         if (page_n == i)
            rsprintf("%d", i);
         else
            rsprintf("<a href=\"%s\">%d</a>", ref, i);

         /*
            if (i == num_pages )
            rsprintf("&nbsp;&nbsp;\n");
            else
            rsprintf(", ");
          */
      }

      rsprintf("&nbsp;&nbsp;\n");
   }

   if (page_n != -1 && n_page < n_msg && page_n * n_page < n_msg) {
      sprintf(ref, "page%d", page_n + 1);
      build_ref(ref, sizeof(ref), "", "", "", "");

      rsprintf("<a href=\"%s\">%s</a>&nbsp;&nbsp;", ref, loc("Next"));
   }

   if (getcfg(lbs->name, "All display limit", str, sizeof(str)))
      max_n_msg = atoi(str);
   else
      max_n_msg = 500;

   if (page_n != -1 && n_page < n_msg && n_msg < max_n_msg) {
      sprintf(ref, "page");
      build_ref(ref, sizeof(ref), "", "", "", "");

      rsprintf("<a href=\"%s\">%s</a>\n", ref, loc("All"));
   }

   rsprintf("</span></td></tr>\n");
}

Project: elog

void display_line(LOGBOOK *lbs, int message_id, int number, char *mode, int expand, int level,
                  BOOL printable, int n_line, int show_attachments, int show_att_column,
                  char *date, char *in_reply_to, char *reply_to, int n_attr_disp,
                  char disp_attr[MAX_N_ATTR + 4][NAME_LENGTH], BOOL disp_attr_link[MAX_N_ATTR + 4],
                  char attrib[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, BOOL show_text,
                  char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], char *encoding, BOOL select,
                  int *n_display, char *locked_by, int highlight, regex_t *re_buf, int highlight_mid,
                  int absolute_link, char *draft) {
   char str[NAME_LENGTH], ref[256], *nowrap, rowstyle[80], tdstyle[80], format[256],
           file_name[MAX_PATH_LENGTH], *slist, *svalue, comment[256], param[80], subdir[256];
   char display[NAME_LENGTH], attr_icon[80];
   int i, j, n, i_line, index, colspan, n_attachments, line_len, thumb_status, max_line_len, n_lines,
           max_n_lines;
   BOOL skip_comma;
   FILE *f;
   struct tm *pts;
   time_t ltime;

   slist = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH);
   svalue = (char *) xmalloc((MAX_N_ATTR + 10) * NAME_LENGTH);

   _current_message_id = message_id;
   ref[0] = 0;
   if (absolute_link)
      compose_base_url(lbs, ref, sizeof(ref), FALSE);
   sprintf(ref + strlen(ref), "../%s/%d", lbs->name_enc, message_id);

   if (strieq(mode, "Summary")) {
      if (draft && draft[0])
         strcpy(rowstyle, "listdraft");
      else if (highlight_mid == message_id) {
         if (number % 2 == 1)
            strcpy(rowstyle, "list1h");
         else
            strcpy(rowstyle, "list2h");
      } else {
         if (number % 2 == 1)
            strcpy(rowstyle, "list1");
         else
            strcpy(rowstyle, "list2");
      }
   } else if (strieq(mode, "Full")) {
      if (highlight_mid == message_id)
         strcpy(rowstyle, "list1h");
      else
         strcpy(rowstyle, "list1");
   } else if (strieq(mode, "Threaded")) {
      if (draft && draft[0])
         strcpy(rowstyle, "threaddraft");
      else if (highlight) {
         if (highlight == message_id)
            strcpy(rowstyle, "thread");
         else
            strcpy(rowstyle, "threadreply");
      } else {
         if (highlight_mid == message_id) {
            if (level == 0)
               strcpy(rowstyle, "threadh");
            else
               strcpy(rowstyle, "threadreply");
         } else {
            if (level == 0)
               strcpy(rowstyle, "thread");
            else
               strcpy(rowstyle, "threadreply");
         }
      }
   }

   rsprintf("<tr>");

   /* check attributes for row style */
   for (i = 0; i < n_attr; i++) {
      if (attrib[i][0] == 0)
         sprintf(str, "Style %s \"\"", attr_list[i]);
      else
         sprintf(str, "Style %s %s", attr_list[i], attrib[i]);
      if (getcfg(lbs->name, str, display, sizeof(display))) {
         sprintf(str, "%s\" style=\"%s", rowstyle, display);
         strlcpy(rowstyle, str, sizeof(rowstyle));
         break;
      }
   }

   /* only single cell for threaded display */
   if (strieq(mode, "Threaded")) {
      rsprintf("<td align=left class=\"%s\">", rowstyle);

      if (locked_by && locked_by[0]) {
         sprintf(str, "%s %s", loc("Entry is currently edited by"), locked_by);
         rsprintf("<img src=\"stop.png\" alt=\"%s\" title=\"%s\">&nbsp;", str, str);
      }

      /* show select box */
      if (select && level == 0)
         rsprintf("<input type=checkbox name=\"s%d\" value=%d>\n", (*n_display)++, message_id);

      for (i = 0; i < level; i++)
         rsprintf("&nbsp;&nbsp;&nbsp;");

      /* display "+" if expandable */
      if (expand == 0 && (reply_to[0] || in_reply_to[0]))
         rsprintf("+&nbsp;");
   }

   nowrap = printable ? "" : "nowrap";
   skip_comma = FALSE;

   if (getcfg(lbs->name, "List conditions", str, sizeof(str)) && atoi(str) == 1)
      evaluate_conditions(lbs, attrib);

   if (strieq(mode, "Threaded") && getcfg(lbs->name, "Thread display", display, sizeof(display))) {
      /* check if to use icon from attributes */
      attr_icon[0] = 0;
      if (getcfg(lbs->name, "Thread icon", attr_icon, sizeof(attr_icon))) {
         for (i = 0; i < n_attr; i++)
            if (strieq(attr_list[i], attr_icon))
               break;
         if (i < n_attr && attrib[i][0])
            strcpy(attr_icon, attrib[i]);
         else
            attr_icon[0] = 0;
      }

      if (highlight != message_id)
         rsprintf("\n<a href=\"%s\">", ref);

      if (attr_icon[0])
         rsprintf("\n<img border=0 src=\"icons/%s\" alt=\"%s\" title=\"%s\">\n&nbsp;", attr_icon, attr_icon,
                  attr_icon);
      else {
         /* display standard icons */
         if (level == 0)
            rsprintf("\n<img border=0 src=\"entry.png\" alt=\"%s\" title=\"%s\">\n&nbsp;", loc("Entry"),
                     loc("Entry"));
         else
            rsprintf("\n<img border=0 src=\"reply.png\" alt=\"%s\" title=\"%s\">\n&nbsp;", loc("Reply"),
                     loc("Reply"));
      }
      if (highlight != message_id)
         rsprintf("</a>\n");

      j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib, TRUE);
      sprintf(str, "%d", message_id);
      add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id", str, &j);
      add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time", date,
                     &j, 0);

      strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                    j);

      if (highlight != message_id)
         rsprintf("\n<a href=\"%s\">\n", ref);
      else
         rsprintf("\n<b>");


      if (is_html(display) && !is_script(display) && html_allowed(lbs))
         rsputs(display);
      else
         rsputs2(lbs, absolute_link, display);

      rsputs("&nbsp;");
      for (i = n = 0; i < MAX_ATTACHMENTS; i++)
         if (attachment && attachment[i][0])
            n++;
      if (n > 5) {
         if (highlight != message_id)
            rsprintf("<a href=\"%s\">", ref);
         rsprintf("<b>%dx", n);
         rsprintf("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\"></b></a>");
      } else {
         for (i = 0; i < MAX_ATTACHMENTS; i++)
            if (attachment && attachment[i][0]) {
               strlcpy(str, attachment[i], sizeof(str));
               str[13] = 0;
               sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[i] + 14);
               url_encode(ref, sizeof(ref));    /* for file names with special characters like "+" */

               rsprintf("<a href=\"%s\" target=\"_blank\">", ref);
               rsprintf
                       ("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\" alt=\"%s\" title=\"%s\"></a>",
                        attachment[i] + 14, attachment[i] + 14);
            }
      }

      if (highlight != message_id)
         rsprintf("</a>\n", ref);
      else
         rsprintf("</b>\n");
   } else {
      /* show select box */
      if (select && !strieq(mode, "Threaded")) {
         rsprintf("<td class=\"%s\">", rowstyle);
         rsprintf("<input type=checkbox name=\"s%d\" value=%d>\n", (*n_display)++, message_id);
         rsprintf("</td>\n");
      }

      if (strieq(mode, "Threaded")) {
         if (highlight != message_id)
            rsprintf("\n<a href=\"%s\">\n", ref);
         else
            rsprintf("\n<b>");
      }

      skip_comma = TRUE;
      for (index = 0; index < n_attr_disp; index++) {
         if (strieq(disp_attr[index], loc("ID"))) {
            if (strieq(mode, "Threaded")) {
               if (level == 0)
                  rsprintf("\n<img border=0 src=\"entry.png\" alt=\"%s\" title=\"%s\">&nbsp;", loc("Entry"),
                           loc("Entry"));
               else
                  rsprintf("\n<img border=0 src=\"reply.png\" alt=\"%s\" title=\"%s\">&nbsp;", loc("Reply"),
                           loc("Reply"));

            } else {

               rsprintf("<td class=\"%s\">", rowstyle);

               if (locked_by && locked_by[0]) {
                  sprintf(str, "%s %s", loc("Entry is currently edited by"), locked_by);
                  rsprintf("\n<img src=\"stop.png\" alt=\"%s\" title=\"%s\">&nbsp;", str, str);
               }

               if (draft && draft[0]) {
                  strlcpy(display, loc("Draft"), sizeof(display));
               } else {
                  if (getcfg(lbs->name, "ID display", display, sizeof(display))) {
                     j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                                          attrib, TRUE);
                     sprintf(str, "%d", message_id);
                     add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                                    "message id",
                                    str, &j);
                     add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                                    "entry time", date, &j, 0);

                     strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist,
                                   (char (*)[NAME_LENGTH]) svalue, j);

                  } else
                     sprintf(display, "%d", message_id);
               }

               rsprintf("\n<a href=\"%s\">&nbsp;&nbsp;%s&nbsp;&nbsp;</a>\n", ref, display);
               rsprintf("</td>\n");
            }
         }

         if (strieq(disp_attr[index], loc("Logbook"))) {
            if (strieq(mode, "Threaded")) {
               if (skip_comma) {
                  rsprintf(" %s", lbs->name);
                  skip_comma = FALSE;
               } else
                  rsprintf(", %s", lbs->name);
            } else {
               if (disp_attr_link == NULL || disp_attr_link[index])
                  rsprintf("\n<td class=\"%s\" %s><a href=\"%s\">%s</a></td>\n", rowstyle, nowrap, ref,
                           lbs->name);
               else
                  rsprintf("\n<td class=\"%s\" %s>%s</td>\n", rowstyle, nowrap, lbs->name);
            }
         }

         if (strieq(disp_attr[index], loc("Edit"))) {
            if (!strieq(mode, "Threaded")) {
               rsprintf("\n<td class=\"%s\" %s><a href=\"%s?cmd=%s\">", rowstyle, nowrap, ref, loc("Edit"));
               rsprintf("\n<img src=\"edit.png\" border=0 alt=\"%s\" title=\"%s\"></a></td>\n",
                        loc("Edit entry"), loc("Edit entry"));
            }
         }

         if (strieq(disp_attr[index], loc("Delete"))) {
            if (!strieq(mode, "Threaded")) {
               rsprintf("\n<td class=\"%s\" %s><a href=\"%s?cmd=%s\">", rowstyle, nowrap, ref, loc("Delete"));
               rsprintf("\n<img src=\"delete.png\" border=0 alt=\"%s\" title=\"%s\"></a></td>\n",
                        loc("Delete entry"), loc("Delete entry"));
            }
         }

         if (strieq(disp_attr[index], loc("Date"))) {
            if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
               strcpy(format, DEFAULT_TIME_FORMAT);

            ltime = date_to_ltime(date);
            pts = localtime(&ltime);
            assert(pts);
            my_strftime(str, sizeof(str), format, pts);

            if (strieq(mode, "Threaded")) {
               if (skip_comma) {
                  rsprintf(" %s", str);
                  skip_comma = FALSE;
               } else
                  rsprintf(", %s", str);
            } else {
               if (disp_attr_link == NULL || disp_attr_link[index])
                  rsprintf("\n<td class=\"%s\" %s><a href=\"%s\">%s</a></td>\n", rowstyle, nowrap, ref, str);
               else
                  rsprintf("\n<td class=\"%s\" %s>%s</td>\n", rowstyle, nowrap, str);
            }
         }

         for (i = 0; i < n_attr; i++)
            if (strieq(disp_attr[index], attr_list[i])) {

               /* check attributes for cell style */
               strlcpy(tdstyle, rowstyle, sizeof(tdstyle));
               sprintf(str, "Cell Style %s %s", attr_list[i], attrib[i]);
               if (getcfg(lbs->name, str, display, sizeof(display))) {
                  sprintf(str, "%s\" style=\"%s", rowstyle, display);
                  strlcpy(tdstyle, str, sizeof(rowstyle));
               }

               if (strieq(mode, "Threaded")) {
                  if (strieq(attr_options[i][0], "boolean")) {
                     if (atoi(attrib[i]) == 1) {
                        if (skip_comma) {
                           rsprintf(" ");
                           skip_comma = FALSE;
                        } else
                           rsprintf(", ");

                        if (is_html(attrib[i]) && !is_script(attrib[i]) && html_allowed(lbs))
                           rsputs(attrib[i]);
                        else
                           rsputs2(lbs, absolute_link, attrib[i]);

                        rsprintf("&nbsp");
                     }
                  } else if (attr_flags[i] & AF_DATE) {
                     if (skip_comma) {
                        rsprintf(" ");
                        skip_comma = FALSE;
                     } else
                        rsprintf(", ");

                     sprintf(str, "Date format %s", attr_list[i]);
                     if (!getcfg(lbs->name, str, format, sizeof(format)))
                        if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
                           strcpy(format, DEFAULT_DATE_FORMAT);

                     ltime = atoi(attrib[i]);
                     pts = localtime(&ltime);
                     assert(pts);
                     if (ltime == 0)
                        strcpy(str, "-");
                     else
                        my_strftime(str, sizeof(str), format, pts);

                     rsputs(str);
                  } else if (attr_flags[i] & AF_DATETIME) {
                     if (skip_comma) {
                        rsprintf(" ");
                        skip_comma = FALSE;
                     } else
                        rsprintf(", ");

                     sprintf(str, "Time format %s", attr_list[i]);
                     if (!getcfg(lbs->name, str, format, sizeof(format)))
                        if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
                           strcpy(format, DEFAULT_TIME_FORMAT);

                     ltime = atoi(attrib[i]);
                     pts = localtime(&ltime);
                     assert(pts);
                     if (ltime == 0)
                        strcpy(str, "-");
                     else
                        my_strftime(str, sizeof(str), format, pts);

                     rsputs(str);
                  } else if (attr_flags[i] & AF_ICON) {

                     sprintf(str, "Icon comment %s", attrib[i]);
                     getcfg(lbs->name, str, comment, sizeof(comment));
                     if (!comment[0])
                        strcpy(comment, attrib[i]);

                     if (attrib[i][0])
                        rsprintf("&nbsp;\n<img border=0 src=\"icons/%s\" alt=\"%s\" title=\"%s\">&nbsp;",
                                 attrib[i], comment, comment);
                  } else {
                     if (skip_comma) {
                        rsprintf(" ");
                        skip_comma = FALSE;
                     } else
                        rsprintf(", ");

                     if (is_html(attrib[i]) && !is_script(attrib[i]) && html_allowed(lbs))
                        rsputs(attrib[i]);
                     else
                        rsputs2(lbs, absolute_link, attrib[i]);

                  }
               } else {
                  if (strieq(attr_options[i][0], "boolean")) {
                     if (atoi(attrib[i]) == 1)
                        rsprintf("\n<td class=\"%s\"><input type=checkbox checked disabled></td>\n", tdstyle);
                     else
                        rsprintf("\n<td class=\"%s\"><input type=checkbox disabled></td>\n", tdstyle);
                  } else if (attr_flags[i] & AF_DATE) {

                     sprintf(str, "Date format %s", attr_list[i]);
                     if (!getcfg(lbs->name, str, format, sizeof(format)))
                        if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
                           strcpy(format, DEFAULT_DATE_FORMAT);

                     ltime = atoi(attrib[i]);
                     pts = localtime(&ltime);
                     assert(pts);
                     if (ltime == 0)
                        strcpy(str, "-");
                     else
                        my_strftime(str, sizeof(str), format, pts);

                     if (disp_attr_link == NULL || disp_attr_link[index])
                        rsprintf("\n<td class=\"%s\" %s><a href=\"%s\">%s</a></td>\n", tdstyle, nowrap, ref,
                                 str);
                     else
                        rsprintf("\n<td class=\"%s\" %s>%s</td>\n", tdstyle, nowrap, str);
                  } else if (attr_flags[i] & AF_DATETIME) {

                     sprintf(str, "Time format %s", attr_list[i]);
                     if (!getcfg(lbs->name, str, format, sizeof(format)))
                        if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
                           strcpy(format, DEFAULT_TIME_FORMAT);

                     ltime = atoi(attrib[i]);
                     pts = localtime(&ltime);
                     assert(pts);
                     if (ltime == 0)
                        strcpy(str, "-");
                     else
                        my_strftime(str, sizeof(str), format, pts);

                     if (disp_attr_link == NULL || disp_attr_link[index])
                        rsprintf("\n<td class=\"%s\" %s><a href=\"%s\">%s</a></td>\n", tdstyle, nowrap, ref,
                                 str);
                     else
                        rsprintf("\n<td class=\"%s\" %s>%s</td>\n", tdstyle, nowrap, str);
                  } else if (attr_flags[i] & AF_ICON) {
                     rsprintf("<td class=\"%s\">", rowstyle);

                     sprintf(str, "Icon comment %s", attrib[i]);
                     getcfg(lbs->name, str, comment, sizeof(comment));
                     if (!comment[0])
                        strcpy(comment, attrib[i]);

                     if (attrib[i][0])
                        rsprintf("\n<img border=0 src=\"icons/%s\" alt=\"%s\" title=\"%s\">", attrib[i],
                                 comment, comment);

                     rsprintf("&nbsp;</td>");
                  } else {
                     rsprintf("<td class=\"%s\">", tdstyle);

                     if (disp_attr_link == NULL || disp_attr_link[index])
                        rsprintf("<a href=\"%s\">", ref);

                     sprintf(str, "List Change %s", attr_list[i]);
                     if (getcfg(lbs->name, str, display, sizeof(display))) {
                        j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist,
                                             (char (*)[NAME_LENGTH]) svalue, attrib, TRUE);
                        sprintf(str, "%d", message_id);
                        add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                                       "message id", str, &j);
                        add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                                       "entry time", date, &j, 0);

                        strsubst_list(display, sizeof(display), (char (*)[NAME_LENGTH]) slist,
                                      (char (*)[NAME_LENGTH]) svalue, j);

                     } else
                        strcpy(display, attrib[i]);

                     if (is_html(display) && !is_script(display) && html_allowed(lbs))
                        rsputs(display);
                     else {
                        if (isparam(attr_list[i])) {
                           highlight_searchtext(re_buf + 1 + i, display, str, TRUE);
                           strlcpy(display, str, sizeof(display));
                        } else if (isparam("subtext") && isparam("sall") && atoi(getparam("sall"))) {
                           highlight_searchtext(re_buf, display, str, TRUE);
                           strlcpy(display, str, sizeof(display));
                        }
                        rsputs2(lbs, absolute_link, display);
                     }

                     if (disp_attr_link == NULL || disp_attr_link[index])
                        rsprintf("</a>");

                     /* at least one space to produce non-empty table cell */
                     if (!display[0])
                        rsprintf("&nbsp;");

                     rsprintf("</td>");
                  }
               }
            }
      }

      if (strieq(mode, "Threaded")) {

         rsputs("&nbsp;");

         for (i = n = 0; i < MAX_ATTACHMENTS; i++)
            if (attachment && attachment[i][0])
               n++;
         if (n > 5) {
            if (highlight != message_id)
               rsprintf("<a href=\"%s\">", ref);
            rsprintf("<b>%dx", n);
            rsprintf("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\"></b></a>");
         } else {
            for (i = 0; i < MAX_ATTACHMENTS; i++)
               if (attachment && attachment[i][0]) {
                  strlcpy(str, attachment[i], sizeof(str));
                  str[13] = 0;
                  sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[i] + 14);
                  url_encode(ref, sizeof(ref)); /* for file names with special characters like "+" */

                  rsprintf("<a href=\"%s\" target=\"_blank\">", ref);
                  rsprintf
                          ("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\" alt=\"%s\" title=\"%s\"></a>",
                           attachment[i] + 14, attachment[i] + 14);
               }
         }

         if (highlight != message_id)
            rsprintf("</a>\n");
         else
            rsprintf("</b>\n");
      }
   }

   if (strieq(mode, "Threaded") && expand > 1 && show_text) {
      rsprintf("</td></tr>\n");

      rsprintf("<tr><td class=\"summary\">");

      if (expand == 2) {
         for (i = i_line = line_len = 0; i < (int) sizeof(str) - 1; i++, line_len++) {
            str[i] = text[i];
            if (line_break(text + i, encoding)) {
               i_line++;
               line_len = 0;
            } else
               /* limit line length to 150 characters */
            if (line_len > 150 && text[i] == ' ') {
               str[i] = '\n';
               i_line++;
               line_len = 0;
            }

            if (i_line == n_line)
               break;
         }
         str[i] = 0;

         /* only show text, not to rip apart HTML documents,
            e.g. only the start of a table */
         if (strieq(encoding, "HTML"))
            strip_html(str);
         if (str[0])
            strencode_nouml(str);
         else
            rsputs("&nbsp;");
      } else {
         if (strieq(encoding, "plain")) {
            rsputs("<pre>");
            if (text[0])
               rsputs2(lbs, absolute_link, text);
            else
               rsputs("&nbsp;");
            rsputs("</pre>");
         } else if (strieq(encoding, "ELCode"))
            rsputs_elcode(lbs, FALSE, text);
         else if (text[0])
            rsputs(text);
         else
            rsputs("&nbsp;");
      }

      rsprintf("</td></tr>\n");
   }

   if (strieq(mode, "Summary") && n_line > 0 && show_text) {
      rsprintf("<td class=\"summary\">");

      if (getcfg(lbs->name, "Summary line length", param, sizeof(param)))
         max_line_len = atoi(param);
      else
         max_line_len = n_line >= 10 ? 140 : 40;
      for (i = i_line = line_len = 0; i < (int) sizeof(str) - 1; line_len++, i++) {
         str[i] = text[i];
         if (line_break(text + i, encoding)) {
            i_line++;
            line_len = 0;
         } else
            /* limit line length to max_line_len characters */
         if (line_len > max_line_len && text[i] == ' ') {
            str[i] = '\n';
            i_line++;
            line_len = 0;
         }

         if (i_line == n_line)
            break;
      }
      str[i] = 0;

      /* only show text, not to rip apart HTML documents,
         e.g. only the start of a table */
      if (strieq(encoding, "HTML"))
         strip_html(str);
      if (str[0])
         strencode_nouml(str);
      else
         rsputs("&nbsp;");

      rsputs("</td>\n");
   }

   if (show_att_column) {
      /* show attachment icons */
      rsputs("<td class=\"listatt\">&nbsp;");

      for (i = n = 0; i < MAX_ATTACHMENTS; i++)
         if (attachment && attachment[i][0])
            n++;
      if (n > 5) {
         if (highlight != message_id)
            rsprintf("<a href=\"%s\">", ref);
         rsprintf("<b>%dx", n);
         rsprintf("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\"></b></a>");
      } else {
         for (i = 0; i < MAX_ATTACHMENTS; i++)
            if (attachment && attachment[i][0]) {
               strlcpy(str, attachment[i], sizeof(str));
               str[13] = 0;
               sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[i] + 14);
               url_encode(ref, sizeof(ref));    /* for file names with special characters like "+" */

               rsprintf("<a href=\"%s\" target=\"_blank\">", ref);
               rsprintf
                       ("<img border=\"0\" align=\"absmiddle\" src=\"attachment.png\" alt=\"%s\" title=\"%s\"></a>",
                        attachment[i] + 14, attachment[i] + 14);
            }
      }

      rsputs("&nbsp;</td>");
   }

   colspan = n_attr_disp;

   if (select)
      colspan++;

   if (strieq(mode, "Full") && show_text) {
      if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) {
         rsprintf("<tr><td class=\"messagelist\" colspan=%d>", colspan);

         if (strieq(encoding, "plain")) {
            rsputs("<pre>");
            rsputs2(lbs, absolute_link, text);
            rsputs("</pre>");
         } else if (strieq(encoding, "ELCode"))
            rsputs_elcode(lbs, FALSE, text);
         else
            rsputs(text);

         rsprintf("</td></tr>\n");
      }

      /* count number of attachments */
      n_attachments = 0;
      if (show_attachments) {
         for (index = 0; index < MAX_ATTACHMENTS; index++) {
            if (attachment[index][0]) {
               /* check if attachment is inlined */
               sprintf(str, "[img]elog:/%d[/img]", index + 1);
               if (strieq(encoding, "ELCode") && stristr(text, str))
                  continue;

               n_attachments++;
            }
         }
      }

      for (index = 0; index < MAX_ATTACHMENTS; index++) {
         if (show_attachments && attachment[index][0]) {

            /* check if attachment is inlined */
            if (is_inline_attachment(encoding, message_id, text, index, attachment[index]))
               continue;

            strlcpy(str, attachment[index], sizeof(str));
            str[13] = 0;
            sprintf(ref, "../%s/%s/%s", lbs->name, str, attachment[index] + 14);
            url_encode(ref, sizeof(ref));       /* for file names with special characters like "+" */

            strlcpy(file_name, lbs->data_dir, sizeof(file_name));
            generate_subdir_name(attachment[index], subdir, sizeof(subdir));
            strlcat(file_name, subdir, sizeof(file_name));
            strlcat(file_name, attachment[index], sizeof(file_name));
            thumb_status = create_thumbnail(lbs, file_name);

            if (!show_attachments) {
               rsprintf("<a href=\"%s\" target=\"_blank\">%s</a>&nbsp;&nbsp;&nbsp;&nbsp; ", ref,
                        attachment[index] + 14);
            } else {

               if (thumb_status) {
                  rsprintf
                          ("<tr><td colspan=%d class=\"attachment\">%s %d: <a href=\"%s\" target=\"_blank\">%s</a>\n",
                           colspan, loc("Attachment"), index + 1, ref, attachment[index] + 14);

                  if (show_attachments) {
                     rsprintf("<tr><td colspan=%d class=\"attachmentframe\">\n", colspan);
                     if (thumb_status == 2) {
                        for (i = 0;; i++) {
                           strlcpy(str, file_name, sizeof(str));
                           if (chkext(str, ".pdf") || chkext(str, ".ps"))
                              if (strrchr(str, '.'))
                                 *strrchr(str, '.') = 0;
                           sprintf(str + strlen(str), "-%d.png", i);
                           if (file_exist(str)) {
                              strlcpy(str, ref, sizeof(str));
                              if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
                                 if (strrchr(str, '.'))
                                    *strrchr(str, '.') = 0;
                              sprintf(str + strlen(str), "-%d.png", i);
                              rsprintf("<a name=\"att%d\" href=\"%s\">\n", index + 1, ref);
                              rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\"></a>\n", str,
                                       attachment[index] + 14, attachment[index] + 14);
                           } else
                              break;
                        }
                     } else {
                        rsprintf("<a name=\"att%d\" href=\"%s\">\n", index + 1, ref);
                        strlcpy(str, ref, sizeof(str));
                        if (chkext(str, ".pdf") || chkext(str, ".ps"))
                           if (strrchr(str, '.'))
                              *strrchr(str, '.') = 0;
                        strlcat(str, ".png", sizeof(str));
                        rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\"></a>\n", str, attachment[index]
                                                                                        + 14,
                                 attachment[index] + 14);
                     }
                     rsprintf("</td></tr>\n\n");
                  }
               } else {
                  if (is_image(attachment[index])) {
                     rsprintf
                             ("<tr><td colspan=%d class=\"attachment\">%s %d: <a href=\"%s\" target=\"_blank\">%s</a>\n",
                              colspan, loc("Attachment"), index + 1, ref, attachment[index] + 14);
                     if (show_attachments) {
                        rsprintf("</td></tr><tr>");
                        rsprintf("<td colspan=%d class=\"attachmentframe\">", colspan);
                        rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\">", ref, attachment[index] + 14,
                                 attachment[index] + 14);
                        rsprintf("</td></tr>\n");
                     }
                  } else {
                     rsprintf
                             ("<tr><td colspan=%d class=\"attachment\">%s %d: <a href=\"%s\" target=\"_blank\">%s</a>\n",
                              colspan, loc("Attachment"), index + 1, ref, attachment[index] + 14);

                     strlcpy(file_name, lbs->data_dir, sizeof(file_name));
                     generate_subdir_name(attachment[index], subdir, sizeof(subdir));
                     strlcat(file_name, subdir, sizeof(file_name));
                     strlcat(file_name, attachment[index], sizeof(file_name));

                     if (is_ascii(file_name) && !chkext(attachment[index], ".PS")
                         && !chkext(attachment[index], ".PDF") && !chkext(attachment[index], ".EPS")
                         && !chkext(attachment[index], ".SVG")
                         && !chkext(attachment[index], ".HTM") && !chkext(attachment[index], ".HTML")
                         && show_attachments) {

                        rsprintf("</td></tr>\n");

                        /* display attachment */
                        rsprintf("<tr><td colspan=%d class=\"messageframe\">\n", colspan);

                        /* anchor for references */
                        rsprintf("<a name=\"att%d\"></a>\n", index + 1);

                        /* display attachment */

                        if (!chkext(attachment[index], ".HTML"))
                           rsprintf("<pre class=\"messagepre\">");

                        strlcpy(file_name, lbs->data_dir, sizeof(file_name));
                        generate_subdir_name(attachment[index], subdir, sizeof(subdir));
                        strlcat(file_name, subdir, sizeof(file_name));
                        strlcat(file_name, attachment[index], sizeof(file_name));

                        f = fopen(file_name, "rt");

                        n_lines = 0;
                        if (getcfg(lbs->name, "Attachment lines", str, sizeof(str)))
                           max_n_lines = atoi(str);
                        else
                           max_n_lines = 300;

                        if (f != NULL) {
                           while (!feof(f)) {
                              str[0] = 0;
                              fgets(str, sizeof(str), f);

                              if (n_lines < max_n_lines) {
                                 if (!chkext(attachment[index], ".HTML"))
                                    rsputs2(lbs, absolute_link, str);
                                 else
                                    rsputs(str);
                              }
                              n_lines++;
                           }
                           fclose(f);
                        }

                        if (!chkext(attachment[index], ".HTML"))
                           rsprintf("</pre>");
                        rsprintf("\n");
                        if (max_n_lines == 0)
                           rsprintf("<i><b>%d lines</b></i>\n", n_lines);
                        else if (n_lines > max_n_lines)
                           rsprintf("<i><b>... %d more lines ...</b></i>\n", n_lines - max_n_lines);
                     }
                     rsprintf("</td></tr>\n");
                  }
               }
            }
         }
      }
   }

   xfree(slist);
   xfree(svalue);
}

Project: elog

BOOL is_command_allowed(LOGBOOK *lbs, char *command, int message_id) {
   char str[1000], menu_str[1000], other_str[1000];
   char menu_item[MAX_N_LIST][NAME_LENGTH];
   int i, n;

   if (command[0] == 0)
      return TRUE;

   /* check for guest access */
   if (!getcfg(lbs->name, "Guest Menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs))
      getcfg(lbs->name, "Menu commands", menu_str, sizeof(menu_str));

   /* default menu commands */
   if (menu_str[0] == 0) {
      strcpy(menu_str, "List, New, Edit, Delete, Reply, Duplicate, Synchronize, Find, ");

      if (getcfg(lbs->name, "Password file", str, sizeof(str))) {

         if (is_admin_user(lbs, getparam("unm"))) {

            strcat(menu_str, "Admin, ");
            strcat(menu_str, "Change config file, ");
            strcat(menu_str, "Delete this logbook, ");
            strcat(menu_str, "Rename this logbook, ");
            strcat(menu_str, "Create new logbook, ");
            strcat(menu_str, "GetPwdFile, ");

            if (is_admin_user(NULL, getparam("unm"))) {

               if (lbs->top_group[0]) {
                  sprintf(str, "Change [global %s]", lbs->top_group);
                  strcat(menu_str, str);
                  strcat(menu_str, ", ");
               }

               if (!lbs->top_group[0] || (is_admin_user(NULL, getparam("unm")))) {

                  strcat(menu_str, "Change [global]");
                  strcat(menu_str, ", ");
               }
            }
         }
         strcat(menu_str, "Config, Logout, ");
      } else {
         strcat(menu_str, "Config, ");
         strcat(menu_str, "Change [global], ");
         strcat(menu_str, "Delete this logbook, ");
         strcat(menu_str, "Rename this logbook, ");
         strcat(menu_str, "Create new logbook, ");
      }

      strcat(menu_str, "Help, HelpELCode, ");

   } else {
      /* check for admin command */
      n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE);
      menu_str[0] = 0;
      for (i = 0; i < n; i++) {
         if (strcmp(menu_item[i], "Admin") == 0) {
            if (!is_admin_user(lbs, getparam("unm")))
               continue;
         }
         strcat(menu_str, menu_item[i]);
         strcat(menu_str, ", ");
      }

      strcat(menu_str, "HelpELCode, Synchronize, ");

      if (is_admin_user(lbs, getparam("unm"))) {

         strcat(menu_str, "Change config file, ");
         strcat(menu_str, "Delete this logbook, ");
         strcat(menu_str, "Rename this logbook, ");
         strcat(menu_str, "Create new logbook, ");
         strcat(menu_str, "GetPwdFile, ");

         if (is_admin_user(NULL, getparam("unm"))) {

            if (lbs->top_group[0]) {
               sprintf(str, "Change [global %s]", lbs->top_group);
               strcat(menu_str, str);
               strcat(menu_str, ", ");
            }

            if (!lbs->top_group[0] || (is_admin_user(NULL, getparam("unm")))) {

               strcat(menu_str, "Change [global]");
               strcat(menu_str, ", ");
            }
         }
      }
   }

   /* check list menu commands */
   str[0] = 0;
   if (!getcfg(lbs->name, "Guest List Menu commands", str, sizeof(str)) || logged_in(lbs))
      getcfg(lbs->name, "list menu commands", str, sizeof(str));

   if (!str[0]) {
      if (!getcfg(lbs->name, "Guest Find Menu commands", str, sizeof(str)) || logged_in(lbs))
         getcfg(lbs->name, "Find Menu commands", str, sizeof(str));
   }

   if (str[0])
      strlcat(menu_str, str, sizeof(menu_str));
   else {
      strlcat(menu_str, "New, Find, Select, Last x, Help, HelpELCode, ", sizeof(menu_str));

      if (getcfg(lbs->name, "Password file", str, sizeof(str)))
         strlcat(menu_str, "Admin, Config, Logout, ", sizeof(menu_str));
      else
         strlcat(menu_str, "Config, ", sizeof(menu_str));
   }

   strlcpy(other_str, "Preview, Back, Search, Download, Import, CSV Import, XML Import, ", sizeof(other_str));
   strlcat(other_str, "Cancel, First, Last, Previous, Next, Requested, Forgot, ", sizeof(other_str));

   /* only allow Submit & Co if "New" is allowed */
   if (stristr(menu_str, "New,") || stristr(menu_str, "Edit"))
      strlcat(other_str, "Update, Upload, Submit, Save, ", sizeof(other_str));

   /* add save for new user registration */
   if (isparam("new_user_name"))
      strlcat(other_str, "Save, ", sizeof(other_str));

   /* admin commands */
   if (is_admin_user(lbs, getparam("unm"))) {
      strlcat(other_str, "Remove user, New user, Activate, ", sizeof(other_str));
   } else if (getcfg(lbs->name, "Self register", str, sizeof(str)) && atoi(str) > 0) {
      strlcat(other_str, "Remove user, New user, ", sizeof(other_str));
   }

   /* allow always edit of draft messages */
   if (is_draft(lbs, message_id))
      strlcat(other_str, "Edit, ", sizeof(other_str));

   /* allow change password if "config" possible */
   if (strieq(command, loc("Change password")) && stristr(menu_str, "Config")) {
      return TRUE;
   }
      /* exclude non-localized submit for elog */
   else if (command[0] && strieq(command, "Submit")
            && (stristr(menu_str, "New,") || stristr(menu_str, "Edit"))) {
      return TRUE;
   }
      /* exclude other non-localized commands */
   else if (command[0] && strieq(command, "GetMD5")) {
      return TRUE;
   } else if (command[0] && strieq(command, "IM")) {
      return TRUE;
   }
      /* check if command is present in the menu list */
   else if (command[0]) {
      n = strbreak(menu_str, menu_item, MAX_N_LIST, ",", FALSE);
      for (i = 0; i < n; i++)
         if (strieq(command, menu_item[i]) || strieq(command, loc(menu_item[i])))
            break;

      if (i == n) {
         n = strbreak(other_str, menu_item, MAX_N_LIST, ",", FALSE);
         for (i = 0; i < n; i++)
            if (strieq(command, menu_item[i]) || strieq(command, loc(menu_item[i])))
               break;

         if (i == n)
            return FALSE;
      }
   }

   return TRUE;
}

Project: elog

int getcfg(char *group, char *param, char *value, int vsize)
/*
 Read parameter from configuration file.

 - if group == [global] and top group exists, read
 from [global <top group>]

 - if parameter not in [global <top group>], read from [global]

 - if group is logbook, read from logbook section

 - if parameter not in [<logbook>], read from [global <top group>]
 or [global]
 */
{
   char str[256];
   int status;

   /* if group is [global] and top group exists, read from there */
   if (strieq(group, "global") && getcfg_topgroup()) {
      sprintf(str, "global %s", getcfg_topgroup());
      status = getcfg(str, param, value, vsize);
      if (status)
         return status;
   }

   /* first check if parameter is under condition */
   if (_condition[0]) {
      status = getcfg_simple(group, param, value, vsize, TRUE);
      if (status)
         return status;
   }

   status = getcfg_simple(group, param, value, vsize, FALSE);
   if (status)
      return status;

   /* if parameter not found in logbook, look in [global] section */
   if (!group || is_logbook(group))
      return getcfg("global", param, value, vsize);

   return 0;
}

Project: elog

int xvasprintf(char **ptr, const char *format, va_list ap) {
   int n;
   va_list save;

#ifdef va_copy
   va_copy(save, ap);
#else
                                                                                                                           #ifdef __va_copy
   __va_copy(save, ap);
#else
   save = ap;
#endif
#endif

   n = vasprintf(ptr, format, save);

   if (n == -1 || !*ptr) {
      printf("Not enough memory");
      exit(EXIT_FAILURE);
   }

   return n;
}

Project: elog

int save_config(char *buffer, char *error) {
   int fh, i;
   char *buf;

   error[0] = 0;

   fh = open(config_file, O_RDWR | O_BINARY | O_CREAT, 0644);
   if (fh < 0) {
      sprintf(error, loc("Cannot open file <b>%s</b>"), config_file);
      strcat(error, ": ");
      strcat(error, strerror(errno));
      return 0;
   }

   buf = (char *) xmalloc(strlen(buffer) * 2);
   strlcpy(buf, buffer, strlen(buffer) * 2);
   adjust_crlf(buf, strlen(buffer) * 2);

   i = write(fh, buf, strlen(buf));
   if (i < (int) strlen(buf)) {
      sprintf(error, loc("Cannot write to <b>%s</b>"), config_file);
      strcat(error, ": ");
      strcat(error, strerror(errno));
      close(fh);
      return 0;
   }

   TRUNCATE(fh);

   close(fh);

   /* force re-read of config file */
   check_config_file(TRUE);

   return 1;
}

Project: elog

int delete_logbook(LOGBOOK *lbs, char *error) {
   int fh, i, length;
   char *buf, *p1, *p2;

   error[0] = 0;

   fh = open(config_file, O_RDWR | O_BINARY, 644);
   if (fh < 0) {
      sprintf(error, loc("Cannot open file <b>%s</b>"), config_file);
      strcat(error, ": ");
      strcat(error, strerror(errno));
      return 0;
   }

   /* remove logbook name in groups */
   change_logbook_in_group(lbs, "");

   /* read previous contents */
   length = lseek(fh, 0, SEEK_END);
   lseek(fh, 0, SEEK_SET);
   buf = xmalloc(length + 1);
   read(fh, buf, length);
   buf[length] = 0;

   /* find logbook config */
   p1 = (char *) find_section(buf, lbs->name);
   p2 = (char *) find_next_section(p1 + 1);

   if (p2) {
      i = strlen(p2) + 1;
      strlcpy(p1, p2, i);
   } else
      *p1 = 0;

   lseek(fh, 0, SEEK_SET);
   i = write(fh, buf, strlen(buf));
   if (i < (int) strlen(buf)) {
      sprintf(error, loc("Cannot write to <b>%s</b>"), config_file);
      strcat(error, ": ");
      strcat(error, strerror(errno));
      close(fh);
      xfree(buf);
      return 0;
   }

   TRUNCATE(fh);

   close(fh);
   xfree(buf);

   /* force re-read of config file */
   check_config_file(TRUE);
   el_index_logbooks();

   return 1;
}

Project: elog

void show_elog_delete(LOGBOOK *lbs, int message_id) {
   int i, status, reply = 0, next, nsel;
   char str[256], str2[256], in_reply_to[80], reply_to[MAX_REPLY_TO * 10], owner[256];
   char attrib[MAX_N_ATTR][NAME_LENGTH], mode[80];

   /* check for editing interval */
   if (isparam("nsel")) {
      for (i = 0; i < atoi(getparam("nsel")); i++) {
         sprintf(str, "s%d", i);
         if (isparam(str)) {
            status = check_edit_time(lbs, atoi(getparam(str)));
            if (!status) {
               return;
            }
         }
      }
   } else if (message_id) {
      status = check_edit_time(lbs, message_id);
      if (!status) {
         return;
      }
   }

   /* redirect if confirm = NO */
   if (isparam("confirm") && strcmp(getparam("confirm"), loc("No")) == 0) {
      if (message_id) {
         sprintf(str, "%d", message_id);
         redirect(lbs, str);
      } else {
         strlcpy(str, isparam("lastcmd") ? getparam("lastcmd") : "", sizeof(str));
         url_decode(str);
         redirect(lbs, str);
      }
      return;
   }

   if (isparam("elmode"))
      strlcpy(mode, getparam("elmode"), sizeof(mode));

   if (isparam("confirm")) {
      if (strcmp(getparam("confirm"), loc("Yes")) == 0) {
         if (message_id) {
            /* delete message */
            status = el_delete_message(lbs, message_id, TRUE, NULL, TRUE, TRUE);
            if (status != EL_SUCCESS) {
               sprintf(str, "%s = %d", loc("Error deleting message: status"), status);
               show_error(str);
               return;
            } else {
               strlcpy(str, isparam("nextmsg") ? getparam("nextmsg") : "", sizeof(str));
               if (atoi(str) == 0)
                  sprintf(str, "%d", el_search_message(lbs, EL_LAST, 0, TRUE));
               if (atoi(str) == 0)
                  redirect(lbs, "");
               else
                  redirect(lbs, str);
               return;
            }
         } else {
            if (isparam("nsel")) {
               for (i = 0; i < atoi(getparam("nsel")); i++) {
                  sprintf(str, "s%d", i);
                  if (isparam(str)) {
                     if (strieq(mode, "threaded"))
                        status = el_delete_message(lbs, atoi(getparam(str)), TRUE, NULL, TRUE, TRUE);
                     else
                        status = el_delete_message(lbs, atoi(getparam(str)), TRUE, NULL, TRUE, FALSE);
                  }
               }
            }

            redirect(lbs, isparam("lastcmd") ? getparam("lastcmd") : "");
            return;
         }
      }
   } else {
      /* check if at least one message is selected */
      if (!message_id) {
         nsel = isparam("nsel") ? atoi(getparam("nsel")) : 0;
         for (i = 0; i < nsel; i++) {
            sprintf(str, "s%d", i);
            if (isparam(str))
               break;
         }
         if (i == nsel) {
            show_error(loc("No entry selected for deletion"));
            return;
         }
      }

      /* check for author */
      if (getcfg(lbs->name, "Restrict edit", str, sizeof(str)) && atoi(str) == 1) {
         /* get message for reply/edit */

         el_retrieve(lbs, message_id, NULL, attr_list, attrib, lbs->n_attr, NULL, NULL,
                     NULL, NULL, NULL, NULL, NULL, NULL);

         if (!is_author(lbs, attrib, owner)) {
            strencode2(str2, owner, sizeof(str2));
            sprintf(str, loc("Only user <b>%s</b> can delete this entry"), str2);
            show_error(str);
            return;
         }
      }

      /* header */
      if (message_id)
         sprintf(str, "%d", message_id);
      else
         str[0] = 0;
      show_standard_header(lbs, TRUE, "Delete ELog entry", str, FALSE, NULL, NULL, 0);

      rsprintf("<table class=\"dlgframe\" cellspacing=0 align=center>");
      rsprintf("<tr><td class=\"dlgtitle\">\n");

      /* define hidden field for command */
      rsprintf("<input type=hidden name=cmd value=\"%s\">\n", loc("Delete"));

      if (!message_id) {
         rsprintf("%s</td></tr>\n", loc("Are you sure to delete these messages?"));

         rsprintf("<tr><td align=center class=\"dlgform\">\n");
         if (isparam("nsel"))
            rsprintf("<input type=hidden name=nsel value=%s>\n", getparam("nsel"));

         if (isparam("lastcmd")) {
            strlcpy(str, getparam("lastcmd"), sizeof(str));
            rsprintf("<input type=hidden name=lastcmd value=\"%s\">\n", str);
         }

         if (isparam("nsel")) {
            reply = FALSE;
            for (i = 0; i < atoi(getparam("nsel")); i++) {
               sprintf(str, "s%d", i);
               if (isparam(str)) {
                  rsprintf("#%s ", getparam(str));
                  rsprintf("<input type=hidden name=%s value=%s>\n", str, getparam(str));
               }

               if (!reply) {
                  el_retrieve(lbs, isparam(str) ? atoi(getparam(str)) : 0,
                              NULL, attr_list, NULL, 0, NULL, NULL, in_reply_to, reply_to, NULL, NULL, NULL,
                              NULL);
                  if (reply_to[0])
                     reply = TRUE;
               }
            }
         }

         rsprintf("</td></tr>\n");

         if (reply && strieq(mode, "threaded"))
            rsprintf("<tr><td align=center class=\"dlgform\">%s</td></tr>\n", loc("and all their replies"));

      } else {
         rsprintf("%s</td></tr>\n", loc("Are you sure to delete this entry?"));

         /* check for replies */

         /* retrieve original message */
         el_retrieve(lbs, message_id, NULL, attr_list, NULL, 0, NULL, NULL, in_reply_to, reply_to, NULL,
                     NULL, NULL, NULL);

         if (reply_to[0])
            rsprintf("<tr><td align=center class=\"dlgform\">#%d<br>%s</td></tr>\n", message_id,
                     loc("and all its replies"));
         else
            rsprintf("<tr><td align=center class=\"dlgform\">#%d</td></tr>\n", message_id);

         /* put link to next message */
         next = el_search_message(lbs, EL_NEXT, message_id, TRUE);

         rsprintf("<input type=hidden name=nextmsg value=%d>\n", next);
      }

      rsprintf("<tr><td align=center class=\"dlgform\"><input type=submit name=confirm value=\"%s\">\n",
               loc("Yes"));
      rsprintf("<input type=submit name=confirm value=\"%s\">\n", loc("No"));
      rsprintf("</td></tr>\n\n");
   }

   rsprintf("</table>\n");
   show_bottom_text(lbs);
   rsprintf("</body></html>\r\n");
}

Project: elog

int html_allowed(LOGBOOK *lbs) {
   char str[80];

   return (getcfg(lbs->name, "Allow HTML", str, sizeof(str)) && atoi(str) == 1);
}

Project: elog

size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm) {
   return strftime(s, max, fmt, tm);
}

Project: elog

void set_cookie(LOGBOOK *lbs, char *name, char *value, BOOL global, char *expiration) {
   char lb_name[256], str[NAME_LENGTH], format[80];
   double exp;
   time_t now;
   struct tm *gmt;

   if (lbs)
      strcpy(lb_name, lbs->name);
   else
      strcpy(lb_name, "global");

   if (value != NULL)
      rsprintf("Set-Cookie: %s=%s;", name, value);
   else
      rsprintf("Set-Cookie: %s;", name);

   /* add path */
   if (global) {
      /* path for all logbooks */
      if (getcfg(lb_name, "URL", str, sizeof(str))) {
         extract_path(str);
         url_encode(str, sizeof(str));
         rsprintf(" path=/%s;", str);
      } else
         rsprintf(" path=/;");
   } else {
      /* path for individual logbook */
      if (getcfg(lb_name, "URL", str, sizeof(str))) {
         extract_path(str);
         url_encode(str, sizeof(str));
         if (str[0])
            rsprintf(" path=/%s/%s;", str, lbs->name_enc);
         else
            rsprintf(" path=/%s;", lbs->name_enc);
      } else
         rsprintf(" path=/%s;", lbs->name_enc);
   }

   exp = atof(expiration);

   /* to clear a cookie, set expiration date to yesterday */
   if (value != NULL && value[0] == 0)
      exp = -24;

   /* add expriation date */
   if (exp != 0 && exp < 100000) {
      time(&now);
      now += (int) (3600 * exp);
      gmt = gmtime(&now);
      strcpy(format, "%A, %d-%b-%y %H:%M:%S GMT");
      strftime(str, sizeof(str), format, gmt);

      rsprintf(" expires=%s;", str);
   }

   rsprintf("\r\n");
}

Project: elog

int scan_attributes(char *logbook)
/* scan configuration file for attributes and fill attr_list, attr_options
 and attr_flags arrays */
{
   char list[10000], str[NAME_LENGTH], str2[NAME_LENGTH], type[NAME_LENGTH],
           tmp_list[MAX_N_ATTR][NAME_LENGTH];
   int i, j, n, m, n_options;

   if (getcfg(logbook, "Attributes", list, sizeof(list))) {
      /* reset attribute flags */
      memset(attr_flags, 0, sizeof(attr_flags));

      /* get attribute list */
      memset(attr_list, 0, sizeof(attr_list));
      n = strbreak(list, attr_list, MAX_N_ATTR, ",", FALSE);

      /* check for forbidden attributes */
      for (i = 0; i < n; i++) {
         if (strieq(attr_list[i], "id") ||
             strieq(attr_list[i], "text") ||
             strieq(attr_list[i], "date") ||
             strieq(attr_list[i], "encoding") ||
             strieq(attr_list[i], "reply to") ||
             strieq(attr_list[i], "locked by") ||
             strieq(attr_list[i], "in reply to") || strieq(attr_list[i], "attachment")) {
            snprintf(str, sizeof(str), loc("Attribute \"%s\" is not allowed in config file"), attr_list[i]);
            show_error(str);
            return -1;
         }
      }

      /* get options lists for attributes */
      memset(attr_options, 0, sizeof(attr_options));
      for (i = 0; i < n; i++) {
         n_options = 0;

         snprintf(str, sizeof(str), "Options %s", attr_list[i]);
         if (getcfg(logbook, str, list, sizeof(list)))
            n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);

         snprintf(str, sizeof(str), "MOptions %s", attr_list[i]);
         if (getcfg(logbook, str, list, sizeof(list))) {
            n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
            attr_flags[i] |= AF_MULTI;
         }

         snprintf(str, sizeof(str), "ROptions %s", attr_list[i]);
         if (getcfg(logbook, str, list, sizeof(list))) {
            n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
            attr_flags[i] |= AF_RADIO;
         }

         snprintf(str, sizeof(str), "IOptions %s", attr_list[i]);
         if (getcfg(logbook, str, list, sizeof(list))) {
            n_options = strbreak(list, attr_options[i], MAX_N_LIST, ",", FALSE);
            attr_flags[i] |= AF_ICON;
         }

         snprintf(str2, sizeof(str2), "Sort Attribute Options %s", attr_list[i]);
         if (n_options && getcfg(logbook, str2, str, sizeof(str)) && atoi(str) == 1) {
            qsort(attr_options[i], n_options, NAME_LENGTH, ascii_compare2);
         }
      }

      /* check if attribute required */
      getcfg(logbook, "Required Attributes", list, sizeof(list));
      m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < m; i++) {
         for (j = 0; j < n; j++)
            if (strieq(attr_list[j], tmp_list[i]))
               attr_flags[j] |= AF_REQUIRED;
      }

      /* check if locked attribute */
      getcfg(logbook, "Locked Attributes", list, sizeof(list));
      m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < m; i++) {
         for (j = 0; j < n; j++)
            if (strieq(attr_list[j], tmp_list[i]))
               attr_flags[j] |= AF_LOCKED;
      }

      /* check if fixed attribute for Edit */
      getcfg(logbook, "Fixed Attributes Edit", list, sizeof(list));
      m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < m; i++) {
         for (j = 0; j < n; j++)
            if (strieq(attr_list[j], tmp_list[i]))
               attr_flags[j] |= AF_FIXED_EDIT;
      }

      /* check if fixed attribute for Reply */
      getcfg(logbook, "Fixed Attributes Reply", list, sizeof(list));
      m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < m; i++) {
         for (j = 0; j < n; j++)
            if (strieq(attr_list[j], tmp_list[i]))
               attr_flags[j] |= AF_FIXED_REPLY;
      }

      /* check for extendable options */
      getcfg(logbook, "Extendable Options", list, sizeof(list));
      m = strbreak(list, tmp_list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < m; i++) {
         for (j = 0; j < n; j++)
            if (strieq(attr_list[j], tmp_list[i]))
               attr_flags[j] |= AF_EXTENDABLE;
      }

      for (i = 0; i < n; i++) {
         snprintf(str, sizeof(str), "Type %s", attr_list[i]);
         if (getcfg(logbook, str, type, sizeof(type))) {
            if (strieq(type, "date"))
               attr_flags[i] |= AF_DATE;
            if (strieq(type, "datetime"))
               attr_flags[i] |= AF_DATETIME;
            if (strieq(type, "time"))
               attr_flags[i] |= AF_TIME;
            if (strieq(type, "numeric"))
               attr_flags[i] |= AF_NUMERIC;
            if (strieq(type, "userlist"))
               attr_flags[i] |= AF_USERLIST;
            if (strieq(type, "muserlist"))
               attr_flags[i] |= AF_MUSERLIST;
            if (strieq(type, "useremail"))
               attr_flags[i] |= AF_USEREMAIL;
            if (strieq(type, "museremail"))
               attr_flags[i] |= AF_MUSEREMAIL;
         }
      }

   } else {
      memcpy(attr_list, attr_list_default, sizeof(attr_list_default));
      memcpy(attr_options, attr_options_default, sizeof(attr_options_default));
      memcpy(attr_flags, attr_flags_default, sizeof(attr_flags_default));
      n = 4;
   }

   return n;
}

Project: elog

int adjust_config(char *url) {
   int fh, i, length;
   char *buf, *buf2, *p1, *p2;
   char str[256];

   fh = open(config_file, O_RDWR | O_BINARY, 0644);
   if (fh < 0) {
      sprintf(str, loc("Cannot open file <b>%s</b>"), config_file);
      strcat(str, ": ");
      strcat(str, strerror(errno));
      eputs(str);
      return 0;
   }

   /* read previous contents */
   length = lseek(fh, 0, SEEK_END);
   lseek(fh, 0, SEEK_SET);
   buf = (char *) xmalloc(2 * length + 1000);
   read(fh, buf, length);
   buf[length] = 0;

   /* add mirror server */
   p1 = stristr(buf, "Mirror server =");
   if (p1 != NULL) {
      p2 = strchr(p1, '\n');
      if (p2 && *(p2 - 1) == '\r')
         p2--;
   } else {
      p1 = strstr(buf, "[global]");
      if (p1 == NULL) {
         eputs("Cannot find [global] section in config file.");
         return 0;
      }
      p1 = strchr(p1, '\n');
      while (*p1 == '\n' || *p1 == '\r')
         p1++;

      p2 = p1;
   }

   /* save tail */
   buf2 = NULL;
   if (p2)
      buf2 = xstrdup(p2);

   sprintf(p1, "Mirror server = %s\r\n", url);
   strlcat(p1, buf2, length + 1000);
   xfree(buf2);
   eprintf("Option \"Mirror server = %s\" added to config file.\n", url);

   /* outcomment "URL = xxx" */
   p1 = strstr(buf, "URL =");
   if (p1 != NULL) {

      /* save tail */
      buf2 = xstrdup(p1);

      /* add comment */
      sprintf(p1, "; Following line has been outcommented after cloning\r\n");
      strlcat(p1, "; ", length + 1000);
      strlcat(p1, buf2, length + 1000);
      xfree(buf2);

      eputs("Option \"URL = xxx\" has been outcommented from config file.");
   }

   adjust_crlf(buf, 2 * length + 1000);

   lseek(fh, 0, SEEK_SET);
   i = write(fh, buf, strlen(buf));
   if (i < (int) strlen(buf)) {
      sprintf(str, loc("Cannot write to <b>%s</b>"), config_file);
      strcat(str, ": ");
      strcat(str, strerror(errno));
      eputs(str);
      close(fh);
      xfree(buf);
      return 0;
   }

   TRUNCATE(fh);

   close(fh);
   xfree(buf);

   return 1;
}

Project: elog

int retrieve_remote_md5(LOGBOOK *lbs, char *host, MD5_INDEX **md5_index, char *error_str) {
   int i, n, id, x, version, ssl;
   char *text, *p, url[256], str[1000];

   *md5_index = NULL;

   combine_url(lbs, host, "?cmd=GetMD5", url, sizeof(url), &ssl);

   text = NULL;
   error_str[0] = 0;
   if (retrieve_url(lbs, url, ssl, &text, TRUE) < 0) {
      sprintf(error_str, loc("Cannot connect to remote server \"%s\""), host);
      return -1;
   }
   p = strstr(text, "ELOG HTTP ");
   if (!p) {
      if (isparam("debug"))
         rsputs(text);
      strlcpy(error_str, loc("Remote server is not an ELOG server"), 256);
      xfree(text);
      return -1;
   }
   version = atoi(p + 10) * 100 + atoi(p + 12) * 10 + atoi(p + 14);
   if (version < 250) {
      if (isparam("debug"))
         rsputs(text);
      memset(str, 0, sizeof(str));
      strncpy(str, p + 10, 5);
      sprintf(error_str, loc("Incorrect remote ELOG server version %s"), str);
      xfree(text);
      return -1;
   }

   p = strstr(text, "Location: ");
   if (p) {
      if (isparam("debug"))
         rsputs(text);

      if (strstr(text, "?fail="))
         sprintf(error_str, loc("Invalid user name \"%s\" or password for remote logbook"),
                 isparam("unm") ? getparam("unm") : "");
      else {
         strlcpy(str, p + 9, sizeof(str));
         if (strchr(str, '?'))
            *strchr(str, '?') = 0;
         strlcpy(error_str, loc("URL is redirected to:"), 256);
         strlcat(error_str, str, 256);
      }

      return -3;
   }

   p = strstr(text, "\r\n\r\n");
   if (!p) {
      if (isparam("debug"))
         rsputs(text);
      strlcpy(error_str, loc("Invalid HTTP header"), 256);
      xfree(text);
      return -1;
   }

   for (n = 0;; n++) {
      p = strstr(p, "ID:");
      if (!p)
         break;
      p += 3;

      id = atoi(p);

      p = strstr(p, "MD5:");
      if (!p)
         break;
      p += 4;

      if (n == 0)
         *md5_index = (MD5_INDEX *) xmalloc(sizeof(MD5_INDEX));
      else
         *md5_index = (MD5_INDEX *) xrealloc(*md5_index, (n + 1) * sizeof(MD5_INDEX));

      (*md5_index)[n].message_id = id;

      for (i = 0; i < 16; i++) {
         sscanf(p + 2 * i, "%02X", &x);
         (*md5_index)[n].md5_digest[i] = (unsigned char) x;
      }
   }

   if (n == 0) {
      if (isparam("debug"))
         rsputs(text);
      if (strstr(text, "Login")) {
         strlcpy(error_str, loc("No user name supplied to access remote logbook"), 256);
         xfree(text);
         return -2;
      } else
         strlcpy(error_str, loc("Error accessing remote logbook"), 256);
   }

   xfree(text);

   return n;
}

Project: elog

int ss_file_find(const char *path, char *pattern, char **plist)
/********************************************************************
 Routine: ss_file_find

 Purpose: Return list of files matching 'pattern' from the 'path' location

 Input:
 char  *path             Name of a file in file system to check
 char  *pattern          pattern string (wildcard allowed)

 Output:
 char  **plist           pointer to the file list

 Function value:
 int                     Number of files matching request

 \********************************************************************/
{
#ifdef OS_UNIX
   DIR *dir_pointer;
   struct dirent *dp;
   int i;

   if ((dir_pointer = opendir(path)) == NULL)
      return 0;
   *plist = (char *) xmalloc(MAX_PATH_LENGTH);
   i = 0;
   for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) {
      if (fnmatch1(pattern, dp->d_name) == 0) {
         *plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH);
         strncpy(*plist + (i * MAX_PATH_LENGTH), dp->d_name, strlen(dp->d_name));
         *(*plist + (i * MAX_PATH_LENGTH) + strlen(dp->d_name)) = '\0';
         i++;
      }
   }
   closedir(dir_pointer);
   return i;
#endif

#ifdef OS_WINNT
                                                                                                                           HANDLE pffile;
   LPWIN32_FIND_DATA lpfdata;
   char str[255];
   int i, first;

   strlcpy(str, path, sizeof(str));
   strlcat(str, "\\", sizeof(str));
   strlcat(str, pattern, sizeof(str));
   first = 1;
   i = 0;
   lpfdata = xmalloc(sizeof(WIN32_FIND_DATA));
   *plist = (char *) xmalloc(MAX_PATH_LENGTH);
   pffile = FindFirstFile(str, lpfdata);
   if (pffile == INVALID_HANDLE_VALUE)
      return 0;
   first = 0;
   *plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH);
   strncpy(*plist + (i * MAX_PATH_LENGTH), lpfdata->cFileName, strlen(lpfdata->cFileName));
   *(*plist + (i * MAX_PATH_LENGTH) + strlen(lpfdata->cFileName)) = '\0';
   i++;
   while (FindNextFile(pffile, lpfdata)) {
      *plist = (char *) xrealloc(*plist, (i + 1) * MAX_PATH_LENGTH);
      strncpy(*plist + (i * MAX_PATH_LENGTH), lpfdata->cFileName, strlen(lpfdata->cFileName));
      *(*plist + (i * MAX_PATH_LENGTH) + strlen(lpfdata->cFileName)) = '\0';
      i++;
   }
   xfree(lpfdata);
   return i;
#endif
}

Project: elog

int is_inline_attachment(char *encoding, int message_id, char *text, int i, char *att) {
   char str[256], att_enc[256], domain[256], *pt, *p;

   if (text == NULL)
      return 0;
   if (strieq(encoding, "ELCode")) {
      sprintf(str, "[img]elog:/%d[/img]", i + 1);
      if (stristr(text, str))
         return 1;
      sprintf(str, "[img]elog:%d/%d[/img]", message_id, i + 1);
      if (stristr(text, str))
         return 1;
   } else if (strieq(encoding, "HTML")) {
      strlcpy(att_enc, att, sizeof(att_enc));
      att_enc[13] = '/';
      pt = text;
      while (*pt && stristr(pt, att_enc)) {
         /* make sure that it's really an inline image */
         for (p = stristr(pt, att_enc); p > pt; p--)
            if (*p == '<')
               break;
         if (p > pt) {
            strncpy(str, p, 5);
            if (stristr(str, "<img "))
               return 1;
         }
         pt = stristr(pt, att_enc) + 1;
      }
      retrieve_domain(domain, sizeof(domain));
      sprintf(str, "cid:att%d@%s", i, domain);
      if (*text && stristr(text, str))
         return 1;
   }

   return 0;
}

Project: elog

void generate_subdir_name(char *file_name, char *subdir, int size) {
   char fn[MAX_PATH_LENGTH], path[MAX_PATH_LENGTH];
   int year;

   // extract path from file_name
   strlcpy(path, file_name, size);
   if (strrchr(path, DIR_SEPARATOR))
      *(strrchr(path, DIR_SEPARATOR) + 1) = 0;

   // extract file name
   if (strrchr(file_name, DIR_SEPARATOR))
      strlcpy(fn, strrchr(file_name, DIR_SEPARATOR) + 1, sizeof(fn));
   else
      strlcpy(fn, file_name, sizeof(fn));

   // create subdir from name
   year = (fn[0] - '0') * 10 + (fn[1] - '0');
   // month = (fn[2]-'0')*10+(fn[3]-'0');
   if (year < 80)
      sprintf(subdir, "20%02d", year);
   else
      sprintf(subdir, "19%02d", year);

   strlcat(subdir, DIR_SEPARATOR_STR, size);
}

Project: elog

int is_image(char *att) {
   return (stristr(att, ".GIF") != NULL) || (stristr(att, ".JPG") != NULL) || (stristr(att, ".JPEG") != NULL)
          || (stristr(att, ".PNG") != NULL) || (stristr(att, ".SVG") != NULL);
}

Project: elog

void get_rfc2822_date(char *date, int size, time_t ltime) {
   time_t now;
   char buf[256];
   int offset;
   struct tm *ts;

   /* switch locale temporarily back to english to comply with RFC2822 date format */
   setlocale(LC_ALL, "C");

   if (ltime == 0)
      time(&now);
   else
      now = ltime;
   ts = localtime(&now);
   assert(ts);
   strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", ts);
   offset = (-(int) my_timezone());
   if (ts->tm_isdst)
      offset += 3600;
   snprintf(date, size - 1, "%s %+03d%02d", buf, (int) (offset / 3600),
            (int) ((abs((int) offset) / 60) % 60));
}

Project: elog

void el_delete_attachment(LOGBOOK *lbs, char *file_name) {
   int i;
   char str[MAX_PATH_LENGTH], subdir[MAX_PATH_LENGTH];

   strlcpy(str, lbs->data_dir, sizeof(str));
   generate_subdir_name(file_name, subdir, sizeof(subdir));
   strlcat(str, subdir, sizeof(str));
   strlcat(str, file_name, sizeof(str));
   remove(str);
   strlcat(str, ".png", sizeof(str));
   remove(str);
   for (i = 0;; i++) {
      strlcpy(str, lbs->data_dir, sizeof(str));
      strlcat(str, subdir, sizeof(str));
      strlcat(str, file_name, sizeof(str));
      sprintf(str + strlen(str), "-%d.png", i);
      if (file_exist(str)) {
         remove(str);
         continue;
      }

      strlcpy(str, lbs->data_dir, sizeof(str));
      strlcat(str, subdir, sizeof(str));
      strlcat(str, file_name, sizeof(str));
      if (strrchr(str, '.'))
         *strrchr(str, '.') = 0;
      sprintf(str + strlen(str), "-%d.png", i);
      if (file_exist(str)) {
         remove(str);
         continue;
      }
      break;
   }
}

Project: elog

void str_escape(char *ps, int size)
/********************************************************************\
Encode the given string in-place by adding \\ escapes for `$"\
\********************************************************************/
{
   unsigned char *pd, *p;
   unsigned char str[NAME_LENGTH];

   pd = str;
   p = (unsigned char *) ps;
   while (*p && pd < str + 250) {
      if (strchr("'$\"\\", *p)) {
         *pd++ = '\\';
         *pd++ = *p++;
      } else {
         *pd++ = *p++;
      }
   }
   *pd = '\0';
   strlcpy(ps, (char *) str, size);
}

Project: elog

int add_attribute_option(LOGBOOK *lbs, char *attrname, char *attrvalue, char *condition) {
   int fh, i, length;
   char str[NAME_LENGTH], av_encoded[NAME_LENGTH], *buf, *buf2, *p1, *p2, *p3;

   fh = open(config_file, O_RDWR | O_BINARY, 0644);
   if (fh < 0) {
      sprintf(str, loc("Cannot open file <b>%s</b>"), config_file);
      strcat(str, ": ");
      strcat(str, strerror(errno));
      show_error(str);
      return 0;
   }

   /* do not allow HTML code in value */
   strencode2(av_encoded, attrvalue, sizeof(av_encoded));

   /* read previous contents */
   length = lseek(fh, 0, SEEK_END);
   lseek(fh, 0, SEEK_SET);
   buf = xmalloc(length + strlen(av_encoded) + 3);
   read(fh, buf, length);
   buf[length] = 0;

   /* find location of options */
   if (condition && condition[0])
      set_condition(condition);
   else
      set_condition("");

   sprintf(str, "Options %s", attrname);
   p1 = (char *) find_param(buf, lbs->name, str);
   if (p1 == NULL) {
      sprintf(str, "MOptions %s", attrname);
      p1 = (char *) find_param(buf, lbs->name, str);
   }
   if (p1 == NULL) {
      sprintf(str, "ROptions %s", attrname);
      p1 = (char *) find_param(buf, lbs->name, str);
   }
   if (p1 == NULL)
      return 0;

   p2 = strchr(p1, '\n');
   if (p2 && *(p2 - 1) == '\r')
      p2--;

   /* save tail */
   buf2 = NULL;
   if (p2)
      buf2 = xstrdup(p2);

   /* add option */
   p3 = strchr(p1, '\n');
   if (p3 == NULL)
      p3 = p1 + strlen(p1);
   while (*(p3 - 1) == '\n' || *(p3 - 1) == '\r' || *(p3 - 1) == ' ' || *(p3 - 1) == '\t')
      p3--;

   sprintf(p3, ", %s", av_encoded);
   if (p2) {
      strlcat(buf, buf2, length + strlen(av_encoded) + 3);
      xfree(buf2);
   }

   lseek(fh, 0, SEEK_SET);
   i = write(fh, buf, strlen(buf));
   if (i < (int) strlen(buf)) {
      sprintf(str, loc("Cannot write to <b>%s</b>"), config_file);
      strcat(str, ": ");
      strcat(str, strerror(errno));
      show_error(str);
      close(fh);
      xfree(buf);
      return 0;
   }

   TRUNCATE(fh);

   close(fh);
   xfree(buf);

   /* force re-read of config file */
   check_config_file(TRUE);

   return 1;
}

Project: elog

void submit_config(LOGBOOK *lbs, char *server, char *buffer, char *error_str) {
   int i, n, port, sock, content_length, header_length, ssl;
   char str[256], upwd[80];
   char subdir[256], param[256], remote_host_name[256];
   char *content, boundary[80], request[10000], response[10000];
#ifdef HAVE_SSL
   SSL *ssl_con = NULL;
#else
   void *ssl_con = NULL;
#endif

   error_str[0] = 0;

   combine_url(lbs, server, "", str, sizeof(str), &ssl);
   split_url(str, remote_host_name, &port, subdir, param);

   sock = elog_connect(remote_host_name, port);
   if (sock == -1) {
      sprintf(error_str, loc("Cannot connect to host %s, port %d"), remote_host_name, port);
      return;
   }
#ifdef HAVE_SSL
   if (ssl)
      if (ssl_connect(sock, &ssl_con) < 0) {
         strlcpy(error_str, "Error initiating SSL connection\n", 256);
         return;
      }
#endif

   content_length = 100000;
   content = (char *) xmalloc(content_length);

   /* compose content */
   sprintf(boundary, "---------------------------%04X%04X%04X", rand(), rand(), rand());
   strcpy(content, boundary);
   strcat(content, "\r\nContent-Disposition: form-data; name=\"cmd\"\r\n\r\nSave\r\n");

   if (isparam("unm")) {
      sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"unm\"\r\n\r\n%s\r\n",
              boundary, getparam("unm"));

      if (isparam("upwd"))
         strlcpy(upwd, getparam("upwd"), sizeof(upwd));
      else
         get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL);
      sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"upwd\"\r\n\r\n%s\r\n",
              boundary, upwd);
   }

   sprintf(content + strlen(content),
           "%s\r\nContent-Disposition: form-data; name=\"Text\"\r\n\r\n%s\r\n%s\r\n", boundary, buffer,
           boundary);

   content_length = strlen(content);

   /* compose request */
   strcpy(request, "POST ");
   if (subdir[0]) {
      if (subdir[0] != '/')
         strcat(request, "/");
      strcat(request, subdir);
      if (request[strlen(request) - 1] != '/')
         strcat(request, "/");
   }
   strcat(request, " HTTP/1.0\r\n");

   sprintf(request + strlen(request), "Content-Type: multipart/form-data; boundary=%s\r\n", boundary);
   sprintf(request + strlen(request), "Host: %s\r\n", host_name);
   sprintf(request + strlen(request), "User-Agent: ELOGD\r\n");
   sprintf(request + strlen(request), "Content-Length: %d\r\n", content_length);

   if (isparam("wpwd"))
      sprintf(request + strlen(request), "Cookie: wpwd=%s\r\n", getparam("wpwd"));

   strcat(request, "\r\n");

   header_length = strlen(request);

   send_with_timeout(ssl_con, sock, request, header_length);
   send_with_timeout(ssl_con, sock, content, content_length);


#ifdef HAVE_SSL
   if (ssl)
      /* receive response */
      i = SSL_read(ssl_con, response, 10000);
   else
#endif
      /* receive response */
      i = recv(sock, response, 10000, 0);

   if (i < 0) {
      closesocket(sock);
      strlcpy(error_str, "Cannot receive response", 256);
      return;
   }

   /* discard remainder of response */
   n = i;
   while (i > 0) {
      i = recv(sock, response + n, 10000, 0);
      if (i > 0)
         n += i;
   }
   response[n] = 0;

#ifdef HAVE_SSL
   if (ssl) {
      SSL_shutdown(ssl_con);
      SSL_free(ssl_con);
   }
#endif

   closesocket(sock);

   /* check response status */
   if (strstr(response, "302 Found")) {
      if (strstr(response, "Location:")) {
         if (strstr(response, "fail"))
            strlcpy(error_str, "Invalid usr name or password\n", 256);
      }
   } else if (strstr(response, "Logbook Selection"))
      strlcpy(error_str, "No logbook specified\n", 256);
   else if (strstr(response, "enter password"))
      strlcpy(error_str, "Missing or invalid password\n", 256);
   else if (strstr(response, "form name=form1"))
      strlcpy(error_str, "Missing or invalid user name/password\n", 256);
   else if (strstr(response, "Error: Attribute")) {
      strncpy(str, strstr(response, "Error: Attribute") + 20, sizeof(str));
      if (strchr(str, '<'))
         *strchr(str, '<') = 0;
      sprintf(error_str, "Missing required attribute \"%s\"\n", str);
   } else
      strlcpy(error_str, "Error transmitting message\n", 256);
}

Project: elog

int create_thumbnail(LOGBOOK *lbs, char *file_name) {
   char str[2 * MAX_PATH_LENGTH], cmd[2 * MAX_PATH_LENGTH], thumb_size[256], thumb_options[256];
   int i;

   if (!image_magick_exist)
      return 0;

   if (getcfg(lbs->name, "Thumbnail size", str, sizeof(str))) {
      if (strcmp(str, "0") == 0)
         return 0;
      sprintf(thumb_size, " -thumbnail '%s'", str);
   } else
      thumb_size[0] = 0;

   getcfg(lbs->name, "Thumbnail options", thumb_options, sizeof(thumb_options));

   if (!chkext(file_name, ".ps") && !chkext(file_name, ".pdf") && !chkext(file_name, ".eps")
       && !chkext(file_name, ".gif") && !chkext(file_name, ".jpg") && !chkext(file_name, ".jpeg")
       && !chkext(file_name, ".png") && !chkext(file_name, ".ico") && !chkext(file_name, ".tif")
       && !chkext(file_name, ".svg"))
      return 0;

   i = get_thumb_name(file_name, str, sizeof(str), 0);
   if (i)
      return i;

   strlcpy(str, file_name, sizeof(str));
   if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) {
      if (strrchr(str, '.'))
         *strrchr(str, '.') = 0;
   }
   if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
      strlcat(str, "-%d.png", sizeof(str));
   else
      strlcat(str, ".png", sizeof(str));

   if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
      snprintf(cmd, sizeof(cmd), "%s %s '%s[0-7]'%s '%s'", _convert_cmd, thumb_options, file_name, thumb_size,
               str);
   else
      snprintf(cmd, sizeof(cmd), "%s %s '%s'%s '%s'", _convert_cmd, thumb_options, file_name, thumb_size, str);

#ifdef OS_WINNT
                                                                                                                           for (i = 0; i < (int) strlen(cmd); i++)
      if (cmd[i] == '\'')
         cmd[i] = '\"';
#endif

   snprintf(str, sizeof(str), "SHELL \"%s\"", cmd);
   write_logfile(lbs, str);
   if (get_verbose() >= VERBOSE_INFO) {
      eprintf(str);
      eprintf("\n");
   }

   my_shell(cmd, str, sizeof(str));

   i = get_thumb_name(file_name, str, sizeof(str), 0);
   if (i)
      return i;

   return 3;
}

Project: elog

int cind(char c) {
   int i;

   if (c == '=')
      return 0;

   for (i = 0; i < 64; i++)
      if (map[i] == c)
         return i;

   return -1;
}

Project: elog

void strextract(const char *str, char delim, char *extr, int size)
/* extract a substinr "extr" from "str" until "delim" is found */
{
   int i;

   for (i = 0; str[i] != delim && i < size - 1; i++)
      extr[i] = str[i];
   extr[i] = 0;
}

Project: elog

int build_subst_list(LOGBOOK *lbs, char list[][NAME_LENGTH], char value[][NAME_LENGTH],
                     char attrib[][NAME_LENGTH], BOOL format_date) {
   int i;
   char str[NAME_LENGTH], format[256], full_name[256], user_email[256];
   time_t t;
   struct tm *ts;

   /* copy attribute list */
   i = 0;
   if (attrib != NULL)
      for (; i < lbs->n_attr; i++) {
         strcpy(list[i], attr_list[i]);
         if (attrib) {
            if ((attr_flags[i] & AF_DATE) && format_date) {

               t = (time_t) atoi(attrib[i]);
               ts = localtime(&t);
               assert(ts);

               sprintf(str, "Date format %s", attr_list[i]);
               if (!getcfg(lbs->name, str, format, sizeof(format)))
                  if (!getcfg(lbs->name, "Date format", format, sizeof(format)))
                     strcpy(format, DEFAULT_DATE_FORMAT);

               my_strftime(value[i], NAME_LENGTH, format, ts);

            } else if ((attr_flags[i] & AF_DATETIME) && format_date) {

               t = (time_t) atoi(attrib[i]);
               ts = localtime(&t);
               assert(ts);

               sprintf(str, "Time format %s", attr_list[i]);
               if (!getcfg(lbs->name, str, format, sizeof(format)))
                  if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
                     strcpy(format, DEFAULT_TIME_FORMAT);

               my_strftime(value[i], NAME_LENGTH, format, ts);

            } else
               strcpy(value[i], attrib[i]);
         } else
            strlcpy(value[i], isparam(attr_list[i]) ? getparam(attr_list[i]) : "", NAME_LENGTH);
      }

   /* add remote host */
   strcpy(list[i], "remote_host");
   strlcpy(value[i++], rem_host, NAME_LENGTH);

   /* add local host */
   strcpy(list[i], "host");
   strlcpy(value[i++], host_name, NAME_LENGTH);

   /* add forwarded user */
   strcpy(list[i], "http_user");
   strlcpy(value[i++], http_user, NAME_LENGTH);

   /* add user names */
   strcpy(list[i], "short_name");
   if (isparam("unm")) {
      strlcpy(value[i++], getparam("unm"), NAME_LENGTH);
      get_user_line(lbs, getparam("unm"), NULL, full_name, user_email, NULL, NULL, NULL);
   } else {
      strlcpy(value[i++], loc("Anonymous"), NAME_LENGTH);
      strcpy(full_name, loc("Anonymous"));
      user_email[0] = 0;
   }

   strcpy(list[i], "long_name");
   strlcpy(value[i++], full_name, NAME_LENGTH);

   /* add email */
   if (user_email[0]) {
      strcpy(list[i], "user_email");
      strcpy(value[i], "mailto:");
      strlcat(value[i++], user_email, NAME_LENGTH);
   }

   /* add logbook */
   if (lbs) {
      strcpy(list[i], "logbook");
      strlcpy(value[i++], lbs->name, NAME_LENGTH);

      /* add logbook */
      strcpy(list[i], "elogbook");
      strlcpy(value[i++], lbs->name_enc, NAME_LENGTH);
   }

   /* add date */
   strcpy(list[i], "date");
   time(&t);
   if (format_date) {
      ts = localtime(&t);
      assert(ts);
      if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
         strcpy(format, DEFAULT_TIME_FORMAT);

      my_strftime(str, sizeof(str), format, ts);
   } else
      sprintf(str, "%d", (int) t);
   strcpy(value[i++], str);

   /* add UTC date */
   strcpy(list[i], "utcdate");
   time(&t);
   if (format_date) {
      ts = gmtime(&t);
      if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
         strcpy(format, DEFAULT_TIME_FORMAT);

      my_strftime(str, sizeof(str), format, ts);
   } else
      sprintf(str, "%d", (int) t);
   strcpy(value[i++], str);

   /* add ELOG version and revision */
   strcpy(list[i], "version");
   strcpy(value[i++], VERSION);

   strcpy(list[i], "revision");
   sprintf(value[i++], "%s", git_revision());

   return i;
}

Project: elog

void compose_base_url(LOGBOOK *lbs, char *base_url, int size, BOOL email_notify)
/* return URL for elogd with optional logbook subdirectory */
{
   if (email_notify)
      if (getcfg(lbs->name, "Use Email URL", base_url, size))
         return;

   if (!getcfg("global", "URL", base_url, size)) {

      if (referer[0]) {
         /* get URL from referer */
         strlcpy(base_url, referer, size);
         if (strchr(base_url, '?'))
            *strchr(base_url, '?') = 0;
         if (strrchr(base_url, '/'))
            *(strrchr(base_url, '/') + 1) = 0;
      } else {
         if (_ssl_flag)
            strcpy(base_url, "https://");
         else
            strcpy(base_url, "http://");

         if (elog_tcp_port == 80)
            sprintf(base_url + strlen(base_url), "%s/", host_name);
         else
            sprintf(base_url + strlen(base_url), "%s:%d/", host_name, elog_tcp_port);

         if (lbs) {
            strlcat(base_url, lbs->name_enc, size);
            strlcat(base_url, "/", size);
         }
      }
   } else {
      if (base_url[strlen(base_url) - 1] != '/')
         strlcat(base_url, "/", size);
      if (lbs) {
         strlcat(base_url, lbs->name_enc, size);
         strlcat(base_url, "/", size);
      }
   }
}

Project: elog

int get_thumb_name(const char *file_name, char *thumb_name, int size, int index) {
   char str[MAX_PATH_LENGTH];

   thumb_name[0] = 0;

   /* append .png for all files as thumbnail name, except for PDF files (convert bug!) */
   memset(str, 0, sizeof(str));
   if (chkext(file_name, ".pdf") || chkext(file_name, ".ps")) {
      strlcpy(str, file_name, sizeof(str));
      if (strrchr(str, '.'))
         *strrchr(str, '.') = 0;
      snprintf(str + strlen(str), sizeof(str) - strlen(str) - 1, "-%d.png", index);
      if (file_exist(str)) {
         strlcpy(thumb_name, str, size);
         return 2;
      }

      if (index > 0)
         return 0;

      strlcpy(str, file_name, sizeof(str));
      if (strrchr(str, '.'))
         *strrchr(str, '.') = 0;
      strlcat(str, ".png", sizeof(str));
      if (file_exist(str)) {
         strlcpy(thumb_name, str, size);
         return 1;
      }
   } else {
      strlcpy(str, file_name, sizeof(str));
      sprintf(str + strlen(str), "-%d.png", index);
      if (file_exist(str)) {
         strlcpy(thumb_name, str, size);
         return 2;
      }

      if (index > 0)
         return 0;

      strlcpy(str, file_name, sizeof(str));
      strlcat(str, ".png", sizeof(str));
      if (file_exist(str)) {
         strlcpy(thumb_name, str, size);
         return 1;
      }
   }

   return 0;
}

Project: elog

int is_logbook(char *logbook) {
   char str[256];

   if (strieq(logbook, "global"))
      return 0;

   /* check for 'global group' or 'global_xxx' */
   strlcpy(str, logbook, sizeof(str));
   str[7] = 0;
   return !strieq(str, "global ");
}

Project: elog

int exist_top_group() {
   int i;
   char str[256];

   for (i = 0;; i++) {
      if (!enumcfg("global", str, sizeof(str), NULL, 0, i))
         break;
      str[9] = 0;
      if (strieq(str, "top group"))
         return 1;
   }

   return 0;
}

Project: elog

BOOL get_password_file(LOGBOOK *lbs, char *file_name, int size) {
   char str[256];

   getcfg(lbs->name, "Password file", str, sizeof(str));

   if (!str[0])
      return FALSE;

   if (str[0] == DIR_SEPARATOR || str[1] == ':')
      strlcpy(file_name, str, size);
   else {
      strlcpy(file_name, logbook_dir, size);
      strlcat(file_name, str, size);
   }

   return TRUE;
}

Project: elog

const char *git_revision() {
   const char *p = _git_revision;
   if (strrchr(p, '-'))
      p = strrchr(p, '-') + 2;
   return p;
}

Project: elog

char *param_in_str(char *str, char *param) {
   char *p;

   p = str;
   do {
      if (stristr(p, param) == NULL)
         return NULL;

      p = stristr(p, param);

      /* if parameter not followed by '=', skip it */
      if (*(p + strlen(param)) != '=') {
         p += strlen(param);
         continue;
      }

      /* if parameter is value of another parameter, skip it */
      if (p > str + 1 && *(p - 1) == '=') {
         p += strlen(param);
         continue;
      }

      if (*p == 0)
         return NULL;

      return p;

   } while (1);
}

Project: elog

static BOOL chkext(const char *str, const char *ext) {
   int extl, strl;
   char c1, c2;

   if (ext == NULL || str == NULL)
      return FALSE;

   extl = strlen(ext);
   strl = strlen(str);
   if (extl >= strl)
      return FALSE;
   str = str + strl - extl;
   while (*str) {
      c1 = *str++;
      c2 = *ext++;
      if (my_toupper(c1) != my_toupper(c2))
         return FALSE;
   }
   return TRUE;
}

Project: elog

void el_decode_int(char *message, char *key, char *result, int size) {
   char str[80];

   if (result == NULL)
      return;

   *result = 0;
   el_decode(message, key, str, sizeof(str));
   if (str[0])
      sprintf(str, "%d", atoi(str));
   strlcpy(result, str, size);
}

Project: elog

void build_ref(char *ref, int size, char *mode, char *expand, char *attach, char *new_entries) {
   char str[1000], *p;

   if (strchr(getparam("cmdline"), '?'))
      strlcat(ref, strchr(getparam("cmdline"), '?'), size);

   /* eliminate old search */
   if (strstr(ref, "cmd=Search&")) {
      strlcpy(str, strstr(ref, "cmd=Search&") + 11, sizeof(str));
      p = strstr(ref, "cmd=Search&");
      strlcpy(p, str, size - (p - ref));
   }

   /* eliminate id=xxx */
   if (strstr(ref, "id=")) {
      strlcpy(str, ref, sizeof(str));
      p = strstr(str, "id=") + 3;
      while (*p && isdigit(*p))
         p++;
      strlcpy(strstr(ref, "id="), p, size);
      if (strlen(ref) > 0 && ref[strlen(ref) - 1] == '?')
         ref[strlen(ref) - 1] = 0;
   }

   /* eliminate old mode if new one is present */
   if (mode[0])
      subst_param(ref, size, "mode", mode);

   /* eliminate old expand if new one is present */
   if (expand[0])
      subst_param(ref, size, "expand", expand);

   /* eliminate old attach if new one is present */
   if (attach[0])
      subst_param(ref, size, "attach", attach);

   /* eliminate old new_entries if new one is present */
   if (new_entries[0])
      subst_param(ref, size, "new_entries", new_entries);

   /* eliminate old last= */
   if (isparam("last"))
      subst_param(ref, size, "last", getparam("last"));

   /* replace any '&' by '&amp;' */
   strlcpy(str, ref, sizeof(str));
   strencode2(ref, str, size);
}

Project: elog

void eprintf(const char *format, ...) {
   va_list ap;
   char *msg;

   va_start(ap, format);
   xvasprintf(&msg, format, ap);
   va_end(ap);

   (*printf_handler)(msg);

   free(msg);
}

Project: elog

void btou(char *str)
/* convert all blanks to underscores in a string */
{
   int i;

   for (i = 0; i < (int) strlen(str); i++)
      if (str[i] == ' ')
         str[i] = '_';
}

Project: elog

int el_delete_message(LOGBOOK *lbs, int message_id, BOOL delete_attachments,
                      char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], BOOL delete_bw_ref,
                      BOOL delete_reply_to)
/********************************************************************
 Routine: el_delete_message

 Purpose: Delete an ELog entry including attachments

 Input:
 LOGBOOK *lbs          Pointer to logbook structure
 int     message_id    Message ID
 BOOL    delete_attachments   Delete attachments if TRUE
 char    attachment    Used to return attachments (on move)
 BOOL    delete_bw_ref If true, delete backward references
 BOOL    delete_reply_to If true, delete replies to this message

 Output:
 <none>

 Function value:
 EL_SUCCESS              Successful completion

 \********************************************************************/
{
   int i, index, size, fh, tail_size, old_offset;
   char str[MAX_PATH_LENGTH], file_name[MAX_PATH_LENGTH * 3], reply_to[MAX_REPLY_TO * 10], in_reply_to[256];
   char *buffer, *p;
   char *message, attachment_all[64 * MAX_ATTACHMENTS];
   char attrib[MAX_N_ATTR][NAME_LENGTH];

   for (index = 0; index < *lbs->n_el_index; index++)
      if (lbs->el_index[index].message_id == message_id)
         break;

   if (index == *lbs->n_el_index)
      return -1;

   snprintf(file_name, sizeof(file_name), "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir,
            lbs->el_index[index].file_name);
   fh = open(file_name, O_RDWR | O_BINARY, 0644);
   if (fh < 0)
      return EL_FILE_ERROR;

   message = xmalloc(TEXT_SIZE + 1000);

   lseek(fh, lbs->el_index[index].offset, SEEK_SET);
   i = my_read(fh, message, TEXT_SIZE + 1000 - 1);
   if (i <= 0) {
      xfree(message);
      close(fh);
      return EL_FILE_ERROR;
   }

   if (_logging_level > 1) {
      sprintf(str, "DELETE entry #%d", message_id);
      write_logfile(lbs, str);
   }

   message[i] = 0;

   if (strncmp(message, "$@MID@$:", 8) != 0) {
      close(fh);
      xfree(message);

      /* file might have been edited, rebuild index */
      el_build_index(lbs, TRUE);
      return el_delete_message(lbs, message_id, delete_attachments, attachment, delete_bw_ref,
                               delete_reply_to);
   }

   /* check for correct ID */
   if (atoi(message + 8) != message_id) {
      close(fh);
      xfree(message);
      return EL_FILE_ERROR;
   }

   /* decode message size */
   p = strstr(message + 8, "$@MID@$:");
   if (p == NULL)
      size = strlen(message);
   else
      size = p - message;

   message[size] = 0;

   /* delete attachments */
   el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));

   for (i = 0; i < MAX_ATTACHMENTS; i++) {
      if (i == 0)
         p = strtok(attachment_all, ",");
      else
         p = strtok(NULL, ",");

      if (attachment != NULL) {
         if (attachment[i][0] && p) {
            /* delete old attachment if new one exists */
            el_delete_attachment(lbs, p);
         }

         /* return old attachment if no new one */
         if (!attachment[i][0] && p)
            strcpy(attachment[i], p);
      }

      if (delete_attachments && p)
         el_delete_attachment(lbs, p);
   }

   /* decode references */
   el_decode_intlist(message, "Reply to: ", reply_to, sizeof(reply_to));
   el_decode_int(message, "In reply to: ", in_reply_to, sizeof(in_reply_to));

   /* decoded attributes */
   for (i = 0;; i++) {
      el_enum_attr(message, i, attr_list[i], attrib[i]);
      if (!attr_list[i][0])
         break;
   }

   /* buffer tail of logfile */
   lseek(fh, 0, SEEK_END);
   tail_size = TELL(fh) - (lbs->el_index[index].offset + size);

   buffer = NULL;
   if (tail_size > 0) {
      buffer = xmalloc(tail_size);

      lseek(fh, lbs->el_index[index].offset + size, SEEK_SET);
      my_read(fh, buffer, tail_size);
   }
   lseek(fh, lbs->el_index[index].offset, SEEK_SET);

   if (tail_size > 0) {
      write(fh, buffer, tail_size);
      xfree(buffer);
   }

   /* truncate file here */
   TRUNCATE(fh);

   /* if file length gets zero, delete file */
   tail_size = lseek(fh, 0, SEEK_END);
   close(fh);
   xfree(message);

   if (tail_size == 0)
      remove(file_name);

   /* remove message from index */
   strcpy(str, lbs->el_index[index].file_name);
   old_offset = lbs->el_index[index].offset;
   for (i = index; i < *lbs->n_el_index - 1; i++)
      memcpy(&lbs->el_index[i], &lbs->el_index[i + 1], sizeof(EL_INDEX));

   (*lbs->n_el_index)--;
   if (*lbs->n_el_index > 0)
      lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index));

   /* correct all offsets after deleted message */
   for (i = 0; i < *lbs->n_el_index; i++)
      if (strieq(lbs->el_index[i].file_name, str) && lbs->el_index[i].offset > old_offset)
         lbs->el_index[i].offset -= size;

   /* if other logbook has same index, update pointers */
   for (i = 0; lb_list[i].name[0]; i++)
      if (&lb_list[i] != lbs && lb_list[i].n_el_index == lbs->n_el_index)
         lb_list[i].el_index = lbs->el_index;

   /* delete also replies to this message */
   if (delete_reply_to && reply_to[0]) {
      p = reply_to;
      if (isdigit(*p))
         do {
            if (atoi(p))
               el_delete_message(lbs, atoi(p), TRUE, NULL, FALSE, TRUE);

            while (*p && isdigit(*p))
               p++;
            while (*p && (*p == ',' || *p == ' '))
               p++;
         } while (*p);
   }

   /* delete backward references */
   if (in_reply_to[0] && delete_bw_ref) {
      p = in_reply_to;
      do {
         if (atoi(p))
            remove_reference(lbs, atoi(p), message_id, TRUE);

         while (*p && isdigit(*p))
            p++;
         while (*p && (*p == ',' || *p == ' '))
            p++;
      } while (*p);
   }

   /* execute shell if requested */
   if (getcfg(lbs->name, "Execute delete", str, sizeof(str)))
      execute_shell(lbs, message_id, attrib, NULL, str);

   return EL_SUCCESS;
}

Project: elog

void set_redir(LOGBOOK *lbs, char *redir) {
   char str[NAME_LENGTH];

   str[0] = 0;

   /* prepare relative path */
   if (redir[0])
      strlcpy(str, redir, sizeof(str));
   else {
      if (lbs)
         snprintf(str, sizeof(str), "../%s/", lbs->name_enc);
      else if (getcfg_topgroup())
         sprintf(str, ".");
   }

   set_location(lbs, str);
}

Project: elog

BOOL isparam(char *param) {
   int i;

   if (strieq(param, "text"))
      return _mtext[0] != 0;

   for (i = 0; i < MAX_PARAM && _param[i][0]; i++)
      if (strieq(param, _param[i]))
         break;

   if (i < MAX_PARAM && _param[i][0])
      return TRUE;

   return FALSE;
}

Project: elog

void submit_elog(LOGBOOK *lbs) {
   char str[NAME_LENGTH], str2[NAME_LENGTH], file_name[256], error[1000], date[80], *mail_list, *rcpt_list,
           list[10000], *p, locked_by[256], encoding[80], attrib[MAX_N_ATTR][NAME_LENGTH],
           subst_str[MAX_PATH_LENGTH], in_reply_to[80], reply_to[MAX_REPLY_TO * 10], user[256],
           user_email[256], mail_param[1000], *mail_to, *rcpt_to, full_name[256],
           att_file[MAX_ATTACHMENTS][256], slist[MAX_N_ATTR + 10][NAME_LENGTH],
           svalue[MAX_N_ATTR + 10][NAME_LENGTH], ua[NAME_LENGTH], draft[256];
   int i, j, k, n, missing, first, index, mindex, suppress, message_id, resubmit_orig, mail_to_size,
           rcpt_to_size, ltime, year, month, day, hour, min, sec, n_attr, email_notify[1000], allowed_encoding,
           status, bdraft, old_mail;
   BOOL bedit, bmultiedit;
   struct tm tms;

   bmultiedit = isparam("nsel");
   bedit = isparam("edit_id") && atoi(getparam("edit_id"));
   bdraft = isparam("draft");

   /* check for condition */
   if (isparam("condition")) {
      set_condition(getparam("condition"));

      /* rescan attributes */
      n_attr = scan_attributes(lbs->name);
   } else
      n_attr = lbs->n_attr;

   /* check for editing interval */
   if (isparam("edit_id"))
      if (!check_edit_time(lbs, atoi(getparam("edit_id"))))
         return;

   /* check for required attributs */
   missing = 0;
   for (i = 0; i < lbs->n_attr; i++) {
      strcpy(ua, attr_list[i]);
      stou(ua);

      if (attr_flags[i] & AF_REQUIRED) {
         if (attr_flags[i] & AF_DATE) {
            sprintf(str, "d%d", i);
            if (isparam(str) == 0)
               missing = 1;
            sprintf(str, "m%d", i);
            if (isparam(str) == 0)
               missing = 1;
            sprintf(str, "y%d", i);
            if (isparam(str) == 0)
               missing = 1;
            if (missing)
               break;
         } else if (attr_flags[i] & AF_DATETIME) {
            sprintf(str, "d%d", i);
            if (isparam(str) == 0)
               missing = 1;
            sprintf(str, "m%d", i);
            if (isparam(str) == 0)
               missing = 1;
            sprintf(str, "y%d", i);
            if (isparam(str) == 0)
               missing = 1;
            sprintf(str, "h%d", i);
            if (isparam(str) == 0)
               missing = 1;
            sprintf(str, "n%d", i);
            if (isparam(str) == 0)
               missing = 1;
            if (missing)
               break;
         } else if ((attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL))) {
            for (j = 0; j < MAX_N_LIST; j++) {
               sprintf(str, "%s_%d", ua, j);
               if (isparam(str))
                  break;

               /* check for attributes without the _<i> from elog */
               if (isparam(ua))
                  break;
            }

            if (j == MAX_N_LIST) {
               missing = 1;
               break;
            }
         } else if (isparam(ua) == 0 || *getparam(ua) == 0) {
            missing = 1;
            break;
         }
      }
   }

   if (missing && !bdraft) {
      sprintf(error, "<i>");
      sprintf(error + strlen(error), loc("Error: Attribute <b>%s</b> not supplied"), attr_list[i]);
      sprintf(error + strlen(error), ".</i><p>\n");
      sprintf(error + strlen(error), loc("Please go back and enter the <b>%s</b> field"), attr_list[i]);
      strcat(error, ".\n");

      show_error(error);
      return;
   }

   /* check for numeric attributes */
   if (!bdraft) {
      for (index = 0; index < lbs->n_attr; index++)
         if (attr_flags[index] & AF_NUMERIC) {
            strcpy(ua, attr_list[index]);
            stou(ua);
            strlcpy(str, isparam(ua) ? getparam(ua) : "", sizeof(str));

            for (j = 0; i < (int) strlen(str); i++)
               if (!isdigit(str[i]))
                  break;

            sprintf(str2, "- %s -", loc("keep original values"));
            if (i < (int) strlen(str) && strcmp(str, "<keep>") != 0 && strcmp(str, str2) != 0) {
               sprintf(error, loc("Error: Attribute <b>%s</b> must be numeric"), attr_list[index]);
               show_error(error);
               return;
            }
         }
   }

   for (i = 0; i < n_attr; i++) {
      strcpy(ua, attr_list[i]);
      stou(ua);
      if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL))
         strcat(ua, "_0");

      if (isparam(ua) && *getparam(ua) && attr_options[i][0][0]) {

         if (strieq(attr_options[i][0], "boolean") && !bdraft) {
            if (atoi(getparam(ua)) != 0 && atoi(getparam(ua)) != 1 && strcmp(getparam(ua), "<keep>") != 0) {
               strencode2(str, getparam(ua), sizeof(str));
               sprintf(error, loc("Error: Value <b>%s</b> not allowed for boolean attributes"), str);
               show_error(error);
               return;
            }

         } else {

            /* check if option exists */
            for (j = 0; attr_options[i][j][0]; j++)
               if (strieq(attr_options[i][j], getparam(ua)))
                  break;

            /* check if option without {n} exists */
            if (!attr_options[i][j][0]) {
               for (j = 0; attr_options[i][j][0]; j++) {
                  strlcpy(str, attr_options[i][j], sizeof(str));
                  if (strchr(str, '{'))
                     *strchr(str, '{') = 0;
                  if (strieq(str, getparam(ua)))
                     break;
               }
            }

            if (!attr_options[i][j][0] && isparam(ua) && strcmp(getparam(ua), "<keep>") != 0) {
               if (attr_flags[i] & AF_EXTENDABLE) {

                  /* check if maximal number of options exceeded */
                  if (attr_options[i][MAX_N_LIST - 1][0]) {
                     strcpy(error, loc("Maximum number of attribute options exceeded"));
                     strcat(error, "<br>");
                     strcat(error, loc("Please increase MAX_N_LIST in elogd.c and recompile"));
                     show_error(error);
                     return;
                  }

                  if (!add_attribute_option(lbs, attr_list[i], getparam(ua), getparam("condition")))
                     return;
               } else {
                  char encoded[100];
                  strencode2(encoded, getparam(ua), sizeof(encoded));
                  sprintf(error, loc("Error: Attribute option <b>%s</b> not existing"), encoded);
                  show_error(error);
                  return;
               }
            }
         }
      }
   }

   /* check if allowed encoding */
   if (getcfg(lbs->name, "Allowed encoding", str, sizeof(str)))
      allowed_encoding = atoi(str);
   else
      allowed_encoding = 7;

   strlcpy(encoding, isparam("encoding") ? getparam("encoding") : "plain", sizeof(encoding));

   /* check for valid encoding */
   if (!strieq(encoding, "plain") && !strieq(encoding, "ELCode") && !strieq(encoding, "HTML"))
      strcpy(encoding, "plain");

   if (strieq(encoding, "plain") && (allowed_encoding & 1) == 0) {
      show_error("Plain encoding not allowed");
      return;
   }
   if (strieq(encoding, "ELCode") && (allowed_encoding & 2) == 0) {
      show_error("ELCode encoding not allowed");
      return;
   }
   if (strieq(encoding, "HTML") && (allowed_encoding & 4) == 0) {
      show_error("HTML encoding not allowed");
      return;
   }

   /* get attachments */
   for (i = 0; i < MAX_ATTACHMENTS; i++) {
      sprintf(str, "attachment%d", i);
      strcpy(att_file[i], isparam(str) ? getparam(str) : "");
   }

   /* retrieve attributes */
   for (i = 0; i < n_attr; i++) {

      strcpy(ua, attr_list[i]);
      stou(ua);

      if (strieq(attr_options[i][0], "boolean") && !isparam(ua)) {

         strcpy(attrib[i], "0");

      } else if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) {

         if (isparam(ua)) {
            strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
         } else {
            attrib[i][0] = 0;
            first = 1;
            for (j = 0; j < MAX_N_LIST; j++) {
               sprintf(str, "%s_%d", ua, j);
               if (isparam(str)) {
                  if (first)
                     first = 0;
                  else
                     strlcat(attrib[i], " | ", NAME_LENGTH);
                  if (strlen(attrib[i]) + strlen(getparam(str)) < NAME_LENGTH - 2)
                     strlcat(attrib[i], getparam(str), NAME_LENGTH);
                  else
                     break;
               }
            }
         }
      } else if (attr_flags[i] & AF_DATE) {

         if (isparam(ua))       // from edit/reply of fixed attributes
            strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
         else {

            sprintf(str, "m%d", i);
            if (isparam(str) && strieq(getparam(str), "<keep>"))
               strcpy(attrib[i], "<keep>");
            else {
               sprintf(str, "y%d", i);
               year = isparam(str) ? atoi(getparam(str)) : 0;
               if (year < 100)
                  year += 2000;

               sprintf(str, "m%d", i);
               month = isparam(str) ? atoi(getparam(str)) : 0;

               sprintf(str, "d%d", i);
               day = isparam(str) ? atoi(getparam(str)) : 0;

               if (month == 0 || day == 0)
                  strcpy(attrib[i], "");
               else {
                  memset(&tms, 0, sizeof(struct tm));
                  tms.tm_year = year - 1900;
                  tms.tm_mon = month - 1;
                  tms.tm_mday = day;
                  tms.tm_hour = 12;

                  ltime = (int) mktime(&tms);

                  if (ltime == -1) {
                     show_error(loc("Date must be between 1970 and 2037"));
                     return;
                  }
                  sprintf(attrib[i], "%d", ltime);
               }
            }
         }

      } else if (attr_flags[i] & AF_DATETIME) {

         if (isparam(ua))       // from edit/reply of fixed attributes
            strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
         else {

            sprintf(str, "m%d", i);
            if (isparam(str) && strieq(getparam(str), "<keep>"))
               strcpy(attrib[i], "<keep>");
            else {
               sprintf(str, "y%d", i);
               year = isparam(str) ? atoi(getparam(str)) : 0;
               if (year < 100)
                  year += 2000;

               sprintf(str, "m%d", i);
               month = isparam(str) ? atoi(getparam(str)) : 0;

               sprintf(str, "d%d", i);
               day = isparam(str) ? atoi(getparam(str)) : 0;

               sprintf(str, "h%d", i);
               hour = isparam(str) ? atoi(getparam(str)) : 0;

               sprintf(str, "n%d", i);
               min = isparam(str) ? atoi(getparam(str)) : 0;

               sprintf(str, "c%d", i);
               sec = isparam(str) ? atoi(getparam(str)) : 0;

               if (month == 0 || day == 0)
                  strcpy(attrib[i], "");
               else {
                  memset(&tms, 0, sizeof(struct tm));
                  tms.tm_year = year - 1900;
                  tms.tm_mon = month - 1;
                  tms.tm_mday = day;
                  tms.tm_hour = hour;
                  tms.tm_min = min;
                  tms.tm_sec = sec;
                  tms.tm_isdst = -1;

                  ltime = (int) mktime(&tms);

                  if (ltime == -1) {
                     show_error(loc("Date must be between 1970 and 2037"));
                     return;
                  }
                  sprintf(attrib[i], "%d", ltime);
               }
            }
         }

      } else {
         strlcpy(attrib[i], isparam(ua) ? getparam(ua) : "", NAME_LENGTH);

         /* remove any CR/LF */
         if (strchr(attrib[i], '\r'))
            *strchr(attrib[i], '\r') = 0;
         if (strchr(attrib[i], '\n'))
            *strchr(attrib[i], '\n') = 0;

         /* strip trailing "{...}" */
         if (is_cond_attr(i) && strchr(attrib[i], '{') && strchr(attrib[i], '}'))
            *strchr(attrib[i], '{') = 0;
      }

   }

   /* compile substitution list */
   n = build_subst_list(lbs, slist, svalue, attrib, TRUE);
   if (bedit)
      add_subst_list(slist, svalue, "message id", getparam("edit_id"), &n);

   /* substitute attributes */
   if (!bedit && !isparam("reply_to")) {
      for (index = 0; index < n_attr; index++) {
         sprintf(str, "Subst %s", attr_list[index]);
         if (getcfg(lbs->name, str, subst_str, sizeof(subst_str))) {

            /* do not format date for date attributes */
            i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
                                                             == 0);

            strsubst_list(subst_str, sizeof(subst_str), slist, svalue, i);

            /* check for index substitution if not in edit mode */
            if (!bedit && strchr(subst_str, '#')) {
               /* get index */
               get_auto_index(lbs, index, subst_str, str, sizeof(str));
               strcpy(subst_str, str);
            }
            strcpy(attrib[index], subst_str);
         }
      }
   }

   /* subst attributes for edits */
   if (bedit) {
      for (index = 0; index < n_attr; index++) {
         sprintf(str, "Subst on edit %s", attr_list[index]);
         if (getcfg(lbs->name, str, str2, sizeof(str2))) {

            /* do not format date for date attributes */
            i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
                                                             == 0);

            add_subst_list(slist, svalue, "message id", getparam("edit_id"), &i);

            strsubst_list(str2, sizeof(str2), slist, svalue, i);
            if (strlen(str2) > NAME_LENGTH - 100) {
               if (strstr(str2 + 100, "<br>")) {
                  strlcpy(str, strstr(str2 + 100, "<br>"), sizeof(str));
               } else
                  strlcpy(str, str2 + 100, sizeof(str));

               strcpy(str2, "...");
               strlcat(str2, str, sizeof(str2));
            }
            if (strncmp(str2, "<br>", 4) == 0)
               strcpy(attrib[index], str2 + 4);
            else
               strcpy(attrib[index], str2);
         }
      }
   }

   /* subst attributes for replies */
   if (isparam("reply_to")) {
      for (index = 0; index < n_attr; index++) {
         sprintf(str, "Subst on reply %s", attr_list[index]);
         if (getcfg(lbs->name, str, str2, sizeof(str2))) {
            /* do not format date for date attributes */
            i = build_subst_list(lbs, slist, svalue, attrib, (attr_flags[index] & (AF_DATE | AF_DATETIME))
                                                             == 0);
            if (isparam("reply_to"))
               add_subst_list(slist, svalue, "Reply to", getparam("reply_to"), &i);
            strsubst_list(str2, sizeof(str2), slist, svalue, i);
            strcpy(attrib[index], str2);
         }
      }
   }

   /* check for attributes to keep */
   if (bmultiedit) {
      sprintf(str, "- %s -", loc("keep original values"));
      for (i = 0; i < n_attr; i++) {
         if (strieq(str, attrib[i]))
            strlcpy(attrib[i], "<keep>", NAME_LENGTH);
      }
   }

   message_id = 0;
   reply_to[0] = 0;
   in_reply_to[0] = 0;
   date[0] = 0;
   resubmit_orig = 0;
   locked_by[0] = 0;
   draft[0] = 0;
   if (isparam("draft"))
      strlcpy(draft, getparam("draft"), sizeof(draft));

   if (bedit && isparam("resubmit") && atoi(getparam("resubmit")) == 1) {
      resubmit_orig = atoi(getparam("edit_id"));

      /* get old links */
      el_retrieve(lbs, resubmit_orig, NULL, NULL, NULL, 0, NULL, 0, in_reply_to, reply_to, NULL, NULL, NULL,
                  NULL);

      /* if not message head, move all preceeding messages */
      /* outcommented, users want only resubmitted message occur at end (see what's new)
         if (in_reply_to[0])
         {
         do
         {
         resubmit_orig = atoi(in_reply_to);
         el_retrieve(lbs, resubmit_orig, NULL, NULL, NULL, 0,
         NULL, 0, in_reply_to, reply_to, NULL, NULL);
         } while (in_reply_to[0]);
         }
       */

      message_id = atoi(getparam("edit_id"));
      strcpy(in_reply_to, "<keep>");
      strcpy(reply_to, "<keep>");
      date[0] = 0;
   } else {
      if (bedit) {
         message_id = atoi(getparam("edit_id"));
         strcpy(in_reply_to, "<keep>");
         strcpy(reply_to, "<keep>");
         strcpy(date, "<keep>");
         if (bdraft)
            strcpy(locked_by, "<keep>");
      } else
         strcpy(in_reply_to, isparam("reply_to") ? getparam("reply_to") : "");
   }

   if (_logging_level > 1) {
      if (bmultiedit)
         sprintf(str, "EDIT multiple entries");
      else if (bdraft)
         sprintf(str, "DRAFT entry #%d", message_id);
      else if (bedit && !resubmit_orig)
         sprintf(str, "EDIT entry #%d", message_id);
      else
         sprintf(str, "NEW entry #%d", message_id);

      write_logfile(lbs, str);
   }

   if (bmultiedit) {
      for (i = n = 0; i < atoi(getparam("nsel")); i++) {
         sprintf(str, "s%d", i);
         if (isparam(str)) {

            message_id = atoi(getparam(str));

            status = submit_elog_reply(lbs, message_id, attrib, getparam("text"));
            if (status != EL_SUCCESS) {
               sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs->data_dir);
               strcat(str, "\n<p>");
               strcat(str,
                      loc("Please check that it exists and elogd has write access and disk is not full"));
               show_error(str);
               return;
            }
         }
      }

      redirect(lbs, isparam("redir") ? getparam("redir") : "");
      return;                   /* no email notifications etc */
   } else {
      message_id = el_submit(lbs, message_id, bedit, date, attr_list, attrib, n_attr, getparam("text"),
                             in_reply_to, reply_to, encoding, att_file, TRUE, locked_by, draft);

      if (message_id <= 0) {
         sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs->data_dir);
         strcat(str, "\n<p>");
         strcat(str, loc("Please check that it exists and elogd has write access and disk is not full"));
         show_error(str);
         return;
      }

      if (bdraft) {
         show_http_header(lbs, FALSE, NULL);
         rsprintf("OK %d\n", message_id);
         return;
      }
   }

   /* evaluate propagation of attributes */
   if (getcfg(lbs->name, "Propagate attributes", str, sizeof(str)))
      propagate_attrib(lbs, find_thread_head(lbs, message_id), attrib);

   /* resubmit thread if requested */
   if (resubmit_orig)
      message_id = el_move_message_thread(lbs, resubmit_orig);

   /* retrieve submission date */
   if (date[0] == 0)
      el_retrieve(lbs, message_id, date, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

   /*---- replace relative elog:/x link by elog:n/x link */
   if (stristr(getparam("text"), "elog:/")) {
      p = getparam("text");
      if (stricmp(encoding, "HTML") == 0) {
         sprintf(str, "%d/", message_id);
      } else
         sprintf(str, "elog:%d/", message_id);
      strsubst(p, TEXT_SIZE, "elog:/", str);
      el_submit(lbs, message_id, TRUE, date, attr_list, attrib, n_attr, p, in_reply_to, reply_to, encoding,
                att_file, TRUE, NULL, NULL);
   }

   /*---- replace elog: by HTML link ----*/
   if (strieq(encoding, "HTML") && stristr(getparam("text"), "elog:")) {
      p = stristr(getparam("text"), "elog:");
      while (p) {
         for (i = 0; i < 5 || (p[i] == '/' || isalnum(p[i])); i++)
            str[i] = p[i];
         str[i] = 0;
         convert_elog_link(lbs, str + 5, str + 5, str2, 0, message_id);
         strsubst(p, TEXT_SIZE, str, str2);
         p += strlen(str2);
         p = stristr(p, "elog:");
      }
      el_submit(lbs, message_id, TRUE, date, attr_list, attrib, n_attr, getparam("text"), in_reply_to,
                reply_to, encoding, att_file, TRUE, NULL, NULL);
   }

   /*---- email notifications ----*/

   suppress = isparam("suppress") ? atoi(getparam("suppress")) : 0;

   /* check for mail submissions */
   mail_param[0] = 0;
   mail_to = (char *) xmalloc(256);
   mail_to[0] = 0;
   mail_to_size = 256;
   rcpt_to = (char *) xmalloc(256);
   rcpt_to[0] = 0;
   rcpt_to_size = 256;
   mail_list = (char *) xmalloc(MAX_N_EMAIL * NAME_LENGTH);
   rcpt_list = (char *) xmalloc(MAX_N_EMAIL * NAME_LENGTH);

   if (suppress == 1 || suppress == 3) {
      if (suppress == 1)
         strcpy(mail_param, "?suppress=1");
   } else {
      /* go throuch "Email xxx" in configuration file */
      for (index = mindex = 0; index <= n_attr; index++) {

         strcpy(ua, attr_list[index]);
         stou(ua);

         if (index < n_attr) {
            strcpy(str, "Email ");
            if (strchr(attr_list[index], ' '))
               sprintf(str + strlen(str), "\"%s\"", attr_list[index]);
            else
               strlcat(str, attr_list[index], sizeof(str));
            strcat(str, " ");

            if (attr_flags[index] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) {
               sprintf(str2, "%s_%d", ua, mindex);

               mindex++;
               if (mindex == MAX_N_LIST)
                  mindex = 0;
               else
                  index--;      /* repeat this loop */
            } else
               strlcpy(str2, ua, sizeof(str2));

            if (isparam(str2)) {
               if (strchr(getparam(str2), ' ')) {
                  strlcat(str, "\"", sizeof(str));
                  strlcat(str, getparam(str2), sizeof(str));
                  strlcat(str, "\"", sizeof(str));
               } else
                  strlcat(str, getparam(str2), sizeof(str));
            }
         } else
            sprintf(str, "Email ALL");

         if (getcfg(lbs->name, str, list, sizeof(list))) {
            i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
            strsubst_list(list, sizeof(list), slist, svalue, i);

            n = strbreak(list, (char (*)[1500]) mail_list, 200, ",", FALSE);

            if (get_verbose() >= VERBOSE_INFO)
               eprintf("\n%s to %s\n\n", str, list);

            for (i = 0; i < n; i++) {
               /* remove possible 'mailto:' */
               if ((p = strstr(&mail_list[i * NAME_LENGTH], "mailto:")) != NULL)
                  memmove(p, p + 7, strlen(p + 7) + 1);

               if ((int) strlen(mail_to) + (int) strlen(&mail_list[i * NAME_LENGTH]) + 10 >= mail_to_size) {
                  mail_to_size += 256;
                  mail_to = xrealloc(mail_to, mail_to_size);
               }
               strcat(mail_to, &mail_list[i * NAME_LENGTH]);
               strcat(mail_to, ",");

               if ((int) strlen(rcpt_to) + (int) strlen(&mail_list[i * NAME_LENGTH]) + 10 >= rcpt_to_size) {
                  rcpt_to_size += 256;
                  rcpt_to = xrealloc(rcpt_to, rcpt_to_size);
               }
               strcat(rcpt_to, &mail_list[i * NAME_LENGTH]);
               strcat(rcpt_to, ",");
            }
         }
      }

      if (!getcfg(lbs->name, "Suppress Email to users", str, sizeof(str)) || atoi(str) == 0) {
         /* go through password file */
         for (index = 0;; index++) {
            if (!enum_user_line(lbs, index, user, sizeof(user)))
               break;

            get_user_line(lbs, user, NULL, full_name, user_email, email_notify, NULL, NULL);

            for (i = 0; lb_list[i].name[0] && i < 1000; i++)
               if (strieq(lb_list[i].name, lbs->name))
                  break;

            if (email_notify[i]) {
               /* check if user has access to this logbook */
               if (!check_login_user(lbs, user))
                  continue;

               sprintf(str, "\"%s\" <%s>,", full_name, user_email);
               if ((int) strlen(mail_to) + (int) strlen(str) + 1 >= mail_to_size) {
                  mail_to_size += 256;
                  mail_to = xrealloc(mail_to, mail_to_size);
               }
               strcat(mail_to, str);

               sprintf(str, "%s,", user_email);
               if ((int) strlen(rcpt_to) + (int) strlen(str) + 1 >= rcpt_to_size) {
                  rcpt_to_size += 256;
                  rcpt_to = xrealloc(rcpt_to, rcpt_to_size);
               }
               strcat(rcpt_to, str);
            }
         }
      }
   }

   if (strlen(mail_to) > 0) {
      /* convert any '|' to ',', remove duplicate email to's */
      n = strbreak(rcpt_to, (void *) rcpt_list, MAX_N_EMAIL, ",|", TRUE);
      strbreak(mail_to, (void *) mail_list, MAX_N_EMAIL, ",|", TRUE);
      for (i = 0; i < n - 1; i++) {
         for (j = i + 1; j < n; j++) {
            if (rcpt_list[i * NAME_LENGTH] && rcpt_list[j * NAME_LENGTH] &&
                strstr(&rcpt_list[i * NAME_LENGTH], &rcpt_list[j * NAME_LENGTH])) {
               for (k = j; k < n - 1; k++) {
                  memcpy(&rcpt_list[k * NAME_LENGTH], &rcpt_list[(k + 1) * NAME_LENGTH], NAME_LENGTH);
                  memcpy(&mail_list[k * NAME_LENGTH], &mail_list[(k + 1) * NAME_LENGTH], NAME_LENGTH);
               }
               memset(&rcpt_list[k * NAME_LENGTH], 0, NAME_LENGTH);
               memset(&mail_list[k * NAME_LENGTH], 0, NAME_LENGTH);
               j = j - 1;
               n = n - 1;
               break;
            }
         }
      }
      rcpt_to[0] = 0;
      mail_to[0] = 0;
      for (i = 0; i < n; i++) {

         if ((int) strlen(rcpt_to) + (int) strlen(&rcpt_list[i * NAME_LENGTH]) + 5 >= rcpt_to_size) {
            rcpt_to_size += 256;
            rcpt_to = xrealloc(rcpt_to, rcpt_to_size);
         }
         strcat(rcpt_to, &rcpt_list[i * NAME_LENGTH]);

         if ((int) strlen(mail_to) + (int) strlen(&mail_list[i * NAME_LENGTH]) + 5 >= mail_to_size) {
            mail_to_size += 256;
            mail_to = xrealloc(mail_to, mail_to_size);
         }
         strcat(mail_to, &mail_list[i * NAME_LENGTH]);

         if (i < MAX_N_EMAIL - 1 && rcpt_list[(i + 1) * NAME_LENGTH]) {
            strcat(rcpt_to, ",");
            strcat(mail_to, ",\r\n\t");
         }
      }

      /* fix for edited draft messages. new_entry is a hidden field persisting draft cycles */
      old_mail = bedit;
      if (isparam("new_entry"))
         old_mail = 0;
      if (resubmit_orig)
         old_mail = 0;

      if (compose_email(lbs, rcpt_to, mail_to, message_id, attrib, mail_param, old_mail, att_file,
                        isparam("encoding") ? getparam("encoding") : "plain", atoi(in_reply_to)) == 0) {
         xfree(mail_to);
         xfree(rcpt_to);
         xfree(mail_list);
         xfree(rcpt_list);
         return;
      }
   }

   xfree(mail_to);
   xfree(rcpt_to);
   xfree(mail_list);
   xfree(rcpt_list);

   /*---- shell execution ----*/

   if (!(isparam("shell_suppress") && atoi(getparam("shell_suppress")))) {
      if (!bedit) {
         if (getcfg(lbs->name, "Execute new", str, sizeof(str)))
            execute_shell(lbs, message_id, attrib, att_file, str);
      } else {
         if (getcfg(lbs->name, "Execute edit", str, sizeof(str)))
            execute_shell(lbs, message_id, attrib, att_file, str);
      }
   }

   /*---- custom submit page ----*/

   if (getcfg(lbs->name, "Submit page", str, sizeof(str))) {
      /* check if file starts with an absolute directory */
      if (str[0] == DIR_SEPARATOR || str[1] == ':')
         strcpy(file_name, str);
      else {
         strlcpy(file_name, logbook_dir, sizeof(file_name));
         strlcat(file_name, str, sizeof(file_name));
      }
      send_file_direct(file_name);
      return;
   }

   if (getcfg(lbs->name, "List after submit", str, sizeof(str)) && atoi(str) == 1)
      str[0] = 0;
   else
      sprintf(str, "%d%s", message_id, mail_param);
   redirect(lbs, str);
}

Project: elog

int my_read(int fh, void *buffer, unsigned int bytes) {
#ifdef OS_UNIX
   int i, n = 0;

   do {
      i = read(fh, (char *) buffer + n, bytes - n);

      /* don't return if an alarm signal was cought */
      if (i == -1 && errno == EINTR)
         continue;

      if (i == -1)
         return -1;

      if (i == 0)
         return n;

      n += i;

   } while (n < (int) bytes);

   return n;
#else
   return read(fh, buffer, bytes);
#endif

   return 0;
}

Project: elog

time_t date_to_ltime(char *date) {
   struct tm tms;
   int i, date_zone, local_zone;
   time_t ltime;

   memset(&tms, 0, sizeof(struct tm));

   if (strlen(date) > 25) {

      /* RFC2822 compliant date */
      for (i = 0; i < 12; i++)
         if (strncmp(date + 8, mname[i], 3) == 0)
            break;
      tms.tm_mon = i;

      tms.tm_mday = atoi(date + 5);
      tms.tm_hour = atoi(date + 17);
      tms.tm_min = atoi(date + 20);
      tms.tm_sec = atoi(date + 23);
      tms.tm_year = atoi(date + 12) - 1900;
      tms.tm_isdst = -1;

      if (tms.tm_year < 90)
         tms.tm_year += 100;

      ltime = mktime(&tms);

      /* correct for difference between local time zone (used by mktime) and time zone of date */
      date_zone = atoi(date + 26);

      /* correct for incorrect date_zone */
      if (date_zone > 2400 || date_zone < -2400)
         date_zone = 0;
      date_zone = (abs(date_zone) % 100) * 60 + (date_zone) / 100 * 3600;

      local_zone = my_timezone();
      if (tms.tm_isdst)
         local_zone -= 3600;

      ltime = ltime - local_zone - date_zone;

   } else {

      /* ctime() complient date */
      for (i = 0; i < 12; i++)
         if (strncmp(date + 4, mname[i], 3) == 0)
            break;
      tms.tm_mon = i;

      tms.tm_mday = atoi(date + 8);
      tms.tm_hour = atoi(date + 11);
      tms.tm_min = atoi(date + 14);
      tms.tm_sec = atoi(date + 17);
      tms.tm_year = atoi(date + 20) - 1900;
      tms.tm_isdst = -1;

      if (tms.tm_year < 90)
         tms.tm_year += 100;

      ltime = mktime(&tms);
   }

   return ltime;
}

Project: elog

int el_submit_attachment(LOGBOOK *lbs, const char *afilename, const char *buffer, int buffer_size,
                         char *full_name) {
   char file_name[MAX_PATH_LENGTH], ext_file_name[
           MAX_PATH_LENGTH + 100], str[MAX_PATH_LENGTH], *p, subdir[MAX_PATH_LENGTH];
   int fh;
   time_t now;
   struct tm tms;

   /* strip directory, add date and time to filename */
   strlcpy(str, afilename, sizeof(str));
   p = str;
   while (strchr(p, ':'))
      p = strchr(p, ':') + 1;
   while (strchr(p, '\\'))
      p = strchr(p, '\\') + 1;  /* NT */
   while (strchr(p, '/'))
      p = strchr(p, '/') + 1;   /* Unix */
   strlcpy(file_name, p, sizeof(file_name));

   /* assemble ELog filename */
   if (file_name[0]) {

      if (file_name[6] == '_' && file_name[13] == '_' && isdigit(file_name[0]) && isdigit(file_name[1]))
         strlcpy(ext_file_name, file_name, sizeof(ext_file_name));
      else {
         time(&now);
         memcpy(&tms, localtime(&now), sizeof(struct tm));

         sprintf(ext_file_name, "%02d%02d%02d_%02d%02d%02d_%s", tms.tm_year % 100, tms.tm_mon + 1,
                 tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec, file_name);
      }

      if (full_name)
         strlcpy(full_name, ext_file_name, MAX_PATH_LENGTH);

      strlcpy(str, lbs->data_dir, sizeof(str));
      generate_subdir_name(ext_file_name, subdir, sizeof(subdir));
      strlcat(str, subdir, sizeof(str));
      if (strlen(str) > 0 && str[strlen(str) - 1] == DIR_SEPARATOR)
         str[strlen(str) - 1] = 0;

#ifdef OS_WINNT
      mkdir(str);
#else
      mkdir(str, 0755);
#endif

      strlcat(str, DIR_SEPARATOR_STR, sizeof(str));
      strlcat(str, ext_file_name, sizeof(str));

      /* save attachment */
      fh = open(str, O_CREAT | O_RDWR | O_BINARY, 0644);
      if (fh < 0) {
         strencode2(file_name, str, sizeof(file_name));
         sprintf(str, "Cannot write attachment file \"%s\"", file_name);
         show_error(str);
         return -1;
      } else {
         write(fh, buffer, buffer_size);
         close(fh);
      }
   }

   return 0;
}

Project: elog

int sendmail(LOGBOOK *lbs, char *smtp_host, char *from, char *to, char *text, char *error, int error_size) {
   struct sockaddr_in bind_addr;
   struct hostent *phe;
   int i, n, s, strsize;
   char *str;
   char list[MAX_N_EMAIL][NAME_LENGTH], buffer[10000], decoded[256];

   memset(error, 0, error_size);

   if (get_verbose() >= VERBOSE_INFO)
      eprintf("\n\nEmail from %s to %s, SMTP host %s:\n", from, to, smtp_host);
   sprintf(buffer, "Email from %s to ", from);
   strlcat(buffer, to, sizeof(buffer));
   strlcat(buffer, ", SMTP host ", sizeof(buffer));
   strlcat(buffer, smtp_host, sizeof(buffer));
   strlcat(buffer, ":\n", sizeof(buffer));
   write_logfile(lbs, buffer);

   /* create a new socket for connecting to remote server */
   s = socket(AF_INET, SOCK_STREAM, 0);
   if (s == -1)
      return -1;

   strsize = MAX_CONTENT_LENGTH + 1000;
   str = xmalloc(strsize);

   /* connect to remote node port on SMTP port */
   int smtp_port = 25;
   if (getcfg(lbs->name, "SMTP port", str, strsize))
      smtp_port = atoi(str);

   memset(&bind_addr, 0, sizeof(bind_addr));
   bind_addr.sin_family = AF_INET;
   bind_addr.sin_port = htons((short) smtp_port);

   phe = gethostbyname(smtp_host);
   if (phe == NULL) {
      if (error)
         strlcpy(error, loc("Cannot lookup server name"), error_size);
      return -1;
   }
   memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);

   if (connect(s, (void *) &bind_addr, sizeof(bind_addr)) < 0) {
      closesocket(s);
      if (error)
         strlcpy(error, loc("Cannot connect to server"), error_size);
      return -1;
   }

   recv_string(s, str, strsize, 10000);
   if (get_verbose() >= VERBOSE_INFO)
      efputs(str);
   write_logfile(lbs, str);

   /* drain server messages */
   do {
      str[0] = 0;
      recv_string(s, str, strsize, 300);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(str);
      write_logfile(lbs, str);
   } while (str[0]);

   if (getcfg(lbs->name, "SMTP username", str, strsize)) {

      snprintf(str, strsize - 1, "EHLO %s\r\n", host_name);
      send(s, str, strlen(str), 0);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(str);
      write_logfile(lbs, str);

      do {
         recv_string(s, str, strsize, 3000);
         if (get_verbose() >= VERBOSE_INFO)
            efputs(str);
         write_logfile(lbs, str);
         if (!check_smtp_error(str, 250, error, error_size))
            goto smtp_error;
      } while (stristr(str, "250 ") == NULL);

   } else {

      snprintf(str, strsize - 1, "HELO %s\r\n", host_name);

      send(s, str, strlen(str), 0);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(str);
      write_logfile(lbs, str);
      recv_string(s, str, strsize, 3000);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(str);
      write_logfile(lbs, str);
      if (!check_smtp_error(str, 250, error, error_size))
         goto smtp_error;
   }

   /* optional authentication */
   if (getcfg(lbs->name, "SMTP username", str, strsize)) {

      snprintf(str, strsize - 1, "AUTH LOGIN\r\n");
      send(s, str, strlen(str), 0);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(str);
      write_logfile(lbs, str);
      recv_string(s, str, strsize, 3000);
      if (strchr(str, '\r'))
         *strchr(str, '\r') = 0;
      if (atoi(str) != 334) {
         strcat(str, "\n");
         if (get_verbose() >= VERBOSE_INFO)
            efputs(str);
         write_logfile(lbs, str);
      } else {
         base64_decode(str + 4, decoded);
         strcat(decoded, "\n");
         if (get_verbose() >= VERBOSE_INFO)
            efputs(decoded);
         write_logfile(lbs, decoded);
      }
      if (!check_smtp_error(str, 334, error, error_size))
         goto smtp_error;

      getcfg(lbs->name, "SMTP username", decoded, sizeof(decoded));
      base64_encode((unsigned char *) decoded, (unsigned char *) str, strsize);
      strcat(str, "\r\n");
      send(s, str, strlen(str), 0);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(decoded);
      write_logfile(lbs, decoded);
      recv_string(s, str, strsize, 3000);
      if (strchr(str, '\r'))
         *strchr(str, '\r') = 0;
      base64_decode(str + 4, decoded);
      strcat(decoded, "\n");
      if (get_verbose() >= VERBOSE_INFO)
         efputs(decoded);
      write_logfile(lbs, decoded);
      if (!check_smtp_error(str, 334, error, error_size))
         goto smtp_error;

      getcfg(lbs->name, "SMTP password", str, strsize);
      strcat(str, "\r\n");
      send(s, str, strlen(str), 0);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(str);
      write_logfile(lbs, str);
      recv_string(s, str, strsize, 3000);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(str);
      write_logfile(lbs, str);
      if (!check_smtp_error(str, 235, error, error_size))
         goto smtp_error;
   }

   snprintf(str, strsize - 1, "MAIL FROM: %s\r\n", from);
   send(s, str, strlen(str), 0);
   if (get_verbose() >= VERBOSE_INFO)
      efputs(str);
   write_logfile(lbs, str);
   recv_string(s, str, strsize, 3000);
   if (get_verbose() >= VERBOSE_INFO)
      efputs(str);
   write_logfile(lbs, str);

   if (!check_smtp_error(str, 250, error, error_size))
      goto smtp_error;

   /* break recipients into list */
   n = strbreak(to, list, MAX_N_EMAIL, ",", FALSE);

   for (i = 0; i < n; i++) {
      if (list[i] == 0 || strchr(list[i], '@') == NULL)
         continue;

      snprintf(str, strsize - 1, "RCPT TO: <%s>\r\n", list[i]);
      send(s, str, strlen(str), 0);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(str);
      write_logfile(lbs, str);

      /* increased timeout for SMTP servers with long alias lists */
      recv_string(s, str, strsize, 30000);
      if (get_verbose() >= VERBOSE_INFO)
         efputs(str);
      write_logfile(lbs, str);

      if (!check_smtp_error(str, 250, error, error_size))
         goto smtp_error;
   }

   snprintf(str, strsize - 1, "DATA\r\n");
   send(s, str, strlen(str), 0);
   if (get_verbose() >= VERBOSE_INFO)
      efputs(str);
   write_logfile(lbs, str);
   recv_string(s, str, strsize, 3000);
   if (get_verbose() >= VERBOSE_INFO)
      efputs(str);
   write_logfile(lbs, str);
   if (!check_smtp_error(str, 354, error, error_size))
      goto smtp_error;

   /* replace "." at beginning of line by ".." */
   strlcpy(str, text, strsize);
   strsubst(str, strsize, "\n.", "\n..");

   /* add ".<CR>" to signal end of message */
   strlcat(str, ".\r\n", strsize);

   /* check if buffer exceeded */
   if ((int) strlen(str) == strsize - 1) {
      strlcpy(error, loc("Entry size too large for email notification"), error_size);
      goto smtp_error;
   }

   send(s, str, strlen(str), 0);
   if (get_verbose() >= VERBOSE_INFO)
      efputs(str);
   write_logfile(lbs, str);
   recv_string(s, str, strsize, 10000);
   if (get_verbose() >= VERBOSE_INFO)
      efputs(str);
   write_logfile(lbs, str);
   if (!check_smtp_error(str, 250, error, error_size))
      goto smtp_error;

   snprintf(str, strsize - 1, "QUIT\r\n");
   send(s, str, strlen(str), 0);
   if (get_verbose() >= VERBOSE_INFO)
      efputs(str);
   write_logfile(lbs, str);
   recv_string(s, str, strsize, 3000);
   if (get_verbose() >= VERBOSE_INFO)
      efputs(str);
   write_logfile(lbs, str);
   if (!check_smtp_error(str, 221, error, error_size))
      goto smtp_error;

   closesocket(s);
   xfree(str);
   return 1;

   smtp_error:

   closesocket(s);
   xfree(str);

   return -1;
}