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_forgot_pwd_page(LOGBOOK *lbs) {
   int i;
   char str[1000], str2[1000], login_name[256], full_name[256], user_email[256], name[256], pwd[256],
           redir[256], smtp_host[256], mail_from[256], mail_from_name[256], subject[256],
           mail_text[1000], url[1000], error[1000];

   if (isparam("login_name")) {
      /* seach in pwd file */

      strcpy(name, getparam("login_name"));

      for (i = 0;; i++) {
         if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
            break;

         get_user_line(lbs, login_name, NULL, full_name, user_email, NULL, NULL, NULL);

         if (strieq(name, login_name) || strieq(name, full_name) || strieq(name, user_email)) {
            if (user_email[0] == 0) {
               sprintf(str, loc("No Email address registered with user name <i>\"%s\"</i>"), name);
               strencode2(str2, str, sizeof(str2));
               show_error(str2);
               return;
            }

            /* create random password */
            for (i = 0; i < 16; i++)
               pwd[i] = 'A' + (rand() % 25);
            pwd[i] = 0;

            /* send email with new password */
            if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) {
               show_error(loc("No SMTP host defined in [global] section of configuration file"));
               return;
            }

            /* try to get URL from referer */
            if (!getcfg("global", "URL", url, sizeof(url))) {
               if (referer[0])
                  strcpy(url, referer);
               else {
                  if (elog_tcp_port == 80) {
                     if (_ssl_flag)
                        sprintf(url, "https://%s/", http_host);
                     else
                        sprintf(url, "http://%s/", http_host);
                  } else {
                     if (_ssl_flag)
                        sprintf(url, "https://%s:%d/", http_host, elog_tcp_port);
                     else
                        sprintf(url, "http://%s:%d/", http_host, elog_tcp_port);
                  }
               }
            } else {
               if (url[strlen(url) - 1] != '/')
                  strlcat(url, "/", sizeof(url));
               if (lbs) {
                  strlcat(url, lbs->name_enc, sizeof(url));
                  strlcat(url, "/", sizeof(url));
               }
            }

            url_slash_encode(pwd, sizeof(pwd));
            sprintf(redir, "?cmd=%s&oldpwd=%s", loc("Change password"), pwd);
            url_encode(redir, sizeof(redir));

            strencode2(str2, redir, sizeof(str2));
            sprintf(str, "?redir=%s&uname=%s&upassword=%s", str2, login_name, pwd);
            strlcat(url, str, sizeof(url));

            retrieve_email_from(lbs, mail_from, mail_from_name, NULL);

            if (lbs)
               sprintf(subject, loc("Password recovery for ELOG %s"), lbs->name);
            else
               sprintf(subject, loc("Password recovery for ELOG %s"), http_host);

            mail_text[0] = 0;
            compose_email_header(lbs, subject, mail_from_name, user_email, NULL, mail_text,
                                 sizeof(mail_text), 1, 0, NULL, 0, 0);

            strlcat(mail_text, "\r\n", sizeof(mail_text));
            sprintf(mail_text + strlen(mail_text),
                    loc("This is an automatically generated account recovery email for host %s"), http_host);
            strlcat(mail_text, ".\r\n", sizeof(mail_text));
            strlcat(mail_text, loc("Please click on following link to recover your account"),
                    sizeof(mail_text));
            strlcat(mail_text, ":\r\n\r\n", sizeof(mail_text));
            strlcat(mail_text, url, sizeof(mail_text));
            strlcat(mail_text, "\r\n\r\n", sizeof(mail_text));
            sprintf(mail_text + strlen(mail_text), "ELOG Version %s\r\n", VERSION);

            if (sendmail(lbs, smtp_host, mail_from, user_email, mail_text, error, sizeof(error)) != -1) {
               /* save new password */
               auth_change_password(lbs, login_name, NULL, pwd, str, sizeof(str));

               /* show notification web page */
               show_standard_header(lbs, FALSE, loc("ELOG password recovery"), "", FALSE, NULL, NULL, 0);

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

               rsprintf("<tr><td align=center class=\"dlgform\">\n");
               rsprintf(loc("A password recovery email for user <i>\"%s\"</i> has been sent to %s"),
                        full_name, user_email);
               rsprintf("</td></tr></table>\n");
               show_bottom_text(lbs);
               rsprintf("</body></html>\n");
               return;
            } else {
               sprintf(str, loc("Error sending Email via \"%s\""), smtp_host);
               strlcat(str, ": ", sizeof(str));
               strlcat(str, error, sizeof(str));
               show_error(str);
               return;
            }
         }
      }

      if (strchr(name, '@'))
         sprintf(str, loc("Email address \"%s\" not registered"), name);
      else
         sprintf(str, loc("User name \"%s\" not registered"), name);

      strencode2(str2, str, sizeof(str2));
      show_error(str2);

      return;
   } else {
      /*---- header ----*/

      getcfg(lbs->name, "Authentication", str, sizeof(str));
      if (stristr(str, "Kerberos") || stristr(str, "Webserver") || stristr(str, "PAM")) {
         show_error
                 ("This installation of ELOG uses site authentication\nwhere password recovery is not possible");
         return;
      }

      show_standard_header(lbs, TRUE, loc("ELOG password recovery"), NULL, FALSE, NULL, NULL, 0);

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

      /*---- entry form ----*/

      rsprintf("<tr><td class=\"dlgtitle\">%s</td></tr>\n", loc("Enter your user name or email address"));

      rsprintf("<tr><td align=center class=\"dlgform\">\n");
      rsprintf("<input type=hidden name=cmd value=%s>\n", loc("Forgot"));
      rsprintf("<input type=text size=40 name=login_name></td></tr>\n");

      rsprintf("<tr><td align=center class=\"dlgform\"><input type=submit value=\"%s\">\n", loc("Submit"));

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

Project: elog

int is_html(char *s) {
   char *str, *p;
   int i;

   str = xstrdup(s);

   for (i = 0; i < (int) strlen(s); i++)
      str[i] = toupper(s[i]);
   str[i] = 0;

   for (i = 0; html_tags[i][0]; i++) {
      p = strstr(str, html_tags[i]);
      if (p && strchr(p, '>') && (p == str || (p > str && *(p - 1) != '\\'))) {
         xfree(str);
         return TRUE;
      }
   }

   if (strstr(str, "&#") && strchr(strstr(str, "&#"), ';')) {
      xfree(str);
      return TRUE;
   }

   xfree(str);
   return FALSE;
}

Project: elog

void efputs(const char *buf) {
   (*fputs_handler)(buf);
}

Project: elog

void adjust_crlf(char *buffer, int bufsize) {
   char *p;
   UNUSED(bufsize);

#ifdef OS_UNIX

   /* convert \r\n -> \n */
   p = buffer;
   while ((p = strstr(p, "\r\n")) != NULL) {
      memmove(p, p + 1, strlen(p + 1) + 1);     // strcpy() gives error under Ubuntu
   }
#else

                                                                                                                           char *tmpbuf;

   assert(bufsize);
   tmpbuf = xmalloc(bufsize);

   /* convert \n -> \r\n */
   p = buffer;
   while ((p = strstr(p, "\n")) != NULL) {

      if (p > buffer && *(p - 1) == '\r') {
         p++;
         continue;
      }

      if ((int) strlen(buffer) + 2 >= bufsize) {
         xfree(tmpbuf);
         return;
      }

      strlcpy(tmpbuf, p, bufsize);
      *(p++) = '\r';
      strlcpy(p, tmpbuf, bufsize - (p - buffer));
      p++;
   }

   xfree(tmpbuf);
#endif
}

Project: elog

void _MD5_update(MD5_CONTEXT *pctx, const void *pdata, unsigned int len) {
   unsigned char *pin;
   unsigned int i, index, partlen;

   pin = (unsigned char *) pdata;

   // compute number of bytes mod 64
   index = (unsigned int) ((pctx->count[0] >> 3) & 0x3F);

   // update number of bits
   if ((pctx->count[0] += ((unsigned int) len << 3)) < ((unsigned int) len << 3))
      pctx->count[1]++;
   pctx->count[1] += ((unsigned int) len >> 29);

   partlen = 64 - index;

   // transform as many times as possible.
   if (len >= partlen) {
      memcpy(&pctx->buffer[index], pin, partlen);
      _MD5_transform(pctx->state, pctx->buffer);

      for (i = partlen; i + 63 < len; i += 64)
         _MD5_transform(pctx->state, &pin[i]);

      index = 0;
   } else
      i = 0;

   /* buffer remaining input */
   memcpy(&pctx->buffer[index], &pin[i], len - i);
}

Project: elog

int strbreak(char *str, char list[][NAME_LENGTH], int size, char *brk, BOOL ignore_quotes)
/* break comma-separated list into char array, stripping leading
 and trailing blanks */
{
   int i, j;
   char *p;

   memset(list, 0, size * NAME_LENGTH);
   p = str;
   if (!p || !*p)
      return 0;

   while (*p == ' ')
      p++;

   for (i = 0; *p && i < size; i++) {
      if (*p == '"' && !ignore_quotes) {
         p++;
         j = 0;
         memset(list[i], 0, NAME_LENGTH);
         do {
            /* convert two '"' to one */
            if (*p == '"' && *(p + 1) == '"') {
               list[i][j++] = '"';
               p += 2;
            } else if (*p == '"') {
               break;
            } else
               list[i][j++] = *p++;

         } while (j < NAME_LENGTH - 1);
         list[i][j] = 0;

         /* skip second '"' */
         p++;

         /* skip blanks and break character */
         while (*p == ' ')
            p++;
         if (*p && strchr(brk, *p))
            p++;
         while (*p == ' ')
            p++;

      } else {
         strlcpy(list[i], p, NAME_LENGTH);

         for (j = 0; j < (int) strlen(list[i]); j++)
            if (strchr(brk, list[i][j])) {
               list[i][j] = 0;
               break;
            }

         p += strlen(list[i]);
         while (*p == ' ')
            p++;
         if (*p && strchr(brk, *p))
            p++;
         while (*p == ' ')
            p++;
      }

      while (list[i][strlen(list[i]) - 1] == ' ')
         list[i][strlen(list[i]) - 1] = 0;

      if (!*p)
         break;
   }

   if (i == size)
      return size;

   return i + 1;
}

Project: elog

void highlight_searchtext(regex_t *re_buf, char *src, char *dst, int hidden) {
   char *pt, *pt1;
   int size, status;
   regmatch_t pmatch[10];

   dst[0] = 0;
   pt = src;                    /* original text */
   pt1 = dst;                   /* text with inserted coloring */
   do {
      status = regexec(re_buf, pt, 10, pmatch, 0);
      if (status != REG_NOMATCH) {
         size = pmatch[0].rm_so;

         /* abort if zero length match, for example from "m*" */
         if (pmatch[0].rm_eo - pmatch[0].rm_so == 0) {
            status = REG_NOMATCH;
            strcpy(pt1, pt);
            break;
         }
         /* copy first part original text */
         memcpy(pt1, pt, size);
         pt1 += size;
         pt += size;

         /* add coloring 1st part */

         /* here: \001='<', \002='>', /003='"', and \004=' ' */
         /* see also rsputs2(char* ) */

         if (hidden)
            strcpy(pt1, "\001B\004style=\003color:black;background-color:#ffff66\003\002");
         else
            strcpy(pt1, "<B style=\"color:black;background-color:#ffff66\">");

         pt1 += strlen(pt1);

         /* copy origial search text */
         size = pmatch[0].rm_eo - pmatch[0].rm_so;
         memcpy(pt1, pt, size);
         pt1 += size;
         pt += size;

         /* add coloring 2nd part */
         if (hidden)
            strcpy(pt1, "\001/B\002");
         else
            strcpy(pt1, "</B>");
         pt1 += strlen(pt1);
      }
   } while (status != REG_NOMATCH);

   strcpy(pt1, pt);
}

Project: elog

void xml_import(LOGBOOK *lbs, const char *xml, const char *xmlfile) {
   char str[NAME_LENGTH], date[80], error[256], encoding[256], *list, *p, in_reply_to[80],
           reply_to[MAX_REPLY_TO * 10], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], attachment_all[64
                                                                                                     *
                                                                                                     MAX_ATTACHMENTS];
   int i, j, index, n_attr, iline, n_imported, i_line, line_len, message_id, bedit;
   time_t ltime;
   PMXML_NODE root, entry;

   iline = n_imported = 0;

   n_attr = lbs->n_attr;

   root = mxml_parse_buffer(xml, error, sizeof(error), NULL);
   if (root == NULL) {
      strencode2(str, error, sizeof(str));
      show_error(str);
      return;
   }

   root = mxml_find_node(root, "ELOG_LIST");
   if (root == NULL) {
      sprintf(str, loc("XML file does not contain %s element"), "&lt;ELOG_LIST&gt;");
      show_error(str);
      return;
   }

   entry = mxml_subnode(root, 0);

   if (mxml_find_node(entry, "MID") == NULL) {
      sprintf(str, loc("XML file does not contain %s element"), "&lt;MID&gt;");
      show_error(str);
      return;
   }

   if (mxml_find_node(entry, "DATE") == NULL) {
      sprintf(str, loc("XML file does not contain %s element"), "&lt;DATE&gt;");
      show_error(str);
      return;
   }

   if (mxml_find_node(entry, "ENCODING") == NULL) {
      sprintf(str, loc("XML file does not contain %s element"), "&lt;ENCODING&gt;");
      show_error(str);
      return;
   }

   if (isparam("preview")) {

      /* title row */
      sprintf(str, loc("XML import preview of %s"), xmlfile);
      show_standard_header(lbs, TRUE, str, "./", FALSE, NULL, NULL, 0);
      rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\">\n");
      rsprintf("<tr><td class=\"title1\">%s</td></tr>\n", str, str);

      /* menu buttons */
      rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("XML Import"));

      /* hidden fields */
      if (isparam("head"))
         rsprintf("<input type=hidden name=head value=\"%s\">\n", getparam("head"));
      if (isparam("keep"))
         rsprintf("<input type=hidden name=keep value=\"%s\">\n", getparam("keep"));
      rsprintf("<input type=hidden name=xmlfile value=\"%s\">\n", xmlfile);

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

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

   /* derive attributes from XML file */
   if (isparam("head")) {
      if (isparam("preview")) {
         rsprintf("<tr>\n");
         for (i = 0; i < mxml_get_number_of_children(entry); i++) {
            strlcpy(str, mxml_get_name(mxml_subnode(entry, i)), sizeof(str));
            if (strieq(str, "MID"))
               strcpy(str, "ID");
            if (strieq(str, "DATE"))
               strcpy(str, loc("Date"));
            if (strieq(str, "TEXT"))
               strcpy(str, loc("Text"));
            if (!strieq(str, "ENCODING") && !strieq(str, "IN_REPLY_TO") && !strieq(str, "REPLY_TO")
                && !strieq(str, "ATTACHMENT"))
               rsprintf("<th class=\"listtitle\">%s</th>\n", str);
         }

         rsprintf("</tr>\n");
         n_attr = i;
      } else {
         for (i = j = 0; i < mxml_get_number_of_children(entry); i++) {
            strlcpy(str, mxml_get_name(mxml_subnode(entry, i)), NAME_LENGTH);
            if (stricmp(str, "MID") != 0 && stricmp(str, "DATE") != 0 &&
                stricmp(str, "ENCODING") != 0 && stricmp(str, "TEXT") != 0 &&
                stricmp(str, "IN_REPLY_TO") != 0 && stricmp(str, "REPLY_TO") != 0 &&
                stricmp(str, "ATTACHMENT") != 0)
               strlcpy(attr_list[j++], mxml_get_name(mxml_subnode(entry, i)), NAME_LENGTH);
         }

         if (!set_attributes(lbs, attr_list, j))
            return;
         lbs->n_attr = n_attr = j;
      }
   } else {
      if (isparam("preview")) {
         rsprintf("<tr>\n");
         rsprintf("<th class=\"listtitle\">%s</th>\n", "ID");
         rsprintf("<th class=\"listtitle\">%s</th>\n", loc("Date"));
         for (i = 0; i < n_attr; i++)
            rsprintf("<th class=\"listtitle\">%s</th>\n", attr_list[i]);
         rsprintf("<th class=\"listtitle\">%s</th>\n", loc("Text"));
         rsprintf("</tr>\n");
      }
   }

   for (index = 0; index < mxml_get_number_of_children(root); index++) {

      entry = mxml_subnode(root, index);

      if (isparam("preview")) {
         rsprintf("<tr>\n");
         for (i = 0; i < mxml_get_number_of_children(entry); i++) {

            strlcpy(str, mxml_get_name(mxml_subnode(entry, i)), NAME_LENGTH);
            if (strieq(str, "ENCODING") || strieq(str, "IN_REPLY_TO") || strieq(str, "REPLY_TO")
                || strieq(str, "ATTACHMENT"))
               continue;
            if (strieq(str, "TEXT"))
               break;

            if (iline % 2 == 0)
               rsputs("<td class=\"list1\">");
            else
               rsputs("<td class=\"list2\">");

            strlcpy(str, mxml_get_value(mxml_subnode(entry, i)), NAME_LENGTH);

            if (!str[0])
               rsputs("&nbsp;");
            else
               rsputs(str);

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

         rsputs("<td class=\"summary\">");
         if (mxml_find_node(entry, "TEXT")) {
            strlcpy(str, mxml_get_value(mxml_find_node(entry, "TEXT")), sizeof(str));
            if (str[0]) {

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

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

               strip_html(str);
               if (str[0])
                  strencode(str);
               else
                  rsputs("&nbsp;");
            } else
               rsputs("&nbsp;");
         }
         rsputs("</td>\n");
         rsputs("</tr>\n");
         iline++;

      } else {

         message_id = 0;
         if (isparam("keep"))
            message_id = atoi(mxml_get_value(mxml_find_node(entry, "MID")));

         for (i = 0; i < n_attr; i++) {
            strlcpy(str, attr_list[i], sizeof(str));
            while (strchr(str, ' '))
               *strchr(str, ' ') = '_';
            if (mxml_find_node(entry, str) == NULL)
               *(list + (i * NAME_LENGTH)) = 0;
            else
               strlcpy(list + i * NAME_LENGTH, mxml_get_value(mxml_find_node(entry, str)), NAME_LENGTH);
         }

         /* interprete date entries correctly */
         for (i = 0; i < n_attr; i++) {
            if (attr_flags[i] & AF_DATE) {
               /* convert to seconds in Unix format */
               ltime = convert_date(list + i * NAME_LENGTH);
               if (ltime == 0) {
                  show_error(loc("Invalid date format"));
                  return;
               }
               sprintf(list + i * NAME_LENGTH, "%d", (int) ltime);
            }
            if (attr_flags[i] & AF_DATETIME) {
               /* convert to seconds in Unix format */
               ltime = convert_datetime(list + i * NAME_LENGTH);
               if (ltime == 0) {
                  show_error(loc("Invalid date format"));
                  return;
               }
               sprintf(list + i * NAME_LENGTH, "%d", (int) ltime);
            }
         }

         encoding[0] = 0;
         if (mxml_find_node(entry, "ENCODING"))
            strlcpy(encoding, mxml_get_value(mxml_find_node(entry, "ENCODING")), sizeof(encoding));
         else
            strcpy(encoding, "plain");

         reply_to[0] = 0;
         if (mxml_find_node(entry, "REPLY_TO"))
            strlcpy(reply_to, mxml_get_value(mxml_find_node(entry, "REPLY_TO")), sizeof(reply_to));

         in_reply_to[0] = 0;
         if (mxml_find_node(entry, "IN_REPLY_TO"))
            strlcpy(in_reply_to, mxml_get_value(mxml_find_node(entry, "IN_REPLY_TO")), sizeof(in_reply_to));

         date[0] = 0;
         if (mxml_find_node(entry, "DATE"))
            strlcpy(date, mxml_get_value(mxml_find_node(entry, "DATE")), sizeof(date));

         attachment_all[0] = 0;
         if (mxml_find_node(entry, "ATTACHMENT"))
            strlcpy(attachment_all, mxml_get_value(mxml_find_node(entry, "ATTACHMENT")),
                    sizeof(attachment_all));
         memset(attachment, 0, sizeof(attachment));
         for (i = 0; i < MAX_ATTACHMENTS; i++) {
            if (i == 0)
               p = strtok(attachment_all, ",");
            else
               p = strtok(NULL, ",");

            if (p != NULL)
               strlcpy(attachment[i], p, MAX_PATH_LENGTH);
            else
               break;
         }

         str[0] = 0;
         if (mxml_find_node(entry, "TEXT"))
            p = mxml_get_value(mxml_find_node(entry, "TEXT"));
         else
            p = str;

         bedit = FALSE;
         if (isparam("keep")) {
            for (i = 0; i < *lbs->n_el_index; i++)
               if (lbs->el_index[i].message_id == message_id)
                  break;
            if (lbs->el_index[i].message_id == message_id)
               bedit = TRUE;
         }

         /* submit entry */
         if (el_submit
                 (lbs, message_id, bedit, date, attr_list, (char (*)[NAME_LENGTH]) list, n_attr, p,
                  in_reply_to,
                  reply_to, encoding, attachment, FALSE, NULL, NULL))
            n_imported++;
      }
   }

   xfree(list);

   if (isparam("preview")) {
      rsprintf("</table></td></tr></table>\n");
      show_bottom_text(lbs);
      rsprintf("</form></body></html>\r\n");

      return;
   }

   sprintf(str, loc("%d entries successfully imported"), n_imported);
   show_elog_list(lbs, 0, 0, 0, TRUE, str);
}

Project: elog

void insert_breaks(char *str, int n, int size) {
   int i, j, i_last;

   i_last = 0;
   for (i = 0; i < (int) strlen(str); i++) {
      if (str[i] == '\r')
         i_last = i;

      /* if more than n chars without return, insert one */
      if (i - i_last >= n && (int) strlen(str) + 3 < size) {

         /* find previous blank */
         while (i > i_last && str[i] != ' ')
            i--;
         if (str[i] == ' ')
            i++;

         /* move trailing string one char further */
         for (j = strlen(str) + 2; j > i; j--)
            str[j] = str[j - 2];

         /* set CR */
         str[i++] = '\r';
         str[i++] = '\n';

         i_last = i;
      }
   }
}

Project: elog

BOOL file_exist(char *file_name) {
   int fh;

   fh = open(file_name, O_RDONLY);
   if (fh < 0)
      return FALSE;

   close(fh);
   return TRUE;
}

Project: elog

void show_plain_header(int size, char *file_name) {
   /* header */
   rsprintf("HTTP/1.1 200 Document follows\r\n");
   rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
   rsprintf("Accept-Ranges: bytes\r\n");

   if (keep_alive) {
      rsprintf("Connection: Keep-Alive\r\n");
      rsprintf("Keep-Alive: timeout=60, max=10\r\n");
   }
   rsprintf("Pragma: no-cache\r\n");
   rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n");
   rsprintf("Content-Type: text/plain\r\n");
   rsprintf("Content-disposition: attachment; filename=\"%s\"\r\n", file_name);
   if (size)
      rsprintf("Content-Length: %d\r\n", size);
   rsprintf("\r\n");
}

Project: elog

void rsputs2(LOGBOOK *lbs, int absolute_link, const char *str) {
   int i, j, k, l, n;
   char *p, *pd, link[1000], link_text[1000];

   if (strlen_retbuf + (int) (2 * strlen(str) + 1000) >= return_buffer_size) {
      return_buffer = xrealloc(return_buffer, return_buffer_size + 100000);
      memset(return_buffer + return_buffer_size, 0, 100000);
      return_buffer_size += 100000;
   }

   j = strlen_retbuf;
   for (i = 0; i < (int) strlen(str); i++) {
      for (l = 0; key_list[l][0]; l++) {
         if (strncmp(str + i, key_list[l], strlen(key_list[l])) == 0) {

            /* check for escape character */
            if (i > 0 && *(str + i - 1) == '\\') {
               j--;
               *(return_buffer + j) = 0;
               continue;
            }

            p = (char *) (str + i + strlen(key_list[l]));
            i += strlen(key_list[l]);
            for (k = 0; *p && strcspn(p, " \t\n\r({[)}]\"") && k < (int) sizeof(link); k++, i++)
               link[k] = *p++;
            link[k] = 0;
            i--;

            /* link may not end with a '.'/',' (like in a sentence) */
            if (link[k - 1] == '.' || link[k - 1] == ',') {
               link[k - 1] = 0;
               k--;
               i--;
            }

            /* check if link contains coloring */
            p = strchr(link, '\001');
            if (p != NULL) {
               strlcpy(link_text, link, sizeof(link_text));

               /* skip everything between '<' and '>' */
               pd = p;
               while (*pd && *pd != '\002')
                  *p = *pd++;

               memmove(p, pd + 1, strlen(pd + 1) + 1);

               /* skip '</B>' */
               p = strchr(link, '\001');
               if (p != NULL) {
                  pd = p;

                  while (*pd && *pd != '\002')
                     *p = *pd++;

                  memmove(p, pd + 1, strlen(pd + 1) + 1);
               }

               /* correct link text */
               for (n = 0; n < (int) strlen(link_text); n++) {
                  switch (link_text[n]) {
                     /* the translation for the search highliting */
                     case '\001':
                        link_text[n] = '<';
                        break;
                     case '\002':
                        link_text[n] = '>';
                        break;
                     case '\003':
                        link_text[n] = '\"';
                        break;
                     case '\004':
                        link_text[n] = ' ';
                        break;
                  }
               }

            } else
               strlcpy(link_text, link, sizeof(link_text));

            if (strcmp(key_list[l], "elog:") == 0) {
               convert_elog_link(lbs, link, link_text, return_buffer + j, absolute_link, _current_message_id);
            } else if (strcmp(key_list[l], "mailto:") == 0) {
               sprintf(return_buffer + j, "<a href=\"mailto:%s\">%s</a>", link, link_text);
            } else {
               sprintf(return_buffer + j, "<a href=\"%s", key_list[l]);
               j += strlen(return_buffer + j);
               strlen_retbuf = j;

               /* link can contain special characters */
               rsputs2(lbs, absolute_link, link);
               j = strlen_retbuf;

               sprintf(return_buffer + j, "\">%s", key_list[l]);
               j += strlen(return_buffer + j);
               strlen_retbuf = j;

               /* link_text can contain special characters */
               rsputs2(lbs, absolute_link, link_text);
               j = strlen_retbuf;
               sprintf(return_buffer + j, "</a>");
            }

            j += strlen(return_buffer + j);
            break;
         }
      }

      if (!key_list[l][0]) {
         if (strncmp(str + i, "<br>", 4) == 0) {
            strcpy(return_buffer + j, "<br>");
            j += 4;
            i += 3;
         } else
            switch (str[i]) {
               case '&':
                  strcat(return_buffer, "&amp;");
                  j += 5;
                  break;
               case '<':
                  strcat(return_buffer, "&lt;");
                  j += 4;
                  break;
               case '>':
                  strcat(return_buffer, "&gt;");
                  j += 4;
                  break;

                  /* suppress escape character '\' in front of HTML or ELCode tag */
               case '\\':
                  if (str[i + 1] != '<' && str[i + 1] != '[')
                     return_buffer[j++] = str[i];
                  break;

                  /* the translation for the search highliting */
               case '\001':
                  strcat(return_buffer, "<");
                  j++;
                  break;
               case '\002':
                  strcat(return_buffer, ">");
                  j++;
                  break;
               case '\003':
                  strcat(return_buffer, "\"");
                  j++;
                  break;
               case '\004':
                  strcat(return_buffer, " ");
                  j++;
                  break;

               default:
                  return_buffer[j++] = str[i];
            }
      }
   }

   return_buffer[j] = 0;
   strlen_retbuf = j;
}

Project: elog

void show_admin_page(LOGBOOK *lbs, char *top_group) {
   int rows, cols;
   char *buffer, error_str[256];
   char section[NAME_LENGTH], str[NAME_LENGTH], grp[NAME_LENGTH];

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

   sprintf(str, "ELOG %s", loc("Admin"));
   show_html_header(lbs, FALSE, str, TRUE, FALSE, NULL, FALSE, 0);

   rsprintf("<body><form method=\"POST\" action=\"./\" enctype=\"multipart/form-data\">\n");

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

   show_standard_title(lbs, "", 0);

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

   rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Save"));
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
   rsprintf("<input type=hidden name=cfgpage value=\"1\">\n");

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

            sprintf(str, "global %s", lbs->top_group);

            if (is_group(str)) {
               sprintf(grp, "[global %s]", lbs->top_group);
               sprintf(str, loc("Change %s"), grp);
               rsprintf("<input type=submit name=cmd value=\"%s\">\n", str);
            }
         }
      }
   }

   if (is_group("global") && !strieq(top_group, "global")) {
      if (is_admin_user_global(getparam("unm"))) {
         sprintf(str, loc("Change %s"), "[global]");
         rsprintf("<input type=submit name=cmd value=\"%s\">\n", str);
      }
   }

   if (top_group) {
      if (strieq(top_group, "global")) {
         rsprintf("<input type=hidden name=global value=\"global\">\n");
         strcpy(str, "[global]");
      } else {
         rsprintf("<input type=hidden name=global value=\"%s\">\n", top_group);
         sprintf(str, "[global %s]", top_group);
      }
      rsprintf("<br><center><b>%s</b></center>", str);
   }

   if (is_group("global") && !strieq(top_group, "global")) {
      if (is_admin_user(NULL, getparam("unm"))) {
         rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Delete this logbook"));
         rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Rename this logbook"));
         rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Create new logbook"));
      }
   }

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

   /*---- entry form ----*/

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

   /* extract section of current logbook */
   if (top_group) {
      if (strieq(top_group, "global"))
         strcpy(section, "global");
      else
         sprintf(section, "global %s", top_group);
   } else
      strcpy(section, lbs->name);

   load_config_section(section, &buffer, error_str);

   if (error_str[0]) {
      rsprintf("<h2>%s</h2>\n", error_str);
      rsprintf("</table></td></tr></table>\n");
      rsprintf("</body></html>\r\n");
      return;
   }

   if (getcfg(section, "Admin textarea", str, sizeof(str)) && strchr(str, ',') != NULL) {
      cols = atoi(str);
      rows = atoi(strchr(str, ',') + 1);
   } else {
      cols = 120;
      rows = 30;
   }

   rsprintf("<textarea cols=%d rows=%d wrap=virtual name=Text>", cols, rows);

   rsputs3(buffer);
   xfree(buffer);

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

   /* put link for config page */
   rsprintf("<br><a target=\"_blank\" href=\"https://elog.psi.ch/elog/config.html\">Syntax Help</a>");

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

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

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

   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Save"));
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
   rsprintf("</span></td></tr>\n\n");

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

Project: elog

void show_elog_list(LOGBOOK *lbs, int past_n, int last_n, int page_n, BOOL default_page, char *info) {
   int i, j, n, index, size, status, d1, m1, y1, h1, n1, c1, d2, m2, y2, h2, n2, c2, n_line, flags,
           printable, n_logbook, n_display, reverse, numeric,
           n_attr_disp, n_msg, search_all, message_id, n_page, i_start, i_stop, in_reply_to_id,
           page_mid, page_mid_head, level, refresh, disp_attr_flags[MAX_N_ATTR + 4];
   char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], disp_attr[MAX_N_ATTR + 4][NAME_LENGTH], *list, *text,
           *text1, in_reply_to[80], reply_to[MAX_REPLY_TO * 10], attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH],
           encoding[80], locked_by[256], str[NAME_LENGTH], ref[256], img[80], comment[NAME_LENGTH], mode[80],
           mid[80], menu_str[1000], menu_item[MAX_N_LIST][NAME_LENGTH], param[NAME_LENGTH], format[80],
           sort_attr[MAX_N_ATTR + 4][NAME_LENGTH], mode_cookie[80], charset[25], sort_item[NAME_LENGTH],
           refr[80], str2[80], draft[256];
   char *p, *pt1, *pt2, *slist, *svalue, *gattr, line[1024], iattr[256];
   BOOL show_attachments, threaded, csv, xml, raw, mode_commands, expand, filtering, date_filtering,
           disp_filter, show_text, text_in_attr, searched, found, disp_attr_link[MAX_N_ATTR + 4],
           sort_attributes, show_att_column = 0;
   time_t ltime, ltime_start, ltime_end, now, ltime1, ltime2, entry_ltime;
   struct tm tms, *ptms;
   MSG_LIST *msg_list;
   LOGBOOK *lbs_cur;
   regex_t re_buf[MAX_N_ATTR + 1];
   regmatch_t pmatch[10];

   /* redirect if empty parameters */
   if (strstr(_cmdline, "=&")) {
      while ((pt1 = strstr(_cmdline, "=&")) != NULL) {
         pt2 = pt1;
         while (*pt1 != '&' && *pt1 != '?')
            pt1--;
         pt1++;
         strcpy(param, pt1);
         param[pt2 - pt1] = 0;
         memmove(pt1, pt2 + 2, strlen(pt2 + 2) + 1);

         /* remove param from lastcmd if present */
         if ((pt1 = strstr(_cmdline, "lastcmd=")) != NULL) {
            sprintf(str, "%s%%3D", param);
            if ((pt1 = strstr(_cmdline, str)) != NULL) {
               pt2 = pt1 + strlen(str);
               while (*pt2 && *pt2 != '%')
                  pt2++;
               if (*pt2 == '%')
                  pt2 += 3;
               memmove(pt1, pt2, strlen(pt2) + 1);
            }
         }
      }
      if (_cmdline[strlen(_cmdline) - 1] == '=') {
         pt1 = _cmdline + strlen(_cmdline) - 1;
         while (*pt1 != '&' && *pt1 != '?')
            pt1--;
         pt1++;
         strcpy(param, pt1);
         if (param[strlen(param) - 1] == '=')
            param[strlen(param) - 1] = 0;
         *pt1 = 0;

         /* remove param from lastcmd if present */
         if ((pt1 = strstr(_cmdline, "lastcmd=")) != NULL) {
            sprintf(str, "%s%%3D", param);
            if ((pt1 = strstr(_cmdline, str)) != NULL) {
               pt2 = pt1 + strlen(str);
               while (*pt2 && *pt2 != '%' && *pt2 != '&')
                  pt2++;
               if (*pt2 == '%')
                  pt2 += 3;
               memmove(pt1, pt2, strlen(pt2) + 1);
            }
         }
      }
      if (_cmdline[strlen(_cmdline) - 1] == '&')
         _cmdline[strlen(_cmdline) - 1] = 0;
      redirect(lbs, _cmdline);
      return;
   }

   /* redirect "go" command */
   if (isparam("lastcmd")) {

      strlcpy(str, getparam("lastcmd"), sizeof(str));
      url_decode(str);

      /* subsitute "last" in command line from new parameter */
      if (isparam("last")) {
         if (strieq(getparam("last"), "_all_"))
            subst_param(str, sizeof(str), "last", "");
         else
            subst_param(str, sizeof(str), "last", getparam("last"));
      }

      /* subsitute attributes in command line from new parameter */
      for (i = 0; i < MAX_N_ATTR; i++)
         if (isparam(attr_list[i])) {
            if (strieq(getparam(attr_list[i]), "_all_"))
               subst_param(str, sizeof(str), attr_list[i], "");
            else
               subst_param(str, sizeof(str), attr_list[i], getparam(attr_list[i]));
         }

      /* do the same for subtext */
      if (isparam("subtext"))
         subst_param(str, sizeof(str), "subtext", getparam("subtext"));

      redirect(lbs, str);
      return;
   }

   /* remove remaining "_all_" in parameters */
   if (isparam("last") && strieq(getparam("last"), "_all_")) {
      strlcpy(str, _cmdline, sizeof(str));
      subst_param(str, sizeof(str), "last", "");
      redirect(lbs, str);
      return;
   }

   /* remove remaining "_all_" or empty or "--+<attrib>+--" parameters */
   strlcpy(str, _cmdline, sizeof(str));
   found = 0;
   for (i = 0; i < MAX_N_ATTR; i++) {
      if (isparam(attr_list[i])) {
         if (strieq(getparam(attr_list[i]), "_all_")) {
            subst_param(str, sizeof(str), attr_list[i], "");
            found = 1;
         }
         if (*getparam(attr_list[i]) == 0) {
            subst_param(str, sizeof(str), attr_list[i], "");
            found = 1;
         }
         sprintf(ref, "-- %s --", attr_list[i]);
         if (strieq(getparam(attr_list[i]), ref)) {
            subst_param(str, sizeof(str), attr_list[i], "");
            found = 1;
         }
      }
   }

   if (isparam("subtext")) {
      if (*getparam("subtext") == 0) {
         subst_param(str, sizeof(str), "subtext", "");
         found = 1;
      }
      sprintf(ref, "-- %s --", loc("Text"));
      if (strieq(getparam("subtext"), ref)) {
         subst_param(str, sizeof(str), "subtext", "");
         found = 1;
      }
   }

   if (found) {
      redirect(lbs, str);
      return;
   }

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

   printable = isparam("Printable") ? atoi(getparam("Printable")) : 0;

   /* in printable mode, display all pages */
   if (printable)
      page_n = -1;

   if (isparam("Reverse"))
      reverse = atoi(getparam("Reverse"));
   else {
      reverse = 0;
      if (getcfg(lbs->name, "Reverse sort", str, sizeof(str)))
         reverse = atoi(str);
   }

   /* get message ID from "list" command */
   if (isparam("id"))
      page_mid = atoi(getparam("id"));
   else
      page_mid = 0;
   page_mid_head = 0;

   /* default mode */
   strlcpy(mode, "Summary", sizeof(mode));
   show_attachments = FALSE;

   /* check for valid page_n */
   if (page_n < -1)
      page_n = 0;

   if (past_n || last_n || page_n || page_mid || default_page) {
      /* for page display, get mode from config file */
      if (getcfg(lbs->name, "Display Mode", str, sizeof(str)))
         strlcpy(mode, str, sizeof(mode));

      /* supersede mode from cookie */
      if (isparam("elmode"))
         strlcpy(mode, getparam("elmode"), sizeof(mode));

      /* supersede mode from direct parameter */
      if (isparam("mode"))
         strlcpy(mode, getparam("mode"), sizeof(mode));

   } else {
      /* for find result, get mode from find form */
      if (isparam("mode"))
         strlcpy(mode, getparam("mode"), sizeof(mode));
      else
         strlcpy(mode, "Full", sizeof(mode));
   }

   /* set cookie if mode changed */
   mode_cookie[0] = 0;
   if (strieq(mode, "Summary") || strieq(mode, "Full") || strieq(mode, "Threaded")) {
      if (!isparam("elmode") || !strieq(getparam("elmode"), mode))
         sprintf(mode_cookie, "elmode=%s", mode);
   }

   threaded = strieq(mode, "threaded");
   csv = strieq(mode, "CSV1") || strieq(mode, "CSV2") || strieq(mode, "CSV3");
   xml = strieq(mode, "XML");
   raw = strieq(mode, "Raw");

   if (csv || xml || raw) {
      page_n = -1;              /* display all pages */
      show_attachments = FALSE; /* hide attachments */
   }

   /* show attachments in full mode by default */
   if (strieq(mode, "Full"))
      show_attachments = TRUE;

   /* supersede attachment mode if in cookie */
   if (isparam("elattach"))
      show_attachments = atoi(getparam("elattach"));

   /* supersede attachment mode if in parameter */
   if (isparam("attach"))
      show_attachments = atoi(getparam("attach"));

   /* set cookie if attachment mode changed in full view */
   if (mode_cookie[0] == 0 && strieq(mode, "Full")) {
      if (!isparam("elattach") || atoi(getparam("elattach")) != show_attachments)
         sprintf(mode_cookie, "elattach=%d", show_attachments);
   }

   /*---- convert dates to ltime ----*/

   time(&now);
   ptms = localtime(&now);
   assert(ptms);

   ltime_end = ltime_start = 0;

   d1 = m1 = y1 = h1 = n1 = c1 = d2 = m2 = y2 = h2 = n2 = c2 = 0;

   if (!past_n && !last_n) {

      ltime_start = retrieve_date("a", TRUE);
      if (ltime_start < 0) {
         xfree(slist);
         xfree(svalue);
         xfree(gattr);
         xfree(list);
         return;
      }

      if (ltime_start) {
         memcpy(&tms, localtime(&ltime_start), sizeof(struct tm));
         y1 = tms.tm_year + 1900;
         m1 = tms.tm_mon + 1;
         d1 = tms.tm_mday;
         h1 = tms.tm_hour;
         n1 = tms.tm_min;
         c1 = tms.tm_sec;
      }

      ltime_end = retrieve_date("b", FALSE);
      if (ltime_end < 0) {
         xfree(slist);
         xfree(svalue);
         xfree(gattr);
         xfree(list);
         return;
      }

      if (ltime_end) {

         if (ltime_end <= ltime_start) {
            sprintf(str, "Error: Start date after end date");
            show_error(str);
            xfree(slist);
            xfree(svalue);
            xfree(gattr);
            xfree(list);
            return;
         }

         memcpy(&tms, localtime(&ltime_end), sizeof(struct tm));
         y2 = tms.tm_year + 1900;
         m2 = tms.tm_mon + 1;
         d2 = tms.tm_mday;
         h2 = tms.tm_hour;
         n2 = tms.tm_min;
         c2 = tms.tm_sec;
      }
   }

   if (ltime_start && ltime_end && ltime_start > ltime_end) {
      show_error(loc("Error: start date after end date"));
      xfree(slist);
      xfree(svalue);
      xfree(gattr);
      xfree(list);
      return;
   }

   /*---- if user present but not allowed, log it out (required when several logbooks are
          used with different access rights and global passwords ----*/
   if (isparam("unm") && !check_login_user(lbs, getparam("unm"))) {
      unsetparam("unm");
      sid_remove(getparam("sid"));
      set_sid_cookie(lbs, "", "");
   }

   /*---- apply last login cut ----*/

   if (isparam("new_entries") && atoi(getparam("new_entries")) == 1 && isparam("unm"))
      get_user_line(lbs, getparam("unm"), NULL, NULL, NULL, NULL, &ltime_start, NULL);

   /*---- assemble message list ----*/

   /* check for search all */
   search_all = isparam("all") ? atoi(getparam("all")) : 0;

   if (getcfg(lbs->name, "Search all logbooks", str, sizeof(str)) && atoi(str) == 0)
      search_all = 0;

   n_msg = 0;
   n_display = 0;
   if (search_all) {
      /* count logbooks */
      for (n_logbook = 0;; n_logbook++) {
         if (!lb_list[n_logbook].name[0])
            break;

         if (lbs->top_group[0] && !strieq(lbs->top_group, lb_list[n_logbook].top_group))
            continue;

         if (isparam("unm") && !check_login_user(&lb_list[n_logbook], getparam("unm")))
            continue;

         n_msg += *lb_list[n_logbook].n_el_index;
      }
   } else {
      n_logbook = 1;
      n_msg = *lbs->n_el_index;
   }

   msg_list = (MSG_LIST *) xmalloc(sizeof(MSG_LIST) * n_msg);

   lbs_cur = lbs;
   numeric = TRUE;
   for (i = n = 0; i < n_logbook; i++) {
      if (search_all)
         lbs_cur = &lb_list[i];

      if (lbs->top_group[0] && !strieq(lbs->top_group, lbs_cur->top_group))
         continue;

      if (isparam("unm") && !check_login_user(lbs_cur, getparam("unm")))
         continue;

      for (j = 0; j < *lbs_cur->n_el_index; j++) {
         msg_list[n].lbs = lbs_cur;
         msg_list[n].index = j;
         msg_list[n].number = (int) lbs_cur->el_index[j].file_time;
         msg_list[n].in_reply_to = lbs_cur->el_index[j].in_reply_to;
         n++;
      }
   }

   /*---- apply start/end date cut ----*/

   date_filtering = FALSE;

   if (past_n > 0)
      ltime_start = now - 3600 * 24 * past_n;   // past n days
   else if (past_n < 0)
      ltime_start = now + 3600 * past_n;        // past n hours

   if (last_n && last_n < n_msg) {
      date_filtering = TRUE;
      for (i = n_msg - last_n - 1; i >= 0; i--)
         msg_list[i].lbs = NULL;
   }

   if (ltime_start) {
      date_filtering = TRUE;
      for (i = 0; i < n_msg; i++)
         if (msg_list[i].lbs && msg_list[i].lbs->el_index[msg_list[i].index].file_time < ltime_start)
            msg_list[i].lbs = NULL;
   }

   if (ltime_end) {
      date_filtering = TRUE;
      for (i = 0; i < n_msg; i++)
         if (msg_list[i].lbs && msg_list[i].lbs->el_index[msg_list[i].index].file_time > ltime_end)
            msg_list[i].lbs = NULL;
   }

   if (isparam("last") || getcfg(lbs->name, "Last default", str, sizeof(str))) {
      date_filtering = TRUE;
      if (isparam("last"))
         n = atoi(getparam("last"));
      else
         n = atoi(str);

      if (n > 0) {
         for (i = 0; i < n_msg; i++)
            if (msg_list[i].lbs && msg_list[i].lbs->el_index[msg_list[i].index].file_time < now - 3600 * 24
                                                                                                  * n)
               msg_list[i].lbs = NULL;
      }
   }

   /*---- filter message list ----*/

   filtering = FALSE;
   show_text = TRUE;
   searched = found = FALSE;

   for (i = 0; i < lbs->n_attr; i++) {
      /* check if attribute filter */
      if (isparam(attr_list[i]))
         break;

      if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {
         sprintf(str, "%da", i);
         if (retrieve_date(str, TRUE))
            break;
         sprintf(str, "%db", i);
         if (retrieve_date(str, TRUE))
            break;
      }

      if (attr_flags[i] & AF_MULTI) {
         for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
            sprintf(str, "%s_%d", attr_list[i], j);
            if (isparam(str)) {
               filtering = TRUE;
               break;
            }
         }
      }

      if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) {
         for (j = 0; j < MAX_N_LIST; j++) {
            sprintf(str, "%s_%d", attr_list[i], j);
            if (isparam(str)) {
               filtering = TRUE;
               break;
            }
         }
      }

      /* check if sort by attribute */
      if ((isparam("sort") && strieq(getparam("sort"), attr_list[i]))
          || (isparam("rsort") && strieq(getparam("rsort"), attr_list[i])))
         break;
   }

   /* turn on filtering if found */
   if (i < lbs->n_attr)
      filtering = TRUE;

   if (isparam("subtext"))
      filtering = TRUE;

   if (getcfg(lbs->name, "Sort Attributes", list, 10000))
      filtering = TRUE;

   text = (char *) xmalloc(TEXT_SIZE);
   text1 = (char *) xmalloc(TEXT_SIZE);

   /* prepare for regex search */
   memset(re_buf, 0, sizeof(re_buf));

   /* compile regex for subtext */
   if (isparam("subtext")) {
      strlcpy(str, getparam("subtext"), sizeof(str));
      flags = REG_EXTENDED;
      if (!isparam("casesensitive"))
         flags |= REG_ICASE;
      status = regcomp(re_buf, str, flags);
      if (status) {
         sprintf(line, loc("Error in regular expression \"%s\""), str);
         strlcat(line, ": ", sizeof(line));
         regerror(status, re_buf, str, sizeof(str));
         strlcat(line, str, sizeof(line));
         strencode2(str, line, sizeof(str));
         show_error(str);
         return;
      }
   }

   /* compile regex for attributes */
   for (i = 0; i < lbs->n_attr; i++) {
      if (isparam(attr_list[i])) {
         strlcpy(str, getparam(attr_list[i]), sizeof(str));

         /* if value starts with '$', substitute it */
         if (str[0] == '$') {
            j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, attrib,
                                 TRUE);
            add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "entry time",
                           date, &j, 0);

            strsubst_list(str, sizeof(str), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, j);
            setparam(attr_list[i], str);
         }

         flags = REG_EXTENDED;

         if (!isparam("casesensitive"))
            flags |= REG_ICASE;

         status = regcomp(re_buf + i + 1, str, flags);
         if (status) {
            sprintf(line, loc("Error in regular expression \"%s\""), str);
            strlcat(line, ": ", sizeof(line));
            regerror(status, re_buf + i + 1, str, sizeof(str));
            strlcat(line, str, sizeof(line));
            strencode2(str, line, sizeof(str));
            show_error(str);
            return;
         }
      }
   }

   sort_item[0] = 0;
   if (isparam("sort"))
      strlcpy(sort_item, getparam("sort"), sizeof(sort_item));
   if (isparam("rsort"))
      strlcpy(sort_item, getparam("rsort"), sizeof(sort_item));

   sort_attributes = getcfg(lbs->name, "Sort Attributes", str, sizeof(str));

   /* do filtering */
   for (index = 0; index < n_msg; index++) {
      if (!msg_list[index].lbs)
         continue;

      /* retrieve message */
      size = TEXT_SIZE;
      message_id = msg_list[index].lbs->el_index[msg_list[index].index].message_id;

      if (filtering) {
         status = el_retrieve(msg_list[index].lbs, message_id, date, attr_list, attrib, lbs->n_attr, text,
                              &size, in_reply_to, reply_to, attachment, encoding, locked_by, draft);
         if (status != EL_SUCCESS)
            break;

         /* apply filter for attributes */
         for (i = 0; i < lbs->n_attr; i++) {

            /* replace icon name with their comments if present */
            if (attr_flags[i] & AF_ICON) {
               sprintf(str, "Icon comment %s", attrib[i]);
               if (getcfg(lbs->name, str, comment, sizeof(comment)))
                  strlcpy(attrib[i], comment, NAME_LENGTH);
            }

            /* check for multi attributes */
            if (attr_flags[i] & AF_MULTI) {

               /* OR of any of the values */
               searched = found = FALSE;
               for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
                  sprintf(str, "%s_%d", attr_list[i], j);
                  if (isparam(str)) {
                     searched = TRUE;
                     if (strstr(attrib[i], getparam(str))) {
                        found = TRUE;
                        break;
                     }
                  }
               }

               /* search for parameter without '_' coming from quick filter */
               if (isparam(attr_list[i])) {
                  searched = TRUE;

                  strlcpy(str, getparam(attr_list[i]), sizeof(str));
                  if (str[0] == '^' && str[strlen(str) - 1] == '$') {
                     str[strlen(str) - 1] = 0;
                     strlcpy(comment, str + 1, NAME_LENGTH);
                  } else
                     strlcpy(comment, str, NAME_LENGTH);
                  strlcpy(str, comment, sizeof(str));

                  if (strstr(attrib[i], str))
                     found = TRUE;
               }

               if (searched && !found)
                  break;
            }

               /* check for multi user list or multi user email */
            else if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) {

               /* OR of any of the values */
               searched = found = FALSE;
               for (j = 0; j < MAX_N_LIST; j++) {
                  sprintf(str, "%s_%d", attr_list[i], j);
                  if (isparam(str)) {
                     searched = TRUE;
                     if (strstr(attrib[i], getparam(str))) {
                        found = TRUE;
                        break;
                     }
                  }
               }

               /* search for parameter without '_' coming from quick filter */
               if (isparam(attr_list[i])) {
                  searched = TRUE;
                  if (strstr(attrib[i], getparam(attr_list[i])))
                     found = TRUE;
               }

               if (searched && !found)
                  break;

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

               /* check for last[i]/next[i] */

               ltime = isparam(attr_list[i]) ? atoi(getparam(attr_list[i])) : 0;

               /* today 12h noon */
               time(&now);
               memcpy(&tms, localtime(&now), sizeof(struct tm));
               tms.tm_hour = 12;
               tms.tm_min = 0;
               tms.tm_sec = 0;
               now = mktime(&tms);

               /* negative i: last [i] days */
               if (ltime < 0)
                  if (atoi(attrib[i]) < now + ltime * 3600 * 24 - 3600 * 12 || atoi(attrib[i]) > now)
                     break;

               /* positive i: next [i] days */
               if (ltime > 0)
                  if (atoi(attrib[i]) > now + ltime * 3600 * 24 + 3600 * 12 || atoi(attrib[i]) < now)
                     break;

               /* check for start date / end date */
               sprintf(str, "%da", i);
               ltime = retrieve_date(str, TRUE);
               if (ltime > 0 && atoi(attrib[i]) < ltime)
                  break;

               sprintf(str, "%db", i);
               ltime = retrieve_date(str, FALSE);
               if (ltime > 0 && (atoi(attrib[i]) > ltime || atoi(attrib[i]) == 0))
                  break;

            } else {

               strlcpy(str, isparam(attr_list[i]) ? getparam(attr_list[i]) : "", sizeof(str));

               /* if value starts with '$', substitute it */
               if (str[0] == '$') {
                  j = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                                       attrib, TRUE);
                  sprintf(mid, "%d", message_id);
                  add_subst_list((char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, "message id",
                                 mid, &j);
                  add_subst_time(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                                 "entry time", date, &j, 0);

                  strsubst_list(str, sizeof(str), (char (*)[NAME_LENGTH]) slist,
                                (char (*)[NAME_LENGTH]) svalue, j);
                  setparam(attr_list[i], str);
               }

               if (isparam(attr_list[i])) {
                  status = regexec(re_buf + 1 + i, attrib[i], 10, pmatch, 0);
                  if (status == REG_NOMATCH)
                     break;
               }
            }
         }

         if (i < lbs->n_attr) {
            msg_list[index].lbs = NULL;
            continue;
         }

         if (isparam("subtext")) {

            status = regexec(re_buf, text, 10, pmatch, 0);
            if (isparam("sall") && atoi(getparam("sall")) && status == REG_NOMATCH) {

               // search text in attributes
               for (i = 0; i < lbs->n_attr; i++) {
                  status = regexec(re_buf, attrib[i], 10, pmatch, 0);
                  if (status != REG_NOMATCH)
                     break;
               }

               if (i == lbs->n_attr) {
                  msg_list[index].lbs = NULL;
                  continue;
               }
            } else if (status == REG_NOMATCH) {
               msg_list[index].lbs = NULL;
               continue;
            }
         }
      }                         // if (filtering)

      /* evaluate "sort attributes" */
      if (sort_attributes) {
         getcfg(lbs->name, "Sort Attributes", list, 10000);
         msg_list[index].string[0] = 0;
         n = strbreak(list, sort_attr, MAX_N_ATTR, ",", FALSE);
         for (i = 0; i < n; i++) {
            for (j = 0; j < lbs->n_attr; j++) {
               if (strieq(sort_attr[i], attr_list[j])) {
                  strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string));
                  strlcat(msg_list[index].string, attrib[j], sizeof(msg_list[index].string));
                  if (attr_flags[i] & (AF_NUMERIC | AF_DATETIME | AF_DATE)) {
                     msg_list[index].number = atoi(attrib[j]);
                     numeric = TRUE;
                  } else
                     numeric = FALSE;
                  break;
               }
            }
            if (strieq(sort_attr[i], loc("ID"))) {
               strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string));
               sprintf(str, "%08d", message_id);
               strlcat(msg_list[index].string, str, sizeof(msg_list[index].string));
            } else if (strieq(sort_attr[i], loc("Logbook"))) {
               strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string));
               strlcat(msg_list[index].string, msg_list[index].lbs->name, sizeof(msg_list[index].string));
            } else if (strieq(sort_attr[i], loc("Date"))) {
               strlcat(msg_list[index].string, " ", sizeof(msg_list[index].string));
               entry_ltime = date_to_ltime(date);
               sprintf(str, "%08d", (int) entry_ltime);
               strlcat(msg_list[index].string, str, sizeof(msg_list[index].string));
            }
         }
      }

      /* add attribute for sorting */
      if (sort_item[0]) {
         for (i = 0; i < lbs->n_attr; i++) {
            if (strieq(sort_item, attr_list[i])) {
               if (attr_flags[i] & (AF_NUMERIC | AF_DATETIME | AF_DATE)) {
                  numeric = TRUE;
                  msg_list[index].number = atoi(attrib[i]);
               } else {
                  numeric = FALSE;
                  strlcpy(msg_list[index].string, attrib[i], 256);
               }
            }

            if (strieq(sort_item, loc("ID"))) {
               numeric = TRUE;
               msg_list[index].number = message_id;
            }

            if (strieq(sort_item, loc("Logbook")))
               strlcpy(msg_list[index].string, msg_list[index].lbs->name, 256);
         }

         if (isparam("rsort"))
            reverse = 1;

         if (isparam("sort"))
            reverse = 0;
      }
   }

   /*---- in threaded mode, set date of latest entry of thread ----*/

   if (threaded && !filtering && !date_filtering) {
      for (index = 0; index < n_msg; index++) {
         if (!msg_list[index].lbs)
            continue;

         message_id = msg_list[index].lbs->el_index[msg_list[index].index].message_id;
         in_reply_to_id = msg_list[index].lbs->el_index[msg_list[index].index].in_reply_to;
         if (!in_reply_to_id)
            continue;

         do {
            message_id = in_reply_to_id;

            /* search index of message */
            for (i = 0; i < *msg_list[index].lbs->n_el_index; i++)
               if (msg_list[index].lbs->el_index[i].message_id == message_id)
                  break;

            /* stop if not found */
            if (i == *msg_list[index].lbs->n_el_index)
               break;

            in_reply_to_id = msg_list[index].lbs->el_index[i].in_reply_to;

         } while (in_reply_to_id);

         /* if head not found, skip message */
         if (i == *msg_list[index].lbs->n_el_index) {
            msg_list[index].lbs = NULL;
            continue;
         }

         /* set new page message ID with head message */
         if (page_mid && msg_list[index].lbs->el_index[msg_list[index].index].message_id == page_mid)
            page_mid_head = message_id;

         /* search message head in list */
         for (j = 0; j < n_msg; j++)
            if (msg_list[j].lbs == msg_list[index].lbs && msg_list[j].index == i)
               break;

         if (j < index) {
            /* set date from current message, if later */
            if (msg_list[j].number < msg_list[index].number)
               msg_list[j].number = msg_list[index].number;
         }

         /* now delete current message, to leave only heads in list */
         msg_list[index].lbs = NULL;
      }
   }

   /*---- compact messasges ----*/

   for (i = j = 0; i < n_msg; i++)
      if (msg_list[i].lbs)
         memcpy(&msg_list[j++], &msg_list[i], sizeof(MSG_LIST));
   n_msg = j;

   /*---- sort messasges ----*/

   if (numeric)
      qsort(msg_list, n_msg, sizeof(MSG_LIST), reverse ? msg_compare_reverse_numeric : msg_compare_numeric);
   else
      qsort(msg_list, n_msg, sizeof(MSG_LIST), reverse ? msg_compare_reverse : msg_compare);

   /*---- search page for specific message ----*/

   if (getcfg(lbs->name, "Entries per page", str, sizeof(str)))
      n_page = atoi(str);
   else
      n_page = 20;
   if (isparam("npp")) {
      n_page = atoi(getparam("npp"));
      if (n_page < 1)
         n_page = 1;
      if (n_page > 100000)
         n_page = 100000;
   }

   if (page_mid) {
      default_page = 0;

      for (i = 0; i < n_msg; i++)
         if (msg_list[i].lbs->el_index[msg_list[i].index].message_id == page_mid
             || msg_list[i].lbs->el_index[msg_list[i].index].message_id == page_mid_head)
            break;

      if (i < n_msg)
         page_n = i / n_page + 1;
   }

   /*---- number of messages per page ----*/

   n_attr_disp = n_line = 0;
   i_start = 0;
   i_stop = n_msg - 1;
   if (!csv && !xml && !raw) {
      if (page_n || default_page) {
         if (default_page && page_n != -1)
            page_n = reverse ? 1 : (n_msg - 1) / n_page + 1;

         if (page_n != -1) {
            i_start = (page_n - 1) * n_page;
            i_stop = i_start + n_page - 1;

            if (i_start >= n_msg && n_msg > 0) {
               page_n = 1;
               i_start = 0;
            }

            if (i_stop >= n_msg)
               i_stop = n_msg - 1;
         }
      }
   }

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

   if (getcfg(lbs->name, "List Page Title", str, sizeof(str))) {
      i = build_subst_list(lbs, (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, NULL, TRUE);
      strsubst_list(str, sizeof(str), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue, i);
      strip_html(str);
   } else
      sprintf(str, "ELOG %s", lbs->name);

   if (csv) {

      /* no menus and tables */
      show_plain_header(0, "export.csv");

      rsprintf("\"%s\"", loc("Message ID"));
      if (strieq(mode, "CSV1"))
         rsprintf(",");
      else
         rsprintf(";");

      rsprintf("\"%s\"", loc("Date"));
      if (strieq(mode, "CSV1"))
         rsprintf(",");
      else
         rsprintf(";");

      for (i = 0; i < lbs->n_attr; i++) {
         strlcpy(str, attr_list[i], sizeof(str));
         if (str[0]) {
            rsputs("\"");
            pt1 = str;
            while ((pt2 = strchr(pt1, '"')) != NULL) {
               *pt2 = 0;
               rsputs(pt1);
               rsputs("\"\"");
               pt1 = pt2 + 1;
            }
            rsputs(pt1);
            rsputs("\"");
         }
         if (i < lbs->n_attr - 1) {
            if (strieq(mode, "CSV1"))
               rsprintf(",");
            else
               rsprintf(";");
         } else {
            if (strieq(mode, "CSV3"))
               rsprintf(";\"Text\"");
            rsprintf("\r\n");
         }
      }

   } else if (xml) {

      /* no menus and tables */
      show_plain_header(0, "export.xml");
      if (!getcfg("global", "charset", charset, sizeof(charset)))
         strcpy(charset, DEFAULT_HTTP_CHARSET);
      rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", charset);
      rsprintf("<!-- ELOGD Version %s export.xml -->\n", VERSION);
      rsprintf("<ELOG_LIST>\n");

   } else if (raw) {

      /* no menus and tables */
      show_plain_header(0, "export.txt");

   } else {

      if (getcfg(lbs->name, "Refresh", refr, sizeof(refr)))
         refresh = atoi(refr);
      else
         refresh = 0;
      show_standard_header(lbs, TRUE, str, NULL, TRUE, mode_cookie, NULL, refresh);

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

      strlcpy(str, ", ", sizeof(str));
      if (past_n == 1)
         strcat(str, loc("Last day"));
      else if (past_n > 1)
         sprintf(str + strlen(str), loc("Last %d days"), past_n);
      else if (past_n < 0)
         sprintf(str + strlen(str), loc("Last %d hours"), -past_n);
      else if (last_n)
         sprintf(str + strlen(str), loc("Last %d entries"), last_n);
      else if (page_n == -1)
         strlcpy(str + strlen(str), loc("all entries"), sizeof(str) - strlen(str));
      else if (page_n)
         sprintf(str + strlen(str), loc("Page %d of %d"), page_n, (n_msg - 1) / n_page + 1);
      if (strlen(str) == 2)
         str[0] = 0;

      if (printable)
         show_standard_title(lbs, str, 1);
      else
         show_standard_title(lbs, str, 0);

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

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

         /* current command line for select command */
         strlcpy(str, isparam("cmdline") ? getparam("cmdline") : "", sizeof(str));

         /* remove select switch */
         if (strstr(str, "select=1")) {
            *strstr(str, "select=1") = 0;
            if (strlen(str) > 1 && (str[strlen(str) - 1] == '&' || str[strlen(str) - 1] == '?'))
               str[strlen(str) - 1] = 0;
         }

         /* store current command line as hidden parameter for page navigation */
         if (str[0] && !strieq(str, "?")) {
            rsprintf("<input type=hidden name=lastcmd value=\"");
            rsputs3(str);
            rsprintf("\">\n", str);
         }

         if (!getcfg(lbs->name, "Guest Find menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs))
            getcfg(lbs->name, "Find menu commands", menu_str, sizeof(menu_str));

         if (!menu_str[0]) {
            if (!getcfg(lbs->name, "Guest list menu commands", menu_str, sizeof(menu_str)) || logged_in(lbs))
               getcfg(lbs->name, "list menu commands", menu_str, sizeof(menu_str));
         }

         /* default menu commands */
         if (menu_str[0] == 0) {
            strlcpy(menu_str, "New, Find, Select, Import, ", sizeof(menu_str));

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

            if (getcfg(lbs->name, "Mirror server", str, sizeof(str)))
               strlcat(menu_str, "Synchronize, ", sizeof(menu_str));

            strlcpy(str, loc("Last x"), sizeof(str));
            strlcat(menu_str, "Last x, Help, ", sizeof(menu_str));
         }

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

         for (i = 0; i < n; i++) {
            if (is_user_allowed(lbs, menu_item[i])) {
               if (strieq(menu_item[i], "Last x")) {
                  if (past_n > 0) {
                     sprintf(str, loc("Last %d days"), past_n * 2);
                     rsprintf("&nbsp;<a href=\"past%d?mode=%s\">%s</a>&nbsp;|\n", past_n * 2, mode, str);
                  } else {
                     strlcpy(str, loc("Last day"), sizeof(str));
                     rsprintf("&nbsp;<a href=\"past1?mode=%s\">%s</a>&nbsp;|\n", mode, str);
                  }

                  if (last_n) {
                     sprintf(str, loc("Last %d entries"), last_n * 2);
                     rsprintf("&nbsp;<a href=\"last%d?mode=%s\">%s</a>&nbsp;|\n", last_n * 2, mode, str);
                  }
               } else if (strieq(menu_item[i], "Select")) {
                  strlcpy(str, getparam("cmdline"), sizeof(str));
                  if (isparam("select") && atoi(getparam("select")) == 1) {
                     /* remove select switch */
                     if (strstr(str, "select=1")) {
                        *strstr(str, "select=1") = 0;
                        if (strlen(str) > 1 && (str[strlen(str) - 1] == '&' || str[strlen(str) - 1] == '?'))
                           str[strlen(str) - 1] = 0;
                     }
                  } else {
                     /* add select switch */
                     if (strchr(str, '?'))
                        strcat(str, "&select=1");
                     else
                        strcat(str, "?select=1");
                  }
                  rsprintf("&nbsp;<a href=\"");
                  rsputs3(str);
                  rsprintf("\">%s</a>&nbsp;|\n", loc("Select"));
               } else {
                  strlcpy(str, loc(menu_item[i]), sizeof(str));
                  url_encode(str, sizeof(str));

                  if (i < n - 1)
                     rsprintf("&nbsp;<a href=\"?cmd=%s\">%s</a>&nbsp;|\n", str, loc(menu_item[i]));
                  else
                     rsprintf("&nbsp;<a href=\"?cmd=%s\">%s</a>&nbsp;\n", str, loc(menu_item[i]));
               }
            }
         }

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

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

      if ((getcfg(lbs->name, "find menu text", str, sizeof(str)) || getcfg(lbs->name, "list menu text", str,
                                                                           sizeof(str))) && !printable) {
         FILE *f;
         char file_name[256], *buf;

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

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

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

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

            rsputs(buf);

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

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

      /*---- display filters ----*/

      disp_filter = isparam("ma") || isparam("ya") || isparam("da") || isparam("mb") || isparam("yb")
                    || isparam("db") || isparam("subtext") || isparam("last");

      for (i = 0; i < lbs->n_attr; i++)
         if (isparam(attr_list[i]) && (attr_flags[i] & (AF_DATE | AF_DATETIME)) == 0)
            disp_filter = TRUE;

      for (i = 0; i < lbs->n_attr; i++) {
         if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {
            sprintf(str, "%da", i);
            ltime = retrieve_date(str, TRUE);
            if (ltime > 0)
               disp_filter = TRUE;
            sprintf(str, "%db", i);
            ltime = retrieve_date(str, FALSE);
            if (ltime > 0)
               disp_filter = TRUE;
         }
         if (attr_flags[i] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL)) {
            for (j = 0; j < MAX_N_LIST; j++) {
               sprintf(str, "%s_%d", attr_list[i], j);
               if (isparam(str))
                  disp_filter = TRUE;
               if (isparam(attr_list[i]))
                  disp_filter = TRUE;
            }
         }
      }

      if (isparam("new_entries") && atoi(getparam("new_entries")) == 1) {
         rsprintf("<tr><td class=\"listframe\">\n");
         rsprintf("<table width=\"100%%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
         rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", loc("New entries since"));
         memcpy(&tms, localtime(&ltime_start), sizeof(struct tm));
         my_strftime(str, sizeof(str), "%c", &tms);
         rsprintf("<td class=\"attribvalue\">%s</td></tr>", str);
         rsprintf("</table></td></tr>\n\n");
      }

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

         if (isparam("last")) {
            rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>",
                     loc("Restrict search to last"));
            strencode2(str, getparam("last"), sizeof(str));
            rsprintf("<td class=\"attribvalue\">%s %s</td></tr>", str, loc("days"));
         }

         if (isparam("ma") || isparam("ya") || isparam("da") || isparam("ha") || isparam("na")
             || isparam("ca")) {
            memset(&tms, 0, sizeof(struct tm));
            tms.tm_year = y1 - 1900;
            tms.tm_mon = m1 - 1;
            tms.tm_mday = d1;
            tms.tm_hour = h1;
            tms.tm_min = n1;
            tms.tm_sec = c1;
            if (tms.tm_year < 90)
               tms.tm_year += 100;
            mktime(&tms);
            if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
               strcpy(format, DEFAULT_TIME_FORMAT);
            strftime(str, sizeof(str), format, &tms);

            rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", loc("Start date"));
            rsprintf("<td class=\"attribvalue\">%s</td></tr>", str);
         }

         if (isparam("mb") || isparam("yb") || isparam("db") || isparam("hb") || isparam("nb")
             || isparam("cb")) {
            memset(&tms, 0, sizeof(struct tm));
            tms.tm_year = y2 - 1900;
            tms.tm_mon = m2 - 1;
            tms.tm_mday = d2;
            tms.tm_hour = h2;
            tms.tm_min = n2;
            tms.tm_sec = c2;
            if (tms.tm_year < 90)
               tms.tm_year += 100;
            ltime = mktime(&tms);
            memcpy(&tms, localtime(&ltime), sizeof(struct tm));
            if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
               strcpy(format, DEFAULT_TIME_FORMAT);
            strftime(str, sizeof(str), format, &tms);

            rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", loc("End date"));

            rsprintf("<td class=\"attribvalue\">%s</td></tr>", str);
         }

         for (i = 0; i < lbs->n_attr; i++) {
            if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {

               sprintf(str, "%da", i);
               ltime1 = retrieve_date(str, TRUE);
               sprintf(str, "%db", i);
               ltime2 = retrieve_date(str, TRUE);

               if (ltime1 > 0 || ltime2 > 0) {
                  rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", attr_list[i]);
                  rsprintf("<td class=\"attribvalue\">");
                  if (ltime1) {
                     memcpy(&tms, localtime(&ltime1), sizeof(struct tm));
                     if (attr_flags[i] & AF_DATE)
                        strcpy(format, DEFAULT_DATE_FORMAT);
                     else
                        strcpy(format, DEFAULT_TIME_FORMAT);
                     my_strftime(str, sizeof(str), format, &tms);
                     if (ltime2 > 0)
                        rsprintf("%s %s", loc("From"), str);
                     else
                        rsprintf("%s %s", loc("After"), str);
                  }
                  if (ltime2) {
                     memcpy(&tms, localtime(&ltime2), sizeof(struct tm));
                     if (attr_flags[i] & AF_DATE)
                        strcpy(format, DEFAULT_DATE_FORMAT);
                     else
                        strcpy(format, DEFAULT_TIME_FORMAT);
                     my_strftime(str, sizeof(str), format, &tms);
                     if (ltime1 > 0)
                        rsprintf(" %s %s", loc("to"), str);
                     else
                        rsprintf("%s %s", loc("Before"), str);
                  }
                  rsprintf("</td></tr>", comment);
               }

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

               line[0] = 0;

               for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
                  sprintf(iattr, "%s_%d", attr_list[i], j);
                  if (isparam(iattr)) {

                     comment[0] = 0;
                     if (attr_flags[i] & AF_ICON) {
                        sprintf(str, "Icon comment %s", getparam(iattr));
                        getcfg(lbs->name, str, comment, sizeof(comment));
                     }

                     if (line[0])
                        strlcat(line, " | ", sizeof(line));

                     if (comment[0] == 0) {
                        strlcpy(str, getparam(iattr), sizeof(str));
                        if (str[0] == '^' && str[strlen(str) - 1] == '$') {
                           str[strlen(str) - 1] = 0;
                           strlcpy(comment, str + 1, NAME_LENGTH);
                        } else
                           strlcpy(comment, str, NAME_LENGTH);
                        strlcpy(str, comment, sizeof(str));

                        strencode2(line + strlen(line), str, sizeof(line) - strlen(line));
                     } else
                        strlcat(line, comment, sizeof(line));
                  }
               }

               if (isparam(attr_list[i])) {
                  comment[0] = 0;
                  if (attr_flags[i] & AF_ICON) {
                     sprintf(str, "Icon comment %s", getparam(attr_list[i]));
                     getcfg(lbs->name, str, comment, sizeof(comment));
                  }

                  if (line[0])
                     strlcat(line, " | ", sizeof(line));

                  if (comment[0] == 0) {
                     strlcpy(str, getparam(attr_list[i]), sizeof(str));
                     if (str[0] == '^' && str[strlen(str) - 1] == '$') {
                        str[strlen(str) - 1] = 0;
                        strlcpy(comment, str + 1, NAME_LENGTH);
                     } else
                        strlcpy(comment, str, NAME_LENGTH);
                     strlcpy(str, comment, sizeof(str));
                     strencode2(line + strlen(line), str, sizeof(line) - strlen(line));
                  } else
                     strlcat(line, comment, sizeof(line));
               }

               if (line[0]) {
                  rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", attr_list[i]);
                  rsprintf("<td class=\"attribvalue\"><span style=\"color:black;background-color:#ffff66\">");
                  rsprintf("%s</span></td></tr>", line);
               }

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

               line[0] = 0;

               for (j = 0; j < MAX_N_LIST; j++) {
                  sprintf(iattr, "%s_%d", attr_list[i], j);
                  if (isparam(iattr)) {
                     if (line[0])
                        strlcat(line, " | ", sizeof(line));
                     strlcat(line, getparam(iattr), sizeof(line));
                  }
               }

               if (isparam(attr_list[i])) {
                  if (line[0])
                     strlcat(line, " | ", sizeof(line));
                  strencode2(line + strlen(line), getparam(attr_list[i]), sizeof(line) - strlen(line));
               }

               if (line[0]) {
                  rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", attr_list[i]);
                  rsprintf("<td class=\"attribvalue\"><span style=\"color:black;background-color:#ffff66\">");
                  rsprintf("%s</span></td></tr>", line);
               }

            } else if (isparam(attr_list[i])) {

               strlcpy(str, getparam(attr_list[i]), sizeof(str));
               if (str[0] && !strieq(str, "_all_") && strncmp(str, "--", 2) != 0) {
                  comment[0] = 0;
                  if (attr_flags[i] & AF_ICON) {
                     sprintf(str, "Icon comment %s", getparam(attr_list[i]));
                     getcfg(lbs->name, str, comment, sizeof(comment));
                  }

                  if (comment[0] == 0) {
                     strlcpy(str, getparam(attr_list[i]), sizeof(str));
                     if (str[0] == '^' && str[strlen(str) - 1] == '$') {
                        str[strlen(str) - 1] = 0;
                        strlcpy(comment, str + 1, NAME_LENGTH);
                     } else
                        strlcpy(comment, str, NAME_LENGTH);
                     strlcpy(str, comment, sizeof(str));
                     strencode2(comment, str, sizeof(comment));
                  }

                  rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", attr_list[i]);
                  rsprintf("<td class=\"attribvalue\"><span style=\"color:black;background-color:#ffff66\">");
                  rsprintf("%s</span></td></tr>", comment);
               }
            }
         }

         if (isparam("subtext")) {
            rsprintf("<tr><td nowrap width=\"10%%\" class=\"attribname\">%s:</td>", loc("Text"));
            rsprintf("<td class=\"attribvalue\"><span style=\"color:black;background-color:#ffff66\">");
            strencode2(str, getparam("subtext"), sizeof(str));
            rsprintf("%s</span></td></tr>", str);
         }

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

      /* get number of summary lines */
      n_line = 3;
      if (getcfg(lbs->name, "Summary lines", str, sizeof(str)))
         n_line = atoi(str);

      /* suppress summary completely if text body is disabled */
      if (getcfg(lbs->name, "Show text", str, sizeof(str)) && atoi(str) == 0)
         n_line = 0;

      /* suppress attachment colum if switched off */
      show_att_column = strieq(mode, "Summary");
      if (getcfg(lbs->name, "Enable attachments", str, sizeof(str)) && atoi(str) == 0)
         show_att_column = FALSE;

      /* get mode commands flag */
      mode_commands = TRUE;
      if (getcfg(lbs->name, "Mode commands", str, sizeof(str)) && atoi(str) == 0)
         mode_commands = FALSE;

      /*---- evaluate conditions for quick filters */
      for (i = 0; i < lbs->n_attr; i++) {
         attrib[i][0] = 0;
         if (isparam(attr_list[i])) {
            strlcpy(str, getparam(attr_list[i]), sizeof(str));
            if (str[0] == '^' && str[strlen(str) - 1] == '$') {
               str[strlen(str) - 1] = 0;
               strlcpy(attrib[i], str + 1, NAME_LENGTH);
            } else
               strlcpy(attrib[i], str, NAME_LENGTH);
         }
      }
      evaluate_conditions(lbs, attrib);

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

      if (info && info[0]) {
         rsprintf("<tr><td class=\"notifyleft\">%s</td></tr>\n", info);
      }

      /*---- page navigation ----*/

      if (!printable) {
         show_page_filters(lbs, n_msg, page_n, mode_commands, mode);
         show_page_navigation(lbs, n_msg, page_n, n_page);
      }

      /*---- select navigation ----*/

      if (isparam("select") && atoi(getparam("select")) == 1)
         show_select_navigation(lbs);

      /*---- table titles ----*/

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

      size = printable ? 2 : 3;
      show_text = TRUE;
      text_in_attr = FALSE;

      list[0] = 0;
      getcfg(lbs->name, "List display", list, 10000);

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

         strcpy(list, str);

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

         if (n > 0 && j == n)
            show_text = FALSE;
         else
            text_in_attr = TRUE;
      }

      memset(disp_attr_flags, 0, sizeof(disp_attr_flags));
      if (list[0]) {
         n_attr_disp = strbreak(list, disp_attr, MAX_N_ATTR, ",", FALSE);

         /* if text is in guest display list, adjust number of *real* attributes */
         if (text_in_attr)
            n_attr_disp--;

         if (search_all) {
            for (i = n_attr_disp - 1; i >= 0; i--)
               strcpy(disp_attr[i + 1], disp_attr[i]);
            strcpy(disp_attr[0], loc("Logbook"));
            n_attr_disp++;
         }
      } else {
         if (search_all) {
            n_attr_disp = lbs->n_attr + 3;

            strcpy(disp_attr[0], loc("Logbook"));
            strcpy(disp_attr[1], loc("ID"));
            strcpy(disp_attr[2], loc("Date"));
            memcpy(disp_attr + 3, attr_list, sizeof(attr_list));
            memcpy(disp_attr_flags + 3, attr_flags, sizeof(attr_flags));
         } else {
            n_attr_disp = lbs->n_attr + 2;

            strcpy(disp_attr[0], loc("ID"));
            strcpy(disp_attr[1], loc("Date"));
            memcpy(disp_attr + 2, attr_list, sizeof(attr_list));
            memcpy(disp_attr_flags + 2, attr_flags, sizeof(attr_flags));
         }
      }

      list[0] = 0;
      getcfg(lbs->name, "Link display", list, 10000);
      if (list[0]) {
         n = strbreak(list, (char (*)[NAME_LENGTH]) gattr, MAX_N_ATTR, ",", FALSE);

         for (i = 0; i < n_attr_disp; i++) {
            for (j = 0; j < n; j++)
               if (strieq(gattr + j * NAME_LENGTH, disp_attr[i]))
                  break;

            if (j < n)
               disp_attr_link[i] = TRUE;
            else
               disp_attr_link[i] = FALSE;
         }
      } else
         for (i = 0; i < n_attr_disp; i++)
            disp_attr_link[i] = TRUE;

      if (threaded) {
      } else {
         rsprintf("<tr>\n");

         /* empty title for selection box */
         if (isparam("select") && atoi(getparam("select")) == 1)
            rsprintf("<th class=\"listtitle\">&nbsp;</th>\n");

         for (i = 0; i < n_attr_disp; i++) {
            /* assemble current command line, replace sort statements */
            strlcpy(ref, getparam("cmdline"), sizeof(ref));

            strlcpy(str, disp_attr[i], sizeof(str));
            url_encode(str, sizeof(str));
            if (isparam("sort") && strcmp(getparam("sort"), disp_attr[i]) == 0) {
               subst_param(ref, sizeof(ref), "sort", "");
               subst_param(ref, sizeof(ref), "rsort", str);
            } else {
               if (ref[0] == 0) {
                  if (getcfg(lbs->name, "Reverse sort", str2, sizeof(str2)) && atoi(str2) == 1)
                     sprintf(ref, "?rsort=%s", str);
                  else
                     sprintf(ref, "?sort=%s", str);
               } else {
                  subst_param(ref, sizeof(ref), "rsort", "");
                  subst_param(ref, sizeof(ref), "sort", str);
               }
            }

            img[0] = 0;
            if (isparam("sort") && strcmp(getparam("sort"), disp_attr[i]) == 0)
               sprintf(img, "<img align=top src=\"up.png\" alt=\"%s\" title=\"%s\">", loc("up"), loc("up"));
            else if (isparam("rsort") && strcmp(getparam("rsort"), disp_attr[i]) == 0)
               sprintf(img, "<img align=top src=\"down.png\" alt=\"%s\" title=\"%s\">", loc("down"),
                       loc("down"));

            sprintf(str, "Tooltip %s", disp_attr[i]);
            if (getcfg(lbs->name, str, comment, sizeof(comment)))
               sprintf(str, "title=\"%s\"", comment);
            else
               str[0] = 0;

            if (strieq(disp_attr[i], "Edit") || strieq(disp_attr[i], "Delete"))
               rsprintf("<th %s class=\"listtitle\">%s</th>\n", str, disp_attr[i]);
            else {
               rsprintf("<th %s class=\"listtitle\"><a href=\"", str);
               rsputs3(ref);
               rsprintf("\">%s</a>%s</th>\n", disp_attr[i], img);
            }
         }

         if (!strieq(mode, "Full") && n_line > 0 && show_text)
            rsprintf("<th class=\"listtitle2\">%s</th>\n", loc("Text"));

         if (show_att_column)
            rsprintf("<th class=\"listtitle3\"><img src=\"attachment.png\" alt=\"%s\" title=\"%s\"</th>",
                     loc("Attachments"), loc("Attachments"));

         rsprintf("</tr>\n\n");
      }
   }                            /* if (!csv && !xml) */

   /*---- display message list ----*/

   for (index = i_start; index <= i_stop; index++) {
      size = TEXT_SIZE;
      message_id = msg_list[index].lbs->el_index[msg_list[index].index].message_id;

      status = el_retrieve(msg_list[index].lbs, message_id, date, attr_list, attrib, lbs->n_attr, text,
                           &size, in_reply_to, reply_to, attachment, encoding, locked_by, draft);
      if (status != EL_SUCCESS)
         break;

      /* skip drafts */
      if (getcfg(lbs->name, "List drafts", str, sizeof(str)) && atoi(str) == 0)
         if (draft[0])
            continue;

      if (csv) {

         rsprintf("%d", message_id);
         if (strieq(mode, "CSV1"))
            rsprintf(",");
         else
            rsprintf(";");

         strlcpy(str, date, sizeof(str));
         while (strchr(str, ','))
            *strchr(str, ',') = ' ';
         rsprintf(str);
         if (strieq(mode, "CSV1"))
            rsprintf(",");
         else
            rsprintf(";");

         for (i = 0; i < lbs->n_attr; i++) {
            strlcpy(str, attrib[i], sizeof(str));
            if (str[0]) {

               if (attr_flags[i] & AF_DATE) {

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

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

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

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

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

               }

               rsputs("\"");
               pt1 = str;
               while ((pt2 = strchr(pt1, '"')) != NULL) {
                  *pt2 = 0;
                  rsputs(pt1);
                  rsputs("\"\"");
                  pt1 = pt2 + 1;
               }
               rsputs(pt1);
               rsputs("\"");
            }
            if (i < lbs->n_attr - 1) {
               if (strieq(mode, "CSV1"))
                  rsprintf(",");
               else
                  rsprintf(";");
            } else {
               if (strlen(text) > 0 && strieq(mode, "CSV3")) {
                  rsprintf(";");
                  strlcpy(str, text, sizeof(str));
                  rsputs("\"");
                  pt1 = str;
                  while ((pt2 = strchr(pt1, '"')) != NULL) {
                     *pt2 = 0;
                     rsputs(pt1);
                     rsputs("\"\"");
                     pt1 = pt2 + 1;
                  }
                  rsputs(pt1);
                  rsputs("\"");
               }
               rsprintf("\r\n");
            }
         }

      } else if (xml) {

         rsputs("\t<ENTRY>\n");
         rsprintf("\t\t<MID>%d</MID>\n", message_id);
         rsprintf("\t\t<DATE>%s</DATE>\n", date);
         if (in_reply_to[0])
            rsprintf("\t\t<IN_REPLY_TO>%s</IN_REPLY_TO>\n", in_reply_to);
         if (reply_to[0])
            rsprintf("\t\t<REPLY_TO>%s</REPLY_TO>\n", reply_to);
         if (attachment[0][0]) {
            rsprintf("\t\t<ATTACHMENT>");
            rsprintf(attachment[0]);
            for (i = 1; i < MAX_ATTACHMENTS; i++)
               if (attachment[i][0])
                  rsprintf(",%s", attachment[i]);
            rsprintf("</ATTACHMENT>\n", attachment);
         }
         rsprintf("\t\t<ENCODING>%s</ENCODING>\n", encoding);

         for (i = 0; i < lbs->n_attr; i++) {
            strcpy(iattr, attr_list[i]);
            for (j = 0; j < (int) strlen(iattr); j++)
               /* replace special characters with "_", exclude any UTF-8 */
               if (!isalnum(iattr[j]) && ((unsigned char) iattr[j] < 128))
                  iattr[j] = '_';
            rsprintf("\t\t<%s>", iattr);

            strlcpy(str, attrib[i], sizeof(str));
            if (attr_flags[i] & AF_DATE) {

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

               ltime = atoi(attrib[i]);
               ptms = localtime(&ltime);
               assert(ptms);
               if (ltime == 0)
                  strcpy(str, "-");
               else
                  my_strftime(str, sizeof(str), format, ptms);
               sprintf(str + strlen(str), " [%ld]", (long)ltime);

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

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

               ltime = atoi(attrib[i]);
               ptms = localtime(&ltime);
               assert(ptms);
               if (ltime == 0)
                  strcpy(str, "-");
               else
                  my_strftime(str, sizeof(str), format, ptms);
               sprintf(str + strlen(str), " [%ld]", (long)ltime);
            }

            xmlencode(str);
            rsprintf("</%s>\n", iattr);
         }
         rsputs("\t\t<TEXT>");
         xmlencode(text);
         rsputs("</TEXT>\n");

         rsputs("\t</ENTRY>\n");

      } else if (raw) {

         rsprintf("$@MID@$: %d\r\n", message_id);
         rsprintf("Date: %s\r\n", date);

         if (reply_to[0])
            rsprintf("Reply to: %s\r\n", reply_to);

         if (in_reply_to[0])
            rsprintf("In reply to: %s\r\n", in_reply_to);

         for (i = 0; i < lbs->n_attr; i++)
            rsprintf("%s: %s\r\n", attr_list[i], attrib[i]);

         rsprintf("Attachment: ");

         if (attachment[0][0]) {
            rsprintf("%s", attachment[0]);
            for (i = 1; i < MAX_ATTACHMENTS; i++)
               if (attachment[i][0])
                  rsprintf(",%s", attachment[i]);
         }
         rsprintf("\r\n");

         rsprintf("Encoding: %s\r\n", encoding);
         if (locked_by[0])
            rsprintf("Locked by: %s\r\n", locked_by);

         rsprintf("========================================\r\n");
         rsputs(text);
         rsputs("\r\n");

      } else {

         /*---- add highlighting for searched subtext ----*/

         if (isparam("subtext")) {
            highlight_searchtext(re_buf, text, text1, strieq(encoding, "plain") || strieq(encoding, "ELCode")
                                                      || !strieq(mode, "Full"));
            strlcpy(text, text1, TEXT_SIZE);
         }

         /*---- display line ----*/

         expand = 1;
         if (threaded) {
            if (getcfg(lbs->name, "Expand default", str, sizeof(str)))
               expand = atoi(str);

            if (isparam("expand"))
               expand = atoi(getparam("expand"));
         }

         level = 0;
         if (!filtering && !date_filtering) {
            if (expand == 0 && (!getcfg(lbs->name, "Collapse to last", str, sizeof(str)) || atoi(str) == 1)) {
               /* search last entry in this thread */
               if (reply_to[0]) {
                  search_last_reply(msg_list[index].lbs, &message_id);
                  size = TEXT_SIZE;
                  status =
                          el_retrieve(msg_list[index].lbs, message_id, date, attr_list, attrib, lbs->n_attr,
                                      text,
                                      &size, in_reply_to, reply_to, attachment, encoding, locked_by, draft);
                  if (status == SUCCESS)
                     level = 1;
               }
            }
         } else if (in_reply_to[0])
            level = 1;

         display_line(msg_list[index].lbs, message_id, index, mode, expand, level, printable, n_line,
                      show_attachments, show_att_column, date, in_reply_to, reply_to, n_attr_disp, disp_attr,
                      disp_attr_link, attrib, lbs->n_attr, text, show_text, attachment, encoding,
                      isparam("select") ? atoi(getparam("select")) : 0, &n_display, locked_by, 0, re_buf,
                      page_mid, FALSE, draft);

         if (threaded && !filtering && !date_filtering) {
            if (reply_to[0] && expand > 0) {
               p = reply_to;
               do {
                  display_reply(msg_list[index].lbs, atoi(p), printable, expand, n_line, n_attr_disp,
                                disp_attr, show_text, 1, 0, re_buf, page_mid, FALSE);

                  while (*p && isdigit(*p))
                     p++;
                  while (*p && (*p == ',' || *p == ' '))
                     p++;
               } while (*p);
            }
         }
      }                         /* if (!csv && !xml) */
   }                            /* for() */

   if (!csv && !xml && !raw) {
      rsprintf("</table>\n");

      if (n_display)
         rsprintf("<input type=hidden name=nsel value=%d>\n", n_display);

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

      if (n_msg == 0)
         rsprintf("<tr><td class=\"errormsg\">%s</td></tr>", loc("No entries found"));

      /*---- page navigation ----*/

      if (!printable)
         show_page_navigation(lbs, n_msg, page_n, n_page);

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

   if (xml) {
      rsputs("</ELOG_LIST>\n");
   }

   regfree(re_buf);
   for (i = 0; i < lbs->n_attr; i++)
      regfree(re_buf + 1 + i);

   xfree(slist);
   xfree(svalue);
   xfree(gattr);
   xfree(list);
   xfree(msg_list);
   xfree(text);
   xfree(text1);
}

Project: elog

int is_ascii(char *file_name) {
   int i, fh, length;
   unsigned char *buf;

   fh = open(file_name, O_RDONLY | O_BINARY);
   if (fh < 0)
      return FALSE;
   lseek(fh, 0, SEEK_END);
   length = TELL(fh);
   lseek(fh, 0, SEEK_SET);
   if (length > 1000)
      length = 1000;
   buf = xmalloc(length);
   read(fh, buf, length);
   close(fh);

   for (i = 0; i < length; i++) {
      if (buf[i] < 32 && buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t') {
         xfree(buf);
         return FALSE;
      }
   }

   xfree(buf);
   return TRUE;
}

Project: elog

void el_enum_attr(char *message, int n, char *attr_name, char *attr_value) {
   char *p, str[NAME_LENGTH], tmp[NAME_LENGTH];
   int i;

   p = message;
   for (i = 0; i <= n; i++) {
      strlcpy(str, p, sizeof(str));
      if (strchr(str, '\n'))
         *strchr(str, '\n') = 0;
      if (strchr(str, '\r'))
         *strchr(str, '\r') = 0;

      if (strcmp(str, "========================================") == 0)
         break;

      p = strchr(p, '\n');
      if (!p) {
         str[0] = 0;            /* not a valid line */
         break;
      }
      while (*p == '\n' || *p == '\r')
         p++;

      if (strchr(str, ':')) {
         strcpy(tmp, str);
         *strchr(tmp, ':') = 0;

         if (strieq(tmp, "$@MID@$") || strieq(tmp, "Date") || strieq(tmp, "Attachment") || strieq(tmp,
                                                                                                  "Reply To")
             || strieq(tmp, "In Reply To") || strieq(tmp, "Encoding") || strieq(tmp, "Locked by"))
            i--;
      }
   }

   attr_name[0] = 0;
   attr_value[0] = 0;
   if (strchr(str, ':')) {
      strlcpy(attr_name, str, NAME_LENGTH);
      *strchr(attr_name, ':') = 0;
      strlcpy(attr_value, strchr(str, ':') + 2, NAME_LENGTH);
   }

}

Project: elog

int get_verbose(void) {
   return _verbose_level;
}

Project: elog

void replace_inline_img(LOGBOOK *lbs, char *str) {
   char *p, *pn, *pa, old[256], link[256], base_url[256], domain[256];
   int index;

   p = str;
   do {
      p = strstr(p, "<img ");
      if (p) {
         pn = strstr(p, "name=");
         if (pn) {
            pn += 9;
            index = atoi(pn);
            while (*pn && *pn != '>')
               pn++;
            if (*pn == '>')
               pn++;
            retrieve_domain(domain, sizeof(domain));
            sprintf(p, "<img border=\"0\" src=\"cid:att%d@%s\">", index, domain);
            memmove(p + strlen(p), pn, strlen(pn) + 1);

            /* now change href to absolute link */
            pa = p - 1;
            while (pa > str && *pa != '<')
               // search '<a href=...>'
               pa--;
            pn = strstr(pa, "href=");
            if (pn && pn - pa < 10) {
               strlcpy(old, pn + 6, sizeof(old));
               if (strchr(old, '\"'))
                  *strchr(old, '\"') = 0;
               compose_base_url(lbs, base_url, sizeof(base_url), FALSE);
               strlcpy(link, base_url, sizeof(link));
               strlcat(link, old, sizeof(link));
               if (strchr(link, '?'))
                  *strchr(link, '?') = 0;
               strsubst(pn + 6, TEXT_SIZE, old, link);
               if (strlen(link) > strlen(old))
                  p += strlen(link) - strlen(old);
            }

            p++;
         } else
            p++;
      }
   } while (p != NULL);
}

Project: elog

void serialdate2date(double days, int *day, int *month, int *year)
/* convert days since 1.1.1900 to date */
{
   int i, j, l, n;

   l = (int) days + 68569 + 2415019;
   n = (int) ((4 * l) / 146097);
   l = l - (int) ((146097 * n + 3) / 4);
   i = (int) ((4000 * (l + 1)) / 1461001);
   l = l - (int) ((1461 * i) / 4) + 31;
   j = (int) ((80 * l) / 2447);
   *day = l - (int) ((2447 * j) / 80);
   l = (int) (j / 11);
   *month = j + 2 - (12 * l);
   *year = 100 * (n - 49) + i + l;
}

Project: elog

void base64_bufenc(unsigned char *s, int len, char *d) {
   unsigned int t, pad;
   int i;

   pad = 3 - len % 3;
   if (pad == 3)
      pad = 0;
   for (i = 0; i < len;) {
      t = s[i++] << 16;
      if (i < len)
         t |= s[i++] << 8;
      if (i < len)
         t |= s[i++] << 0;

      *(d + 3) = map[t & 63];
      t >>= 6;
      *(d + 2) = map[t & 63];
      t >>= 6;
      *(d + 1) = map[t & 63];
      t >>= 6;
      *(d + 0) = map[t & 63];

      d += 4;
   }
   *d = 0;
   while (pad--)
      *(--d) = '=';
}

Project: elog

void rsputs(const char *str) {
   if (strlen_retbuf + (int) strlen(str) + 1 >= return_buffer_size) {
      return_buffer = xrealloc(return_buffer, return_buffer_size + (int) strlen(str) + 100000);
      memset(return_buffer + return_buffer_size, 0, (int) strlen(str) + 100000);
      return_buffer_size += (int) strlen(str) + 100000;
   }

   strcpy(return_buffer + strlen_retbuf, str);
   strlen_retbuf += strlen(str);
}

Project: elog

void receive_config(LOGBOOK *lbs, char *server, char *error_str) {
   char str[256], pwd[256], *buffer, *p;
   int status, version, ssl;

   error_str[0] = 0;

   do {

      combine_url(lbs, server, "", str, sizeof(str), &ssl);
      if (lbs == NULL)
         strcat(str, "?cmd=GetConfig"); // request complete config file
      else
         strcat(str, "?cmd=Download");  // request config section of logbook

      if (retrieve_url(lbs, str, ssl, &buffer, TRUE) < 0) {
         *strchr(str, '?') = 0;
         sprintf(error_str, "Cannot contact elogd server at http://%s", str);
         return;
      }

      /* check version */
      p = strstr(buffer, "ELOG HTTP ");
      if (!p) {
         if (get_verbose() >= VERBOSE_INFO)
            puts(buffer);
         sprintf(error_str, "Remote server is not an ELOG server");
         xfree(buffer);
         return;
      }
      version = atoi(p + 10) * 100 + atoi(p + 12) * 10 + atoi(p + 14);
      if (version < 254) {
         if (get_verbose() >= VERBOSE_INFO)
            puts(buffer);

         strlcpy(str, p + 10, 10);
         if (strchr(str, '\r'))
            *strchr(str, '\r') = 0;

         sprintf(error_str, "Incorrect remote ELOG server version %s, must be 2.5.4 or later", str);
         xfree(buffer);
         return;
      }

      /* evaluate status */
      p = strchr(buffer, ' ');
      if (p == NULL) {
         if (get_verbose() >= VERBOSE_INFO)
            puts(buffer);
         xfree(buffer);
         *strchr(str, '?') = 0;
         sprintf(error_str, "Received invalid response from elogd server at http%s://%s", ssl ? "s" : "",
                 str);
         xfree(buffer);
         return;
      }
      p++;
      status = atoi(p);
      if (status == 401) {
         if (get_verbose() >= VERBOSE_INFO)
            puts(buffer);
         xfree(buffer);
         eprintf("Please enter password to access remote elogd server: ");
         fgets(pwd, sizeof(pwd), stdin);
         while (pwd[strlen(pwd) - 1] == '\n' || pwd[strlen(pwd) - 1] == '\r')
            pwd[strlen(pwd) - 1] = 0;
      } else if (status != 200) {
         if (get_verbose() >= VERBOSE_INFO)
            puts(buffer);
         xfree(buffer);
         *strchr(str, '?') = 0;
         sprintf(error_str, "Received invalid response from elogd server at http%s://%s", ssl ? "s" : "",
                 str);
         return;
      }

   } while (status != 200);

   p = strstr(buffer, "\r\n\r\n");
   if (p == NULL) {
      if (get_verbose() >= VERBOSE_INFO)
         puts(buffer);
      xfree(buffer);
      sprintf(error_str, loc("Cannot receive \"%s\""), str);
      return;
   }
   p += 4;

   if (strstr(p, "[global]") == NULL) {
      strlcpy(error_str, p, 256);
      xfree(buffer);
      return;
   }

   if (lbs == NULL) {
      if (!save_config(p, str))
         rsprintf("%s", str);
   } else {
      if (!save_admin_config(lbs->name, p, str))
         rsprintf("%s", str);
   }

   xfree(buffer);
}

Project: elog

void csv_import(LOGBOOK *lbs, const char *csv, const char *csvfile) {
   const char *p;
   char *line, *list;
   char str[256], date[80], sep[80];
   int i, j, n, n_attr, iline, n_imported, textcol, datecol, attr_offset;
   BOOL first, in_quotes, filltext;
   time_t ltime;

   list = (char *) xmalloc((MAX_N_ATTR + 2) * NAME_LENGTH);
   line = (char *) xmalloc(10000);

   first = TRUE;
   in_quotes = FALSE;
   iline = n_imported = 0;
   filltext = FALSE;
   textcol = -1;
   datecol = -1;
   attr_offset = 0;

   strcpy(sep, ",");
   if (isparam("sep"))
      strcpy(sep, getparam("sep"));
   if (sep[0] == 0)
      strcpy(sep, ",");
   if (strieq(sep, "auto")) {

      /* count commas */
      for (i = 0, p = csv; p; i++) {
         p = strchr(p, ',');
         if (p)
            p++;
      }
      n = i;

      /* count semicolon */
      for (i = 0, p = csv; p; i++) {
         p = strchr(p, ';');
         if (p)
            p++;
      }

      strcpy(sep, i > n ? ";" : ",");
   }

   n_attr = lbs->n_attr;

   if (isparam("preview")) {

      /* title row */
      sprintf(str, loc("CSV import preview of %s"), csvfile);
      show_standard_header(lbs, TRUE, str, "./", FALSE, NULL, NULL, 0);
      rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\">\n");
      rsprintf("<tr><td class=\"title1\">%s</td></tr>\n", str, str);

      /* menu buttons */
      rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("CSV Import"));

      /* hidden fields */
      rsprintf("<input type=hidden name=sep value=\"%s\">\n", sep);
      if (isparam("head"))
         rsprintf("<input type=hidden name=head value=\"%s\">\n", getparam("head"));
      if (isparam("notignore"))
         rsprintf("<input type=hidden name=notignore value=\"%s\">\n", getparam("notignore"));
      if (isparam("filltext"))
         rsprintf("<input type=hidden name=filltext value=\"%s\">\n", getparam("filltext"));
      rsprintf("<input type=hidden name=csvfile value=\"%s\">\n", csvfile);

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

      rsprintf("<tr><td><table class=\"listframe\" width=\"100%%\" cellspacing=0>");
   }

   p = csv;
   datecol = -1;
   attr_offset = 0;

   do {
      for (i = 0; i < 10000 && *p; i++) {
         if (!in_quotes && (*p == '\r' || *p == '\n'))
            break;

         line[i] = *p++;
         if (line[i] == '"')
            in_quotes = !in_quotes;
      }

      line[i] = 0;
      while (*p == '\r' || *p == '\n')
         p++;
      if (!*p)
         break;

      memset(list, 0, MAX_N_ATTR * NAME_LENGTH);
      n = strbreak(line, (char (*)[NAME_LENGTH]) list, MAX_N_ATTR, sep, FALSE);

      if (n == MAX_N_ATTR) {
         strlcpy(str, loc("Too many attributes in CSV file"), sizeof(str));
         show_error(str);
      }

      /* check if text column is present */
      if (first && isparam("filltext") && atoi(getparam("filltext"))) {
         for (i = 0; i < n; i++)
            if (strieq(list + i * NAME_LENGTH, loc("text"))) {
               filltext = TRUE;
               textcol = i;
               break;
            }
      }

      /* interprete date entries correctly */
      if (!(first && isparam("head"))) {
         for (i = attr_offset; i < n; i++) {
            if (attr_flags[i - attr_offset] & AF_DATE) {
               /* convert to seconds in Unix format */
               ltime = convert_date(list + i * NAME_LENGTH);
               if (ltime == 0) {
                  show_error(loc("Invalid date format"));
                  return;
               }
               sprintf(list + i * NAME_LENGTH, "%d", (int) ltime);
            }
            if (attr_flags[i - attr_offset] & AF_DATETIME) {
               /* convert to seconds in Unix format */
               ltime = convert_datetime(list + i * NAME_LENGTH);
               if (ltime == 0) {
                  show_error(loc("Invalid date format"));
                  return;
               }
               sprintf(list + i * NAME_LENGTH, "%d", (int) ltime);
            }
         }
      }

      if (first) {
         /* check for date column */
         for (i = attr_offset = 0; i < n; i++)
            if (strieq(list + i * NAME_LENGTH, "Date"))
               datecol = i;

         /* skip message ID */
         for (i = attr_offset = 0; i < n; i++)
            if (strieq(list + i * NAME_LENGTH, "Message ID") || strieq(list + i * NAME_LENGTH, "Date"))
               attr_offset++;
      }

      /* derive attributes from first line */
      if (first && isparam("head")) {

         if (isparam("preview")) {
            rsprintf("<tr>\n");
            for (i = attr_offset; i < n; i++)
               if (i != textcol)
                  rsprintf("<th class=\"listtitle\">%s</th>\n", list + i * NAME_LENGTH);

            if (filltext)
               rsprintf("<th class=\"listtitle\">%s</th>\n", loc("text"));

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

            if (filltext)
               n_attr = n - 1 - attr_offset;
            else
               n_attr = n - attr_offset;

         } else {
            for (i = j = attr_offset; i < n; i++)
               if (i != textcol)
                  strlcpy(attr_list[j++ - attr_offset], list + i * NAME_LENGTH, NAME_LENGTH);

            if (filltext) {
               if (!set_attributes(lbs, attr_list, n - 1 - attr_offset))
                  return;
               lbs->n_attr = n - 1 - attr_offset;
            } else {
               if (!set_attributes(lbs, attr_list, n - attr_offset))
                  return;
               lbs->n_attr = n - attr_offset;
            }
            n_attr = lbs->n_attr;
         }

      } else {

         /* ignore first line */
         if (first && !isparam("notignore")) {
            first = FALSE;
            continue;
         }

         if (isparam("preview")) {
            rsprintf("<tr>\n");
            for (i = j = attr_offset; i < n_attr + attr_offset; i++) {
               if (iline % 2 == 0)
                  rsputs("<td class=\"list1\">");
               else
                  rsputs("<td class=\"list2\">");

               /* skip text column */
               if (i == textcol)
                  j++;

               if (i >= n || !list[j * NAME_LENGTH])
                  rsputs("&nbsp;");
               else
                  rsputs(list + j * NAME_LENGTH);

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

            if (filltext) {
               rsputs("<td class=\"summary\">");
               if (list[textcol * NAME_LENGTH])
                  rsputs(list + textcol * NAME_LENGTH);
               else
                  rsputs("&nbsp;");
               rsputs("</td>\n");
            }

            rsputs("</tr>\n");
            iline++;

         } else {

            /* get date and check it */
            if (datecol != -1) {
               strlcpy(date, list + datecol * NAME_LENGTH, sizeof(date));
               ltime = date_to_ltime(date);
               if (ltime <= 0) {
                  /* try other date formats */
                  ltime = convert_datetime(date);
                  if (ltime <= 0)
                     ltime = convert_date(date);
                  if (ltime <= 0) {
                     strcpy(str, loc("Invalid date format"));
                     strlcat(str, ": \"", sizeof(str));
                     strlcat(str, date, sizeof(str));
                     strlcat(str, "\"", sizeof(str));
                     show_error(str);
                     return;
                  }
                  /* convert back ltime to date */
                  get_rfc2822_date(date, sizeof(date), ltime);
               }
            } else
               date[0] = 0;

            if (!filltext) {
               /* submit entry */
               if (el_submit
                       (lbs, 0, FALSE, date, attr_list,
                        (char (*)[NAME_LENGTH]) (list + attr_offset * NAME_LENGTH), n_attr, "", "", "",
                        "plain",
                        NULL, TRUE, NULL, NULL))
                  n_imported++;
            } else {
               strlcpy(line, list + textcol * NAME_LENGTH, 10000);
               insert_breaks(line, 78, 10000);

               for (i = textcol; i < n_attr + attr_offset; i++)
                  strlcpy(list + i * NAME_LENGTH, list + (i + 1) * NAME_LENGTH, NAME_LENGTH);

               /* submit entry */
               if (el_submit
                       (lbs, 0, FALSE, date, attr_list,
                        (char (*)[NAME_LENGTH]) (list + attr_offset * NAME_LENGTH), n_attr, line, "", "",
                        "plain",
                        NULL, TRUE, NULL, NULL))
                  n_imported++;
            }
         }
      }

      first = FALSE;

   } while (*p);

   xfree(line);
   xfree(list);

   if (isparam("preview")) {
      rsprintf("</table></td></tr></table>\n");
      show_bottom_text(lbs);
      rsprintf("</form></body></html>\r\n");

      return;
   }

   sprintf(str, loc("%d entries successfully imported"), n_imported);
   show_elog_list(lbs, 0, 0, 0, TRUE, str);
}

Project: elog

BOOL subst_param(char *str, int size, char *param, char *value) {
   int len;
   char *p1, *p2, *s, param_enc[256], str2[256];

   strlcpy(param_enc, param, sizeof(param_enc));
   url_slash_encode(param_enc, sizeof(param_enc));

   if (!value[0]) {

      /* remove parameter */
      s = param_in_str(str, param_enc);

      if (s == NULL)
         return FALSE;

      /* remove parameter */
      p1 = s - 1;

      for (p2 = p1 + strlen(param_enc) + 1; *p2 && *p2 != '&'; p2++);
      strlcpy(str2, p2, sizeof(str2));
      strlcpy(p1, str2, size - (p1 - str));

      if (!strchr(str, '?') && strchr(str, '&'))
         *strchr(str, '&') = '?';

      return TRUE;
   }

   if ((p1 = param_in_str(str, param_enc)) == NULL) {
      if (strchr(str, '?'))
         strlcat(str, "&", size);
      else
         strlcat(str, "?", size);

      strlcat(str, param_enc, size);
      strlcat(str, "=", size);
      strlcat(str, value, size);
      return FALSE;
   }

   p1 += strlen(param_enc) + 1;
   for (p2 = p1; *p2 && *p2 != '&'; p2++);
   len = p2 - p1;
   if (len > (int) strlen(value)) {
      /* new value is shorter than old one */
      strlcpy(str2, value, size - (p1 - str));
      strlcpy(str2 + strlen(value), p2, size - (p1 + strlen(value) - str));
      strlcpy(p1, str2, size - (p1 - str));
   } else {
      /* new value is longer than old one */
      s = (char *) xmalloc(size);
      strlcpy(s, p2, size);
      strlcpy(str2, value, size - (p1 - str));
      strlcat(str2, s, size - (p1 + strlen(value) - str));
      strlcpy(p1, str2, size - (p1 - str));
      xfree(s);
   }

   return TRUE;
}

Project: elog

int recv_string(int sock, char *buffer, int buffer_size, int millisec) {
   int i, n;
   fd_set readfds;
   struct timeval timeout;

   n = 0;
   memset(buffer, 0, buffer_size);

   do {
      if (millisec > 0) {
         FD_ZERO(&readfds);
         FD_SET(sock, &readfds);

         timeout.tv_sec = millisec / 1000;
         timeout.tv_usec = (millisec % 1000) * 1000;

         select(FD_SETSIZE, (void *) &readfds, NULL, NULL, (void *) &timeout);

         if (!FD_ISSET(sock, &readfds))
            break;
      }

      i = recv(sock, buffer + n, 1, 0);

      if (i <= 0)
         break;

      n++;

      if (n >= buffer_size)
         break;

   } while (buffer[n - 1] && buffer[n - 1] != 10);

   return n - 1;
}

Project: elog

int max_attachment_size(LOGBOOK *lbs, int message_id) {
   char att_file[MAX_ATTACHMENTS][MAX_PATH_LENGTH];
   int index, max_size, fh;
   char str[256], subdir[256], file_name[256];

   el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, att_file, NULL, NULL, NULL);

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

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

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

      fh = open(file_name, O_RDONLY | O_BINARY);
      if (fh > 0) {
         off_t size = lseek(fh, 0L, SEEK_END);
         max_size = size > max_size ? size : max_size;
      }
   }

   return max_size;
}

Project: elog

void send_file_direct(char *file_name) {
   int fh, i, length, delta;
   char str[MAX_PATH_LENGTH], dir[MAX_PATH_LENGTH], charset[80];

   getcwd(dir, sizeof(dir));
   fh = open(file_name, O_RDONLY | O_BINARY);
   if (fh > 0) {
      lseek(fh, 0, SEEK_END);
      length = TELL(fh);
      lseek(fh, 0, SEEK_SET);

      rsprintf("HTTP/1.1 200 Document follows\r\n");
      rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
      rsprintf("Accept-Ranges: bytes\r\n");

      /* set expiration time to one day if no thumbnail */
      if (isparam("thumb")) {
         rsprintf("Pragma: no-cache\r\n");
         rsprintf("Cache-control: private, max-age=0, no-cache, no-store\r\n");
      } else {
         rsprintf("Cache-control: public, max-age=86400\r\n");
      }

      if (keep_alive) {
         rsprintf("Connection: Keep-Alive\r\n");
         rsprintf("Keep-Alive: timeout=60, max=10\r\n");
      }

      /* return proper header for file type */
      for (i = 0; i < (int) strlen(file_name); i++)
         str[i] = toupper(file_name[i]);
      str[i] = 0;

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

      if (!getcfg("global", "charset", charset, sizeof(charset)))
         strcpy(charset, DEFAULT_HTTP_CHARSET);

      if (filetype[i].ext[0]) {
         if (strncmp(filetype[i].type, "text", 4) == 0)
            rsprintf("Content-Type: %s;charset=%s\r\n", filetype[i].type, charset);
         else if (strcmp(filetype[i].ext, ".SVG") == 0) {
            rsprintf("Content-Type: %s\r\n", filetype[i].type);
            if (strrchr(file_name, '/'))
               strlcpy(str, strrchr(file_name, '/')+1, sizeof(str));
            else
               strlcpy(str, file_name, sizeof(str));
            if (str[6] == '_' && str[13] == '_')
               rsprintf("Content-Disposition: attachment; filename=\"%s\"\r\n", str+14);
            else
               rsprintf("Content-Disposition: attachment; filename=\"%s\"\r\n", str);
         } else
            rsprintf("Content-Type: %s\r\n", filetype[i].type);
      } else if (is_ascii(file_name))
         rsprintf("Content-Type: text/plain;charset=%s\r\n", charset);
      else
         rsprintf("Content-Type: application/octet-stream;charset=%s\r\n", charset);

      rsprintf("Content-Length: %d\r\n\r\n", length);

      /* increase return buffer size if file too big */
      if (length > return_buffer_size - (int) strlen(return_buffer)) {
         delta = length - (return_buffer_size - strlen(return_buffer)) + 1000;

         return_buffer = xrealloc(return_buffer, return_buffer_size + delta);
         memset(return_buffer + return_buffer_size, 0, delta);
         return_buffer_size += delta;
      }

      return_length = strlen(return_buffer) + length;
      read(fh, return_buffer + strlen(return_buffer), length);

      close(fh);
   } else {
      char encodedname[256];
      show_html_header(NULL, FALSE, "404 Not Found", TRUE, FALSE, NULL, FALSE, 0);

      rsprintf("<body><h1>Not Found</h1>\r\n");
      rsprintf("The requested file <b>");
      strencode2(encodedname, file_name, sizeof(encodedname));
      if (strchr(file_name, DIR_SEPARATOR))
         rsprintf("%s", encodedname);
      else
         rsprintf("%s%c%s", dir, DIR_SEPARATOR, encodedname);
      rsprintf("</b> was not found on this server<p>\r\n");
      rsprintf("<hr><address>ELOG version %s</address></body></html>\r\n\r\n", VERSION);
      return_length = strlen_retbuf;
      keep_alive = FALSE;
   }
}

Project: elog

void add_logbook_to_group(LOGBOOK *lbs, char *new_name) {
   int i, j, n, flag;
   char str[1000], grpname[256], grpmembers[1000];
   char grplist[MAX_N_LIST][NAME_LENGTH];

   /* enumerate groups */
   for (i = 0;; i++) {
      if (!enumcfg("global", grpname, sizeof(grpname), grpmembers, sizeof(grpmembers), i))
         break;

      flag = 0;
      strlcpy(str, grpname, sizeof(str));
      str[9] = 0;
      if (strieq(str, "top group"))
         flag = 2;
      str[5] = 0;
      if (strieq(str, "group"))
         flag = 1;

      if (flag) {

         n = strbreak(grpmembers, grplist, MAX_N_LIST, ",", FALSE);
         for (j = 0; j < n; j++) {
            if (strieq(lbs->name, grplist[j])) {
               /* rename or remove logbook */
               change_config_line(lbs, grpname, "", new_name);
               break;
            }
         }
      }
   }
}

Project: elog

int setparam(char *param, char *value) {
   int i;
   char str[10000];

   if (strieq(param, "text")) {
      if (strlen(value) >= TEXT_SIZE) {
         sprintf(str,
                 "Error: Entry text too big (%lu bytes). Please increase TEXT_SIZE and recompile elogd\n",
                 (unsigned long) strlen(value));
         show_error(str);
         return 0;
      }

      strlcpy(_mtext, value, TEXT_SIZE);
      return 1;
   }

   if (strieq(param, "cmdline")) {
      if (strlen(value) >= CMD_SIZE) {
         sprintf(str,
                 "Error: Command line too big (%lu bytes). Please increase CMD_SIZE and recompile elogd\n",
                 (unsigned long) strlen(value));
         show_error(str);
         return 0;
      }

      strlcpy(_cmdline, value, CMD_SIZE);
      return 1;
   }

   /* paremeters can be superseeded */
   for (i = 0; i < MAX_PARAM; i++)
      if (_param[i][0] == 0 || strieq(param, _param[i]))
         break;

   if (i < MAX_PARAM) {
      if (strlen(param) >= NAME_LENGTH) {
         sprintf(str, "Error: Parameter name too big (%lu bytes).\n", (unsigned long) strlen(param));
         show_error(str);
         return 0;
      }

      strlcpy(_param[i], param, NAME_LENGTH);

      if (strlen(value) >= NAME_LENGTH) {
         sprintf(str,
                 "Error: Parameter value for parameter <b>%s</b> too big (%lu bytes). Please increase NAME_LENGTH and recompile elogd\n",
                 param, (unsigned long) strlen(value));
         show_error(str);
         return 0;
      }

      strlcpy(_value[i], value, NAME_LENGTH);
   } else {
      sprintf(str, "Error: Too many parameters (> %d). Cannot perform operation.\n", MAX_PARAM);
      show_error(str);
      return 0;
   }

   return 1;
}

Project: elog

int ascii_compare2(const void *s1, const void *s2) {
   return stricmp((char *) s1, (char *) s2);
}

Project: elog

char *getparam(char *param) {
   int i;

   if (strieq(param, "text"))
      return _mtext;

   if (strieq(param, "cmdline"))
      return _cmdline;

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

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

   return NULL;
}

Project: elog

void show_change_pwd_page(LOGBOOK *lbs) {
   char str[256], config[256], old_pwd[256], new_pwd[256], new_pwd2[256], user[256], auth[32], error_str[256];
   int wrong_pwd;
   /* otherwise calls with null lbs which make this procedure crash */
   if (lbs == NULL)
      lbs = get_first_lbs_with_global_passwd();

   if (lbs == NULL)
      return;
   getcfg(lbs->name, "Authentication", auth, sizeof(auth));

   old_pwd[0] = new_pwd[0] = new_pwd2[0] = 0;

   if (isparam("oldpwd"))
      strlcpy(old_pwd, getparam("oldpwd"), sizeof(old_pwd));
   if (isparam("newpwd"))
      strlcpy(new_pwd, getparam("newpwd"), sizeof(new_pwd));
   if (isparam("newpwd2"))
      strlcpy(new_pwd2, getparam("newpwd2"), sizeof(new_pwd2));

   if (isparam("unm"))
      strlcpy(user, getparam("unm"), sizeof(user));
   else
      user[0] = 0;

   if (isparam("config")) {
      strlcpy(str, getparam("config"), sizeof(str));
      strencode2(user, str, sizeof(user));
   }

   wrong_pwd = FALSE;
   error_str[0] = 0;

   if (isparam("fail")) {
      wrong_pwd = atoi(getparam("fail"));
      if (!wrong_pwd) {
         wrong_pwd = 3;
         strlcpy(error_str, getparam("fail"), sizeof(error_str));
      }
   }

   if (old_pwd[0] || new_pwd[0]) {
      if (user[0]) {

         if (stristr(auth, "Kerberos") || stristr(auth, "Webserver") || stristr(auth, "PAM")) {
            if (strcmp(new_pwd, new_pwd2) != 0)
               wrong_pwd = 2;
         } else {
            /* administrator does not have to supply old password if changing other user's password */
            if (isparam("unm") && is_admin_user(lbs, getparam("unm"))
                && stricmp(getparam("unm"), user) != 0)
               wrong_pwd = 0;
            else {
               if (!auth_verify_password(lbs, user, old_pwd, str, sizeof(str)))
                  wrong_pwd = 1;
            }

            if (strcmp(new_pwd, new_pwd2) != 0)
               wrong_pwd = 2;
         }
      }

      if (new_pwd[0]) {
         /* replace password */
         error_str[0] = 0;
         if (!wrong_pwd)
            wrong_pwd =
                    (auth_change_password(lbs, user, old_pwd, new_pwd, error_str, sizeof(error_str)) == 0);

         if (!wrong_pwd && isparam("unm") && strcmp(user, getparam("unm")) == 0) {
            redirect(lbs, "");
            return;
         }

         if (!wrong_pwd) {
            /* redirect back to configuration page */
            if (isparam("config")) {
               strlcpy(config, getparam("config"), sizeof(config));
               sprintf(str, "?cmd=%s&cfg_user=%s", loc("Config"), config);
            } else
               sprintf(str, "?cmd=%s", loc("Config"));
            redirect(lbs, str);
            return;
         }

         /* go though one redirection to avoid passwords to be shown in URL */
         if (wrong_pwd) {
            if (error_str[0])
               sprintf(str, "?cmd=%s&config=%s&fail=%s", loc("Change password"), getparam("unm"), error_str);
            else
               sprintf(str, "?cmd=%s&config=%s&fail=%d", loc("Change password"), getparam("unm"), wrong_pwd);
            redirect(lbs, str);
            return;
         }
      }
   }

   show_standard_header(lbs, TRUE, loc("ELOG change password"), NULL, FALSE, NULL, NULL, 0);

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

   if (wrong_pwd == 1) {
      if (error_str[0]) {
         strencode2(str, error_str, sizeof(str));
         rsprintf("<tr><td colspan=2 class=\"dlgerror\">%s!</td></tr>\n", str);
      } else
         rsprintf("<tr><td colspan=2 class=\"dlgerror\">%s!</td></tr>\n", loc("Wrong password"));
   }

   if (wrong_pwd == 2)
      rsprintf("<tr><td colspan=2 class=\"dlgerror\">%s!</td></tr>\n",
               loc("New passwords do not match, please retype"));

   if (wrong_pwd == 3) {
      strencode2(str, error_str, sizeof(str));
      rsprintf("<tr><td colspan=2 class=\"dlgerror\">%s!</td></tr>\n", str);
   }

   rsprintf("<tr><td colspan=2 class=\"dlgtitle\">\n");

   rsprintf("<input type=hidden name=config value=\"%s\">", user);

   rsprintf("%s \"%s\"</td></tr>\n", loc("Change password for user"), user);

   /* do not ask for old pwasword if admin changes other user's password */
   if (isparam("unm")) {
      if (!is_admin_user(lbs, getparam("unm")) || stricmp(getparam("unm"), user) == 0) {
         if (isparam("oldpwd") && !(wrong_pwd == 1))    // hidden password for password recovery
            rsprintf("<input type=hidden name=oldpwd value=\"%s\"", getparam("oldpwd"));
         else {
            rsprintf("<tr><td align=right class=\"dlgform\">%s:\n", loc("Old password"));
            rsprintf("<td align=left class=\"dlgform\"><input type=password name=oldpwd>\n");
            rsprintf("</td></tr>\n");
         }
      }
   }

   rsprintf("<tr><td align=right class=\"dlgform\">%s:</td>\n", loc("New password"));
   rsprintf("<td align=left class=\"dlgform\"><input type=password name=newpwd></td></tr>\n");

   rsprintf("<tr><td align=right class=\"dlgform\">%s:</td>\n", loc("Retype new password"));
   rsprintf("<td align=left class=\"dlgform\"><input type=password name=newpwd2></td></tr>\n");

   rsprintf("<tr><td align=center colspan=2 class=\"dlgform\"><input type=submit value=\"%s\"></td></tr>",
            loc("Submit"));

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

Project: elog

int el_move_message_thread(LOGBOOK *lbs, int message_id) {
   int i, n, size, new_id;
   char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
           encoding[80], locked_by[256], draft[256];
   char list[MAX_N_ATTR][NAME_LENGTH], str[256];
   char att_file[MAX_ATTACHMENTS][256];

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

   /* submit as new message */
   date[0] = 0;
   new_id = el_submit(lbs, 0, FALSE, date, attr_list, attrib, lbs->n_attr, text, in_reply_to, reply_to,
                      encoding, att_file, FALSE, locked_by, draft);

   xfree(text);

   /* correct links */
   el_correct_links(lbs, message_id, new_id);

   /* delete original message */
   el_delete_message(lbs, message_id, FALSE, NULL, FALSE, FALSE);

   /* move all replies recursively */
   if (getcfg(lbs->name, "Resubmit replies", str, sizeof(str)) && atoi(str) == 1) {
      if (reply_to[0]) {
         n = strbreak(reply_to, list, MAX_N_ATTR, ",", FALSE);
         for (i = 0; i < n; i++)
            el_move_message_thread(lbs, atoi(list[i]));
      }
   }

   return new_id;
}

Project: elog

int submit_message(LOGBOOK *lbs, char *host, int message_id, char *error_str) {
   int size, i, n, status, fh, port, sock, content_length, header_length, remote_id, n_attr, ssl;
   char str[256], file_name[MAX_PATH_LENGTH], attrib[MAX_N_ATTR][NAME_LENGTH];
   char subdir[256], param[256], remote_host_name[256], url[256], upwd[80];
   char date[80], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
           attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], encoding[80], locked_by[256], draft[256], *buffer;
   char *content, *p, boundary[80], request[10000], response[10000];
#ifdef HAVE_SSL
   SSL *ssl_con = NULL;
#else
   void *ssl_con = NULL;
#endif

   text = (char *) xmalloc(TEXT_SIZE);
   error_str[0] = 0;

   /* get message with attribute list devied from database */
   size = TEXT_SIZE;
   status = el_retrieve(lbs, message_id, date, attr_list, attrib, -1, text, &size, in_reply_to, reply_to,
                        attachment, encoding, locked_by, draft);

   if (status != EL_SUCCESS) {
      xfree(text);
      strcpy(error_str, loc("Cannot read entry from local logbook"));
      return -1;
   }

   /* count attributes */
   for (n_attr = 0; attr_list[n_attr][0]; n_attr++);

   combine_url(lbs, host, "", url, sizeof(url), &ssl);
   split_url(url, remote_host_name, &port, subdir, param);

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

   content_length = 100000;
   for (i = 0; i < MAX_ATTACHMENTS; i++)
      if (attachment[i][0]) {
         strlcpy(file_name, lbs->data_dir, sizeof(file_name));
         generate_subdir_name(attachment[i], subdir, sizeof(subdir));
         strlcat(file_name, subdir, sizeof(file_name));
         strlcat(file_name, attachment[i], sizeof(file_name));

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

         content_length += size;
      }

   content = (char *) xmalloc(content_length);

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

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

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

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

   if (in_reply_to[0])
      sprintf(content + strlen(content),
              "%s\r\nContent-Disposition: form-data; name=\"in_reply_to\"\r\n\r\n%s\r\n", boundary,
              in_reply_to);

   if (reply_to[0])
      sprintf(content + strlen(content),
              "%s\r\nContent-Disposition: form-data; name=\"reply_to\"\r\n\r\n%s\r\n", boundary, reply_to);

   for (i = 0; i < n_attr; i++)
      sprintf(content + strlen(content), "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n",
              boundary, attr_list[i], attrib[i]);

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

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

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

   content_length = strlen(content);
   p = content + content_length;

   /* read attachments */
   for (i = 0; i < MAX_ATTACHMENTS; i++)
      if (attachment[i][0]) {
         strlcpy(file_name, lbs->data_dir, sizeof(file_name));
         generate_subdir_name(attachment[i], subdir, sizeof(subdir));
         strlcat(file_name, subdir, sizeof(file_name));
         strlcat(file_name, attachment[i], sizeof(file_name));

         fh = open(file_name, O_RDONLY | O_BINARY);
         if (fh > 0) {
            lseek(fh, 0, SEEK_END);
            size = TELL(fh);
            lseek(fh, 0, SEEK_SET);
            buffer = (char *) xmalloc(size);
            read(fh, buffer, size);
            close(fh);

            /* submit attachment */
            sprintf(p, "Content-Disposition: form-data; name=\"attfile%d\"; filename=\"%s\"\r\n\r\n", i + 1,
                    attachment[i]);

            content_length += strlen(p);
            p += strlen(p);

            memcpy(p, buffer, size);
            p += size;
            strcpy(p, boundary);
            strcat(p, "\r\n");

            content_length += size + strlen(p);
            p += strlen(p);

            xfree(buffer);
         }
      }

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

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

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

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

   header_length = strlen(request);

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


#ifdef HAVE_SSL
   if (ssl)
      /* receive response */
      i = SSL_read(ssl_con, response, 10000);

   else
#endif
      /* receive response */
      i = recv(sock, response, 10000, 0);

   if (i < 0) {
      closesocket(sock);
      xfree(text);
      strcpy(error_str, "Cannot receive response");
      return -1;
   }

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

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

   closesocket(sock);
   remote_id = -1;

   /* check response status */
   if (strstr(response, "302 Found")) {
      if (strstr(response, "Location:")) {
         if (strstr(response, "fail"))
            sprintf(error_str, "Invalid user name or password\n");

         strlcpy(str, strstr(response, "Location:") + 9, sizeof(str));
         if (strchr(str, '\n'))
            *strchr(str, '\n') = 0;
         if (strchr(str, '?'))
            *strchr(str, '?') = 0;

         if (strrchr(str, '/'))
            remote_id = atoi(strrchr(str, '/') + 1);
         else
            remote_id = atoi(str);
      }
   } else if (strstr(response, "Logbook Selection"))
      sprintf(error_str, "No logbook specified\n");
   else if (strstr(response, "enter password"))
      sprintf(error_str, "Missing or invalid password\n");
   else if (strstr(response, "form name=form1"))
      sprintf(error_str, "Missing or invalid user name/password\n");
   else if (strstr(response, "Error: Attribute")) {
      strncpy(str, strstr(response, "Error: Attribute") + 20, sizeof(str));
      if (strchr(str, '<'))
         *strchr(str, '<') = 0;
      sprintf(error_str, "Missing required attribute \"%s\"\n", str);
   } else
      sprintf(error_str, "Error transmitting message\n");

   if (error_str[0] && isparam("debug"))
      rsputs(response);

   xfree(text);

   if (error_str[0])
      return -1;

   return remote_id;
}

Project: elog

int check_language() {
   char language[256], file_name[256], *p;
   int fh, length, n;
   struct stat cfg_stat;

   getcfg("global", "Language", language, sizeof(language));

   /* set locale for strftime */
   if (language[0])
      setlocale(LC_ALL, language);
   else
      setlocale(LC_ALL, "english");

   /* force re-read configuration file if changed */
   strlcpy(file_name, resource_dir, sizeof(file_name));
   strlcat(file_name, "resources", sizeof(file_name));
   strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
   strlcat(file_name, "eloglang.", sizeof(file_name));
   strlcat(file_name, language, sizeof(file_name));

   if (stat(file_name, &cfg_stat) == 0) {
      if (_locfile_mtime != cfg_stat.st_mtime) {
         _locfile_mtime = cfg_stat.st_mtime;

         if (_locbuffer) {
            xfree(_locbuffer);
            _locbuffer = NULL;
         }
      }
   }

   if (strieq(language, "english") || language[0] == 0) {
      if (_locbuffer) {
         xfree(_locbuffer);
         _locbuffer = NULL;
      }
   } else {
      if (_locbuffer == NULL) {
         fh = open(file_name, O_RDONLY | O_BINARY);
         if (fh < 0)
            return -1;

         length = lseek(fh, 0, SEEK_END);
         lseek(fh, 0, SEEK_SET);
         _locbuffer = xmalloc(length + 1);
         read(fh, _locbuffer, length);
         _locbuffer[length] = 0;
         close(fh);

         /* scan lines, setup orig-translated pointers */
         p = _locbuffer;
         n = 0;
         do {
            while (*p && (*p == '\r' || *p == '\n'))
               p++;

            if (*p && (*p == ';' || *p == '#' || *p == ' ' || *p == '\t')) {
               while (*p && *p != '\n' && *p != '\r')
                  p++;
               continue;
            }

            if (n == 0) {
               _porig = xmalloc(sizeof(char *) * 2);
               _ptrans = xmalloc(sizeof(char *) * 2);
            } else {
               _porig = xrealloc(_porig, sizeof(char *) * (n + 2));
               _ptrans = xrealloc(_ptrans, sizeof(char *) * (n + 2));
            }

            _porig[n] = p;
            while (*p && (*p != '=' && *p != '\r' && *p != '\n'))
               p++;

            if (*p && *p != '=')
               continue;

            _ptrans[n] = p + 1;
            while (*_ptrans[n] == ' ' || *_ptrans[n] == '\t')
               _ptrans[n]++;

            /* remove '=' and surrounding blanks */
            while (*p == '=' || *p == ' ' || *p == '\t')
               *p-- = 0;

            p = _ptrans[n];
            while (*p && *p != '\n' && *p != '\r')
               p++;

            if (p)
               *p++ = 0;

            n++;
         } while (p && *p);

         _porig[n] = NULL;
         _ptrans[n] = NULL;
      }
   }

   return 0;
}

Project: elog

int show_download_page(LOGBOOK *lbs, char *path) {
   char file_name[256], error_str[256];
   int index, message_id, fh, i, size, delta;
   char message[TEXT_SIZE + 1000], *p, *buffer;

   if (stricmp(path, "gbl") == 0) {

      /* return complete config file */
      load_config_section(NULL, &buffer, error_str);
      if (error_str[0]) {
         rsprintf("<h2>%s</h2>", error_str);
         return EL_FILE_ERROR;
      }

      size = strlen(buffer);
      strlcpy(message, buffer, sizeof(message));
      xfree(buffer);

   } else {

      message_id = atoi(path);

      if (message_id == 0) {

         /* return config */
         load_config_section(lbs->name, &buffer, error_str);
         if (error_str[0]) {
            rsprintf("<h2>%s</h2>", error_str);
            return EL_FILE_ERROR;
         }

         size = strlen(buffer);
         strlcpy(message, buffer, sizeof(message));
         xfree(buffer);

      } else {

         /* return entry */

         if (message_id == -1)
            index = *lbs->n_el_index - 1;  // last entry
         else {
            for (index = 0; index < *lbs->n_el_index; index++)
               if (lbs->el_index[index].message_id == message_id)
                  break;
         }

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

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

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

         message[i] = 0;
         close(fh);

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

         message[size] = 0;
      }
   }

   show_plain_header(size, "export.txt");

   /* increase return buffer size if file too big */
   if (size + 1 >= return_buffer_size - (int) strlen(return_buffer)) {
      delta = size - (return_buffer_size - strlen(return_buffer)) + 1000;

      return_buffer = (char *) xrealloc(return_buffer, return_buffer_size + delta);
      memset(return_buffer + return_buffer_size, 0, delta);
      return_buffer_size += delta;
   }

   return_length = strlen(return_buffer) + size;
   strlcat(return_buffer, message, return_buffer_size);

   return EL_SUCCESS;
}

Project: elog

void strsubst_list(char *string, int size, char name[][NAME_LENGTH], char value[][NAME_LENGTH], int n)
/* subsitute "$name" with value corresponding to name */
{
   int i, j;
   char tmp[2 * NAME_LENGTH], str[2 * NAME_LENGTH], uattr[2 * NAME_LENGTH], *ps, *pt, *p, result[10000];

   pt = tmp;
   ps = string;
   for (p = strchr(ps, '$'); p != NULL; p = strchr(ps, '$')) {

      /* copy leading characters */
      j = (int) (p - ps);
      if (j >= (int) sizeof(tmp))
         return;
      memcpy(pt, ps, j);
      pt += j;
      p++;

      /* extract name */
      strlcpy(str, p, sizeof(str));
      for (j = 0; j < (int) strlen(str); j++)
         str[j] = toupper(str[j]);
      /* do shell substituion at the end, so that shell parameter can
         contain substituted attributes */
      if (strncmp(str, "SHELL(", 6) == 0) {
         strlcpy(pt, "$shell(", sizeof(tmp) - (pt - tmp));
         ps += 7;
         pt += 7;
         continue;
      }

      /* search name */
      for (i = 0; i < n; i++) {
         strlcpy(uattr, name[i], sizeof(uattr));
         for (j = 0; j < (int) strlen(uattr); j++)
            uattr[j] = toupper(uattr[j]);

         if (strncmp(str, uattr, strlen(uattr)) == 0)
            break;
      }

      /* copy value */
      if (i < n) {
         strlcpy(pt, value[i], sizeof(tmp) - (pt - tmp));
         pt += strlen(pt);
         ps = p + strlen(uattr);
      } else {
         *pt++ = '$';
         ps = p;
      }
   }

   /* copy remainder */
   strlcpy(pt, ps, sizeof(tmp) - (pt - tmp));
   strlcpy(string, tmp, size);

   /* check for $shell() subsitution */
   pt = tmp;
   ps = string;
   for (p = strchr(ps, '$'); p != NULL; p = strchr(ps, '$')) {

      /* copy leading characters */
      j = (int) (p - ps);
      if (j >= (int) sizeof(tmp))
         return;
      memcpy(pt, ps, j);
      pt += j;
      p++;

      /* extract name */
      strlcpy(str, p, sizeof(str));
      for (j = 0; j < (int) strlen(str); j++)
         str[j] = toupper(str[j]);

      if (strncmp(str, "SHELL(", 6) == 0) {
         ps += 7;
         if (strrchr(p, '\"')) {
            ps += strrchr(p, '\"') - p - 5;
            if (strchr(ps, ')'))
               ps = strchr(ps, ')') + 1;
         } else {
            if (strchr(ps, ')'))
               ps = strchr(ps, ')') + 1;
         }

         if (str[6] == '"') {
            strcpy(str, p + 7);
            if (strrchr(str, '\"'))
               *strrchr(str, '\"') = 0;
         } else {
            strcpy(str, p + 6);
            if (strrchr(str, ')'))
               *strrchr(str, ')') = 0;
         }

         if (!enable_execute) {
            strlcpy(result, loc("Shell execution not enabled via -x flag"), sizeof(result));
            eprintf("Shell execution not enabled via -x flag.\n");
         } else
            my_shell(str, result, sizeof(result));

         strlcpy(pt, result, sizeof(tmp) - (pt - tmp));
         pt += strlen(pt);
      } else {
         *pt++ = '$';
         ps = p;
      }
   }

   /* copy remainder */
   strlcpy(pt, ps, sizeof(tmp) - (pt - tmp));

   /* return result */
   strlcpy(string, tmp, size);
}

Project: elog

BOOL equal_md5(unsigned char m1[16], unsigned char m2[16]) {
   int i;
   for (i = 0; i < 16; i++)
      if (m1[i] != m2[i])
         break;

   return i == 16;
}

Project: elog

int load_md5(LOGBOOK *lbs, char *server, MD5_INDEX **md5_index) {
   char str[256], url[256], file_name[256], *p;
   int i, j, x;
   FILE *f;

   *md5_index = NULL;

   combine_url(lbs, server, "", url, sizeof(url), NULL);
   url_decode(url);
   if (strstr(url, "http://"))
      strlcpy(str, url + 7, sizeof(str));
   else if (strstr(url, "https://"))
      strlcpy(str, url + 8, sizeof(str));
   else
      strlcpy(str, url, sizeof(str));

   for (i = 0; i < (int) strlen(str); i++)
      if (strchr(":/\\ ", str[i]))
         str[i] = '_';

   while (str[strlen(str) - 1] == '_')
      str[strlen(str) - 1] = 0;

   strlcpy(file_name, logbook_dir, sizeof(file_name));
   strlcat(file_name, str, sizeof(file_name));
   strlcat(file_name, ".md5", sizeof(file_name));

   f = fopen(file_name, "rt");
   if (f == NULL)
      return 0;

   for (i = 0; !feof(f); i++) {
      str[0] = 0;
      fgets(str, sizeof(str), f);
      if (!str[0])
         break;

      if (i == 0)
         *md5_index = (MD5_INDEX *) xcalloc(sizeof(MD5_INDEX), 1);
      else
         *md5_index = (MD5_INDEX *) xrealloc(*md5_index, sizeof(MD5_INDEX) * (i + 1));

      p = str + 2;

      (*md5_index)[i].message_id = atoi(p);
      while (*p && *p != ' ')
         p++;
      while (*p && *p == ' ')
         p++;

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

   fclose(f);
   return i;
}

Project: elog

void base64_decode(char *s, char *d) {
   unsigned int t;

   while (s && *s) {
      t = cind(*s) << 18;
      s++;
      t |= cind(*s) << 12;
      s++;
      t |= cind(*s) << 6;
      s++;
      t |= cind(*s) << 0;
      s++;

      *(d + 2) = (char) (t & 0xFF);
      t >>= 8;
      *(d + 1) = (char) (t & 0xFF);
      t >>= 8;
      *d = (char) (t & 0xFF);

      d += 3;
   }
   *d = 0;
}

Project: elog

void free_config() {
   int i, j;

   for (i = 0; i < n_lb_config; i++) {
      for (j = 0; j < lb_config[i].n_params; j++) {
         xfree(lb_config[i].config_param[j].param);
         xfree(lb_config[i].config_param[j].uparam);
         xfree(lb_config[i].config_param[j].value);
      }
      if (lb_config[i].config_param)
         xfree(lb_config[i].config_param);
      xfree(lb_config[i].section_name);
   }
   xfree(lb_config);
   lb_config = NULL;
   n_lb_config = 0;
}

Project: elog

void show_new_user_page(LOGBOOK *lbs, char *user) {
   char str[256];

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

   show_html_header(lbs, TRUE, loc("ELOG new user"), TRUE, FALSE, NULL, FALSE, 0);
   rsprintf("<body><center><br><br>\n");
   show_top_text(lbs);
   rsprintf("<form name=\"form1\" id=\"form1\" method=\"GET\" action=\".\">\n\n");

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

   if (lbs)
      show_standard_title(lbs, "", 1);
   else
      show_standard_title(NULL, "", 1);

   /* table for two-column items */
   rsprintf("<tr><td class=\"form2\">");
   rsprintf("<table width=\"100%%\" cellspacing=0>\n");

   /*---- entry form ----*/

   rsprintf("<tr><td nowrap>%s:</td>\n", loc("Login name"));
   if (user && user[0]) {
      strencode2(str, user, sizeof(str));
      rsprintf("<td><input type=text size=40 name=new_user_name value=\"%s\" readonly></td>\n", str);
      rsprintf("<td>&nbsp;</td>\n");
   } else {
      rsprintf("<td><input type=text size=40 name=new_user_name></td>\n");
      rsprintf("<td nowrap align=left><font size=2><i>(%s)</i></font></td></tr>\n",
               loc("name may not contain blanks"));
   }

   rsprintf("<tr><td nowrap>%s:</td>\n", loc("Full name"));
   rsprintf("<td colspan=2><input type=text size=40 name=new_full_name></tr>\n");

   rsprintf("<tr><td nowrap>Email:</td>\n");
   rsprintf("<td colspan=2><input type=text size=40 name=new_user_email></tr>\n");
   getcfg(lbs->name, "Authentication", str, sizeof(str));
   if (!stristr(str, "Kerberos") && !stristr(str, "Webserver") && !stristr(str, "PAM")) {
      rsprintf("<tr><td nowrap>%s:</td>\n", loc("Password"));
      rsprintf("<td colspan=2><input type=password size=40 name=newpwd>\n");

      rsprintf("<tr><td nowrap>%s:</td>\n", loc("Retype password"));
      rsprintf("<td colspan=2><input type=password size=40 name=newpwd2>\n");
   }
   rsprintf("</td></tr></table>\n");

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

   rsprintf("<tr><td class=\"menucenter\">\n");
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Save"));
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
   rsprintf("</td></tr>\n\n");

   rsprintf("</td></tr></table>\n\n");
   rsprintf("</form></center></body></html>\r\n");
}

Project: elog

void call_image_magick(LOGBOOK *lbs) {
   char str[1024], cmd[1024], file_name[256], thumb_name[256], subdir[256];
   int cur_width, cur_height, new_size, cur_rot, new_rot, thumb_status;

   if (!isparam("req") || !isparam("img")) {
      show_error("Unknown IM request received");
      return;
   }

   strlcpy(file_name, lbs->data_dir, sizeof(file_name));
   generate_subdir_name(getparam("img"), subdir, sizeof(subdir));
   strlcat(file_name, subdir, sizeof(file_name));
   strlcat(file_name, getparam("img"), sizeof(file_name));
   thumb_status = get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0);

   sprintf(cmd, "%s -format '%%wx%%h %%c' '%s'", _identify_cmd, thumb_name);
#ifdef OS_WINNT
                                                                                                                           {
   int i;
   for (i = 0; i < (int) strlen(cmd); i++)
      if (cmd[i] == '\'')
         cmd[i] = '\"';
   }
#endif
   my_shell(cmd, str, sizeof(str));

   if (atoi(str) > 0) {
      cur_width = atoi(str);
      if (strchr(str, 'x')) {
         cur_height = atoi(strchr(str, 'x') + 1);
      } else
         cur_height = cur_width;
      if (strchr(str, ' ')) {
         cur_rot = atoi(strchr(str, ' ') + 1);
      } else
         cur_rot = 0;
   } else {
      show_http_header(NULL, FALSE, NULL);
      rsputs(str);
      return;
   }

   if (thumb_status == 2)
      strsubst(thumb_name, sizeof(thumb_name), "-0", "-%d");

   cmd[0] = 0;
   if (strieq(getparam("req"), "rotleft")) {
      new_rot = (cur_rot + 360 - 90) % 360;
      sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name,
              new_rot,
              cur_height, new_rot, thumb_name);
   }

   if (strieq(getparam("req"), "rotright")) {
      new_rot = (cur_rot + 90) % 360;
      sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name,
              new_rot,
              cur_height, new_rot, thumb_name);
   }

   if (strieq(getparam("req"), "original")) {
      new_size = (int) (cur_width / 1.5);
      sprintf(cmd, "%s '%s' '%s'", _convert_cmd, file_name, thumb_name);
   }

   if (strieq(getparam("req"), "smaller")) {
      new_size = (int) (cur_width / 1.5);
      sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name,
              cur_rot,
              new_size, cur_rot, thumb_name);
   }

   if (strieq(getparam("req"), "larger")) {
      new_size = (int) (cur_width * 1.5);
      sprintf(cmd, "%s '%s' -rotate %d -thumbnail %d -set comment ' %d' '%s'", _convert_cmd, file_name,
              cur_rot,
              new_size, cur_rot, thumb_name);
   }

   if (cmd[0]) {
#ifdef OS_WINNT
                                                                                                                              int i;
      for (i = 0; i < (int) strlen(cmd); i++)
         if (cmd[i] == '\'')
            cmd[i] = '\"';
#endif
      my_shell(cmd, str, sizeof(str));
      show_http_header(NULL, TRUE, NULL);
      rsputs(str);
   }
   return;
}

Project: elog

int parse_file(LOGBOOK *lbs, char *file_name) {
   char str[256], date[256], *buffer, *p, *pn, in_reply_to[80];
   int length, i, fh, len;

   fh = open(file_name, O_RDONLY | O_BINARY, 0644);

   if (fh < 0) {
      sprintf(str, "Cannot open file \"%s\"", file_name);
      eprintf("%s; %s\n", str, strerror(errno));
      return EL_FILE_ERROR;
   }

   /* read file into buffer */
   length = lseek(fh, 0, SEEK_END);

   if (length > 0) {
      buffer = xmalloc(length + 1);
      lseek(fh, 0, SEEK_SET);
      read(fh, buffer, length);
      buffer[length] = 0;
      close(fh);

      /* go through buffer */
      p = buffer;

      do {
         p = strstr(p, "$@MID@$:");

         if (p) {
            lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index + 1));
            if (lbs->el_index == NULL) {
               eprintf("Not enough memory to allocate entry index\n");
               return EL_MEM_ERROR;
            }

            strlcpy(lbs->el_index[*lbs->n_el_index].subdir, file_name + strlen(lbs->data_dir), 256);
            if (strrchr(lbs->el_index[*lbs->n_el_index].subdir, DIR_SEPARATOR))
               *(strrchr(lbs->el_index[*lbs->n_el_index].subdir, DIR_SEPARATOR) + 1) = 0;

            if (strrchr(file_name, DIR_SEPARATOR))
               strlcpy(str, strrchr(file_name, DIR_SEPARATOR) + 1, sizeof(str));
            else
               strlcpy(str, file_name, sizeof(str));
            strcpy(lbs->el_index[*lbs->n_el_index].file_name, str);

            el_decode(p, "Date: ", date, sizeof(date));
            el_decode_int(p, "In reply to: ", in_reply_to, sizeof(in_reply_to));

            lbs->el_index[*lbs->n_el_index].file_time = date_to_ltime(date);

            lbs->el_index[*lbs->n_el_index].message_id = atoi(p + 8);
            lbs->el_index[*lbs->n_el_index].offset = p - buffer;
            lbs->el_index[*lbs->n_el_index].in_reply_to = atoi(in_reply_to);

            pn = strstr(p + 8, "$@MID@$:");
            if (pn)
               len = pn - p;
            else
               len = strlen(p);

            MD5_checksum(p, len, lbs->el_index[*lbs->n_el_index].md5_digest);

            if (lbs->el_index[*lbs->n_el_index].message_id > 0) {
               if (get_verbose() == VERBOSE_DEBUG) {
                  eprintf("  ID %3d, %s, ofs %5d, %s, MD5=", lbs->el_index[*lbs->n_el_index].message_id,
                          str, lbs->el_index[*lbs->n_el_index].offset,
                          lbs->el_index[*lbs->n_el_index].in_reply_to ? "reply" : "thead");

                  for (i = 0; i < 16; i++)
                     eprintf("%02X", lbs->el_index[*lbs->n_el_index].md5_digest[i]);
                  eprintf("\n");
               }

               /* valid ID */
               (*lbs->n_el_index)++;
            }

            p += 8;
         }

      } while (p);

      xfree(buffer);
   }

   return SUCCESS;
}

Project: elog

void print_syslog(const char *msg) {
   char *p;

   /* strip trailing \r and \n */
   p = xstrdup(msg);
   while (p[strlen(p) - 1] == '\r' || p[strlen(p) - 1] == '\n')
      p[strlen(p) - 1] = 0;

#ifdef OS_UNIX
   syslog(SYSLOG_PRIORITY, "%s", p);
#else
   ReportEvent(hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, &p, NULL);
#endif

   xfree(p);
}

Project: elog

time_t search_last_reply(LOGBOOK *lbs, int *message_id) {
   char reply_to[MAX_REPLY_TO * 10], date[80];
   int n_reply, i, id;
   char *list;
   time_t lt, last;

   list = (char *) xmalloc(MAX_REPLY_TO * NAME_LENGTH);

   el_retrieve(lbs, *message_id, date, NULL, NULL, 0, NULL, 0, NULL, reply_to, NULL, NULL, NULL, NULL);
   lt = date_to_ltime(date);

   /* if no reply, this is the last message in thread */
   if (reply_to[0] == 0) {
      xfree(list);
      return lt;
   }

   n_reply = strbreak(reply_to, (char (*)[NAME_LENGTH]) list, MAX_REPLY_TO, ",", FALSE);
   last = lt;
   for (i = 0; i < n_reply; i++) {
      id = atoi(list + i * NAME_LENGTH);
      lt = search_last_reply(lbs, &id);
      if (lt > last) {
         last = lt;
         *message_id = id;
      }
   }

   xfree(list);

   return last;
}

Project: elog

int find_thread_head(LOGBOOK *lbs, int message_id) {
   int i;

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

   if (lbs->el_index[i].in_reply_to)
      return find_thread_head(lbs, lbs->el_index[i].in_reply_to);

   return message_id;
}

Project: elog

int el_retrieve_attachment(LOGBOOK *lbs, int message_id, int n, char name[MAX_PATH_LENGTH]) {
   int i, index, size, fh;
   char file_name[MAX_PATH_LENGTH * 3], *p;
   char message[TEXT_SIZE + 1000], attachment_all[64 * MAX_ATTACHMENTS];

   if (message_id == 0)
      return EL_EMPTY;

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

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

   snprintf(file_name, sizeof(file_name), "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir,
            lbs->el_index[index].file_name);
   fh = open(file_name, O_RDONLY | O_BINARY, 0644);
   if (fh < 0) {
      /* file might have been deleted, rebuild index */
      el_build_index(lbs, TRUE);
      return el_retrieve_attachment(lbs, message_id, n, name);
   }

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

   message[i] = 0;
   close(fh);

   if (strncmp(message, "$@MID@$:", 8) != 0) {
      /* file might have been edited, rebuild index */
      el_build_index(lbs, TRUE);
      return el_retrieve_attachment(lbs, message_id, n, name);
   }

   /* check for correct ID */
   if (atoi(message + 8) != message_id)
      return EL_FILE_ERROR;

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

   message[size] = 0;

   el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));

   name[0] = 0;

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

      if (p == NULL)
         break;
   }

   if (p)
      strlcpy(name, p, MAX_PATH_LENGTH);

   return EL_SUCCESS;
}

Project: elog

void compose_email_header(LOGBOOK *lbs, char *subject, char *from, char *to, char *url, char *mail_text,
                          int size, int mail_encoding, int n_attachments, char *multipart_boundary,
                          int message_id, int reply_id) {
   char buffer[256], charset[256], subject_enc[5000];
   char buf[80], str[256];
   int i, offset, multipart;
   time_t now;
   struct tm *ts;

   i = 0;
   if (mail_encoding & 1)
      i++;
   if (mail_encoding & 2)
      i++;
   if (mail_encoding & 4)
      i++;
   multipart = i > 1;

   if (!getcfg("global", "charset", charset, sizeof(charset)))
      strcpy(charset, DEFAULT_HTTP_CHARSET);

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

   time(&now);
   ts = localtime(&now);
   assert(ts);
   strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S", ts);
   offset = (-(int) my_timezone());
   if (ts->tm_isdst)
      offset += 3600;
   if (get_verbose() >= VERBOSE_INFO) {
      sprintf(str, "timezone: %d, offset: %d\n", (int) my_timezone(), (int) offset);
      efputs(str);
   }
   snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Date: %s %+03d%02d\r\n", buf,
            (int) (offset / 3600), (int) ((abs((int) offset) / 60) % 60));

   getcfg("global", "Language", str, sizeof(str));
   if (str[0])
      setlocale(LC_ALL, str);

   strlcat(mail_text, "To: ", size);

   if (getcfg(lbs->name, "Omit Email to", str, sizeof(str)) && atoi(str) == 1)
      strlcat(mail_text, "ELOG", size);
   else
      strlcat(mail_text, to, size);

   strlcat(mail_text, "\r\n", size);

   snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "From: %s\r\n", from);
   snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "User-Agent: Elog Version %s\r\n",
            VERSION);

   strlcat(mail_text, "MIME-Version: 1.0\r\n", size);

   memset(subject_enc, 0, sizeof(subject_enc));

   for (i = 0; i < (int) strlen(subject); i++)
      if (subject[i] < 0)
         break;

   if (i < (int) strlen(subject)) {
      /* subject contains local characters, so encode it using charset */
      for (i = 0; i < (int) strlen(subject); i += 40) {
         strlcpy(buffer, subject + i, sizeof(buffer));
         buffer[40] = 0;
         strlcat(subject_enc, "=?", sizeof(subject_enc));
         strlcat(subject_enc, charset, sizeof(subject_enc));
         strlcat(subject_enc, "?B?", sizeof(subject_enc));
         base64_encode((unsigned char *) buffer, (unsigned char *) (subject_enc + strlen(subject_enc)),
                       sizeof(subject_enc) - strlen(subject_enc));
         strlcat(subject_enc, "?=", sizeof(subject_enc));
         if (strlen(subject + i) < 40)
            break;

         strlcat(subject_enc, "\r\n ", sizeof(subject_enc));    // another encoded-word
      }
   } else
      strlcpy(subject_enc, subject, sizeof(subject_enc));

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

   if (strchr(from, '@')) {
      strlcpy(str, strchr(from, '@') + 1, sizeof(str));
      if (strchr(str, '>'))
         *strchr(str, '>') = 0;
   } else
      strlcpy(str, "elog.org", sizeof(str));

   if (message_id)
      snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "Message-ID: <%s-%d@%s>\r\n",
               lbs->name_enc, message_id, str);
   if (reply_id)
      snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "In-Reply-To: <%s-%d@%s>\r\n",
               lbs->name_enc, reply_id, str);

   if (url)
      snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1, "X-Elog-URL: %s\r\n", url);

   strlcat(mail_text, "X-Elog-submit-type: web|elog\r\n", size);

   if (multipart) {

      sprintf(multipart_boundary, "------------%04X%04X%04X", rand(), rand(), rand());
      snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
               "MIME-Version: 1.0\r\nContent-Type: multipart/alternative;\r\n boundary=\"%s\"\r\n\r\n",
               multipart_boundary);

      strlcat(mail_text, "This is a multi-part message in MIME format.\r\n", size);
   } else {
      if (n_attachments) {
         sprintf(multipart_boundary, "------------%04X%04X%04X", rand(), rand(), rand());
         snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
                  "MIME-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n boundary=\"%s\"\r\n\r\n",
                  multipart_boundary);

         strlcat(mail_text, "This is a multi-part message in MIME format.\r\n", size);
      } else {
         if (multipart_boundary)
            multipart_boundary[0] = 0;
      }
   }
}

Project: elog

void format_email_html2(LOGBOOK *lbs, int message_id, char att_file[MAX_ATTACHMENTS][256], int old_mail,
                        char *multipart_boundary, char *mail_text, int size, int flags) {
   char str[256], charset[256], multipart_boundary_related[256], command[256], *p;
   int max_att_size;

   sprintf(str, "%d", message_id);
   if (!getcfg("global", "charset", charset, sizeof(charset)))
      strcpy(charset, DEFAULT_HTTP_CHARSET);

   if (multipart_boundary[0]) {
      strlcat(mail_text, "--", size);
      strlcat(mail_text, multipart_boundary, size);
      strlcat(mail_text, "\r\n", size);
   }

   max_att_size = max_attachment_size(lbs, message_id);

   if (max_att_size && (flags & 16) > 0) {
      sprintf(multipart_boundary_related, "------------%04X%04X%04X", rand(), rand(), rand());
      snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
               "MIME-Version: 1.0\r\nContent-Type: multipart/related;\r\n boundary=\"%s\"\r\n\r\n",
               multipart_boundary_related);
      strlcat(mail_text, "--", size);
      strlcat(mail_text, multipart_boundary_related, size);
      strlcat(mail_text, "\r\n", size);
   }

   snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
            "Content-Type: text/html; charset=\"%s\"\r\n", charset);
   snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
            "Content-Transfer-Encoding: 7bit\r\n\r\n");

   strlen_retbuf = 0;
   if (old_mail)
      strcpy(command, "oldemail");
   else
      strcpy(command, "email");

   if ((flags & 64) > 0)
      strlcat(command, "-att-links", sizeof(command));

   show_elog_entry(lbs, str, command);

   p = strstr(return_buffer, "\r\n\r\n");
   if (p)
      strlcpy(mail_text + strlen(mail_text), p + 4, size - strlen(mail_text));
   strlen_retbuf = 0;
   strlcat(mail_text, "\r\n", size);

   if (max_att_size && (flags & 16) > 0) {
      format_email_attachments(lbs, message_id, 2, att_file, mail_text, size, multipart_boundary_related,
                               TRUE);
      strlcat(mail_text, "--", size);
      strlcat(mail_text, multipart_boundary_related, size);
      strlcat(mail_text, "--\r\n\r\n", size);
   }
}

Project: elog

BOOL get_author(LOGBOOK *lbs, char attrib[MAX_N_ATTR][NAME_LENGTH], char *author) {
   char str[NAME_LENGTH], preset[NAME_LENGTH];
   int i;

   /* search attribute which contains full_name of author */
   for (i = 0; i < lbs->n_attr; i++) {
      sprintf(str, "Preset %s", attr_list[i]);
      if (getcfg(lbs->name, str, preset, sizeof(preset))) {
         if (stristr(preset, "$long_name")) {
            strcpy(author, attrib[i]);
            return TRUE;
         }
      }
   }

   /* if not found, search attribute which contains short_name of author */
   for (i = 0; i < lbs->n_attr; i++) {
      sprintf(str, "Preset %s", attr_list[i]);
      if (getcfg(lbs->name, str, preset, sizeof(preset))) {
         if (stristr(preset, "$short_name")) {
            strcpy(author, attrib[i]);
            return TRUE;
         }
      }
   }

   return FALSE;
}

Project: elog

void retrieve_email_from(LOGBOOK *lbs, char *ret, char *ret_name, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
   char email_from[256], email_from_name[256], str[256], *p, login_name[256],
           slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH],
           full_name[256], user_email[256];
   int i;

   if (getcfg(lbs->name, "Use Email from", str, sizeof(str))) {
      if (str[0] != '<') {
         strlcpy(email_from, "<", sizeof(email_from));
         strlcat(email_from, str, sizeof(email_from));
         strlcat(email_from, ">", sizeof(email_from));
      } else
         strlcpy(email_from, str, sizeof(email_from));
      strlcpy(email_from_name, str, sizeof(email_from));
   } else if (isparam("unm")) {
      get_user_line(lbs, getparam("unm"), NULL, full_name, user_email, NULL, NULL, NULL);
      strlcpy(email_from_name, full_name, sizeof(email_from_name));
      strlcat(email_from_name, " <", sizeof(email_from_name));
      strlcat(email_from_name, user_email, sizeof(email_from_name));
      strlcat(email_from_name, ">", sizeof(email_from_name));
      strlcpy(email_from, "<", sizeof(email_from));
      strlcat(email_from, user_email, sizeof(email_from));
      strlcat(email_from, ">", sizeof(email_from));
   } else if (getcfg(lbs->name, "Default Email from", str, sizeof(str))) {
      if (str[0] != '<') {
         strlcpy(email_from, "<", sizeof(email_from));
         strlcat(email_from, str, sizeof(email_from));
         strlcat(email_from, ">", sizeof(email_from));
      } else
         strlcpy(email_from, str, sizeof(email_from));
      strlcpy(email_from_name, str, sizeof(email_from));
   } else {
      sprintf(email_from_name, "ELog <ELog@%s>", host_name);
      sprintf(email_from, "<ELog@%s>", host_name);
   }

   if (attrib) {
      i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
      strsubst_list(email_from_name, sizeof(email_from_name), slist, svalue, i);
      strsubst_list(email_from, sizeof(email_from), slist, svalue, i);

      /* remove possible 'mailto:' */
      if ((p = strstr(email_from_name, "mailto:")) != NULL)
         memmove(p, p + 7, strlen(p + 7) + 1);
      if ((p = strstr(email_from, "mailto:")) != NULL)
         memmove(p, p + 7, strlen(p + 7) + 1);
   }

   /* if nothing available, figure out email from an administrator */
   if (strchr(email_from, '@') == NULL) {
      for (i = 0;; i++) {
         if (!enum_user_line(lbs, i, login_name, sizeof(login_name)))
            break;
         get_user_line(lbs, login_name, NULL, NULL, email_from, NULL, NULL, NULL);
         sprintf(email_from_name, "%s <%s>", login_name, email_from);
         if (is_admin_user(lbs, login_name) && strchr(email_from, '@'))
            break;
      }
   }

   if (ret)
      strcpy(ret, email_from);
   if (ret_name)
      strcpy(ret_name, email_from_name);
}

Project: elog

int ssl_connect(int sock, SSL **ssl_con) {
   SSL_METHOD *meth;
   SSL_CTX *ctx;
   X509 *cert = NULL;
   int i;

   SSL_library_init();
   SSL_load_error_strings();

   meth = (SSL_METHOD *) TLSv1_2_method();
   ctx = SSL_CTX_new(meth);

   *ssl_con = SSL_new(ctx);
   SSL_set_fd(*ssl_con, sock);
   if (SSL_connect(*ssl_con) <= 0)
      return -1;

   cert = SSL_get_peer_certificate(*ssl_con);
   if (cert == NULL)
      return -1;

   i = SSL_get_verify_result(*ssl_con);
   if (i != X509_V_OK)
      printf("Possibly invalid certificate, continue on your own risk!\n");

   return 0;
}

Project: elog

int change_config_line(LOGBOOK *lbs, char *option, char *old_value, char *new_value) {
   int fh, i, j, n, length, bufsize;
   char str[NAME_LENGTH], *buf, *buf2, *p1, *p2, *p3;
   char list[MAX_N_LIST][NAME_LENGTH], line[NAME_LENGTH];

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

   /* read previous contents */
   length = lseek(fh, 0, SEEK_END);
   lseek(fh, 0, SEEK_SET);
   bufsize = 2 * (length + strlen(new_value) + 10);
   buf = xmalloc(bufsize);
   read(fh, buf, length);
   buf[length] = 0;

   /* find location of option */
   p1 = (char *) find_param(buf, lbs->name, option);
   if (p1 == NULL)
      return 0;

   p2 = strchr(p1, '=');
   if (p2 == 0)
      return 0;

   p2++;
   while (*p2 == ' ' || *p2 == '\t')
      p2++;

   strlcpy(line, p2, sizeof(line));
   if (strchr(line, '\r'))
      *strchr(line, '\r') = 0;
   if (strchr(line, '\n'))
      *strchr(line, '\n') = 0;
   n = strbreak(line, list, MAX_N_LIST, ",", FALSE);

   /* save tail */
   p3 = strchr(p2, '\n');
   if (p3 && *(p3 - 1) == '\r')
      p3--;

   buf2 = NULL;
   if (p3)
      buf2 = xstrdup(p3);

   if (old_value[0]) {
      for (i = 0; i < n; i++) {
         if (strieq(old_value, list[i])) {
            if (new_value[0]) {
               /* rename value */
               strcpy(list[i], new_value);
            } else {
               /* delete value */
               for (j = i; j < n - 1; j++)
                  strcpy(list[j], list[j + 1]);
               n--;
            }
            break;
         }
      }
   } else {
      if (n < MAX_N_LIST)
         strcpy(list[n++], new_value);
   }

   /* write new option list */
   for (i = 0; i < n; i++) {
      strcpy(p2, list[i]);
      if (i < n - 1)
         strcat(p2, ", ");
      p2 += strlen(p2);
   }

   /* append tail */
   if (buf2) {
      strlcat(p2, buf2, length + strlen(new_value) + 10);
      xfree(buf2);
   }

   adjust_crlf(buf, bufsize);

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

   TRUNCATE(fh);

   close(fh);
   xfree(buf);

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

   return 1;
}

Project: elog

int activate_user(LOGBOOK *lbs, char *user_name, int code) {
   int inactive, self_register;
   char str[256], str2[256], smtp_host[256], url[256], mail_text[2000],
           error[256], mail_from_name[256], mail_from[256], user_email[256], logbook[256];

   if (lbs == NULL)
      strlcpy(logbook, "global", sizeof(logbook));
   else
      strlcpy(logbook, lbs->name, sizeof(logbook));

   get_user_line(lbs, user_name, NULL, NULL, user_email, NULL, NULL, &inactive);
   if (code != inactive) {
      show_error(loc("Invalid activation code"));
      return FALSE;
   }

   if (!set_user_inactive(lbs, user_name, 0)) {
      show_error(loc("Error activating user"));
      return FALSE;
   }

   self_register = 0;
   if (getcfg(logbook, "Self register", str, sizeof(str)))
      self_register = atoi(str);

   if (self_register == 3) {

      if (!getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) {
         show_error(loc("No SMTP host defined in [global] section of configuration file"));
         return FALSE;
      }

      /* try to get URL from referer */
      if (!getcfg("global", "URL", url, sizeof(url))) {
         if (referer[0])
            strcpy(url, referer);
         else {
            if (elog_tcp_port == 80) {
               if (_ssl_flag)
                  sprintf(url, "https://%s/", http_host);
               else
                  sprintf(url, "http://%s/", http_host);
            } else {
               if (_ssl_flag)
                  sprintf(url, "https://%s:%d/", http_host, elog_tcp_port);
               else
                  sprintf(url, "http://%s:%d/", http_host, elog_tcp_port);
            }
            if (lbs) {
               strlcat(url, lbs->name_enc, sizeof(url));
               strlcat(url, "/", sizeof(url));
            }
         }
      } else {
         if (url[strlen(url) - 1] != '/')
            strlcat(url, "/", sizeof(url));
         if (lbs) {
            strlcat(url, lbs->name_enc, sizeof(url));
            strlcat(url, "/", sizeof(url));
         }
      }

      retrieve_email_from(lbs, mail_from, mail_from_name, NULL);

      mail_text[0] = 0;
      compose_email_header(lbs, loc("Your ELOG account has been activated"), mail_from_name,
                           user_email, NULL, mail_text, sizeof(mail_text), 1, 0, NULL, 0, 0);

      strlcat(mail_text, "\r\n", sizeof(mail_text));
      strlcat(mail_text, loc("Your ELOG account has been activated on host"), sizeof(mail_text));
      sprintf(mail_text + strlen(mail_text), " %s", http_host);
      sprintf(mail_text + strlen(mail_text), ".\r\n\r\n");
      sprintf(url + strlen(url), "?unm=%s", user_name);
      sprintf(mail_text + strlen(mail_text), "%s %s.\r\n\r\n", loc("You can access it at"), url);
      sprintf(mail_text + strlen(mail_text), "%s.\r\n",
              loc("To subscribe to any logbook, click on 'Config' in that logbook"));

      if (sendmail(lbs, smtp_host, mail_from, user_email, mail_text, error, sizeof(error)) == -1) {
         sprintf(str, loc("Cannot send email notification to \"%s\""), user_email);
         strlcat(str, " : ", sizeof(str));
         strlcat(str, error, sizeof(str));
         strencode2(str2, str, sizeof(str2));
         show_error(str2);
         return FALSE;
      }
   }

   return TRUE;
}

Project: elog

void print_stderr(const char *msg) {
   fprintf(stderr, "%s", msg);
}

Project: elog

char *loc(char *orig) {
   int n;
   char language[256];
   static char result[256];

   if (!_locbuffer)
      return orig;

   /* search string and return translation */
   for (n = 0; _porig[n]; n++)
      if (strcmp(orig, _porig[n]) == 0) {
         if (*_ptrans[n])
            return _ptrans[n];
         return orig;
      }

   /* special case: "Change %s" */
   if (strstr(orig, "Change ") && strcmp(orig, "Change %s") != 0) {
      sprintf(result, loc("Change %s"), orig + 7);
      return result;
   }

   /* special case: some intrinsic commands */
   if (strstr(orig, "GetPwdFile")) {
      strcpy(result, orig);
      return result;
   }

   getcfg("global", "Language", language, sizeof(language));
   eprintf("Language error: string \"%s\" not found for language \"%s\"\n", orig, language);

   return orig;
}

Project: elog

void show_page_filters(LOGBOOK *lbs, int n_msg, int page_n, BOOL mode_commands, char *mode) {
   int cur_exp, n, i, j, i1, i2, index, attr_index, size;
   char ref[256], str[NAME_LENGTH], comment[NAME_LENGTH], list[MAX_N_LIST][NAME_LENGTH],
           option[NAME_LENGTH], option_whole[NAME_LENGTH];

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

   rsprintf("<tr>\n");

   if (mode_commands) {
      rsprintf("<td class=\"menu2a\">\n");

      if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) {
         if (page_n != 1)
            sprintf(ref, "page%d", page_n);
         else
            ref[0] = 0;
         build_ref(ref, sizeof(ref), "full", "", "", "");

         if (strieq(mode, "full"))
            rsprintf("&nbsp;%s&nbsp;|", loc("Full"));
         else
            rsprintf("&nbsp;<a href=\"%s\">%s</a>&nbsp;|", ref, loc("Full"));
      }

      if (page_n != 1)
         sprintf(ref, "page%d", page_n);
      else
         ref[0] = 0;
      build_ref(ref, sizeof(ref), "summary", "", "", "");

      if (strieq(mode, "summary"))
         rsprintf("&nbsp;%s&nbsp;|", loc("Summary"));
      else
         rsprintf("&nbsp;<a href=\"%s\">%s</a>&nbsp;|", ref, loc("Summary"));

      if (page_n != 1)
         sprintf(ref, "page%d", page_n);
      else
         ref[0] = 0;
      build_ref(ref, sizeof(ref), "threaded", "", "", "");

      if (strieq(mode, "threaded"))
         rsprintf("&nbsp;%s&nbsp;", loc("Threaded"));
      else
         rsprintf("&nbsp;<a href=\"%s\">%s</a>&nbsp;", ref, loc("Threaded"));

      if (strieq(mode, "full")) {
         if (page_n != 1)
            sprintf(ref, "page%d", page_n);
         else
            ref[0] = 0;

         cur_exp = 0;
         if (strieq(mode, "full"))
            cur_exp = 1;
         if (isparam("elattach"))
            cur_exp = atoi(getparam("elattach"));
         if (isparam("attach"))
            cur_exp = atoi(getparam("attach"));

         if (cur_exp) {
            build_ref(ref, sizeof(ref), "", "", "0", "");
            rsprintf("|&nbsp;<a href=\"%s\">%s</a>&nbsp;", ref, loc("Hide attachments"));
         } else {
            build_ref(ref, sizeof(ref), "", "", "1", "");
            rsprintf("|&nbsp;<a href=\"%s\">%s</a>&nbsp;", ref, loc("Show attachments"));
         }
      }

      if (strieq(mode, "threaded")) {
         if (page_n != 1)
            sprintf(ref, "page%d", page_n);
         else
            ref[0] = 0;

         cur_exp = 1;
         if (getcfg(lbs->name, "Expand default", str, sizeof(str)))
            cur_exp = atoi(str);
         if (isparam("expand"))
            cur_exp = atoi(getparam("expand"));

         if (cur_exp > 0) {
            sprintf(str, "%d", cur_exp > 0 ? cur_exp - 1 : 0);
            build_ref(ref, sizeof(ref), "", str, "", "");
            rsprintf("|&nbsp;<a href=\"%s\">%s</a>&nbsp;", ref, loc("Collapse"));
         } else
            rsprintf("|&nbsp;%s&nbsp;", loc("Collapse"));

         if (cur_exp < 3) {
            if (page_n != 1)
               sprintf(ref, "page%d", page_n);
            else
               ref[0] = 0;
            sprintf(str, "%d", cur_exp < 3 ? cur_exp + 1 : 3);
            build_ref(ref, sizeof(ref), "", str, "", "");
            rsprintf("|&nbsp;<a href=\"%s\">%s</a>&nbsp;", ref, loc("Expand"));
         } else
            rsprintf("|&nbsp;%s&nbsp;", loc("Expand"));
      }

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

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

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

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

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

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

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

         rsputs(buf);

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

   ref[0] = 0;
   if (!isparam("new_entries") || atoi(getparam("new_entries")) == 0) {
      build_ref(ref, sizeof(ref), "", "", "", "1");
      rsprintf
              ("<a href=\"%s\"><img align=\"middle\" border=\"0\" src=\"new_entry.png\" alt=\"%s\" title=\"%s\"></a>&nbsp;&nbsp;",
               ref, loc("Show only new entries"), loc("Show only new entries"));
   } else {
      build_ref(ref, sizeof(ref), "", "", "", "0");
      rsprintf
              ("<a href=\"%s\"><img align=\"middle\" border=\"0\" src=\"all_entry.png\" alt=\"%s\" title=\"%s\"></a>&nbsp;&nbsp;",
               ref, loc("Show all entries"), loc("Show all entries"));
   }

   if (getcfg(lbs->name, "Quick filter", str, sizeof(str))) {

      n = strbreak(str, list, MAX_N_LIST, ",", FALSE);

      if (getcfg(lbs->name, "Case sensitive search", str, sizeof(str)) && atoi(str))
         rsprintf("<input type=hidden name=\"casesensitive\" value=1>\n");

      for (index = 0; index < n; index++) {
         /* find according attribute index */
         for (attr_index = 0; attr_index < lbs->n_attr; attr_index++)
            if (strieq(list[index], attr_list[attr_index]))
               break;

         if (attr_index == lbs->n_attr && !strieq(list[index], "Date") && !strieq(list[index], "Subtext")
             && !strieq(list[index], "ID")) {
            rsprintf("Error: Attribute \"%s\" for quick filter not found", list[index]);
            attr_index = 0;
         }

         if (strieq(list[index], "Date")) {
            i = isparam("last") ? atoi(getparam("last")) : 0;

            if (i == 0 && getcfg(lbs->name, "Last default", str, sizeof(str))) {
               i = atoi(str);
               setparam("last", str);
            }

            rsprintf("<select title=\"%s\" name=last onChange=\"document.form1.submit()\">\n",
                     loc("Select period"));

            rsprintf("<option value=\"_all_\">-- %s --\n", loc("All entries"));

            rsprintf("<option %s value=1>%s\n", i == 1 ? "selected" : "", loc("Last day"));
            rsprintf("<option %s value=3>%s\n", i == 3 ? "selected" : "", loc("Last 3 Days"));
            rsprintf("<option %s value=7>%s\n", i == 7 ? "selected" : "", loc("Last week"));
            rsprintf("<option %s value=31>%s\n", i == 31 ? "selected" : "", loc("Last month"));
            rsprintf("<option %s value=92>%s\n", i == 92 ? "selected" : "", loc("Last 3 Months"));
            rsprintf("<option %s value=182>%s\n", i == 182 ? "selected" : "", loc("Last 6 Months"));
            rsprintf("<option %s value=364>%s\n", i == 364 ? "selected" : "", loc("Last Year"));

            rsprintf("</select>\n");
         } else if (strieq(attr_options[attr_index][0], "boolean")) {

            sprintf(str, loc("Select %s"), list[index]);
            rsprintf("<select title=\"%s\" name=\"%s\" onChange=\"document.form1.submit()\">\n", str,
                     list[index]);

            rsprintf("<option value=\"_all_\">-- %s --\n", list[index]);

            if (isparam(attr_list[attr_index]) && strieq("1", getparam(attr_list[attr_index])))
               rsprintf("<option selected value=\"1\">1\n");
            else
               rsprintf("<option value=\"1\">1\n");

            if (isparam(attr_list[attr_index]) && strieq("0", getparam(attr_list[attr_index])))
               rsprintf("<option selected value=\"0\">0\n");
            else
               rsprintf("<option value=\"0\">0\n");

            rsprintf("</select>\n");
         } else {

            /* check if attribute has options */
            if (attr_list[attr_index][0] == 0 || attr_options[attr_index][0][0] == 0) {

               if (attr_flags[attr_index] & (AF_DATE | AF_DATETIME)) {

                  rsprintf("<select name=\"%s\" onChange=\"document.form1.submit()\">\n", list[index]);

                  i = isparam(list[index]) ? atoi(getparam(list[index])) : 0;

                  rsprintf("<option %s value=-364>%s %s\n", i == -364 ? "selected" : "", loc("Last"),
                           loc("Year"));
                  rsprintf("<option %s value=-182>%s %s\n", i == -182 ? "selected" : "", loc("Last"),
                           loc("6 Months"));
                  rsprintf("<option %s value=-92>%s %s\n", i == -92 ? "selected" : "", loc("Last"),
                           loc("3 Months"));
                  rsprintf("<option %s value=-31>%s %s\n", i == -31 ? "selected" : "", loc("Last"),
                           loc("Month"));
                  rsprintf("<option %s value=-7>%s %s\n", i == -7 ? "selected" : "", loc("Last"),
                           loc("Week"));
                  rsprintf("<option %s value=-3>%s %s\n", i == -3 ? "selected" : "", loc("Last"),
                           loc("3 Days"));
                  rsprintf("<option %s value=-1>%s %s\n", i == -1 ? "selected" : "", loc("Last"), loc("Day"));

                  sprintf(str, "-- %s  --", list[index]);
                  rsprintf("<option %s value=\"_all_\">%s\n", i == 0 ? "selected" : "", str);

                  rsprintf("<option %s value=1>%s %s\n", i == 1 ? "selected" : "", loc("Next"), loc("Day"));
                  rsprintf("<option %s value=3>%s %s\n", i == 3 ? "selected" : "", loc("Next"),
                           loc("3 Days"));
                  rsprintf("<option %s value=7>%s %s\n", i == 7 ? "selected" : "", loc("Next"), loc("Week"));
                  rsprintf("<option %s value=31>%s %s\n", i == 31 ? "selected" : "", loc("Next"),
                           loc("Month"));
                  rsprintf("<option %s value=92>%s %s\n", i == 92 ? "selected" : "", loc("Next"),
                           loc("3 Months"));
                  rsprintf("<option %s value=182>%s %s\n", i == 182 ? "selected" : "", loc("Next"),
                           loc("6 Months"));
                  rsprintf("<option %s value=364>%s %s\n", i == 364 ? "selected" : "", loc("Next"),
                           loc("Year"));

                  rsprintf("</select>\n");
               } else {
                  if (strieq(list[index], "Subtext")) {
                     rsprintf
                             ("<input onClick=\"this.value='';\" title=\"%s\" type=text onChange=\"document.form1.submit()\"",
                              loc("Enter text"));
                     sprintf(str, "-- %s --", loc("Text"));
                     if (isparam(list[index]) && *getparam(list[index]))
                        strencode2(str, getparam(list[index]), sizeof(str));

                     rsprintf(" name=\"Subtext\" value=\"%s\">\n", str);
                  } else {
                     sprintf(str, loc("Enter %s"), list[index]);
                     rsprintf
                             ("<input onClick=\"this.value='';\" title=\"%s\" type=text onChange=\"document.form1.submit()\"",
                              str);
                     sprintf(str, "-- %s --", list[index]);
                     if (isparam(list[index]) && *getparam(list[index]))
                        strencode2(str, getparam(list[index]), sizeof(str));

                     rsprintf(" name=\"%s\" value=\"%s\">\n", list[index], str);
                  }
               }
            } else {

               sprintf(str, loc("Select %s"), list[index]);
               rsprintf("<select title=\"%s\" name=\"%s\" onChange=\"document.form1.submit()\">\n", str,
                        list[index]);

               rsprintf("<option value=\"_all_\">-- %s --\n", list[index]);

               for (j = 0; j < MAX_N_LIST && attr_options[attr_index][j][0]; j++) {
                  comment[0] = 0;
                  if (attr_flags[attr_index] & AF_ICON) {
                     sprintf(str, "Icon comment %s", attr_options[attr_index][j]);
                     getcfg(lbs->name, str, comment, sizeof(comment));
                  }

                  if (comment[0] == 0)
                     strcpy(comment, attr_options[attr_index][j]);

                  for (i1 = i2 = 0; i1 <= (int) strlen(comment); i1++) {
                     if (comment[i1] == '(') {
                        option[i2++] = '\\';
                        option[i2++] = '(';
                     } else if (comment[i1] == '{') {
                        option[i2] = 0;
                        break;
                     } else
                        option[i2++] = comment[i1];
                  }

                  sprintf(option_whole, "^%s$", option);
                  if (isparam(attr_list[attr_index]) &&
                      (strieq(option, getparam(attr_list[attr_index])) ||
                       strieq(option_whole, getparam(attr_list[attr_index]))))
                     rsprintf("<option selected value=\"%s\">%s\n", option_whole, option);
                  else
                     rsprintf("<option value=\"%s\">%s\n", option_whole, option);
               }

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

      /* show button if JavaScript is switched off */
      rsprintf("<noscript>\n");
      rsprintf("<input type=submit value=\"%s\">\n", loc("Search"));
      rsprintf("</noscript>\n");

      /* show submit button for IE (otherwise <Return> will not work) */
      if (strstr(browser, "MSIE"))
         rsprintf("<input type=submit value=\"%s\">\n", loc("Search"));
   }

   rsprintf("&nbsp;<b>%d %s</b>&nbsp;", n_msg, loc("Entries"));

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

Project: elog

BOOL is_cond_attr(int index) {
   int i;

   for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++)
      if (strchr(attr_options[index][i], '{') && strchr(attr_options[index][i], '}'))
         return TRUE;

   return FALSE;
}