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_html_header(LOGBOOK *lbs, BOOL expires, char *title, BOOL close_head, BOOL rss_feed, char *cookie,
                      int embed_css, int refresh) {
   int i, n;
   char css[1000], css_base[1000], str[1000], media[1000], file_name[256];
   char css_list[MAX_N_LIST][NAME_LENGTH];

   show_http_header(lbs, expires, cookie);

   /* DOCTYPE */
   rsprintf("<!DOCTYPE html>\n");

   /* this code would be for XML files...
      rsprintf("<?xml-stylesheet type=\"text/xsl\" href=\"http://www.w3.org/Math/XSL/mathml.xsl\"?>\n");
      rsprintf("<html xmlns=\"http://www.w3.org/1999/xhtml\"><head>\n");
    */

   /* page title */
   rsprintf("<html><head>\n");
   rsprintf("<meta name=\"ROBOTS\" content=\"NOINDEX, NOFOLLOW\">\n");
   if (refresh)
      rsprintf("<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh);
   rsprintf("<title>%s</title>\n", title);

   /* Cascading Style Sheet */
   if (embed_css) {
      strlcpy(css, "elog.css", sizeof(css));
      if (lbs != NULL && getcfg(lbs->name, "CSS", str, sizeof(str)))
         strlcpy(css, str, sizeof(css));
      else if (lbs == NULL && getcfg("global", "CSS", str, sizeof(str)))
         strlcpy(css, str, sizeof(css));

      strlcpy(file_name, resource_dir, sizeof(file_name));
      strlcat(file_name, "themes", sizeof(file_name));
      strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
      strlcat(file_name, theme_name, sizeof(file_name));
      strlcat(file_name, DIR_SEPARATOR_STR, sizeof(file_name));
      strlcat(file_name, css, sizeof(file_name));

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

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

         rsprintf("<style>\n");
         rsputs(buf);
         rsprintf("</style>\n");

         xfree(buf);
      }
   } else {
      rsprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"elog.css\">\n");
   }

   if (lbs != NULL && getcfg(lbs->name, "CSS", str, sizeof(str)))
      strlcpy(css, str, sizeof(css));
   else if (lbs == NULL && getcfg("global", "CSS", str, sizeof(str)))
      strlcpy(css, str, sizeof(css));
   else
      css[0] = 0;

   if (css[0]) {
      if (strchr(css, ',')) {
         n = strbreak(css, css_list, MAX_N_LIST, ",", FALSE);
         for (i = 0; i < n; i++) {
            strlcpy(str, css_list[i], sizeof(str));
            if (strchr(str, '&')) {
               strlcpy(media, strchr(str, '&') + 1, sizeof(media));
               *strchr(str, '&') = 0;
               rsprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\" media=\"%s\">\n", str, media);
            }
         }
      } else
         rsprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"%s%s\">\n", css_base, css);
   }

   if (!embed_css) {
      rsprintf("<link rel=\"shortcut icon\" href=\"favicon.ico\" />\n");
      rsprintf("<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\" />\n");
   }

   if (rss_feed && !embed_css) {
      rsprintf("<link rel=\"alternate\" type=\"application/rss+xml\" ");
      rsprintf("title=\"ELOG %s\" ", lbs->name);
      rsprintf("href=\"elog.rdf\" />\n");
   }

   if (close_head)
      rsprintf("</head>\n");
}

Project: elog

void flush_return_buffer() {
#ifdef HAVE_SSL
   send_with_timeout(_ssl_con, _sock, return_buffer, strlen_retbuf);
#else
   send_with_timeout(NULL, _sock, return_buffer, strlen_retbuf);
#endif
   memset(return_buffer, 0, return_buffer_size);
   strlen_retbuf = 0;
}

Project: elog

void xfree(void *pointer) {
   char *temp;
   int old_size;

   if (!pointer)
      return;

   /* check for magic byte */
   temp = pointer;
   assert(*((unsigned int *) (temp - 4)) == 0xdeadc0de);
   old_size = *((unsigned int *) (temp - 8));
   assert(*((unsigned int *) (temp + old_size)) == 0xdeadc0de);

   free(temp - 8);
}

Project: elog

int sid_check(char *sid, char *user_name) {
   int i;
   time_t now;

   if (sid == NULL)
      return FALSE;

   time(&now);
   for (i = 0; i < _n_sid; i++) {
      if (strcmp(_sid[i].host_ip, (char *) inet_ntoa(rem_addr)) == 0 && strcmp(_sid[i].session_id, sid) == 0) {
         strcpy(user_name, _sid[i].user_name);
         _sid[i].time = now;
         return TRUE;
      }
   }

   return FALSE;
}

Project: elog

void show_top_text(LOGBOOK *lbs) {
   char str[NAME_LENGTH];
   int size;

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

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

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

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

         rsputs(buf);
      } else
         rsputs(str);
   }
}

Project: elog

int check_edit_time(LOGBOOK *lbs, int message_id) {
   char str[256];
   int i;

   if (is_admin_user(lbs, getparam("unm"))) {
      if (getcfg(lbs->name, "Admin Restrict edit time", str, sizeof(str))) {
         for (i = 0; i < *lbs->n_el_index; i++)
            if (lbs->el_index[i].message_id == message_id)
               break;

         if (i < *lbs->n_el_index && time(NULL) > lbs->el_index[i].file_time + atof(str) * 3600 && atof(str) > 0) {
            sprintf(str, loc("Entry can only be edited %1.2lg hours after creation"), atof(str));
            show_error(str);
            return 0;
         }
      }
   } else {
      if (getcfg(lbs->name, "Restrict edit time", str, sizeof(str))) {
         for (i = 0; i < *lbs->n_el_index; i++)
            if (lbs->el_index[i].message_id == message_id)
               break;

         if (i < *lbs->n_el_index && time(NULL) > lbs->el_index[i].file_time + atof(str) * 3600) {
            sprintf(str, loc("Entry can only be edited %1.2lg hours after creation"), atof(str));
            show_error(str);
            return 0;
         }
      }
   }

   return 1;
}

Project: elog

int receive_message(LOGBOOK *lbs, char *url, int message_id, char *error_str, BOOL bnew) {
   int i, status, size, n_attr, header_size, ssl;
   char str[NAME_LENGTH], str2[NAME_LENGTH], *p, *p2, *message, date[80], attrib[MAX_N_ATTR][NAME_LENGTH],
           in_reply_to[80], reply_to[MAX_REPLY_TO * 10], encoding[80], locked_by[256],
           attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH], attachment_all[64 * MAX_ATTACHMENTS];

   error_str[0] = 0;

   combine_url(lbs, url, "", str, sizeof(str), &ssl);
   sprintf(str + strlen(str), "%d?cmd=%s", message_id, loc("Download"));

   retrieve_url(lbs, str, ssl, &message, TRUE);
   if (message == NULL) {
      sprintf(error_str, loc("Cannot receive \"%s\""), str);
      return -1;
   }
   p = strstr(message, "\r\n\r\n");
   if (p == NULL) {
      if (isparam("debug"))
         rsputs(message);
      xfree(message);
      sprintf(error_str, loc("Cannot receive \"%s\""), str);
      return -1;
   }
   p += 4;

   /* check for correct ID */
   if (atoi(p + 8) != message_id) {
      if (isparam("debug"))
         rsputs(message);
      sprintf(error_str, loc("Received wrong entry id \"%d\""), atoi(p + 8));
      xfree(message);
      return -1;
   }

   /* decode entry */
   el_decode(p, "Date: ", date, sizeof(date));
   el_decode_intlist(p, "Reply to: ", reply_to, sizeof(reply_to));
   el_decode_int(p, "In reply to: ", in_reply_to, sizeof(in_reply_to));

   /* derive attribute names from message */
   for (i = 0;; i++) {
      el_enum_attr(p, i, attr_list[i], attrib[i]);
      if (!attr_list[i][0])
         break;
   }
   n_attr = i;

   el_decode(p, "Attachment: ", attachment_all, sizeof(attachment_all));
   el_decode(p, "Encoding: ", encoding, sizeof(encoding));

   /* break apart attachements */
   for (i = 0; i < MAX_ATTACHMENTS; i++)
      attachment[i][0] = 0;

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

      if (p2 != NULL)
         strcpy(attachment[i], p2);
      else
         break;
   }

   el_decode(p, "Locked by: ", locked_by, sizeof(locked_by));
   if (locked_by[0]) {
      xfree(message);
      sprintf(error_str, loc("Entry #%d is locked on remote server"), message_id);
      return -1;
   }

   p = strstr(message, "========================================\n");

   /* check for \n -> \r conversion (e.g. zipping/unzipping) */
   if (p == NULL)
      p = strstr(message, "========================================\r");

   if (p != NULL) {
      p += 41;

      /* remove last CR */
      if (p[strlen(p) - 1] == '\n')
         p[strlen(p) - 1] = 0;

      status = el_submit(lbs, message_id, !bnew, date, attr_list, attrib, n_attr, p, in_reply_to, reply_to,
                         encoding, attachment, FALSE, "", NULL);

      xfree(message);

      if (status != message_id) {
         strlcpy(error_str, loc("Cannot save remote entry locally"), 256);
         return -1;
      }

      for (i = 0; i < MAX_ATTACHMENTS; i++) {
         if (attachment[i][0]) {

            combine_url(lbs, url, "", str, sizeof(str), &ssl);
            strlcpy(str2, attachment[i], sizeof(str2));
            str2[13] = '/';
            strlcat(str, str2, sizeof(str));

            size = retrieve_url(lbs, str, ssl, &message, TRUE);
            p = strstr(message, "\r\n\r\n");
            if (p == NULL) {
               xfree(message);
               sprintf(error_str, loc("Cannot receive \"%s\""), str);
               return -1;
            }
            p += 4;
            header_size = p - message;

            el_submit_attachment(lbs, attachment[i], p, size - header_size, NULL);
            xfree(message);
         }
      }
   } else {
      xfree(message);
      return -1;
   }

   return 1;
}

Project: elog

int propagate_attrib(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
   int n, i, j, status;
   char str[NAME_LENGTH], att_file[MAX_ATTACHMENTS][256], *attr, *list, reply_to[MAX_REPLY_TO * 10];

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

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

   getcfg(lbs->name, "Propagate attributes", str, sizeof(str));
   n = strbreak(str, (char (*)[1500]) list, MAX_N_ATTR, ",", FALSE);
   for (i = 0; i < n; i++) {
      for (j = 0; j < lbs->n_attr; j++)
         if (stricmp(attr_list[j], list + i * NAME_LENGTH) == 0) {
            strlcpy(attr + j * NAME_LENGTH, attrib[j], NAME_LENGTH);
            break;
         }
   }

   message_id = el_submit(lbs, message_id, TRUE, "<keep>", attr_list, (char (*)[1500]) attr, lbs->n_attr,
                          "<keep>",
                          "<keep>", "<keep>", "<keep>", att_file, TRUE, NULL, NULL);
   if (message_id < 0) {
      xfree(list);
      xfree(attr);
      return 0;
   }

   // go through all replies of this entry
   n = strbreak(reply_to, (char (*)[1500]) list, MAX_N_ATTR, ",", FALSE);
   for (i = 0; i < n; i++)
      propagate_attrib(lbs, atoi(list + i * NAME_LENGTH), attrib);

   xfree(list);
   xfree(attr);

   return EL_SUCCESS;
}

Project: elog

static int int_vasprintf(char **result, const char *format, va_list args) {
   const char *p = format;
   /* Add one to make sure that it is never zero, which might cause malloc
      to return NULL.  */
   int total_width = strlen(format) + 1;
   va_list ap;

#ifdef va_copy
   va_copy(ap, args);
#else
   memcpy(&ap, &args, sizeof(va_list));
#endif

   while (*p != '\0') {
      if (*p++ == '%') {
         while (strchr("-+ #0", *p))
            ++p;
         if (*p == '*') {
            ++p;
            total_width += abs(va_arg(ap, int));
         } else
            total_width += strtoul(p, (char **) &p, 10);
         if (*p == '.') {
            ++p;
            if (*p == '*') {
               ++p;
               total_width += abs(va_arg(ap, int));
            } else
               total_width += strtoul(p, (char **) &p, 10);
         }
         while (strchr("hlL", *p))
            ++p;
         /*
          * Should be big enough for any format specifier
          * except %s and floats.
          */
         total_width += 30;
         switch (*p) {
            case 'd':
            case 'i':
            case 'o':
            case 'u':
            case 'x':
            case 'X':
            case 'c':
               (void) va_arg(ap, int);
               break;
            case 'f':
            case 'e':
            case 'E':
            case 'g':
            case 'G':
               (void) va_arg(ap, double);
               /*
             * Since an ieee double can have an exponent of 307, we'll
             * make the buffer wide enough to cover the gross case.
             */
               total_width += 307;
               break;
            case 's':
               total_width += strlen(va_arg(ap, char *));
               break;
            case 'p':
            case 'n':
               (void) va_arg(ap, char *);
               break;
         }
         p++;
      }
   }
#ifdef va_copy
   va_end(ap);
#endif
   *result = (char *) malloc(total_width);
   if (*result != NULL)
      return vsprintf(*result, format, args);
   else
      return -1;
}

Project: elog

void attrib_from_param(int n_attr, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
   int i, j, first, year, month, day, hour, min, sec;
   char str[NAME_LENGTH], ua[NAME_LENGTH];
   time_t ltime;
   struct tm ts;

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

         if (isparam(ua))
            strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
         else {
            sprintf(str, "y%d", i);
            year = atoi(isparam(str) ? getparam(str) : "");
            if (year < 100)
               year += 2000;

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

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

            memset(&ts, 0, sizeof(struct tm));
            ts.tm_year = year - 1900;
            ts.tm_mon = month - 1;
            ts.tm_mday = day;
            ts.tm_hour = 12;

            if (month && day) {
               ltime = mktime(&ts);
               sprintf(attrib[i], "%d", (int) ltime);
            } else
               strcpy(attrib[i], "");
         }

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

         if (isparam(ua))
            strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
         else {
            sprintf(str, "y%d", i);
            year = atoi(isparam(str) ? getparam(str) : "");
            if (year < 100)
               year += 2000;

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

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

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

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

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

            memset(&ts, 0, sizeof(struct tm));
            ts.tm_year = year - 1900;
            ts.tm_mon = month - 1;
            ts.tm_mday = day;
            ts.tm_hour = hour;
            ts.tm_min = min;
            ts.tm_sec = sec;
            ts.tm_isdst = -1;

            if (month && day) {
               ltime = mktime(&ts);
               sprintf(attrib[i], "%d", (int) ltime);
            } else
               strcpy(attrib[i], "");
         }

      } else {
         if (isparam(attr_list[i]))
            strlcpy(attrib[i], getparam(attr_list[i]), NAME_LENGTH);
         else if (isparam(ua))
            strlcpy(attrib[i], getparam(ua), NAME_LENGTH);
         else
            attrib[i][0] = 0;
      }
   }
}

Project: elog

void *xcalloc(size_t count, size_t bytes) {
   char *temp;

   /* Align buffer on 4 byte boundery for HP UX and other 64 bit systems to prevent Bus error (core dump) */
   if (bytes & 3)
      bytes += 4 - (bytes & 3);

   temp = (char *) malloc(count * bytes + 12);
   if (temp == 0)
      memory_error_and_abort("xcalloc");
   memset(temp, 0, count * bytes + 12);

   /* put magic number around array */
   *(unsigned int *) (temp + 0) = count * bytes;
   *(unsigned int *) (temp + 4) = 0xdeadc0de;
   *(unsigned int *) (temp + count * bytes + 8) = 0xdeadc0de;

   return (temp + 8);
}

Project: elog

int vasprintf(char **result, const char *format, va_list args)
#endif
{
   return int_vasprintf(result, format, args);
}

Project: elog

int seteuser(char *str) {
#ifdef OS_UNIX
   struct passwd *pw;

   pw = getpwnam(str);

   if (pw != NULL) {
      chown(logbook_dir, pw->pw_uid, -1);
      if (setreuid(-1, pw->pw_uid) >= 0) {
         return 0;
      } else {
         eprintf("Cannot set effective UID to user \"%s\"\n", str);
         eprintf("setuser: %s\n", strerror(errno));
      }
   } else
      eprintf("User \"%s\" not found\n", str);

   return -1;
#else
   return 0;
#endif
}

Project: elog

void show_import_page_xml(LOGBOOK *lbs) {
   /*---- header ----*/

   show_html_header(lbs, FALSE, loc("ELOG XML import"), 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("Cancel"));
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Import"));

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

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

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

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

   if (isparam("head"))
      rsprintf("<input type=checkbox checked id=\"head\" name=\"head\" value=\"1\">\n");
   else
      rsprintf("<input type=checkbox id=\"head\" name=\"head\" value=\"1\">\n");
   rsprintf("<label for=\"head\">%s</label><br>\n", loc("Derive attributes from XML file"));

   if (isparam("keep"))
      rsprintf("<input type=checkbox checked id=\"keep\" name=\"keep\" value=\"1\">\n");
   else
      rsprintf("<input type=checkbox id=\"keep\" name=\"keep\" value=\"1\">\n");
   rsprintf("<label for=\"keep\">%s</label><br>\n",
            loc
                    ("Keep original entry IDs (may overwrite existing entries, but is required if imported entries contain replies)"));

   rsprintf("<input type=checkbox id=\"preview\" name=\"preview\" value=\"1\">\n");
   rsprintf("<label for=\"preview\">%s</label><br>\n", loc("Preview import"));

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

   rsprintf("<tr><td class=\"attribname\" nowrap width=\"10%%\">%s:</td>\n", loc("XML filename"));
   rsprintf("<td class=\"attribvalue\">");

   if (isparam("xmlfile"))
      rsprintf("<b>%s</b>:<br>\n", loc("Please re-enter filename"));

   rsprintf("<input type=\"file\" size=\"60\" maxlength=\"200\" name=\"xmlfile\" ");
   if (isparam("xmlfile"))
      rsprintf(" value=\"%s\" ", getparam("xmlfile"));
   rsprintf("></td></tr>\n");

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

}

Project: elog

char *stristr(const char *str, const char *pattern) {
   size_t i;

   if (!*pattern)
      return (char *) str;

   for (; *str; str++) {
      if (toupper(*str) == toupper(*pattern)) {
         for (i = 1;; i++) {
            if (!pattern[i])
               return (char *) str;
            if (toupper(str[i]) != toupper(pattern[i]))
               break;
         }
      }
   }
   return NULL;
}

Project: elog

void free_logbook_hierarchy(LBLIST root) {
   int i;

   for (i = 0; i < root->n_members; i++) {
      if (root->member[i]) {
         free_logbook_hierarchy(root->member[i]);
         root->member[i] = NULL;
      }
   }

   xfree(root->member);
   xfree(root);
}

Project: elog

void el_decode(char *message, char *key, char *result, int size) {
   char *pc, *ph;
   int i;

   if (result == NULL)
      return;

   *result = 0;

   ph = strstr(message, "========================================");
   if (ph == NULL)
      return;

   do {
      if (ph[40] == '\r' || ph[40] == '\n')
         break;
      ph = strstr(ph + 40, "========================================");
      if (ph == NULL)
         return;

   } while (1);

   /* go through all lines */
   for (pc = message; pc < ph;) {

      if (strncmp(pc, key, strlen(key)) == 0) {
         pc += strlen(key);
         for (i = 0; *pc != '\n' && *pc != '\r' && i < size - 1; i++)
            result[i] = *pc++;
         result[i] = 0;
         return;
      }

      pc = strchr(pc, '\n');
      if (pc == NULL)
         return;
      while (*pc && (*pc == '\n' || *pc == '\r'))
         pc++;
   }
}

Project: elog

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

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

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

   return EL_SUCCESS;
}

Project: elog

void show_logbook_rename(LOGBOOK *lbs) {
   int i;
   char str[256], lbn[256];

   if (isparam("lbname")) {

      /* check if logbook name exists already */
      strcpy(lbn, getparam("lbname"));
      for (i = 0; lb_list[i].name[0]; i++)
         if (strieq(lbn, lb_list[i].name)) {
            sprintf(str, loc("Logbook \"%s\" exists already, please choose different name"), lbn);
            show_error(str);
            return;
         }

      if (!rename_logbook(lbs, getparam("lbname")))
         return;

      sprintf(str, "../%s/?cmd=Config", getparam("lbname"));
      redirect(NULL, str);
      return;

   } else {

      strcpy(str, loc("Rename logbook"));
      show_standard_header(lbs, TRUE, str, "", FALSE, NULL, NULL, 0);

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

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

      rsprintf("%s</td></tr>\n", loc("Enter new logbook name"));

      rsprintf("<tr><td align=center class=\"dlgform\">");
      rsprintf("<input type=text name=\"lbname\"><br><br>\n");
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Rename this logbook"));
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Cancel"));
      rsprintf("</td></tr>\n\n");
   }

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

Project: elog

int el_retrieve(LOGBOOK *lbs, int message_id, char *date, char attr_list[MAX_N_ATTR][NAME_LENGTH],
                char attrib[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, int *textsize,
                char *in_reply_to, char *reply_to, char attachment[MAX_ATTACHMENTS][MAX_PATH_LENGTH],
                char *encoding, char *locked_by, char *draft)
/********************************************************************
 Routine: el_retrieve

 Purpose: Retrieve an ELog entry by its message tab

 Input:
 LOGBOOK lbs             Logbook structure
 int    message_id       Message ID to retrieve
 int    *size            Size of text buffer

 Output:
 char   *tag             tag of retrieved message
 char   *date            Date/time of message recording
 char   attr_list        Names of attributes
 char   attrib           Values of attributes
 int    n_attr           Number of attributes
 char   *text            Message text
 char   *in_reply_to     Original message if this one is a reply
 char   *reply_to        Replies for current message
 char   *attachment[]    File attachments
 char   *encoding        Encoding of message
 char   *locked_by       User/Host if locked for editing
 char   *draft           User who drafted that message
 int    *size            Actual message text size

 Function value:
 EL_SUCCESS              Successful completion
 EL_EMPTY                Logbook is empty
 EL_NO_MSG               Message doesn't exist
 EL_FILE_ERROR           Internal error

 \********************************************************************/
{
   int i, index, size, fh;
   char str[NAME_LENGTH], file_name[MAX_PATH_LENGTH * 3], *p;
   char *message, attachment_all[64 * MAX_ATTACHMENTS];

   if (message_id == 0)
      /* open most recent message */
      message_id = el_search_message(lbs, EL_LAST, 0, FALSE);

   if (message_id == 0)
      return EL_EMPTY;

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

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

   snprintf(file_name, sizeof(file_name), "%s%s%s", lbs->data_dir, lbs->el_index[index].subdir,
            lbs->el_index[index].file_name);
   fh = open(file_name, O_RDONLY | O_BINARY, 0644);
   if (fh < 0) {
      /* file might have been deleted, rebuild index */
      el_build_index(lbs, TRUE);
      return el_retrieve(lbs, message_id, date, attr_list, attrib, n_attr, text, textsize, in_reply_to,
                         reply_to, attachment, encoding, locked_by, draft);
   }

   message = xmalloc(TEXT_SIZE + 1000);

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

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

   if (strncmp(message, "$@MID@$:", 8) != 0) {
      xfree(message);
      /* file might have been edited, rebuild index */
      el_build_index(lbs, TRUE);
      return el_retrieve(lbs, message_id, date, attr_list, attrib, n_attr, text, textsize, in_reply_to,
                         reply_to, attachment, encoding, locked_by, draft);
   }

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

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

   message[size] = 0;

   /* decode message */
   if (date)
      el_decode(message, "Date: ", date, 80);
   if (reply_to)
      el_decode_intlist(message, "Reply to: ", reply_to, MAX_REPLY_TO * 10);
   if (in_reply_to)
      el_decode_int(message, "In reply to: ", in_reply_to, 80);

   if (n_attr == -1) {
      /* derive attribute names from message */
      for (i = 0;; i++) {
         el_enum_attr(message, i, attr_list[i], attrib[i]);
         if (!attr_list[i][0])
            break;
      }
      n_attr = i;

   } else {
      if (attrib)
         for (i = 0; i < n_attr; i++) {
            sprintf(str, "%s: ", attr_list[i]);
            el_decode(message, str, attrib[i], NAME_LENGTH);
         }
   }

   el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
   if (encoding)
      el_decode(message, "Encoding: ", encoding, 80);

   if (attachment) {
      /* break apart attachements */
      for (i = 0; i < MAX_ATTACHMENTS; i++)
         if (attachment[i] != NULL)
            attachment[i][0] = 0;

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

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

   if (locked_by)
      el_decode(message, "Locked by: ", locked_by, 80);

   if (draft)
      el_decode(message, "Draft: ", draft, 80);

   p = strstr(message, "========================================\n");

   /* check for \n -> \r conversion (e.g. zipping/unzipping) */
   if (p == NULL)
      p = strstr(message, "========================================\r");

   if (text) {
      if (p != NULL) {
         p += 41;
         if ((int) strlen(p) >= *textsize) {
            strlcpy(text, p, *textsize);
            show_error("Entry too long to display. Please increase TEXT_SIZE and recompile elogd.");
            xfree(message);
            return EL_FILE_ERROR;
         } else {
            strlcpy(text, p, *textsize);

            /* strip CR at end */
            if (text[strlen(text) - 1] == '\n') {
               text[strlen(text) - 1] = 0;
               if (text[strlen(text) - 1] == '\r')
                  text[strlen(text) - 1] = 0;
            }

            *textsize = strlen(text);
         }
      } else {
         text[0] = 0;
         *textsize = 0;
      }
   }

   xfree(message);
   return EL_SUCCESS;
}

Project: elog

void format_email_html(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH],
                       char att_file[MAX_ATTACHMENTS][256], int old_mail, char *encoding, char *url,
                       char *multipart_boundary, char *mail_text, int size) {
   int i, j, k, flags, n_email_attr, attr_index[MAX_N_ATTR], max_att_size, max_allowed_att_size;
   char str[NAME_LENGTH + 100], str2[256], mail_from[256], mail_from_name[256], format[256],
           list[MAX_N_ATTR][NAME_LENGTH], comment[256], charset[256], multipart_boundary_related[256],
           heading[256], slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH];
   time_t ltime;
   struct tm *pts;

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

   if (multipart_boundary[0]) {
      strlcat(mail_text, "--", size);
      strlcat(mail_text, multipart_boundary, size);
      strlcat(mail_text, "\r\n", size);
   }

   max_att_size = max_attachment_size(lbs, message_id);
   max_allowed_att_size = (int) 10E6;
   if (getcfg(lbs->name, "Max email attachment size", str, sizeof(str)))
      max_allowed_att_size = atoi(str);

   if (max_att_size) {
      sprintf(multipart_boundary_related, "------------%04X%04X%04X", rand(), rand(), rand());
      snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
               "MIME-Version: 1.0\r\nContent-Type: multipart/related;\r\n boundary=\"%s\"\r\n\r\n",
               multipart_boundary_related);
      strlcat(mail_text, "--", size);
      strlcat(mail_text, multipart_boundary_related, size);
      strlcat(mail_text, "\r\n", size);
   }

   snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
            "Content-Type: text/html; charset=\"%s\"\r\n", charset);
   snprintf(mail_text + strlen(mail_text), size - strlen(mail_text) - 1,
            "Content-Transfer-Encoding: 7bit\r\n\r\n");

   retrieve_email_from(lbs, mail_from, mail_from_name, attrib);

   flags = 63;
   if (getcfg(lbs->name, "Email format", str, sizeof(str)))
      flags = atoi(str);

   // if attachments too large, include only links to attachments
   if (max_att_size > max_allowed_att_size && (flags & 16) > 0) {
      flags &= ~(16);
      flags |= 64;
   }

   strcpy(mail_text + strlen(mail_text),
          "<!DOCTYPE html>\r\n");
   strcpy(mail_text + strlen(mail_text), "<html>\r\n<head>\r\n  <title></title>\r\n</head>\r\n<body>\r\n");

   if (flags & 1) {
      strcpy(mail_text + strlen(mail_text), "<h3>\r\n");
      if (getcfg(lbs->name, "Use Email heading", heading, sizeof(heading))) {
         if (old_mail) {
            if (!getcfg(lbs->name, "Use Email heading edit", heading, sizeof(heading)))
               getcfg(lbs->name, "Use Email heading", heading, sizeof(heading));
         }

         i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
         strsubst_list(heading, sizeof(heading), slist, svalue, i);

         strlcpy(mail_text + strlen(mail_text), heading, size - strlen(mail_text));

      } else {
         if (old_mail)
            sprintf(mail_text + strlen(mail_text), loc("A old entry has been updated on %s"), host_name);
         else
            sprintf(mail_text + strlen(mail_text), loc("A new entry has been submitted on %s"), host_name);
         strcat(mail_text, ":");
      }

      strlcpy(mail_text + strlen(mail_text), "</h3>\r\n", size - strlen(mail_text));
   }

   strlcpy(mail_text + strlen(mail_text), "<table border=\"3\" cellpadding=\"4\">\r\n",
           size - strlen(mail_text));

   if (flags & 32) {
      sprintf(mail_text + strlen(mail_text), "<tr><td bgcolor=\"#CCCCFF\">%s</td>", loc("Logbook"));
      sprintf(mail_text + strlen(mail_text), "<td bgcolor=\"#DDEEBB\">%s</td></tr>\r\n", lbs->name);
   }

   if (flags & 2) {

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

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

         i = attr_index[j];
         strcpy(str, "                                                                       ");
         memcpy(str, attr_list[i], strlen(attr_list[i]));

         comment[0] = 0;
         if (attr_flags[i] & AF_ICON) {

            sprintf(str2, "Icon comment %s", attrib[i]);
            getcfg(lbs->name, str2, comment, sizeof(comment));

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

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

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

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

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

         if (!comment[0])
            strcpy(comment, attrib[i]);

         if (strieq(attr_options[i][0], "boolean"))
            strcpy(comment, atoi(attrib[i]) ? "1" : "0");

         for (k = strlen(str) - 1; k > 0; k--)
            if (str[k] != ' ')
               break;

         sprintf(mail_text + strlen(mail_text), "<tr><td bgcolor=\"#CCCCFF\">%s</td>", attr_list[i]);
         sprintf(mail_text + strlen(mail_text), "<td bgcolor=\"#DDEEBB\">%s</td></tr>\r\n", comment);
      }
   }

   if (flags & 4) {
      sprintf(mail_text + strlen(mail_text),
              "<tr><td bgcolor=\"#CCCCFF\">%s URL</td><td bgcolor=\"#DDEEBB\">", loc("Logbook"));
      sprintf(mail_text + strlen(mail_text), "<a href=\"%s\">%s</a></td></tr>\r\n", url, url);
   }

   if (flags & 64) {
      for (i = 0; i < MAX_ATTACHMENTS && att_file[i][0]; i++) {
         sprintf(mail_text + strlen(mail_text),
                 "<tr><td bgcolor=\"#CCCCFF\">%s %d</td><td bgcolor=\"#DDEEBB\">", loc("Attachment"), i + 1);
         sprintf(mail_text + strlen(mail_text), "<a href=\"%s/%d\">%s</a></td></tr>\r\n", url, i + 1,
                 att_file[i] + 14);
      }
   }

   sprintf(mail_text + strlen(mail_text), "</table>\r\n");

   if (flags & 8) {
      if (isparam("text")) {
         if (encoding[0] == 'H')
            sprintf(mail_text + strlen(mail_text), "\r\n<HR>\r\n%s", getparam("text"));
         else if (encoding[0] == 'E') {
            sprintf(mail_text + strlen(mail_text), "\r\n<HR>\r\n");
            strlen_retbuf = 0;
            rsputs_elcode(lbs, TRUE, getparam("text"));
            strlcpy(mail_text + strlen(mail_text), return_buffer, TEXT_SIZE + 1000 - strlen(mail_text));
            strlen_retbuf = 0;
         } else
            sprintf(mail_text + strlen(mail_text), "\r\n=================================\r\n\r\n%s",
                    getparam("text"));
      }
   }

   strcpy(mail_text + strlen(mail_text), "\r\n</html></body>\r\n\r\n");

   if (max_att_size && (flags & 64)) {
      format_email_attachments(lbs, message_id, 2, att_file, mail_text, size, multipart_boundary_related,
                               TRUE);
      strlcat(mail_text, "--", size);
      strlcat(mail_text, multipart_boundary_related, size);
      strlcat(mail_text, "--\r\n\r\n", size);
   }
}

Project: elog

void remove_reference(LOGBOOK *lbs, int message_id, int remove_id, BOOL reply_to_flag) {
   char date[80], attr[MAX_N_ATTR][NAME_LENGTH], enc[80], in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
           att[MAX_ATTACHMENTS][256], lock[256], draft[256], *p, *ps, *message;
   int size, status;

   /* retrieve original message */
   size = TEXT_SIZE + 1000;
   message = (char *) xmalloc(size);
   status = el_retrieve(lbs, message_id, date, attr_list, attr, lbs->n_attr, message, &size, in_reply_to,
                        reply_to, att, enc, lock, draft);
   if (status != EL_SUCCESS)
      return;

   if (reply_to_flag)
      p = reply_to;
   else
      p = in_reply_to;

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

      ps = p;
      while (isdigit(*ps))
         ps++;

      while (*ps && (*ps == ',' || *ps == ' '))
         ps++;

      if (atoi(p) == remove_id)
         strcpy(p, ps);
      else
         while (isdigit(*p))
            p++;
   }

   /* write modified message */
   el_submit(lbs, message_id, TRUE, date, attr_list, attr, lbs->n_attr, message, in_reply_to, reply_to, enc,
             att, TRUE, lock, NULL);

   xfree(message);
}

Project: elog

int save_admin_config(char *section, char *buffer, char *error) {
   int fh, i, length;
   char *buf, *buf2, *p1, *p2;

   error[0] = 0;

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

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

   /* find previous logbook config */
   p1 = (char *) find_section(buf, section);
   p2 = (char *) find_next_section(p1 + 1);

   /* save tail */
   buf2 = NULL;

   if (p2)
      buf2 = xstrdup(p2);

   /* combine old and new config */
   sprintf(p1, "[%s]\r\n", section);
   strcat(p1, buffer);
   strcat(p1, "\r\n\r\n");

   if (p2) {
      strlcat(p1, buf2, length + strlen(buffer) + 1);
      xfree(buf2);
   }

   adjust_crlf(buf, length + strlen(buffer) + 10);

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

   TRUNCATE(fh);

   close(fh);
   xfree(buf);

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

   return 1;
}

Project: elog

void redirect_to_stderr(void) {
   printf_handler = print_stderr;
   fputs_handler = fputs_stderr;

   current_output_stream = stderr;
}

Project: elog

void show_select_navigation(LOGBOOK *lbs) {
   int i, n_log;
   char str[NAME_LENGTH];
   char lbk_list[MAX_N_LIST][NAME_LENGTH];

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

   rsprintf("<script language=\"JavaScript\" type=\"text/javascript\">\n");
   rsprintf("<!--\n");
   rsprintf("function ToggleAll()\n");
   rsprintf("  {\n");
   rsprintf("  for (var i = 0; i < document.form1.elements.length; i++)\n");
   rsprintf("    {\n");
   rsprintf
           ("    if (document.form1.elements[i].type == 'checkbox' && document.form1.elements[i].disabled == false)\n");
   rsprintf("      document.form1.elements[i].checked = !(document.form1.elements[i].checked);\n");
   rsprintf("    }\n");
   rsprintf("  }\n");
   rsprintf("//-->\n");
   rsprintf("</script>\n");

   rsprintf("%s:&nbsp;\n", loc("Selected entries"));

   rsprintf("<input type=button value=\"%s\" onClick=\"ToggleAll();\">\n", loc("Toggle all"));

   if (!getcfg(lbs->name, "Menu commands", str, sizeof(str)) || stristr(str, "Delete")) {
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Delete"));
   }

   if (!getcfg(lbs->name, "Menu commands", str, sizeof(str)) || stristr(str, "Edit")) {
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Edit"));
   }

   if (getcfg(lbs->name, "Menu commands", str, sizeof(str)) && stristr(str, "Copy to")) {
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Copy to"));
      rsprintf("<select name=destc>\n");

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

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

            if (!is_logbook(str))
               continue;

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

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

   if (getcfg(lbs->name, "Menu commands", str, sizeof(str)) && stristr(str, "Move to")) {
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Move to"));
      rsprintf("<select name=destm>\n");

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

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

            if (!is_logbook(str))
               continue;

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

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

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

Project: elog

int param_compare(const void *p1, const void *p2) {
   return stricmp(((CONFIG_PARAM *) p1)->uparam, ((CONFIG_PARAM *) p2)->uparam);
}

Project: elog

void base64_encode(unsigned char *s, unsigned char *d, int size) {
   unsigned int t, pad;
   unsigned char *p;

   pad = 3 - strlen((char *) s) % 3;
   if (pad == 3)
      pad = 0;
   p = d;
   while (*s) {
      t = (*s++) << 16;
      if (*s)
         t |= (*s++) << 8;
      if (*s)
         t |= (*s++) << 0;

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

      d += 4;
      if (d - p >= size - 3)
         return;
   }
   *d = 0;
   while (pad--)
      *(--d) = '=';
}

Project: elog

char *month_name(int m)
/* return name of month in current locale, m=0..11 */
{
   struct tm ts;
   static char name[32];

   memset(&ts, 0, sizeof(ts));
   ts.tm_mon = m;
   ts.tm_mday = 15;
   ts.tm_year = 2000;

   mktime(&ts);
   strftime(name, sizeof(name), "%B", &ts);
   return name;
}

Project: elog

int el_search_message(LOGBOOK *lbs, int mode, int message_id, BOOL head_only)
/********************************************************************
 Routine: el_search_message

 Purpose: Search for a specific message in a logbook

 Input:
 int   mode              Search mode, EL_FIRST, EL_LAST, EL_NEXT, EL_PREV
 int   message_id        Message id for EL_NEXT and EL_PREV

 Function value:
 int                     New message id

 \********************************************************************/
{
   int i;

   if (lbs->n_el_index == 0)
      return 0;

   if (mode == EL_FIRST) {
      if (head_only) {
         for (i = 0; i < *lbs->n_el_index; i++)
            if (lbs->el_index[i].in_reply_to == 0)
               return lbs->el_index[i].message_id;

         return 0;
      }
      if (*lbs->n_el_index == 0)
         return 0;
      return lbs->el_index[0].message_id;
   }

   if (mode == EL_LAST) {
      if (head_only) {
         for (i = *lbs->n_el_index - 1; i >= 0; i--)
            if (lbs->el_index[i].in_reply_to == 0)
               return lbs->el_index[i].message_id;

         return 0;
      }
      if (*lbs->n_el_index == 0)
         return 0;
      return lbs->el_index[*lbs->n_el_index - 1].message_id;
   }

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

      if (i == *lbs->n_el_index)
         return 0;              // message not found

      if (i == *lbs->n_el_index - 1)
         return 0;              // last message

      if (head_only) {
         for (i++; i < *lbs->n_el_index; i++)
            if (lbs->el_index[i].in_reply_to == 0)
               return lbs->el_index[i].message_id;

         return 0;
      }

      return lbs->el_index[i + 1].message_id;
   }

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

      if (i == *lbs->n_el_index)
         return 0;              // message not found

      if (i == 0)
         return 0;              // first message

      if (head_only) {
         for (i--; i >= 0; i--)
            if (lbs->el_index[i].in_reply_to == 0)
               return lbs->el_index[i].message_id;

         return 0;
      }

      return lbs->el_index[i - 1].message_id;
   }

   return 0;
}

Project: elog

void MD5_checksum(const void *pdata, unsigned int len, unsigned char digest[16]) {
   MD5_CONTEXT ctx;
   unsigned char bits[8];
   unsigned int i, padlen;

   /* to allow multithreading we have to locate the padding memory here */
   unsigned char PADDING[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                0, 0, 0, 0,
                                0, 0, 0, 0, 0, 0, 0
   };

   memset(&ctx, 0, sizeof(MD5_CONTEXT));
   ctx.count[0] = ctx.count[1] = 0;

   /* load magic initialization constants */
   ctx.state[0] = 0x67452301;
   ctx.state[1] = 0xefcdab89;
   ctx.state[2] = 0x98badcfe;
   ctx.state[3] = 0x10325476;

   _MD5_update(&ctx, pdata, len);

   // save number of bits
   _MD5_encode(bits, ctx.count, 8);

   // pad out to 56 mod 64
   i = (unsigned int) ((ctx.count[0] >> 3) & 0x3f);
   padlen = (i < 56) ? (56 - i) : (120 - i);
   _MD5_update(&ctx, PADDING, padlen);

   // append length (before padding)
   _MD5_update(&ctx, bits, 8);

   // store state in digest
   _MD5_encode(digest, ctx.state, 16);
}

Project: elog

const char *find_section(const char *buf, const char *name) {
   const char *pstart;
   char *pstr, str[80];

   do {
      if (*buf == '[') {
         pstart = buf;
         buf++;
         pstr = str;
         while (*buf && *buf != ']' && *buf != '\n' && *buf != '\r')
            *pstr++ = *buf++;
         *pstr = 0;
         if (strieq(str, name))
            return pstart;
      }

      if (buf)
         buf = strchr(buf, '\n');
      if (buf)
         buf++;
      if (buf && *buf == '\r')
         buf++;

   } while (buf);

   return NULL;
}

Project: elog

int send_with_timeout(void *p, int sock, char *buf, int buf_size) {
   int status, sent, send_size, send_packet;
   time_t start, now;
   char *pbuf;
#ifdef HAVE_SSL
   SSL *ssl;
#endif

   time(&start);
   sent = 0;
   send_size = buf_size;
   pbuf = p; // fix compiler warning
   pbuf = buf;

   do {
      if (send_size > 65536)
         send_packet = 65536;
      else
         send_packet = send_size;

#ifdef HAVE_SSL
      ssl = (SSL *) p;
      if (ssl)
         status = SSL_write(ssl, pbuf, send_packet);
      else
#endif
         status = send(sock, pbuf, send_packet, 0);

      // abort after 30 seconds
      time(&now);
      if (now > start + 30) {
         printf("Timeout after 30 seconds\n");
         break;
      }

      // repeat if we were interrupted by alarm() signal
      if (status == -1 && errno == EINTR) {
         continue;
      }

      if (status == -1)
         break;

      if (status > 0)
         sent += status;

      if (status > 0 && sent < buf_size) {
         pbuf += status;
         send_size -= status;
      }

   } while (sent < buf_size);

   return sent;
}

Project: elog

int sid_remove(char *sid) {
   int i;

   if (sid == NULL)
      return FALSE;

   for (i = 0; i < _n_sid; i++) {
      if (strcmp(_sid[i].session_id, sid) == 0) {
         memset(&_sid[i], 0, sizeof(SESSION_ID));
         return TRUE;
      }
   }

   return FALSE;
}

Project: elog

int my_shell(char *cmd, char *result, int size) {
#ifdef OS_WINNT

                                                                                                                           HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
       hChildStdoutRd, hChildStdoutWr, hChildStderrRd, hChildStderrWr, hSaveStdin, hSaveStdout, hSaveStderr;

   SECURITY_ATTRIBUTES saAttr;
   PROCESS_INFORMATION piProcInfo;
   STARTUPINFO siStartInfo;
   char buffer[10000];
   DWORD dwRead, dwAvail, i;

   /* Set the bInheritHandle flag so pipe handles are inherited. */
   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
   saAttr.bInheritHandle = TRUE;
   saAttr.lpSecurityDescriptor = NULL;

   /* Save the handle to the current STDOUT. */
   hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

   /* Create a pipe for the child's STDOUT. */
   if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
      return 0;

   /* Set a write handle to the pipe to be STDOUT. */
   if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
      return 0;

   /* Save the handle to the current STDERR. */
   hSaveStderr = GetStdHandle(STD_ERROR_HANDLE);

   /* Create a pipe for the child's STDERR. */
   if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0))
      return 0;

   /* Set a read handle to the pipe to be STDERR. */
   if (!SetStdHandle(STD_ERROR_HANDLE, hChildStderrWr))
      return 0;

   /* Save the handle to the current STDIN. */
   hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);

   /* Create a pipe for the child's STDIN. */
   if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
      return 0;

   /* Set a read handle to the pipe to be STDIN. */
   if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
      return 0;

   /* Duplicate the write handle to the pipe so it is not inherited. */
   if (!DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, FALSE,   /* not inherited */
                        DUPLICATE_SAME_ACCESS))
      return 0;

   CloseHandle(hChildStdinWr);

   /* Now create the child process. */
   memset(&siStartInfo, 0, sizeof(siStartInfo));
   siStartInfo.cb = sizeof(STARTUPINFO);
   siStartInfo.lpReserved = NULL;
   siStartInfo.lpReserved2 = NULL;
   siStartInfo.cbReserved2 = 0;
   siStartInfo.lpDesktop = NULL;
   siStartInfo.dwFlags = 0;

   /* command to execute */
   sprintf(buffer, "cmd /q /c %s", cmd);

   if (!CreateProcess(NULL, buffer,     /* command line */
                      NULL,     /* process security attributes */
                      NULL,     /* primary thread security attributes */
                      TRUE,     /* handles are inherited */
                      0,        /* creation flags */
                      NULL,     /* use parent's environment */
                      NULL,     /* use parent's current directory */
                      &siStartInfo,     /* STARTUPINFO pointer */
                      &piProcInfo))     /* receives PROCESS_INFORMATION */
      return 0;

   /* After process creation, restore the saved STDIN and STDOUT. */
   SetStdHandle(STD_INPUT_HANDLE, hSaveStdin);
   SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout);
   SetStdHandle(STD_ERROR_HANDLE, hSaveStderr);

   memset(result, 0, size);

   do {
      /* query stdout */
      do {
         if (!PeekNamedPipe(hChildStdoutRd, buffer, 256, &dwRead, &dwAvail, NULL))
            break;
         if (dwRead > 0) {
            ReadFile(hChildStdoutRd, buffer, 256, &dwRead, NULL);
            buffer[dwRead] = 0;
            strlcat(result, buffer, size);
         }
      } while (dwAvail > 0);

      /* query stderr */
      do {
         if (!PeekNamedPipe(hChildStderrRd, buffer, 256, &dwRead, &dwAvail, NULL))
            break;
         if (dwRead > 0) {
            ReadFile(hChildStderrRd, buffer, 256, &dwRead, NULL);
            buffer[dwRead] = 0;
            strlcat(result, buffer, size);
         }
      } while (dwAvail > 0);

      /* check if subprocess still alive */
      if (!GetExitCodeProcess(piProcInfo.hProcess, &i))
         break;
      if (i != STILL_ACTIVE)
         break;

      /* give some CPU to subprocess */
      Sleep(10);

   } while (TRUE);

   CloseHandle(hChildStdinWrDup);
   CloseHandle(hChildStdinRd);
   CloseHandle(hChildStderrRd);
   CloseHandle(hChildStdoutRd);

   /* strip trailing CR/LF */
   while (strlen(result) > 0 && (result[strlen(result) - 1] == '\r' || result[strlen(result) - 1] == '\n'))
      result[strlen(result) - 1] = 0;

   return 1;

#endif                          /* OS_WINNT */

#ifdef OS_UNIX
   pid_t child_pid;
   int fh, status, wait_status;
   char str[1024];
   char tmp_filename[1024];

   strlcpy(tmp_filename, "/tmp/elog_XXXXXX", sizeof(tmp_filename));
   fh = mkstemp(tmp_filename);
   if (fh == 0) {
      eprintf("Error getting TMP file name.\n");
      return 0;
   }
   close(fh);

   if ((child_pid = fork()) < 0)
      return 0;
   else if (child_pid > 0) {
      /* parent process waits for child */
      do {
         wait_status = waitpid(child_pid, &status, 0);
      } while (wait_status == -1 && errno == EINTR);

      /* read back result */
      memset(result, 0, size);
      fh = open(tmp_filename, O_RDONLY);
      if (fh > 0) {
         read(fh, result, size - 1);
         close(fh);
      }

      /* remove temporary file */
      remove(tmp_filename);

      /* strip trailing CR/LF */
      while (strlen(result) > 0 && (result[strlen(result) - 1] == '\r' || result[strlen(result) - 1] == '\n'))
         result[strlen(result) - 1] = 0;

   } else {
      /* child process */

      /* restore original UID/GID */
      if (setregid(-1, orig_gid) < 0 || setreuid(-1, orig_uid) < 0)
         eprintf("Cannot restore original GID/UID.\n");

      /* give up root privilege permanently */
      if (geteuid() == 0) {
         if (!getcfg("global", "Grp", str, sizeof(str)) || setgroup(str) < 0) {
            eprintf("Falling back to default group \"elog\"\n");
            if (setgroup("elog") < 0) {
               eprintf("Falling back to default group \"%s\"\n", DEFAULT_GROUP);
               if (setgroup(DEFAULT_GROUP) < 0) {
                  eprintf("Refuse to run as setgid root.\n");
                  eprintf("Please consider to define a Grp statement in configuration file\n");
                  exit(EXIT_FAILURE);
               }
            }
         } else if (get_verbose() >= VERBOSE_INFO)
            eprintf("Falling back to group \"%s\"\n", str);

         if (!getcfg("global", "Usr", str, sizeof(str)) || setuser(str) < 0) {
            eprintf("Falling back to default user \"elog\"\n");
            if (setuser("elog") < 0) {
               eprintf("Falling back to default user \"%s\"\n", DEFAULT_USER);
               if (setuser(DEFAULT_USER) < 0) {
                  eprintf("Refuse to run as setuid root.\n");
                  eprintf("Please consider to define a Usr statement in configuration file\n");
                  exit(EXIT_FAILURE);
               }
            }
         } else if (get_verbose() >= VERBOSE_INFO)
            eprintf("Falling back to user \"%s\"\n", str);
      }

      /* execute shell with redirection to /tmp/elog-shell */
      sprintf(str, "/bin/sh -c \"%s\" > %s 2>&1", cmd, tmp_filename);

      if (get_verbose() >= VERBOSE_INFO) {
         efputs("Going to execute: ");
         efputs(str);
         efputs("\n");
      }

      system(str);
      _exit(0);
   }

   return 1;

#endif                          /* OS_UNIX */
}

Project: elog

int check_smtp_error(char *str, int expected, char *error, int error_size) {
   if (atoi(str) != expected) {
      if (error)
         strlcpy(error, str + 4, error_size);
      return 0;
   }

   return 1;
}

Project: elog

void show_config_page(LOGBOOK *lbs) {
   char str[256], user[80], password[80], full_name[256], user_email[256], logbook[256], auth[32], **user_list;
   int i, j, n, cols, inactive;
   BOOL email_notify[1000], sort_email;

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

   /* get user */
   strcpy(user, isparam("unm") ? getparam("unm") : "");
   if (isparam("cfg_user"))
      strcpy(user, getparam("cfg_user"));

   /* get sort_email flag */
   sort_email = FALSE;
   if (isparam("sort_email") && atoi(getparam("sort_email")) > 0)
      sort_email = TRUE;

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

   show_standard_header(lbs, TRUE, loc("ELOG user config"), ".", FALSE, NULL, NULL, 0);

   /*---- javascript to warn removal of user and of deactivation ----*/
   rsprintf("<script type=\"text/javascript\">\n");
   rsprintf("<!--\n\n");
   rsprintf("function chkrem()\n");
   rsprintf("{\n");
   strencode2(str, user, sizeof(str));
   strlcpy(user, str, sizeof(user));
   sprintf(str, loc("Really remove user \\\"%s\\\"?"), user);
   rsprintf("    var subm = confirm(\"%s\");\n", str);
   rsprintf("    return subm;\n");
   rsprintf("}\n\n");
   rsprintf("function chkdeact(c)\n");
   rsprintf("{\n");
   strlcpy(str, loc("Are you sure you want to deactivate your own account?"), sizeof(str));
   rsprintf("    if (c.checked == false)\n");
   rsprintf("       return confirm(\"%s\");\n", str);
   rsprintf("    return true;\n");
   rsprintf("}\n\n");
   rsprintf("//-->\n");
   rsprintf("</script>\n\n");

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

   show_standard_title(lbs, "", 0);

   /*---- activation notice ----*/

   if (isparam("notice")) {
      rsprintf("<tr><td class=\"notifymsg\" colspan=2>\n");
      rsprintf(getparam("notice"));
      rsprintf("</tr>\n");
   }

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

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

   rsprintf("<input type=hidden name=cmd value=\"%s\">\n", loc("Config"));      // for select javascript
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Save"));
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Back"));
   strencode2(str, user, sizeof(str));
   rsprintf("<input type=hidden name=config value=\"%s\">\n", str);
   rsprintf("<input type=hidden name=cfgpage value=\"1\">\n");                  // needed for "Save" command

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

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

   /*---- if admin user, show user list ----*/

   if (is_admin_user(lbs, getparam("unm"))) {
      rsprintf("<input type=hidden name=admin value=1>\n");
      rsprintf("<tr><td nowrap width=\"10%%\">%s:</td>\n", loc("Select user"));
      rsprintf("<td><select name=cfg_user onChange=\"document.form1.submit()\">\n");

      /* count user list */
      for (n = 0;; n++) {
         if (!enum_user_line(lbs, n, str, sizeof(str)))
            break;
      }

      /* allocate list of users and populate it */
      user_list = (char **) xcalloc(sizeof(char *), n);
      for (i = 0; i < n; i++)
         user_list[i] = (char *) xcalloc(NAME_LENGTH, 1);

      for (i = 0; i < n; i++) {
         enum_user_line(lbs, i, user_list[i], NAME_LENGTH);
         get_user_line(lbs, user_list[i], NULL, full_name, user_email, NULL, NULL, NULL);
         if (sort_email)
            strlcpy(user_list[i], user_email, NAME_LENGTH);
      }

      /* sort list */
      qsort(user_list, n, sizeof(char *), ascii_compare);

      for (i = 0; i < n; i++) {
         if (sort_email) {
            strlcpy(user_email, user_list[i], sizeof(user_email));
            user_list[i][0] = 0;
            get_user_line(lbs, user_list[i], NULL, full_name, user_email, NULL, NULL, NULL);
         } else
            get_user_line(lbs, user_list[i], NULL, full_name, user_email, NULL, NULL, NULL);
         if (strcmp(user_list[i], user) == 0) {
            strencode2(str, user_list[i], sizeof(str));
            if (sort_email)
               rsprintf("<option selected value=\"%s\">&lt;%s&gt; %s\n", str, user_email, str);
            else
               rsprintf("<option selected value=\"%s\">%s &lt;%s&gt;\n", str, str, user_email);
         } else {
            strencode2(str, user_list[i], sizeof(str));
            if (sort_email)
               rsprintf("<option value=\"%s\">&lt;%s&gt; %s\n", str, user_email, str);
            else
               rsprintf("<option value=\"%s\">%s &lt;%s&gt;\n", str, str, user_email);

         }
      }

      for (i = 0; i < n; i++)
         xfree(user_list[i]);
      xfree(user_list);

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

      /* show "update" button only of javascript is not enabled */
      rsprintf("<noscript>\n");
      rsprintf("<input type=submit value=\"%s\">\n", loc("Update"));
      rsprintf("</noscript>\n");

      if (sort_email)
         rsprintf(
                 "<input type=\"checkbox\" checked name=\"sort_email\" value=\"1\" onChange=\"document.form1.submit()\">Sort by email");
      else
         rsprintf(
                 "<input type=\"checkbox\" name=\"sort_email\" value=\"1\" onChange=\"document.form1.submit()\">Sort by email");
   }

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

   if (get_user_line(lbs, user, password, full_name, user_email, email_notify, NULL, &inactive) != 1)
      sprintf(str, loc("User [%s] has been deleted"), user);
   else
      strlcpy(str, user, sizeof(str));

   if (is_admin_user(lbs, getparam("unm"))) {
      rsprintf("<tr><td nowrap width=\"15%%\">%s:</td>\n", loc("Active"));
      if (stricmp(user, getparam("unm")) == 0)
         rsprintf
                 ("<td><input type=checkbox name=active value=1 %s onClick=\"return chkdeact(this);\"></td></tr>\n",
                  inactive ? "" : "checked");
      else
         rsprintf("<td><input type=checkbox name=active value=1 %s></td></tr>\n", inactive ? "" : "checked");
   } else {
      rsprintf("<tr><td nowrap width=\"10%%\">%s:</td>\n", loc("Active"));
      rsprintf("<td><input type=checkbox name=active value=1 readonly %s></td></tr>\n",
               inactive ? "" : "checked");
   }

   rsprintf("<tr><td nowrap width=\"15%%\">%s:</td>\n", loc("Login name"));

   getcfg(lbs->name, "Authentication", auth, sizeof(auth));

   strencode2(str, user, sizeof(str));
   if (stristr(auth, "Kerberos") || stristr(auth, "Webserver") || stristr(auth, "PAM"))
      rsprintf("<td><input type=text size=40 name=new_user_name value=\"%s\" readonly></td></tr>\n", str);
   else
      rsprintf("<td><input type=text size=40 name=new_user_name value=\"%s\"></td></tr>\n", str);

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

   rsprintf("<tr><td nowrap width=\"15%%\">Email:</td>\n");
   rsprintf("<td><input type=text size=40 name=new_user_email value=\"%s\"></td></tr>\n", user_email);

   for (i = n = 0; lb_list[i].name[0]; i++) {

      if (!getcfg_topgroup() || strieq(getcfg_topgroup(), lb_list[i].top_group)) {

         /* check if user has access */
         if (!isparam("unm") || check_login_user(&lb_list[i], getparam("unm"))) {

            /* check if emails are enabled for this logbook */
            if (!getcfg(lb_list[i].name, "Suppress email to users", str, sizeof(str)) || atoi(str) == 0)
               n++;
         }
      }
   }

   if (n > 0) {
      for (i = 0; lb_list[i].name[0]; i++) {}
      j = (int) (i / 16) + 1;
      cols = ((j > 5) ? 5 : j);
      rsprintf("<tr><td colspan=\"2\"><br />%s:\n", loc("Subscribe to logbooks"));

      rsprintf("<br><span class=\"selcomment\"><b>(%s)</b></span>\n",
               loc("enable automatic email notifications"));

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

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

      for (j = i = 0; lb_list[i].name[0]; i++) {

         if (!getcfg_topgroup() || strieq(getcfg_topgroup(), lb_list[i].top_group)) {

            /* check if user has access */
            if (!isparam("unm") || check_login_user(&lb_list[i], getparam("unm"))) {

               /* check if emails are enabled for this logbook */
               if (!getcfg(lb_list[i].name, "Suppress email to users", str, sizeof(str)) || atoi(str) == 0) {
                  if (email_notify[i])
                     rsprintf("<td><input type=checkbox checked id=\"lb%d\" name=\"sub_lb%d\" value=\"1\">\n",
                              i,
                              i);
                  else
                     rsprintf("<td><input type=checkbox id=\"lb%d\" name=\"sub_lb%d\" value=\"1\">\n", i, i);
                  rsprintf("<label for=\"lb%d\">%s</label></td>\n", i, lb_list[i].name);
                  j++;
               }
            }
         }
         if (j > 0 && (j % cols) == 0)
            rsprintf("</tr>\n<tr>");
      }
      rsprintf("</tr></table><br />\n");
   }

   if (n > 2) {
      rsprintf("<script language=\"JavaScript\" type=\"text/javascript\">\n");
      rsprintf("<!--\n");
      rsprintf("function SetNone()\n");
      rsprintf("  {\n");
      rsprintf("    for (var i=0,els=document.querySelectorAll('[name^=\"sub_lb\"]') ; i<els.length ; i++)\n");
      rsprintf("      els[i].checked = false;\n");
      rsprintf("  }\n");
      rsprintf("function SetAll()\n");
      rsprintf("  {\n");
      rsprintf("    for (var i=0,els=document.querySelectorAll('[name^=\"sub_lb\"]') ; i<els.length ; i++)\n");
      rsprintf("      els[i].checked = true;\n");
      rsprintf("  }\n");
      rsprintf("//-->\n");
      rsprintf("</script>\n");

      rsprintf("<input type=button value=\"%s\" onClick=\"SetAll();\">\n", loc("Set all"));
      rsprintf("<input type=button value=\"%s\" onClick=\"SetNone();\">\n", loc("Set none"));
   }

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

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

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

   if (is_admin_user(lbs, getparam("unm")) || !getcfg(logbook, "allow password change", str, sizeof(str))
       || atoi(str) == 1)
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Change password"));

   rsprintf("<input type=submit name=cmd value=\"%s\" onClick=\"return chkrem();\">\n", loc("Remove user"));

   if (is_admin_user(lbs, getparam("unm"))) {
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("New user"));
      strlcpy(str, loc("Change config file"), sizeof(str));
      rsprintf("<input type=submit name=cmd value=\"%s\">\n", str);
   }

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

Project: elog

time_t retrieve_date(char *index, BOOL bstart) {
   int year, month, day, hour, min, sec, current_year, current_month;
   char pm[10], py[10], pd[10], ph[10], pn[10], ps[10], str[NAME_LENGTH], str2[NAME_LENGTH];
   struct tm tms;
   time_t ltime;

   sprintf(pm, "m%s", index);
   sprintf(py, "y%s", index);
   sprintf(pd, "d%s", index);
   sprintf(ph, "h%s", index);
   sprintf(pn, "n%s", index);
   sprintf(ps, "c%s", index);

   time(&ltime);
   memcpy(&tms, localtime(&ltime), sizeof(tms));
   current_year = tms.tm_year + 1900;
   current_month = tms.tm_mon + 1;

   if (!isparam(pm) && !isparam(py) && !isparam(pd))
      return 0;

   /* if year not given, use current year */
   if (!isparam(py))
      year = current_year;
   else
      year = atoi(getparam(py));
   if (year < 1970) {
      sprintf(str, "Error: Year %s out of range", getparam(py));
      strencode2(str2, str, sizeof(str2));
      show_error(str2);
      return -1;
   }

   /* if month not given, use current month */
   if (isparam(pm)) {
      month = atoi(getparam(pm));
   } else
      month = current_month;

   if (isparam(pd))
      day = atoi(getparam(pd));
   else {
      /* if day not given, use 1 if start date */
      if (bstart)
         day = 1;
      else {
         /* use last day of month */
         memset(&tms, 0, sizeof(struct tm));
         tms.tm_year = year - 1900;
         tms.tm_mon = month - 1 + 1;
         tms.tm_mday = 1;
         tms.tm_hour = 12;

         if (tms.tm_year < 90)
            tms.tm_year += 100;
         ltime = mktime(&tms);
         ltime -= 3600 * 24;
         memcpy(&tms, localtime(&ltime), sizeof(struct tm));
         day = tms.tm_mday;
      }

   }

   /* if hour not given, use 0 */
   if (isparam(ph)) {
      hour = atoi(getparam(ph));
   } else
      hour = 0;

   /* if minute not given, use 0 */
   if (isparam(pn)) {
      min = atoi(getparam(pn));
   } else
      min = 0;

   /* if second not given, use 0 */
   if (isparam(ps)) {
      sec = atoi(getparam(ps));
   } else
      sec = 0;

   memset(&tms, 0, sizeof(struct tm));
   tms.tm_year = year - 1900;
   tms.tm_mon = month - 1;
   tms.tm_mday = day;
   tms.tm_hour = hour;
   tms.tm_min = min;
   tms.tm_sec = sec;
   tms.tm_isdst = -1;

   if (tms.tm_year < 90)
      tms.tm_year += 100;

   ltime = mktime(&tms);

   if (!bstart && isparam(ph) == 0)
      /* end time is first second of next day */
      ltime += 3600 * 24;

   return ltime;
}

Project: elog

void rsputs3(const char *text) {
   int i;
   char str[2];

   str[1] = 0;
   for (i = 0; i < (int) strlen(text); i++) {
      switch (text[i]) {
         case '<':
            rsputs("&lt;");
            break;
         case '>':
            rsputs("&gt;");
            break;
         case '&':
            rsputs("&amp;");
            break;
         case '\"':
            rsputs("&quot;");
            break;

         default:
            str[0] = text[i];
            rsputs(str);
      }
   }
}

Project: elog

int submit_elog_reply(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH], char *text) {
   int n_reply, i, status;
   char str1[80], str2[80], att_file[MAX_ATTACHMENTS][256], reply_to[MAX_REPLY_TO * 10],
           list[MAX_N_ATTR][NAME_LENGTH];

   status = el_retrieve(lbs, message_id, NULL, attr_list, NULL, 0,
                        NULL, NULL, NULL, reply_to, att_file, NULL, NULL, NULL);
   if (status != EL_SUCCESS)
      return status;

   sprintf(str1, "- %s -", loc("keep original text"));
   sprintf(str2, "<p>- %s -</p>", loc("keep original text"));
   if (strcmp(text, str1) == 0 || strcmp(text, str2) == 0)
      message_id = el_submit(lbs, message_id, TRUE, "<keep>", attr_list, attrib, lbs->n_attr, "<keep>",
                             "<keep>", "<keep>", "<keep>", att_file, TRUE, NULL, NULL);
   else
      message_id = el_submit(lbs, message_id, TRUE, "<keep>", attr_list, attrib, lbs->n_attr, text, "<keep>",
                             "<keep>", "<keep>", att_file, TRUE, NULL, NULL);

   if (message_id < 0)
      return 0;

   if (isparam("elmode") && strieq(getparam("elmode"), "threaded")) {

      // go through all replies in threaded mode
      n_reply = strbreak(reply_to, list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < n_reply; i++) {
         submit_elog_reply(lbs, atoi(list[i]), attrib, text);
      }
   }

   return EL_SUCCESS;
}

Project: elog

void add_subst_list(char list[][NAME_LENGTH], char value[][NAME_LENGTH], char *item, char *str, int *i) {
   strlcpy(list[*i], item, NAME_LENGTH);
   strlcpy(value[(*i)++], str, NAME_LENGTH);
}

Project: elog

void remove_crlf(char *buffer) {
   char *p;

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

Project: elog

int el_submit(LOGBOOK *lbs, int message_id, BOOL bedit, char *date, char attr_name[MAX_N_ATTR][NAME_LENGTH],
              char attr_value[MAX_N_ATTR][NAME_LENGTH], int n_attr, char *text, char *in_reply_to,
              char *reply_to, char *encoding, char afilename[MAX_ATTACHMENTS][256], BOOL mark_original,
              char *locked_by, char *draft)
/********************************************************************
 Routine: el_submit

 Purpose: Submit an ELog entry

 Input:
 LOGBOOK lbs             Logbook structure
 int    message_id       Message id
 BOOL   bedit            TRUE for existing message, FALSE for new message
 char   *date            Message date
 char   attr_name[][]    Name of attributes
 char   attr_value[][]   Value of attributes
 int    n_attr           Number of attributes

 char   *text            Message text
 char   *in_reply_to     In reply to this message
 char   *reply_to        Replie(s) to this message
 char   *encoding        Text encoding, either HTML or plain

 char   *afilename[]     File name of attachments
 char   *tag             If given, edit existing message
 int    *tag_size        Maximum size of tag
 BOOL   mark_original    Tag original message for replies
 char   *locked_by       User/Host which locked message for edit
 char   *draft           User which drafted message

 Function value:
 int                     New message ID

 \********************************************************************/
{
   int n, i, j, size, fh, index, tail_size, orig_size, delta, reply_id;
   char file_name[MAX_PATH_LENGTH * 3], dir[256], str[NAME_LENGTH], date1[256], attrib[MAX_N_ATTR][NAME_LENGTH],
           reply_to1[MAX_REPLY_TO * 10], in_reply_to1[MAX_REPLY_TO * 10], encoding1[80], *message, *p,
           *old_text, *buffer, locked_by1[256];
   char attachment_all[64 * MAX_ATTACHMENTS], subdir[MAX_PATH_LENGTH];
   time_t ltime;

   tail_size = orig_size = 0;

   buffer = NULL;
   message = xmalloc(TEXT_SIZE + 100);
   old_text = NULL;
   memcpy(attrib, attr_value, sizeof(attrib));
   strlcpy(reply_to1, reply_to, sizeof(reply_to1));
   strlcpy(in_reply_to1, in_reply_to, sizeof(in_reply_to1));
   strlcpy(encoding1, encoding, sizeof(encoding1));
   strlcpy(date1, date, sizeof(date1));
   if (locked_by)
      strlcpy(locked_by1, locked_by, sizeof(locked_by1));
   else
      locked_by1[0] = 0;

   /* generate new file name YYMMDD.log in data directory */
   strcpy(dir, lbs->data_dir);

   if (bedit) {
      /* edit existing message */
      for (index = 0; index < *lbs->n_el_index; index++)
         if (lbs->el_index[index].message_id == message_id)
            break;

      if (index == *lbs->n_el_index) {
         xfree(message);
         return -1;
      }

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

      lseek(fh, lbs->el_index[index].offset, SEEK_SET);
      i = my_read(fh, message, TEXT_SIZE + 100 - 1);
      if (i >= 0)
         message[i] = 0;
      else
         message[0] = 0;

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

         /* file might have been edited, rebuild index */
         el_build_index(lbs, TRUE);
         return el_submit(lbs, message_id, bedit, date, attr_name, attrib, n_attr, text, in_reply_to,
                          reply_to, encoding, afilename, mark_original, locked_by, draft);
      }

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

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

      message[size] = 0;

      if (strcmp(text, "<keep>") == 0) {

         p = strstr(message, "========================================\n");
         /* check for \n -> \r conversion (e.g. zipping/unzipping) */
         if (p == NULL)
            p = strstr(message, "========================================\r");
         if (p) {
            p += 41;
            old_text = xmalloc(size + 1);
            strlcpy(old_text, p, size);
            if (old_text[strlen(old_text) - 1] == '\n' || old_text[strlen(old_text) - 1] == '\r')
               old_text[strlen(old_text) - 1] = 0;
         }
      }

      if (strieq(date1, "<keep>"))
         el_decode(message, "Date: ", date1, sizeof(date1));
      else
         strlcpy(date1, date, sizeof(date1));
      if (strieq(locked_by1, "<keep>"))
         el_decode_intlist(message, "Locked by: ", locked_by1, sizeof(locked_by1));
      if (strieq(reply_to1, "<keep>"))
         el_decode_intlist(message, "Reply to: ", reply_to1, sizeof(reply_to1));
      if (strieq(in_reply_to1, "<keep>"))
         el_decode_int(message, "In reply to: ", in_reply_to1, sizeof(in_reply_to1));
      if (strieq(encoding1, "<keep>"))
         el_decode(message, "Encoding: ", encoding1, sizeof(encoding1));
      el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));

      for (i = 0; i < n_attr; i++) {
         sprintf(str, "%s: ", attr_name[i]);
         if (strieq(attrib[i], "<keep>"))
            el_decode(message, str, attrib[i], NAME_LENGTH);
      }

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

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

         lseek(fh, lbs->el_index[index].offset + size, SEEK_SET);
         n = my_read(fh, buffer, tail_size);
      }
      lseek(fh, lbs->el_index[index].offset, SEEK_SET);
   } else {
      /* create new message */
      if (!date[0]) {
         get_rfc2822_date(date1, sizeof(date1), 0);
         ltime = date_to_ltime(date1);
      } else {
         ltime = date_to_ltime(date);
         get_rfc2822_date(date1, sizeof(date1), ltime);
      }

      for (i = 0; i < 12; i++)
         if (strncmp(date1 + 8, mname[i], 3) == 0)
            break;

      snprintf(file_name, sizeof(file_name), "%c%c%02d%c%ca.log", date1[14], date1[15], i + 1, date1[5], date1[6]);

      generate_subdir_name(file_name, subdir, sizeof(subdir));
      sprintf(str, "%s%s", dir, subdir);
      if (strlen(str) > 0 && str[strlen(str) - 1] == DIR_SEPARATOR)
         str[strlen(str) - 1] = 0;

#ifdef OS_WINNT
      mkdir(str);
#else
      mkdir(str, 0755);
#endif

      sprintf(str, "%s%s%s", dir, subdir, file_name);
      fh = open(str, O_CREAT | O_RDWR | O_BINARY, 0644);
      if (fh < 0) {
         xfree(message);
         if (old_text)
            xfree(old_text);
         return -1;
      }

      lseek(fh, 0, SEEK_END);

      /* new message id is old plus one */
      if (message_id == 0) {
         message_id = 1;
         for (i = 0; i < *lbs->n_el_index; i++)
            if (lbs->el_index[i].message_id >= message_id)
               message_id = lbs->el_index[i].message_id + 1;
      }

      /* enter message in index */
      index = *lbs->n_el_index;

      (*lbs->n_el_index)++;
      lbs->el_index = xrealloc(lbs->el_index, sizeof(EL_INDEX) * (*lbs->n_el_index));
      lbs->el_index[index].message_id = message_id;
      strlcpy(lbs->el_index[index].file_name, file_name, sizeof(lbs->el_index[index].file_name));
      strlcpy(lbs->el_index[index].subdir, subdir, sizeof(lbs->el_index[index].subdir));
      lbs->el_index[index].file_time = ltime;
      lbs->el_index[index].offset = TELL(fh);
      lbs->el_index[index].in_reply_to = atoi(in_reply_to1);

      /* if index not ordered, sort it */
      i = *lbs->n_el_index;
      if (i > 1 && lbs->el_index[i - 1].file_time < lbs->el_index[i - 2].file_time) {
         qsort(lbs->el_index, i, sizeof(EL_INDEX), eli_compare);

         /* search message again, index could have been changed by sorting */
         for (index = 0; index < *lbs->n_el_index; index++)
            if (lbs->el_index[index].message_id == message_id)
               break;
      }

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

   /* compose message */

   sprintf(message, "$@MID@$: %d\n", message_id);
   sprintf(message + strlen(message), "Date: %s\n", date1);

   if (reply_to1[0])
      sprintf(message + strlen(message), "Reply to: %s\n", reply_to1);

   if (in_reply_to1[0])
      sprintf(message + strlen(message), "In reply to: %s\n", in_reply_to1);

   for (i = 0; i < n_attr; i++)
      sprintf(message + strlen(message), "%s: %s\n", attr_name[i], attrib[i]);

   sprintf(message + strlen(message), "Attachment: ");

   if (afilename) {
      sprintf(message + strlen(message), "%s", afilename[0]);
      for (i = 1; i < MAX_ATTACHMENTS; i++)
         if (afilename[i][0])
            sprintf(message + strlen(message), ",%s", afilename[i]);
   }
   sprintf(message + strlen(message), "\n");

   sprintf(message + strlen(message), "Encoding: %s\n", encoding1);
   if (locked_by1[0])
      sprintf(message + strlen(message), "Locked by: %s\n", locked_by1);
   if (draft && draft[0])
      sprintf(message + strlen(message), "Draft: %s\n", draft);

   sprintf(message + strlen(message), "========================================\n");

   if (strieq(text, "<keep>") && old_text)
      strlcat(message, old_text, TEXT_SIZE + 100);
   else
      strlcat(message, text, TEXT_SIZE + 100);
   strlcat(message, "\n", TEXT_SIZE + 100);

   if (old_text)
      xfree(old_text);

   n = write(fh, message, strlen(message));
   if (n != (int) strlen(message)) {
      if (tail_size > 0)
         xfree(buffer);
      close(fh);
      return -1;
   }

   /* update MD5 checksum */
   MD5_checksum(message, strlen(message), lbs->el_index[index].md5_digest);

   if (bedit) {
      if (tail_size > 0) {
         n = write(fh, buffer, tail_size);
         xfree(buffer);

         /* correct offsets for remaining messages in same file */
         delta = strlen(message) - orig_size;

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

         for (j = i + 1; j < *lbs->n_el_index && strieq(lbs->el_index[i].file_name,
                                                        lbs->el_index[j].file_name); j++)
            lbs->el_index[j].offset += delta;
      }

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

   close(fh);

   /* if reply, mark original message */
   reply_id = atoi(in_reply_to);

   if (mark_original && in_reply_to[0] && !bedit && atoi(in_reply_to) > 0) {
      char date[80], attr[MAX_N_ATTR][NAME_LENGTH], enc[80], att[MAX_ATTACHMENTS][256],
              reply_to[MAX_REPLY_TO * 10], in_reply_to[MAX_REPLY_TO * 10], lock[256], draft[256];

      /* retrieve original message */
      size = TEXT_SIZE + 100;
      el_retrieve(lbs, reply_id, date, attr_list, attr, n_attr, message, &size, in_reply_to, reply_to, att,
                  enc, lock, draft);

      if (reply_to[0])
         strcat(reply_to, ", ");
      sprintf(reply_to + strlen(reply_to), "%d", message_id);

      /* write modified message */
      el_submit(lbs, reply_id, TRUE, date, attr_list, attr, n_attr, message, in_reply_to, reply_to, enc, att,
                TRUE, lock, draft);
   }

   xfree(message);
   return message_id;
}

Project: elog

int sid_new(LOGBOOK *lbs, const char *user, const char *host, char *sid) {
   double exp;
   time_t now;
   int i, new_i;
   char str[256];

   time(&now);
   new_i = 0;

   if (_sid == NULL) {
      _sid = (SESSION_ID *) malloc(sizeof(SESSION_ID));
      _n_sid = 1;
      new_i = 0;
   } else {

      exp = 24;
      str[0] = 0;
      if (lbs == NULL)
         getcfg("global", "Login expiration", str, sizeof(str));
      else
         getcfg(lbs->name, "Login expiration", str, sizeof(str));

      if (atof(str) > 0)
         exp = atof(str);
      if (exp < 24)
         exp = 24;              /* one day minimum for dangling edit pages */

      /* search for expired sid */
      for (i = 0; i < _n_sid; i++) {
         if (_sid[i].time + exp * 3600 < now) {
            new_i = i;
            break;
         }
      }

      if (i == _n_sid) {
         _sid = (SESSION_ID *) realloc(_sid, sizeof(SESSION_ID) * (_n_sid + 1));
         new_i = _n_sid;
         _n_sid++;
      }
   }

   strlcpy(_sid[new_i].user_name, user, sizeof(_sid[0].user_name));
   strlcpy(_sid[new_i].host_ip, host, sizeof(_sid[0].host_ip));
   for (i = 0; i < 4; i++)
      sprintf(sid + i * 4, "%04X", rand() % 0x10000);
   sid[16] = 0;
   strlcpy(_sid[new_i].session_id, sid, sizeof(_sid[0].session_id));
   _sid[new_i].time = now;

   return 1;
}

Project: elog

int msg_compare_reverse_numeric(const void *m1, const void *m2) {
   return ((MSG_LIST *) m2)->number - ((MSG_LIST *) m1)->number;
}

Project: elog

int el_index_logbooks() {
   char str[256], data_dir[256], logbook[256], cwd[256], *p;
   int i, j, n, status = 0;

   if (lb_list) {
      for (i = 0; lb_list[i].name[0]; i++) {
         if (lb_list[i].el_index != NULL) {
            xfree(lb_list[i].el_index);
            xfree(lb_list[i].n_el_index);

            /* check if other logbook uses same index */
            for (j = i + 1; lb_list[j].name[0]; j++) {
               /* mark that logbook already freed */
               if (lb_list[j].el_index == lb_list[i].el_index)
                  lb_list[j].el_index = NULL;
            }
         }
      }
      xfree(lb_list);
   }

   /* count logbooks */
   for (i = n = 0;; i++) {
      if (!enumgrp(i, str))
         break;

      if (!is_logbook(str))
         continue;

      n++;
   }

   lb_list = xcalloc(sizeof(LOGBOOK), n + 1);
   for (i = n = 0;; i++) {
      if (!enumgrp(i, logbook))
         break;

      if (!is_logbook(logbook))
         continue;

      /* check for duplicate name */
      for (j = 0; j < i && lb_list[j].name[0]; j++)
         if (strieq(lb_list[j].name, logbook)) {
            eprintf("Error in configuration file: Duplicate logbook \"%s\"\n", logbook);
            return EL_DUPLICATE;
         }

      /* store logbook in list */
      strcpy(lb_list[n].name, logbook);
      strcpy(lb_list[n].name_enc, logbook);
      url_encode(lb_list[n].name_enc, sizeof(lb_list[n].name_enc));

      /* get data dir from configuration file (old method) */
      if (getcfg(logbook, "Data dir", str, sizeof(str))) {
         if (str[0] == DIR_SEPARATOR || str[1] == ':')
            strlcpy(data_dir, str, sizeof(data_dir));
         else {
            strlcpy(data_dir, resource_dir, sizeof(data_dir));
            strlcat(data_dir, str, sizeof(data_dir));
         }
      } else {
         /* use logbook_dir + "Subdir" (new method) */
         strlcpy(data_dir, logbook_dir, sizeof(data_dir));
         if (data_dir[strlen(data_dir) - 1] != DIR_SEPARATOR)
            strlcat(data_dir, DIR_SEPARATOR_STR, sizeof(data_dir));

         if (getcfg(logbook, "Subdir", str, sizeof(str))) {
            if (str[0] == DIR_SEPARATOR)
               strlcpy(data_dir, str, sizeof(data_dir));
            else
               strlcat(data_dir, str, sizeof(data_dir));
         } else
            strlcat(data_dir, logbook, sizeof(data_dir));       /* use logbook name as default */
      }

      if (data_dir[strlen(data_dir) - 1] != DIR_SEPARATOR)
         strlcat(data_dir, DIR_SEPARATOR_STR, sizeof(data_dir));

      /* create data directory if not existing */
      getcwd(cwd, sizeof(cwd));

      j = chdir(data_dir);
      if (j < 0) {
         p = data_dir;
         if (*p == DIR_SEPARATOR) {
            chdir(DIR_SEPARATOR_STR);
            p++;
         }
         if (p[1] == ':') {
            strcpy(str, p);
            if (str[2] == DIR_SEPARATOR)
               str[3] = 0;
            else
               str[2] = 0;

            chdir(str);
            p += strlen(str);
         }

         do {
            if (strchr(p, DIR_SEPARATOR)) {
               strlcpy(str, p, sizeof(str));
               *strchr(str, DIR_SEPARATOR) = 0;
               p = strchr(p, DIR_SEPARATOR) + 1;
            } else {
               strlcpy(str, p, sizeof(str));
               p = NULL;
            }

            j = chdir(str);
            if (j < 0) {

#ifdef OS_WINNT
               j = mkdir(str);
#else
               j = mkdir(str, 0755);
#endif

               if (j == 0) {
                  if (get_verbose() >= VERBOSE_INFO)
                     eprintf("Created directory \"%s\"\n", str);
               } else {
                  eprintf("el_index_logbooks: %s\n", strerror(errno));
                  eprintf("Cannot create directory \"%s\"\n", str);
               }

               chdir(str);
            }

         } while (p && *p);
      }

      chdir(cwd);
      strcpy(lb_list[n].data_dir, data_dir);
      lb_list[n].el_index = NULL;

      /* check if other logbook uses the same directory */
      for (j = 0; j < n; j++)
         if (strcmp(lb_list[j].data_dir, lb_list[n].data_dir) == 0) {
            if (get_verbose() >= VERBOSE_INFO)
               eprintf("Logbook \"%s\" uses same directory as logbook \"%s\"\n", logbook, lb_list[j].name);
            lb_list[n].el_index = lb_list[j].el_index;
            lb_list[n].n_el_index = lb_list[j].n_el_index;
            break;
         }

      if (j == n) {
         if (get_verbose() >= VERBOSE_INFO)
            eprintf("Indexing logbook \"%s\" in \"%s\" ... ", logbook, lb_list[n].data_dir);
         eflush();
         status = el_build_index(&lb_list[n], FALSE);
         if (get_verbose() >= VERBOSE_INFO)
            if (status == EL_SUCCESS)
               eprintf("ok\n");
      }

      if (status == EL_EMPTY) {
         if (get_verbose() >= VERBOSE_INFO)
            eprintf("Found empty logbook \"%s\"\n", logbook);
      } else if (status != EL_SUCCESS) {
         eprintf("Error generating index.\n");
         return status;
      }

      n++;
   }

   /* if top groups defined, set top group in logbook */
   if (exist_top_group()) {
      LBLIST phier;

      phier = get_logbook_hierarchy();
      for (i = 0; i < phier->n_members; i++)
         if (phier->member[i]->is_top)
            for (j = 0; lb_list[j].name[0]; j++)
               if (is_logbook_in_group(phier->member[i], lb_list[j].name))
                  strcpy(lb_list[j].top_group, phier->member[i]->name);

      free_logbook_hierarchy(phier);
   }

   if (!load_password_files())
      return EL_INVAL_FILE;

   return EL_SUCCESS;
}

Project: elog

int eli_compare(const void *e1, const void *e2) {

   if (((EL_INDEX *) e1)->file_time < ((EL_INDEX *) e2)->file_time)
      return -1;
   if (((EL_INDEX *) e1)->file_time >= ((EL_INDEX *) e2)->file_time)
      return 1;
   return 0;
}

Project: elog

int rename_logbook(LOGBOOK *lbs, char *new_name) {
   int fh, i, length, bufsize;
   char *buf, *buf2, *p1, *p2;
   char str[256], lb_dir[256], old_dir[256], new_dir[256];

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

   /* rename logbook file */
   if (!getcfg(lbs->name, "Subdir", str, sizeof(str))) {
      strlcpy(lb_dir, logbook_dir, sizeof(lb_dir));
      if (lb_dir[strlen(lb_dir) - 1] != DIR_SEPARATOR)
         strlcat(lb_dir, DIR_SEPARATOR_STR, sizeof(lb_dir));

      sprintf(old_dir, "%s%s", lb_dir, lbs->name);
      sprintf(new_dir, "%s%s", lb_dir, new_name);
      rename(old_dir, new_dir);
   }

   /* change logbook name in groups */
   change_logbook_in_group(lbs, new_name);

   /* read previous contents */
   length = lseek(fh, 0, SEEK_END);
   lseek(fh, 0, SEEK_SET);
   bufsize = 2 * (length + strlen(new_name) + 10);
   buf = xmalloc(bufsize);
   read(fh, buf, length);
   buf[length] = 0;

   /* find logbook config */
   p1 = (char *) find_section(buf, lbs->name);
   p2 = strchr(p1, ']');
   if (p2 == NULL) {
      close(fh);
      xfree(buf);
      show_error(loc("Syntax error in config file"));
      return 0;
   }
   p2++;

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

   /* replace logbook name */
   sprintf(p1, "[%s]", new_name);

   strlcat(p1, buf2, length + strlen(new_name) + 1);
   xfree(buf2);

   adjust_crlf(buf, bufsize);

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

   TRUNCATE(fh);

   close(fh);
   xfree(buf);

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

   return 1;
}

Project: elog

void set_condition(char *c) {
   strlcpy(_condition, c, sizeof(_condition));
}

Project: elog

void *xrealloc(void *pointer, size_t bytes) {
   char *temp;
   int old_size;

   /* Align buffer on 4 byte boundery for HP UX and other 64 bit systems to prevent Bus error (core dump) */
   if (bytes & 3)
      bytes += 4 - (bytes & 3);

   if (pointer == NULL)
      return xmalloc(bytes);

   /* check old magic number */
   temp = pointer;
   assert(*((unsigned int *) (temp - 4)) == 0xdeadc0de);
   old_size = *((unsigned int *) (temp - 8));
   assert(*((unsigned int *) (temp + old_size)) == 0xdeadc0de);

   temp = (char *) realloc(temp - 8, bytes + 12);

   if (temp == 0)
      memory_error_and_abort("xrealloc");

   /* put magic number around array */
   *(unsigned int *) (temp + 0) = bytes;
   *(unsigned int *) (temp + 4) = 0xdeadc0de;
   *(unsigned int *) (temp + bytes + 8) = 0xdeadc0de;

   return (temp + 8);
}

Project: elog

void check_config() {
   check_config_file(FALSE);
   check_language();
}

Project: elog

int scan_dir_tree(LOGBOOK *lbs, const char *dir, char **file_list, int *n) {
   int index, n_files;
   char str[MAX_PATH_LENGTH];
   char *fl, *p;

   fl = NULL;
   n_files = ss_file_find(dir, "*", &fl);
   if (n_files == 0) {
      if (fl)
         xfree(fl);
      return 0;
   }

   if (*file_list == NULL)
      *file_list = (char *) xmalloc(n_files * MAX_PATH_LENGTH);
   else
      *file_list = (char *) xrealloc(*file_list, ((*n) + n_files) * MAX_PATH_LENGTH);

   /* go through all files */
   for (index = 0; index < n_files; index++) {
      if (fnmatch1("??????a.log", &fl[index * MAX_PATH_LENGTH]) == 0) {
         p = *file_list + ((*n) * MAX_PATH_LENGTH);
         strlcpy(p, dir, MAX_PATH_LENGTH);
         if (p[strlen(p) - 1] != DIR_SEPARATOR)
            strlcat(p, DIR_SEPARATOR_STR, MAX_PATH_LENGTH);
         strlcat(p, fl + index * MAX_PATH_LENGTH, MAX_PATH_LENGTH);
         (*n)++;
      }
   }

   /* go through all sub-directories */
   for (index = 0; index < n_files; index++) {
      if (fnmatch1("????", &fl[index * MAX_PATH_LENGTH]) == 0 ||
          fnmatch1("??", &fl[index * MAX_PATH_LENGTH]) == 0) {
         if (strieq(fl + index * MAX_PATH_LENGTH, ".."))
            continue;
         strlcpy(str, dir, sizeof(str));
         if (str[strlen(str) - 1] != DIR_SEPARATOR)
            strlcat(str, DIR_SEPARATOR_STR, sizeof(str));
         strlcat(str, fl + index * MAX_PATH_LENGTH, sizeof(str));
         scan_dir_tree(lbs, str, file_list, n);
      }
   }

   if (fl)
      xfree(fl);

   return *n;
}

Project: elog

void redirect(LOGBOOK *lbs, char *rel_path) {
   /* redirect */
   rsprintf("HTTP/1.1 302 Found\r\n");
   rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
   if (keep_alive) {
      rsprintf("Connection: Keep-Alive\r\n");
      rsprintf("Keep-Alive: timeout=60, max=10\r\n");
   }

   set_location(lbs, rel_path);
}

Project: elog

BOOL strnieq(const char *str1, const char *str2, int n) {
   char c1, c2;
   int i;

   if (str1 == NULL && str2 == NULL && n == 0)
      return TRUE;
   if (str1 == NULL || str2 == NULL)
      return FALSE;
   if ((int) strlen(str1) < n || (int) strlen(str2) < n)
      return FALSE;

   for (i = 0; i < n && *str1; i++) {
      c1 = *str1++;
      c2 = *str2++;
      if (my_toupper(c1) != my_toupper(c2))
         return FALSE;
   }

   if (i < n)
      return FALSE;

   return TRUE;
}

Project: elog

void fputs_syslog(const char *buf) {
   char *p;

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

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

   xfree(p);
}

Project: elog

const char *find_next_section(const char *buf) {
   do {
      if (*buf == '[')
         return buf;

      if (buf)
         buf = strchr(buf, '\n');
      if (buf)
         buf++;
   } while (buf);

   return NULL;
}

Project: elog

void display_reply(LOGBOOK *lbs, int message_id, int printable, int expand, int n_line, int n_attr_disp,
                   char disp_attr[MAX_N_ATTR + 4][NAME_LENGTH], BOOL show_text, int level, int highlight,
                   regex_t *re_buf, int highlight_mid, int absolute_link) {
   char *date, *text, *in_reply_to, *reply_to, *encoding, *attachment, *locked_by, *draft, *attrib, *p;
   int status, size;

   text = (char *) xmalloc(TEXT_SIZE);
   attachment = (char *) xmalloc(MAX_PATH_LENGTH * MAX_ATTACHMENTS);
   attrib = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH);
   date = (char *) xmalloc(80);
   in_reply_to = (char *) xmalloc(80);
   reply_to = (char *) xmalloc(256);
   encoding = (char *) xmalloc(80);
   locked_by = (char *) xmalloc(256);
   draft = (char *) xmalloc(256);

   if (draft == NULL)
      return;

   reply_to[0] = 0;
   size = TEXT_SIZE;
   status = el_retrieve(lbs, message_id, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text, &size,
                        in_reply_to, reply_to, (char (*)[256]) attachment, encoding, locked_by, draft);

   if (status != EL_SUCCESS || draft[0]) {
      xfree(text);
      xfree(attachment);
      xfree(attrib);
      xfree(date);
      xfree(in_reply_to);
      xfree(reply_to);
      xfree(encoding);
      xfree(locked_by);
      xfree(draft);
      return;
   }

   display_line(lbs, message_id, 0, "threaded", expand, level, printable, n_line, FALSE, FALSE, date,
                in_reply_to, reply_to, n_attr_disp, disp_attr, NULL, (char (*)[1500]) attrib, lbs->n_attr,
                text, show_text, (char (*)[256]) attachment, encoding, 0, NULL, locked_by, highlight,
                &re_buf[0], highlight_mid, absolute_link, draft);

   if (reply_to[0]) {
      p = reply_to;
      do {
         display_reply(lbs, atoi(p), printable, expand, n_line, n_attr_disp, disp_attr, show_text, level + 1,
                       highlight, &re_buf[0], highlight_mid, absolute_link);

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

   xfree(text);
   xfree(attachment);
   xfree(attrib);
   xfree(date);
   xfree(in_reply_to);
   xfree(reply_to);
   xfree(encoding);
   xfree(locked_by);
   xfree(draft);
}

Project: elog

int remove_user(LOGBOOK *lbs, char *user) {
   char file_name[256], str[1000], str2[1000];
   PMXML_NODE node;

   if (lbs->pwd_xml_tree == NULL) {
      show_error("No password file loaded");
      return FALSE;
   }

   sprintf(str, "/list/user[name=%s]", user);
   node = mxml_find_node(lbs->pwd_xml_tree, str);
   if (node == NULL) {
      sprintf(str, loc("User \"%s\" not found in password file"), user);
      strencode2(str2, str, sizeof(str2));
      show_error(str2);
      return FALSE;
   }

   mxml_delete_node(node);

   if (get_password_file(lbs, file_name, sizeof(file_name))) {
      if (!mxml_write_tree(file_name, lbs->pwd_xml_tree)) {
         sprintf(str, loc("Cannot write to file <b>%s</b>"), file_name);
         strcat(str, ": ");
         strcat(str, strerror(errno));
         show_error(str);
         return FALSE;
      }
   }

   return TRUE;
}

Project: elog

void mprint(LOGBOOK *lbs, int mode, char *str) {
   char line[1000];

   if (mode == SYNC_HTML)
      rsprintf("%s\n", str);
   else if (mode == SYNC_CRON) {
      if (_logging_level > 1) {
         sprintf(line, "MIRROR: %s", str);
         write_logfile(lbs, line);
      }
   } else
      eputs(str);
}

Project: elog

BOOL match_param(char *str, char *param, int conditional_only) {
   int ncl, npl, nand, i, j, k;
   char *p, pcond[256], clist[10][NAME_LENGTH], plist[10][NAME_LENGTH], alist[10][NAME_LENGTH];

   if (conditional_only && str[0] != '{')
      return FALSE;

   if (!_condition[0] || str[0] != '{')
      return (stricmp(str, param) == 0);

   p = str;
   if (strchr(p, '}'))
      p = strchr(p, '}') + 1;
   while (*p == ' ')
      p++;

   strlcpy(pcond, str, sizeof(pcond));
   if (strchr(pcond, '}'))
      *strchr(pcond, '}') = 0;
   if (strchr(pcond, '{'))
      *strchr(pcond, '{') = ' ';

   npl = strbreak(pcond, plist, 10, ",", FALSE);
   ncl = strbreak(_condition, clist, 10, ",", FALSE);

   for (i = 0; i < ncl; i++)
      for (j = 0; j < npl; j++)
         if (stricmp(clist[i], plist[j]) == 0) {
            /* condition matches */
            return stricmp(p, param) == 0;
         }

   /* check and'ed conditions */
   for (i = 0; i < npl; i++)
      if (strchr(plist[i], '&')) {
         nand = strbreak(plist[i], alist, 10, "&", FALSE);
         for (j = 0; j < nand; j++) {
            for (k = 0; k < ncl; k++)
               if (stricmp(clist[k], alist[j]) == 0)
                  break;

            if (k == ncl)
               return FALSE;
         }

         if (j == nand)
            return stricmp(p, param) == 0;
      }

   return 0;
}

Project: elog

int enumcfg(char *group, char *param, int psize, char *value, int vsize, int index) {
   int i;

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

         if (index < lb_config[i].n_params) {
            strlcpy(param, lb_config[i].config_param[index].param, psize);
            strlcpy(value, lb_config[i].config_param[index].value, vsize);
            return 1;
         }

         return 0;

      }

   return 0;
}

Project: elog

void copy_to(LOGBOOK *lbs, int src_id, char *dest_logbook, int move, int orig_id) {
   int size, i, j, n, n_done, n_done_reply, n_reply, index, status, fh, source_id, message_id,
           thumb_status, next_id = 0;
   char str[256], str2[256], file_name[MAX_PATH_LENGTH], thumb_name[MAX_PATH_LENGTH],
           *attrib, date[80], *text, msg_str[32], in_reply_to[80], subdir[256],
           reply_to[MAX_REPLY_TO * 10], *attachment, encoding[80], locked_by[256], draft[256],
           *buffer, *list;
   LOGBOOK *lbs_dest;
   BOOL bedit;

   attachment = xmalloc(MAX_ATTACHMENTS * MAX_PATH_LENGTH);
   attrib = xmalloc(MAX_N_ATTR * NAME_LENGTH);
   list = xmalloc(MAX_N_ATTR * NAME_LENGTH);
   text = xmalloc(TEXT_SIZE);

   for (i = 0; lb_list[i].name[0]; i++)
      if (strieq(lb_list[i].name, dest_logbook))
         break;
   if (!lb_list[i].name[0])
      return;
   lbs_dest = &lb_list[i];

   if (src_id)
      n = 1;
   else
      n = isparam("nsel") ? atoi(getparam("nsel")) : 0;

   n_done = n_done_reply = source_id = status = next_id = 0;
   for (index = 0; index < n; index++) {
      if (src_id)
         source_id = src_id;
      else {
         sprintf(str, "s%d", index);
         if (!isparam(str))
            continue;

         source_id = isparam(str) ? atoi(getparam(str)) : 0;
      }

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

      if (status != EL_SUCCESS) {
         sprintf(msg_str, "%d", source_id);
         sprintf(str, loc("Entry %s cannot be read from logbook \"%s\""), msg_str, lbs->name);
         show_error(str);
         xfree(attachment);
         xfree(attrib);
         xfree(list);
         xfree(text);
         return;
      }

      if (orig_id == 0) {
         /* search message head */
         while (atoi(in_reply_to) > 0) {
            source_id = atoi(in_reply_to);
            size = TEXT_SIZE;
            status = el_retrieve(lbs, source_id, date, attr_list, (void *) attrib, lbs->n_attr, text, &size,
                                 in_reply_to, reply_to, (void *) attachment, encoding, locked_by, draft);

            if (status != EL_SUCCESS) {
               sprintf(msg_str, "%d", source_id);
               sprintf(str, loc("Entry %s cannot be read from logbook \"%s\""), msg_str, lbs->name);
               show_error(str);
               xfree(attachment);
               xfree(attrib);
               xfree(list);
               xfree(text);
               return;
            }
         }
      }

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

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

               /* keep original file name for inline references */
               strlcpy(file_name, attachment + i * MAX_PATH_LENGTH, MAX_PATH_LENGTH);

               el_submit_attachment(lbs_dest, file_name, buffer, size, NULL);

               if (buffer)
                  xfree(buffer);
            } else
               /* attachment is invalid */
               attachment[i * MAX_PATH_LENGTH] = 0;

            /* check for thumbnail */
            strlcpy(file_name, lbs->data_dir, sizeof(file_name));
            generate_subdir_name(attachment + i * MAX_PATH_LENGTH, subdir, sizeof(subdir));
            strlcat(file_name, subdir, sizeof(file_name));
            strlcat(file_name, attachment + i * MAX_PATH_LENGTH, sizeof(file_name));
            thumb_status = get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0);

            if (thumb_status == 1) {
               fh = open(thumb_name, O_RDONLY | O_BINARY);
               if (fh > 0) {
                  lseek(fh, 0, SEEK_END);
                  size = TELL(fh);
                  lseek(fh, 0, SEEK_SET);
                  buffer = xmalloc(size);
                  read(fh, buffer, size);
                  close(fh);

                  /* keep original file name for inline references */
                  if (strrchr(thumb_name, '\\'))
                     strlcpy(str, strrchr(thumb_name, '\\') + 1, sizeof(str));
                  else
                     strlcpy(str, thumb_name, sizeof(str));

                  el_submit_attachment(lbs_dest, str, buffer, size, NULL);

                  if (buffer)
                     xfree(buffer);
               }
            }
            if (thumb_status == 2) {
               for (j = 0;; j++) {
                  get_thumb_name(file_name, thumb_name, sizeof(thumb_name), j);
                  if (thumb_name[0]) {
                     fh = open(thumb_name, O_RDONLY | O_BINARY);
                     if (fh > 0) {
                        lseek(fh, 0, SEEK_END);
                        size = TELL(fh);
                        lseek(fh, 0, SEEK_SET);
                        buffer = xmalloc(size);
                        read(fh, buffer, size);
                        close(fh);

                        /* keep original file name for inline references */
                        if (strrchr(thumb_name, '\\'))
                           strlcpy(str, strrchr(thumb_name, '\\') + 1, sizeof(str));
                        else
                           strlcpy(str, thumb_name, sizeof(str));

                        el_submit_attachment(lbs_dest, str, buffer, size, NULL);

                        if (buffer)
                           xfree(buffer);
                     }
                  } else
                     break;
               }
            }
         }

      /* correct possible references to attachments */
      if (strieq(encoding, "ELCode")) {
         sprintf(str, "[IMG]elog:%d/", src_id);
         while (stristr(text, str))
            strsubst(text, TEXT_SIZE, str, "[IMG]elog:/");
      } else if (strieq(encoding, "HTML")) {
         sprintf(str, "?lb=%s\"", lbs->name_enc);
         sprintf(str2, "?lb=%s\"", dest_logbook);
         while (stristr(text, str))
            strsubst(text, TEXT_SIZE, str, str2);
         sprintf(str, "?lb=%s&", lbs->name_enc);
         sprintf(str2, "?lb=%s&", dest_logbook);
         while (stristr(text, str))
            strsubst(text, TEXT_SIZE, str, str2);
      }

      /* keep original message ID if requested */
      message_id = 0;
      bedit = FALSE;
      if (getcfg(lbs->name, "Preserve IDs", str, sizeof(str)) && atoi(str) == 1) {
         message_id = source_id;

         /* test if entry exists already */
         status =
                 el_retrieve(lbs_dest, message_id, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL,
                             NULL);
         bedit = (status == EL_SUCCESS);
      }

      /* submit in destination logbook without links, submit all attributes from
         the source logbook even if the destination has a differnt number of attributes */

      if (getcfg(lbs->name, "Preserve IDs", str, sizeof(str)) && atoi(str) == 1)
         message_id =
                 el_submit(lbs_dest, message_id, bedit, date, attr_list, (void *) attrib, lbs->n_attr, text,
                           in_reply_to, reply_to, encoding, (void *) attachment, FALSE, NULL, NULL);
      else {
         /* if called recursively (for threads), put in correct in_reply_to */
         str[0] = 0;
         if (orig_id)
            sprintf(str, "%d", orig_id);

         message_id =
                 el_submit(lbs_dest, message_id, bedit, date, attr_list, (void *) attrib, lbs->n_attr, text,
                           str,
                           "", encoding, (void *) attachment, TRUE, NULL, NULL);
      }

      if (message_id <= 0) {
         sprintf(str, loc("New entry cannot be written to directory \"%s\""), lbs_dest->data_dir);
         strcat(str, "\n<p>");
         strcat(str, loc("Please check that it exists and elogd has write access"));
         show_error(str);
         xfree(attachment);
         xfree(attrib);
         xfree(list);
         xfree(text);
         return;
      }

      n_done++;

      /* submit all replies */
      n_reply = strbreak(reply_to, (void *) list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < n_reply; i++) {
         copy_to(lbs, atoi(list + i * NAME_LENGTH), dest_logbook, move, message_id);
      }

      n_done_reply += n_reply;

      /* delete original message for move */
      next_id = source_id;
      if (move && orig_id == 0) {

         /* find next message head */
         next_id = el_search_message(lbs, EL_NEXT, source_id, TRUE);
         if (next_id <= 0)
            next_id = el_search_message(lbs, EL_LAST, 0, FALSE);

         el_delete_message(lbs, source_id, TRUE, NULL, TRUE, TRUE);
      }
   }

   xfree(attachment);
   xfree(attrib);
   xfree(list);
   xfree(text);

   if (orig_id)
      return;

   /* redirect to next entry of source logbook */
   if (next_id)
      sprintf(str, "%d", next_id);
   else
      str[0] = 0;
   redirect(lbs, str);
   return;
}

Project: elog

int set_attributes(LOGBOOK *lbs, char attributes[][NAME_LENGTH], int n) {
   int fh, i, length, size;
   char str[NAME_LENGTH], *buf, *buf2, *p1, *p2, *p3;

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

   /* determine length of attributes */
   for (i = size = 0; i < n; i++)
      size += strlen(attributes[i]) + 2;

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

   /* find location of attributes */
   p1 = (char *) find_param(buf, lbs->name, "Attributes");
   if (p1 == NULL) {
      sprintf(str, loc("No 'Attributes' option present in %s"), config_file);
      show_error(str);
      return 0;
   }

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

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

   /* add list */
   p3 = strchr(p1, '=');
   if (p3 == NULL)
      return 0;
   p3++;
   while (*p3 == ' ')
      p3++;

   for (i = 0; i < n - 1; i++) {
      sprintf(p3, "%s, ", attributes[i]);
      p3 += strlen(p3);
   }
   sprintf(p3, "%s", attributes[i]);

   if (p2) {
      strlcat(buf, buf2, length + size + 3);
      xfree(buf2);
   }

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

   TRUNCATE(fh);

   close(fh);
   xfree(buf);

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

   return 1;
}

Project: elog

void unsetparam(char *param) {
   int i;

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

   if (i < MAX_PARAM) {
      for (; i < MAX_PARAM - 1; i++) {
         strlcpy(_param[i], _param[i + 1], NAME_LENGTH);
         strlcpy(_value[i], _value[i + 1], NAME_LENGTH);
      }
      _param[MAX_PARAM - 1][0] = 0;
      _value[MAX_PARAM - 1][0] = 0;
   }
}

Project: elog

void show_time_selector(int hour, int min, int sec, char *index) {
   int i;

   rsprintf("<select name=\"h%s\">\n", index);

   if (isparam("nsel"))
      rsprintf("<option value=\"<keep>\">- %s -\n", loc("keep original values"));
   else
      rsprintf("<option value=\"\">\n");
   for (i = 0; i < 24; i++)
      if (i == hour)
         rsprintf("<option selected value=\"%d\">%02d\n", i, i);
      else
         rsprintf("<option value=\"%d\">%02d\n", i, i);
   rsprintf("</select>&nbsp;<b>:</b>&nbsp;\n");

   rsprintf("<select name=\"n%s\">", index);
   rsprintf("<option value=\"\">\n");
   for (i = 0; i < 60; i++)
      if (i == min)
         rsprintf("<option selected value=%d>%02d\n", i, i);
      else
         rsprintf("<option value=%d>%02d\n", i, i);
   rsprintf("</select>&nbsp;<b>:</b>&nbsp;\n");

   rsprintf("<select name=\"c%s\">", index);
   rsprintf("<option value=\"\">\n");
   for (i = 0; i < 60; i++)
      if (i == sec)
         rsprintf("<option selected value=%d>%02d\n", i, i);
      else
         rsprintf("<option value=%d>%02d\n", i, i);
   rsprintf("</select>\n");

   rsprintf("\n<script language=\"javascript\" type=\"text/javascript\">\n");
   rsprintf("<!--\n");
   rsprintf("function settime_%s()\n", index);
   rsprintf("{\n");
   rsprintf("  var d = new Date();\n");
   rsprintf("  document.form1.d%s.value = d.getDate();\n", index);
   rsprintf("  document.form1.m%s.value = d.getMonth()+1;\n", index);
   rsprintf("  year = d.getYear();\n");
   rsprintf("  if (year < 1900)\n");
   rsprintf("     year += 1900;\n");
   rsprintf("  document.form1.y%s.value = year;\n", index);
   rsprintf("  document.form1.h%s.value = d.getHours();\n", index);
   rsprintf("  document.form1.n%s.value = d.getMinutes();\n", index);
   rsprintf("  document.form1.c%s.value = d.getSeconds();\n", index);
   rsprintf("}\n\n");

   rsprintf("  document.write(\"&nbsp;&nbsp;\");\n");
   rsprintf("  document.write(\"<a href=\\\"javascript:settime_%s()\\\">\");\n", index);
   rsprintf("  document.writeln(\"<img src=\\\"clock.png\\\" align=\\\"middle\\\" border=\\\"0\\\" ");
   rsprintf("alt=\\\"%s\\\"></a>\");\n", loc("Insert current time"));

   rsprintf("//-->\n");
   rsprintf("</script>\n");

}

Project: elog

int setgroup(char *str) {
#ifdef OS_UNIX
   struct group *gr;

   gr = getgrnam(str);

   if (gr != NULL)
      if (setgid(gr->gr_gid) >= 0 && initgroups(gr->gr_name, gr->gr_gid) >= 0)
         return 0;
      else {
         eprintf("Cannot set effective GID to group \"%s\"\n", gr->gr_name);
         eprintf("setgroup: %s\n", strerror(errno));
      }
   else
      eprintf("Group \"%s\" not found\n", str);

   return -1;
#else
   return 0;
#endif
}

Project: elog

int el_move_message(LOGBOOK *lbs, int old_id, int new_id) {
   int status, size;
   char date[80], attrib[MAX_N_ATTR][NAME_LENGTH], *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10],
           encoding[80], locked_by[256], draft[256], att_file[MAX_ATTACHMENTS][256];

   /* retrieve message */
   text = xmalloc(TEXT_SIZE);
   size = TEXT_SIZE;
   status = el_retrieve(lbs, old_id, date, attr_list, attrib, lbs->n_attr, text, &size, in_reply_to,
                        reply_to, att_file, encoding, locked_by, draft);
   if (status != EL_SUCCESS)
      return 0;

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

   xfree(text);

   if (status != new_id)
      return 0;

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

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

   return 1;
}

Project: elog

int el_build_index(LOGBOOK *lbs, BOOL rebuild)
/* scan all ??????a.log files and build an index table in eli[] */
{
   char *file_list, error_str[256], base_dir[256], *buffer;
   int index, n;
   int i, status;
   unsigned char digest[16];

   if (rebuild) {
      xfree(lbs->el_index);
      xfree(lbs->n_el_index);
   }

   lbs->n_el_index = xmalloc(sizeof(int));
   *lbs->n_el_index = 0;
   lbs->el_index = xmalloc(0);

   /* get data directory */
   strcpy(base_dir, lbs->data_dir);

   if (get_verbose() >= VERBOSE_DEBUG) {
      /* show MD5 from config file */

      load_config_section(lbs->name, &buffer, error_str);
      if (error_str[0])
         eprintf(error_str);
      else {
         remove_crlf(buffer);
         MD5_checksum(buffer, strlen(buffer), digest);
         eprintf("\n\nConfig [%s],                           MD5=", lbs->name);

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

   if (get_verbose() >= VERBOSE_DEBUG)
      eprintf("Entries:\n");

   // move files to directories if (new layout to reduce number of files per directory)
   restructure_dir(base_dir);

   file_list = NULL;
   n = 0;
   scan_dir_tree(lbs, base_dir, &file_list, &n);

   /* go through all files */
   for (index = 0; index < n; index++) {
      status = parse_file(lbs, file_list + index * MAX_PATH_LENGTH);
      if (status != SUCCESS) {
         if (file_list)
            xfree(file_list);
         return status;
      }
   }

   if (file_list)
      xfree(file_list);

   /* sort entries according to date */
   qsort(lbs->el_index, *lbs->n_el_index, sizeof(EL_INDEX), eli_compare);

   if (get_verbose() >= VERBOSE_DEBUG) {
      eprintf("After sort:\n");
      for (i = 0; i < *lbs->n_el_index; i++)
         eprintf("  ID %3d, %s, ofs %5d\n", lbs->el_index[i].message_id, lbs->el_index[i].file_name,
                 lbs->el_index[i].offset);
   }

   if (rebuild && n == 0) {
      eprintf("Logbook files seem to have disappeared, aborting program.\n");
      assert(rebuild && n > 0);
   }

   return EL_SUCCESS;
}

Project: elog

time_t convert_datetime(char *date_string) {
   /* convert date string in MM/DD/YY h:m:s AM/PM or DD.MM.YY hh:m:s format into Unix time */
   int year, month, day, hour, min = 0, sec = 0;
   char *p, str[256];
   struct tm tms;
   time_t ltime;

   strlcpy(str, date_string, sizeof(str));
   month = day = year = 0;

   if (strchr(str, '[')) {
      ltime = atoi(strchr(str, '[') + 1);
      return ltime;
   } else if (strchr(str, '/')) {
      /* MM/DD/YY format */
      p = strtok(str, "/");
      if (p) {
         month = atoi(p);
         p = strtok(NULL, "/");
         if (p) {
            day = atoi(p);
            p = strtok(NULL, "/");
            if (p)
               year = atoi(p);
         }
      }
   } else if (strchr(str, '.')) {
      /* DD.MM.YY format */
      p = strtok(str, ".");
      if (p) {
         day = atoi(p);
         p = strtok(NULL, ".");
         if (p) {
            month = atoi(p);
            p = strtok(NULL, ".");
            if (p)
               year = atoi(p);
         }
      }
   } else
      return 0;

   if (!strchr(p, ' '))
      return 0;
   p = strchr(p, ' ') + 1;

   strlcpy(str, p, sizeof(str));
   p = strtok(str, ":");
   if (p) {
      hour = atoi(p);
      p = strtok(NULL, ":");
      if (p) {
         min = atoi(p);
         p = strtok(NULL, ":");
         if (p)
            sec = atoi(p);
      }
   } else
      return 0;

   if (stristr(p, "PM") && hour < 12)
      hour += 12;

   /* calculate years */
   if (year > 1900)             /* 1900-2100 */
      year += 0;
   else if (year < 70)          /* 00-69 */
      year += 2000;
   else if (year < 100)         /* 70-99 */
      year += 1900;

   /* use last day of month */
   memset(&tms, 0, sizeof(struct tm));
   tms.tm_year = year - 1900;
   tms.tm_mon = month - 1;
   tms.tm_mday = day;
   tms.tm_hour = hour;
   tms.tm_min = min;
   tms.tm_sec = sec;

   ltime = mktime(&tms);

   return ltime;
}

Project: elog

void _MD5_encode(unsigned char *pout, unsigned int *pin, unsigned int len) {
   unsigned int i, j;

   for (i = 0, j = 0; j < len; i++, j += 4) {
      pout[j] = (unsigned char) (pin[i] & 0x0ff);
      pout[j + 1] = (unsigned char) ((pin[i] >> 8) & 0x0ff);
      pout[j + 2] = (unsigned char) ((pin[i] >> 16) & 0x0ff);
      pout[j + 3] = (unsigned char) ((pin[i] >> 24) & 0x0ff);
   }
}

Project: elog

void show_logbook_delete(LOGBOOK *lbs) {
   char str[256];

   /* redirect if confirm = NO */
   if (isparam("confirm") && strcmp(getparam("confirm"), loc("No")) == 0) {

      redirect(lbs, "?cmd=Config");
      return;
   }

   if (isparam("confirm")) {
      if (strcmp(getparam("confirm"), loc("Yes")) == 0) {

         /* delete logbook */
         str[0] = 0;
         delete_logbook(lbs, str);
         if (str[0])
            show_error(str);
         else
            redirect(NULL, "../");
         return;
      }

   } else {

      strcpy(str, "Delete logbook");
      show_standard_header(lbs, TRUE, str, "", FALSE, NULL, NULL, 0);

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

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

      sprintf(str, loc("Are you sure to delete logbook \"%s\"?"), lbs->name);
      rsprintf("%s</td></tr>\n", str);

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

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

Project: elog

void evaluate_conditions(LOGBOOK *lbs, char attrib[MAX_N_ATTR][NAME_LENGTH]) {
   char condition[256], str[256];
   int index, i;

   condition[0] = 0;
   set_condition("");
   for (index = 0; index < lbs->n_attr; index++) {
      for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {

         if (strchr(attr_options[index][i], '{') && strchr(attr_options[index][i], '}')) {

            strlcpy(str, attr_options[index][i], sizeof(str));
            *strchr(str, '{') = 0;

            if (strieq(str, attrib[index])) {
               strlcpy(str, strchr(attr_options[index][i], '{') + 1, sizeof(str));
               if (*strchr(str, '}'))
                  *strchr(str, '}') = 0;

               if (condition[0] == 0)
                  strlcpy(condition, str, sizeof(condition));
               else {
                  strlcat(condition, ",", sizeof(condition));
                  strlcat(condition, str, sizeof(condition));
               }

               set_condition(condition);
               scan_attributes(lbs->name);
            }
         }
      }
   }
}

Project: elog

void get_auto_index(LOGBOOK *lbs, int index, char *format, char *retstr, int size)
/* return value of specific attribute of last entry, can be used to
 auto-increment tags */
{
   int i, message_id, loc, len, old_index;
   char *p, attrib[MAX_N_ATTR][NAME_LENGTH], att[MAX_ATTACHMENTS][256], draft[256];
   time_t now;

   if (strchr(format, '%') == NULL && strchr(format, '#') == NULL) {
      strlcpy(retstr, format, size);
      return;
   }

   time(&now);
   my_strftime(retstr, size, format, localtime(&now));

   p = strchr(retstr, '#');
   if (p == NULL)
      return;

   if (p > retstr && *(p - 1) == '\\') {        // escape
      memmove(p - 1, p, strlen(p) + 1);
      return;
   }

   /* record location and length of ###'s */
   for (i = loc = 0, len = 1; i < (int) strlen(retstr); i++) {
      if (retstr[i] == '#') {
         if (loc == 0)
            loc = i;
         if (i > 0 && retstr[i - 1] == '#')
            len++;
      }
   }

   /* get attribute from last entry */
   message_id = el_search_message(lbs, EL_LAST, 0, FALSE);

   if (!message_id) {
      /* start with 1 */
      sprintf(retstr + loc, "%0*d", len, 1);
      return;
   }

   /* search all entries to find largest index */
   old_index = 0;
   do {
      el_retrieve(lbs, message_id, NULL, attr_list, attrib, lbs->n_attr, NULL, 0, NULL, NULL, att, NULL,
                  NULL, draft);

      /* if same date found, obtain largest index */
      if (strlen(attrib[index]) > 0 && strncmp(attrib[index], retstr, loc) == 0)
         if (atoi(attrib[index] + loc) > old_index)
            old_index = atoi(attrib[index] + loc);

      message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);

   } while (message_id);

   /* increment index */
   sprintf(retstr + loc, "%0*d", len, old_index + 1);
}

Project: elog

int fnmatch1(const char *pattern, const char *string) {
   char c, test;

   for (;;)
      switch (c = *pattern++) {
         case EOS:
            return (*string == EOS ? 0 : 1);
         case '?':
            if (*string == EOS)
               return (1);
            ++string;
            break;
         case '*':
            c = *pattern;
            /* Collapse multiple stars. */
            while (c == '*')
               c = *++pattern;

            /* Optimize for pattern with * at end or before /. */
            if (c == EOS)
               return (0);

            /* General case, use recursion. */
            while ((test = *string) != EOS) {
               if (!fnmatch1(pattern, string))
                  return (0);
               ++string;
            }
            return (1);
            /* FALLTHROUGH */
         default:
            if (c != *string)
               return (1);
            string++;
            break;
      }
}

Project: elog

BOOL is_logbook_in_group(LBLIST pgrp, char *logbook)
/* test if "logbook" is in group node plb */
{
   int i;

   if (strieq(logbook, pgrp->name))
      return TRUE;

   for (i = 0; i < pgrp->n_members; i++) {
      if (strieq(logbook, pgrp->member[i]->name))
         return TRUE;

      if (pgrp->member[i]->n_members > 0 && is_logbook_in_group(pgrp->member[i], logbook))
         return TRUE;
   }

   return FALSE;
}

Project: elog

double date2serialdate(int day, int month, int year)
/* convert date to days since 1.1.1900 */
{
   int serialdate;

   serialdate = (int) ((1461 * (year + 4800 + (int) ((month - 14) / 12))) / 4) + (int) ((367 * (month - 2
                                                                                                -
                                                                                                12 *
                                                                                                ((month -
                                                                                                  14) /
                                                                                                 12))) / 12) -
                (int) ((3 * ((int) ((year + 4900 + (int) ((month - 14) / 12))
                                    / 100))) / 4) + day - 2415019 - 32075;

   return serialdate;
}

Project: elog

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

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

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

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

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

   f = fopen(file_name, "wt");
   if (f == NULL)
      return -1;

   for (i = 0; i < n; i++) {
      fprintf(f, "ID%d: ", md5_index[i].message_id);
      for (j = 0; j < 16; j++)
         fprintf(f, "%02X", md5_index[i].md5_digest[j]);
      fprintf(f, "\n");
   }

   fclose(f);
   return 1;
}

Project: elog

int msg_compare_reverse(const void *m1, const void *m2) {
   return strcoll(((MSG_LIST *) m2)->string, ((MSG_LIST *) m1)->string);
}

Project: elog

void do_crypt(const char *s, char *d, int size) {
   strlcpy(d, sha256_crypt(s, "$5$") + 4, size);
}

Project: elog

int setuser(char *str) {
#ifdef OS_UNIX
   struct passwd *pw;

   pw = getpwnam(str);

   if (pw != NULL)
      if (setuid(pw->pw_uid) >= 0)
         return 0;
      else {
         eprintf("Cannot set effective UID to user \"%s\"\n", str);
         eprintf("setuser: %s\n", strerror(errno));
      }
   else
      eprintf("User \"%s\" not found\n", str);

   return -1;
#else
   return 0;
#endif
}

Project: elog

void format_email_text(LOGBOOK *lbs, char attrib[MAX_N_ATTR][NAME_LENGTH],
                       char att_file[MAX_ATTACHMENTS][256], int old_mail, char *url, char *multipart_boundary,
                       char *mail_text, int size) {
   int i, j, k, flags, n_email_attr, attr_index[MAX_N_ATTR];
   char str[NAME_LENGTH + 100], str2[256], mail_from[256], mail_from_name[256], format[256],
           list[MAX_N_ATTR][NAME_LENGTH], comment[256], charset[256], heading[256],
           slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH];
   time_t ltime;
   struct tm *pts;

   if (multipart_boundary[0]) {
      if (!getcfg("global", "charset", charset, sizeof(charset)))
         strcpy(charset, DEFAULT_HTTP_CHARSET);

      strlcat(mail_text, "--", size);
      strlcat(mail_text, multipart_boundary, size);
      strlcat(mail_text, "\r\n", size);
      sprintf(mail_text + strlen(mail_text), "Content-Type: text/plain; charset=%s; format=flowed\r\n",
              charset);
      sprintf(mail_text + strlen(mail_text), "Content-Transfer-Encoding: 7bit\r\n\r\n");
   } else
      strlcat(mail_text, "\r\n", size);

   flags = 63;
   if (getcfg(lbs->name, "Email format", str, sizeof(str)))
      flags = atoi(str);

   retrieve_email_from(lbs, mail_from, mail_from_name, attrib);

   if (flags & 1) {
      if (getcfg(lbs->name, "Use Email heading", heading, sizeof(heading))) {
         if (old_mail) {
            if (!getcfg(lbs->name, "Use Email heading edit", heading, sizeof(heading)))
               getcfg(lbs->name, "Use Email heading", heading, sizeof(heading));
         }

         i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
         strsubst_list(heading, sizeof(heading), slist, svalue, i);

         strlcpy(mail_text + strlen(mail_text), heading, size - strlen(mail_text));

      } else {
         if (old_mail)
            strlcpy(mail_text + strlen(mail_text), loc("An old ELOG entry has been updated"),
                    size - strlen(mail_text));
         else
            strlcpy(mail_text + strlen(mail_text), loc("A new ELOG entry has been submitted"),
                    size - strlen(mail_text));
         strcat(mail_text, ":");
      }

      strlcpy(mail_text + strlen(mail_text), "\r\n\r\n", size - strlen(mail_text));
   }

   if (flags & 32)
      sprintf(mail_text + strlen(mail_text), "%s             : %s\r\n", loc("Logbook"), lbs->name);

   if (flags & 2) {

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

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

         i = attr_index[j];
         strcpy(str, "                                                                       ");
         memcpy(str, attr_list[i], strlen(attr_list[i]));

         comment[0] = 0;
         if (attr_flags[i] & AF_ICON) {

            sprintf(str2, "Icon comment %s", attrib[i]);
            getcfg(lbs->name, str2, comment, sizeof(comment));

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

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

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

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

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

         if (!comment[0])
            strcpy(comment, attrib[i]);

         if (strieq(attr_options[i][0], "boolean"))
            strcpy(comment, atoi(attrib[i]) ? "1" : "0");

         for (k = strlen(str) - 1; k > 0; k--)
            if (str[k] != ' ')
               break;

         if (k < 20)
            sprintf(str + 20, ": %s\r\n", comment);
         else
            sprintf(str + k + 1, ": %s\r\n", comment);

         strcpy(mail_text + strlen(mail_text), str);
      }
   }

   if (flags & 4)
      sprintf(mail_text + strlen(mail_text), "\r\n%s URL         : %s\r\n", loc("Logbook"), url);

   if (flags & 64) {
      for (i = 0; i < MAX_ATTACHMENTS && att_file[i][0]; i++)
         sprintf(mail_text + strlen(mail_text), "\r\n%s %d        : %s (%s/%d)\r\n", loc("Attachment"),
                 i + 1, att_file[i] + 14, url, i + 1);
   }

   if (flags & 8) {
      if (isparam("text")) {
         sprintf(mail_text + strlen(mail_text), "\r\n=================================\r\n\r\n%s",
                 getparam("text"));
      }
   }

   strlcat(mail_text, "\r\n\r\n", size);
}

Project: elog

void redirect_to_syslog(void) {
   static int has_inited = 0;

   /* initiate syslog */
   if (!has_inited) {
#ifdef OS_UNIX
      setlogmask(LOG_UPTO(SYSLOG_PRIORITY));
      openlog("elogd", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
#else
      hEventLog = RegisterEventSource(NULL, "ELOG");
#endif
   }
   has_inited = 1;

   printf_handler = print_syslog;
   fputs_handler = fputs_syslog;

   /* tells that the syslog facility is currently used as output */
   current_output_stream = NULL;
}

Project: elog

void rsprintf(const char *format, ...) {
   va_list argptr;
   char str[10000];

   va_start(argptr, format);
   vsprintf(str, (char *) format, argptr);
   va_end(argptr);

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

   strcpy(return_buffer + strlen_retbuf, str);

   strlen_retbuf += strlen(str);
}

Project: elog

void show_rss_feed(LOGBOOK *lbs) {
   int i, n, size, index, message_id, offset;
   char str[256], charset[256], url[256], attrib[MAX_N_ATTR][NAME_LENGTH], date[80], *text, title[2000],
           slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH], draft[1000];
   time_t ltime;
   struct tm *ts;

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

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

   rsprintf("Content-Type: text/xml;charset=%s\r\n", charset);
   rsprintf("\r\n");

   rsprintf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", charset);
   rsprintf("<rss version=\"2.0\">\n");
   rsprintf("<channel>\n");

   rsprintf("<title>ELOG %s</title>\n", lbs->name);

   /* retrive URL */
   getcfg(lbs->name, "URL", url, sizeof(url));

   /* if HTTP request comes from localhost, use localhost as
      absolute link (needed if running on DSL at home) */
   if (!url[0] && strstr(http_host, "localhost")) {
      if (_ssl_flag)
         strcpy(url, "https://localhost");
      else
         strcpy(url, "http://localhost");
      if (elog_tcp_port != 80)
         sprintf(url + strlen(url), ":%d", elog_tcp_port);
      strcat(url, "/");
   }

   if (!url[0]) {
      /* assemble absolute path from host name and port */
      if (_ssl_flag)
         sprintf(url, "https://%s", host_name);
      else
         sprintf(url, "http://%s", host_name);
      if (elog_tcp_port != 80)
         sprintf(url + strlen(url), ":%d", elog_tcp_port);
      strcat(url, "/");
   }

   /* add trailing '/' if not present */
   if (url[strlen(url) - 1] != '/')
      strcat(url, "/");

   strlcat(url, lbs->name_enc, sizeof(url));

   rsprintf("<link>%s</link>\n", url);

   if (getcfg(lbs->name, "Comment", str, sizeof(str))) {
      rsprintf("<description>");
      xmlencode(str);
      rsprintf("</description>\n");
   }

   rsprintf("<generator>ELOG V%s</generator>\n", VERSION);

   rsprintf("<image>\n");
   rsprintf("<url>%s/elog.png</url>\n", url);
   rsprintf("<title>ELOG %s</title>\n", lbs->name_enc);
   rsprintf("<link>%s</link>\n", url);
   rsprintf("</image>\n");

   /*---- show last <n> items ----*/

   n = 15;
   if (getcfg(lbs->name, "RSS Entries", str, sizeof(str)))
      n = atoi(str);

   text = (char *) xmalloc(TEXT_SIZE);
   message_id = el_search_message(lbs, EL_LAST, 0, FALSE);
   for (index = 0; index < n && message_id; index++) {

      size = TEXT_SIZE;
      el_retrieve(lbs, message_id, date, attr_list, attrib, lbs->n_attr, text, &size, NULL, NULL,
                  NULL, NULL, NULL, draft);

      /* skip drafts */
      if (draft[0]) {
         message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);
         continue;
      }

      /* limit text size to 2k */
      text[2048] = 0;

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

         strsubst_list(title, sizeof(title), (char (*)[NAME_LENGTH]) slist, (char (*)[NAME_LENGTH]) svalue,
                       i);
      } else {

         title[0] = 0;
         for (i = 0; i < lbs->n_attr; i++) {
            if (attrib[i][0]) {
               strlcat(title, attrib[i], sizeof(title));
               if (i < lbs->n_attr - 1)
                  strlcat(title, ", ", sizeof(title));
            }
         }

      }

      rsprintf("<item>\n");

      /* convert date to RFC-822 date */
      setlocale(LC_ALL, "C");
      ltime = date_to_ltime(date);
      ts = localtime(&ltime);
      assert(ts);
      strftime(str, sizeof(str), "%a, %d %b %Y %H:%M:%S", ts);
      offset = (-(int) my_timezone());
      if (ts->tm_isdst)
         offset += 3600;
      snprintf(date, sizeof(date) - 1, "%s %+03d%02d", str, (int) (offset / 3600), (int) ((abs((int) offset)
                                                                                           / 60) % 60));
      getcfg("global", "Language", str, sizeof(str));
      if (str[0])
         setlocale(LC_ALL, str);

      rsprintf("<title>");
      xmlencode(title);
      rsprintf("</title>\n");

      rsprintf("<link>");
      strlcpy(str, url, sizeof(str));
      sprintf(str + strlen(str), "/%d", message_id);
      xmlencode(str);
      rsprintf("</link>\n");

      rsprintf("<description>\n");
      xmlencode(text);
      rsprintf("</description>\n");

      rsprintf("<pubDate>\n");
      rsprintf("%s", date);
      rsprintf("</pubDate>\n");

      rsprintf("</item>\n");
      message_id = el_search_message(lbs, EL_PREV, message_id, FALSE);
   }

   xfree(text);
   rsprintf("</channel>\n");
   rsprintf("</rss>\n");
}

Project: elog

void show_standard_header(LOGBOOK *lbs, BOOL expires, char *title, char *path, BOOL rss_feed, char *cookie,
                          char *script, int refresh) {
   if (script) {
      show_html_header(lbs, expires, title, FALSE, rss_feed, cookie, FALSE, refresh);

      rsprintf("<script type=\"text/javascript\">\n");
      rsprintf("<!--\n");
      show_browser(browser);

      rsprintf("logbook = \"%s\";\n", lbs->name_enc);
      rsprintf("//-->\n");
      rsprintf("</script>\n");
      rsprintf("<script type=\"text/javascript\" src=\"../elcode.js\"></script>\n\n");
      rsprintf("</head>\n");
   } else
      show_html_header(lbs, expires, title, TRUE, rss_feed, cookie, FALSE, refresh);

   if (script)
      rsprintf("<body %s>\n", script);
   else
      rsprintf("<body>\n");

   show_top_text(lbs);

   if (path && path[0])
      rsprintf("<form name=\"form1\" method=\"GET\" action=\"%s\">\n\n", path);
   else
      rsprintf("<form name=\"form1\" method=\"GET\" action=\".\">\n\n");
}

Project: elog

int check_drafts(LOGBOOK *lbs) {
   time_t now;
   char str[1000], draft[256], title[256], datetime[256], attrib[MAX_N_ATTR][NAME_LENGTH];
   int i, n_draft, *draft_id = NULL;

   /* if we got here already and user clicked "Create new entry", ignore is set and we skip this */
   if (isparam("ignore"))
      return 0;

   time(&now);
   /* check if any recent draft */
   for (i = n_draft = 0; i < *(lbs->n_el_index); i++)
      if (lbs->el_index[i].file_time > now - 3600 * 24 * 7) { // only one week to reduce seek time
         el_retrieve(lbs, lbs->el_index[i].message_id, NULL, attr_list, attrib, lbs->n_attr,
                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, draft);
         if (draft[0] && is_author(lbs, attrib, draft)) {
            if (n_draft == 0)
               draft_id = (int *) xmalloc(sizeof(int));
            else
               draft_id = (int *) xrealloc(draft_id, sizeof(int) * (n_draft + 1));
            draft_id[n_draft] = lbs->el_index[i].message_id;
            n_draft++;
         }
      }

   if (n_draft == 0)
      return 0;

   if (n_draft == 1)
      sprintf(title, "%s", loc("Pending draft available"));
   else
      sprintf(title, loc("%d pending drafts available"), n_draft);

   show_standard_header(lbs, TRUE, "Draft query", NULL, FALSE, NULL, NULL, 0);

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

   rsprintf("<tr><td colspan=\"2\" class=\"dlgtitle\">");
   rsprintf("%s</td></tr>\n", title);

   for (i = 0; i < n_draft; i++) {
      el_retrieve(lbs, draft_id[i], datetime, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL,
                  NULL, NULL, draft);

      rsprintf("<tr><td class=\"draftsel\" align=\"center\">");
      sprintf(str, loc("Draft entry created on %s by %s"), datetime, draft);
      rsprintf("%s</td>\n", str);
      rsprintf("<td class=\"draftsel\">");
      rsprintf("<input type=button value=\"%s\" onClick=\"window.location.href='%d?cmd=%s';\">",
               loc("Edit"), draft_id[i], loc("Edit"));
      rsprintf("</td></tr>\n");
   }

   rsprintf("<tr><td colspan=\"2\" align=center class=\"dlgform\">");
   rsprintf("<input type=button value=\"%s\" onClick=\"window.location.href='?cmd=%s&ignore=1';\">\n",
            loc("Create new entry"),
            loc("New"));
   rsprintf("</td></tr>\n\n");

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

   xfree(draft_id);
   return 1;
}

Project: elog

BOOL is_author(LOGBOOK *lbs, char attrib[MAX_N_ATTR][NAME_LENGTH], char *owner) {
   char str[NAME_LENGTH], preset[NAME_LENGTH], full_name[NAME_LENGTH];
   int i;

   /* check if current user is admin */
   if (is_admin_user(lbs, getparam("unm")))
      return TRUE;

   /* search attribute which contains short_name of author */
   for (i = 0; i < lbs->n_attr; i++) {
      sprintf(str, "Preset %s", attr_list[i]);
      if (getcfg(lbs->name, str, preset, sizeof(preset))) {
         if (strstr(preset, "$short_name")) {
            if (!isparam("unm") || strstr(attrib[i], getparam("unm")) == NULL) {
               strcpy(owner, attrib[i]);
               return FALSE;
            } else
               break;
         }
      }
   }

   if (i == lbs->n_attr) {
      /* if not found, search attribute which contains full_name of author */
      if (isparam("unm")) {
         get_full_name(lbs, getparam("unm"), full_name);
         for (i = 0; i < lbs->n_attr; i++) {
            sprintf(str, "Preset %s", attr_list[i]);
            if (getcfg(lbs->name, str, preset, sizeof(preset))) {
               if (strstr(preset, "$long_name")) {
                  if (strstr(attrib[i], full_name) == NULL) {
                     strcpy(owner, attrib[i]);
                     return FALSE;
                  } else
                     break;
               }
            }
         }
      }
   }

   return TRUE;
}

Project: elog

void stou(char *str)
/* convert all special characters to underscores in a string */
{
   int i;

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

Project: elog

BOOL strieq(const char *str1, const char *str2) {
   char c1, c2;

   if (str1 == NULL && str2 == NULL)
      return TRUE;
   if (str1 == NULL || str2 == NULL)
      return FALSE;
   if (strlen(str1) != strlen(str2))
      return FALSE;

   while (*str1) {
      c1 = *str1++;
      c2 = *str2++;
      if (my_toupper(c1) != my_toupper(c2))
         return FALSE;
   }

   if (*str2)
      return FALSE;

   return TRUE;
}

Project: elog

time_t convert_date(char *date_string) {
   /* convert date string in MM/DD/YY or DD.MM.YY format into Unix time */
   int year, month, day;
   char *p, str[256];
   struct tm tms;
   time_t ltime;

   strlcpy(str, date_string, sizeof(str));
   month = day = year = 0;

   if (strchr(str, '[')) {
      ltime = atoi(strchr(str, '[') + 1);
      return ltime;
   } else if (strchr(str, '/')) {
      /* MM/DD/YY format */
      p = strtok(str, "/");
      if (p) {
         month = atoi(p);
         p = strtok(NULL, "/");
         if (p) {
            day = atoi(p);
            p = strtok(NULL, "/");
            if (p)
               year = atoi(p);
         }
      }
   } else if (strchr(str, '.')) {
      /* DD.MM.YY format */
      p = strtok(str, ".");
      if (p) {
         day = atoi(p);
         p = strtok(NULL, ".");
         if (p) {
            month = atoi(p);
            p = strtok(NULL, ".");
            if (p)
               year = atoi(p);
         }
      }
   } else
      return 0;

   /* calculate years */
   if (year > 1900)             /* 1900-2100 */
      year += 0;
   else if (year < 70)          /* 00-69 */
      year += 2000;
   else if (year < 100)         /* 70-99 */
      year += 1900;

   /* use last day of month */
   memset(&tms, 0, sizeof(struct tm));
   tms.tm_year = year - 1900;
   tms.tm_mon = month - 1;
   tms.tm_mday = day;
   tms.tm_hour = 12;

   ltime = mktime(&tms);

   return ltime;
}

Project: elog

void set_verbose(int v) {
   _verbose_level = v;
}

Project: elog

char *xstrdup(const char *string) {
   char *s;

   s = (char *) xmalloc(strlen(string) + 1);
   strcpy(s, string);
   return s;
}

Project: elog

void show_find_form(LOGBOOK *lbs) {
   int i, j, year, month, day, flag;
   char str[NAME_LENGTH], mode[NAME_LENGTH], comment[NAME_LENGTH], option[NAME_LENGTH], login_name[256],
           full_name[256], user_email[256], enc_attr[NAME_LENGTH], whole_attr[NAME_LENGTH],
           attrib[MAX_N_ATTR][NAME_LENGTH];

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

   show_standard_header(lbs, FALSE, loc("ELOG find"), NULL, FALSE, NULL, NULL, 0);

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

   show_standard_title(lbs, "", 0);

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

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

   rsprintf("<input type=submit value=\"%s\">\n", loc("Search"));
   rsprintf("<input type=reset value=\"%s\">\n", loc("Reset Form"));
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Back"));
   rsprintf("<input type=hidden name=jcmd value=\"\">\n");
   rsprintf("</span></td></tr>\n\n");

   /*---- evaluate conditional attributes ----*/

   for (i = 0; i < lbs->n_attr; i++)
      attrib[i][0] = 0;

   /* get attributes from parameters */
   attrib_from_param(lbs->n_attr, attrib);

   evaluate_conditions(lbs, attrib);

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

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

   rsprintf("<b>%s:</b><br>", loc("Mode"));

   if (!getcfg(lbs->name, "Display mode", mode, sizeof(mode)))
      strcpy(mode, "Full");

   if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1) {
      if (strieq(mode, "Full"))
         rsprintf("<input type=radio id=\"full\" name=\"mode\" value=\"full\" checked>");
      else
         rsprintf("<input type=radio id=\"full\" name=\"mode\" value=\"full\">");
      rsprintf("<label for=\"full\">%s&nbsp;&nbsp;</label><br>\n", loc("Display full entries"));

      if (strieq(mode, "Summary"))
         rsprintf("<input type=radio id=\"summary\" name=\"mode\" value=\"summary\" checked>");
      else
         rsprintf("<input type=radio id=\"summary\" name=\"mode\" value=\"summary\">");
      rsprintf("<label for=\"summary\">%s&nbsp;&nbsp;</label><br>\n", loc("Summary only"));

   } else {
      if (strieq(mode, "Full") || strieq(mode, "Summary"))
         rsprintf("<input type=radio id=\"summary\" name=\"mode\" value=\"summary\" checked>");
      else
         rsprintf("<input type=radio id=\"summary\" name=\"mode\" value=\"summary\">");
      rsprintf("<label for=\"summary\">%s&nbsp;&nbsp;</label><br>\n", loc("Summary"));
   }

   if (strieq(mode, "Threaded"))
      rsprintf("<input type=radio id=\"threaded\" name=\"mode\" value=\"threaded\" checked>");
   else
      rsprintf("<input type=radio id=\"threaded\" name=\"mode\" value=\"threaded\">");
   rsprintf("<label for=\"threaded\">%s&nbsp;&nbsp;</label>\n", loc("Display threads"));

   rsprintf("</td><td valign=\"top\" class=\"form1\"><b>%s:</b><br>", loc("Export to"));

   if (strieq(mode, "CSV1"))
      rsprintf("<input type=radio id=\"CSV1\" name=\"mode\" value=\"CSV1\" checked>");
   else
      rsprintf("<input type=radio id=\"CSV1\" name=\"mode\" value=\"CSV1\">");
   rsprintf("<label for=\"CSV1\">%s&nbsp;&nbsp;</label><br>\n", loc("CSV (\",\" separated)"));

   if (strieq(mode, "CSV2"))
      rsprintf("<input type=radio id=\"CSV2\" name=\"mode\" value=\"CSV2\" checked>");
   else
      rsprintf("<input type=radio id=\"CSV2\" name=\"mode\" value=\"CSV2\">");
   rsprintf("<label for=\"CSV2\">%s&nbsp;&nbsp;</label><br>\n", loc("CSV (\";\" separated)"));

   if (strieq(mode, "CSV3"))
      rsprintf("<input type=radio id=\"CSV3\" name=\"mode\" value=\"CSV3\" checked>");
   else
      rsprintf("<input type=radio id=\"CSV3\" name=\"mode\" value=\"CSV3\">");
   rsprintf("<label for=\"CSV3\">%s&nbsp;&nbsp;</label><br>\n", loc("CSV (\";\" separated) + Text"));

   if (strieq(mode, "XML"))
      rsprintf("<input type=radio id=\"XML\" name=\"mode\" value=\"XML\" checked>");
   else
      rsprintf("<input type=radio id=\"XML\" name=\"mode\" value=\"XML\">");
   rsprintf("<label for=\"XML\">XML&nbsp;&nbsp;</label><br>\n");

   if (strieq(mode, "Raw"))
      rsprintf("<input type=radio id=\"Raw\" name=\"mode\" value=\"Raw\" checked>");
   else
      rsprintf("<input type=radio id=\"Raw\" name=\"mode\" value=\"Raw\">");
   rsprintf("<label for=\"Raw\">Raw&nbsp;&nbsp;</label><br>\n");

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

   rsprintf("<td valign=\"top\" class=\"form2\"><b>%s:</b><br>", loc("Options"));

   rsprintf("<input type=checkbox id=\"attach\" name=\"attach\" value=1>");
   rsprintf("<label for=\"attach\">%s<br></label>\n", loc("Show attachments"));

   rsprintf("<input type=checkbox id=\"printable\" name=\"printable\" value=1>");
   rsprintf("<label for=\"printable\">%s<br></label>\n", loc("Printable output"));

   /* put hidden reverse=0, which gets used if the reverse checkbox is unchecked and "reverse sort=1" is in elogd.cfg */
   rsprintf("<input type=hidden name=\"reverse\" value=0>\n");
   if (getcfg(lbs->name, "Reverse sort", str, sizeof(str)) && atoi(str) == 1)
      rsprintf("<input type=checkbox id=\"reverse\" name=\"reverse\" value=1 checked>");
   else
      rsprintf("<input type=checkbox id=\"reverse\" name=\"reverse\" value=1>");
   rsprintf("<label for=\"reverse\">%s<br></label>\n", loc("Sort in reverse order"));

   /* count logbooks */
   for (i = 0;; i++) {
      if (!enumgrp(i, str))
         break;

      if (is_logbook(str))
         continue;
   }

   if (i > 2) {
      if (!getcfg(lbs->name, "Search all logbooks", str, sizeof(str)) || atoi(str) == 1 || atoi(str) == 2) {

         if (atoi(str) == 2)
            rsprintf("<input type=checkbox checked id=all name=all value=1>");
         else
            rsprintf("<input type=checkbox id=all name=all value=1>");
         rsprintf("<label for=\"all\">%s</label><br>\n", loc("Search all logbooks"));
      }
   }

   rsprintf(loc("Display"));
   if (!getcfg(lbs->name, "Entries per page", str, sizeof(str)))
      strcpy(str, "20");
   rsprintf(" <input type=text name=npp size=3 value=%s> ", str);
   rsprintf(loc("entries per page"));

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

   rsprintf("<tr><td class=\"form2\"><b>%s:</b>", loc("Filters"));
   sprintf(str, "<a href=\"http://dmoz.org/Computers/Programming/Languages/Regular_Expressions/\">");
   strcat(str, loc("regular expressions"));
   strcat(str, "</a>");
   rsprintf("&nbsp;&nbsp;<span class=\"selcomment\">(");
   rsprintf(loc("Text fields are treated as %s"), str);
   rsprintf(")</span><br>");

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

   rsprintf("<tr><td class=\"attribname\" nowrap width=\"10%%\">%s:</td>", loc("Entry date"));
   rsprintf("<td class=\"attribvalue\"><table width=\"100%%\" cellspacing=0 border=0>\n");
   rsprintf("<tr><td width=\"1%%\">%s:<td>", loc("Start"));

   year = month = day = 0;
   sprintf(str, "ya");
   if (isparam(str))
      year = atoi(getparam(str));
   sprintf(str, "ma");
   if (isparam(str))
      month = atoi(getparam(str));
   sprintf(str, "da");
   if (isparam(str))
      day = atoi(getparam(str));
   show_date_selector(day, month, year, "a");
   rsprintf("&nbsp;&nbsp;");
   show_time_selector(-1, -1, -1, "a");

   rsprintf("&nbsp;&nbsp;/&nbsp;&nbsp;%s:&nbsp;", loc("Show last"));

   if (getcfg(lbs->name, "Show last default", str, sizeof(str)))
      i = atoi(str);
   else
      i = 0;

   rsprintf("<select name=last>\n");
   if (i > 0)
      rsprintf("<option value=\"\">%s\n", loc("All time"));
   else
      rsprintf("<option value=\"\">\n");
   rsprintf("<option %svalue=1>%s\n", i == 1 ? "selected " : "", loc("Day"));
   rsprintf("<option %svalue=3>%s\n", i == 3 ? "selected " : "", loc("3 Days"));
   rsprintf("<option %svalue=7>%s\n", i == 7 ? "selected " : "", loc("Week"));
   rsprintf("<option %svalue=31>%s\n", i == 31 ? "selected " : "", loc("Month"));
   rsprintf("<option %svalue=92>%s\n", i == 92 ? "selected " : "", loc("3 Months"));
   rsprintf("<option %svalue=182>%s\n", i == 182 ? "selected " : "", loc("6 Months"));
   rsprintf("<option %svalue=364>%s\n", i == 364 ? "selected " : "", loc("Year"));
   rsprintf("</select> \n");

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

   rsprintf("<tr><td width=\"1%%\">%s:<td>", loc("End"));

   year = month = day = 0;
   sprintf(str, "yb");
   if (isparam(str))
      year = atoi(getparam(str));
   sprintf(str, "mb");
   if (isparam(str))
      month = atoi(getparam(str));
   sprintf(str, "db");
   if (isparam(str))
      day = atoi(getparam(str));
   show_date_selector(day, month, year, "b");
   rsprintf("&nbsp;&nbsp;");
   show_time_selector(-1, -1, -1, "b");

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

   for (i = 0; i < lbs->n_attr; i++) {
      rsprintf("<tr><td class=\"attribname\" nowrap>%s:</td>", attr_list[i]);
      rsprintf("<td class=\"attribvalue\">");
      if (attr_options[i][0][0] == 0) {

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

            rsprintf("<table width=\"100%%\" cellspacing=0 border=0>\n");
            rsprintf("<tr><td width=\"1%%\">%s:<td>", loc("Start"));

            year = month = day = 0;
            sprintf(str, "y%da", i);
            if (isparam(str))
               year = atoi(getparam(str));
            sprintf(str, "m%da", i);
            if (isparam(str))
               month = atoi(getparam(str));
            sprintf(str, "d%da", i);
            if (isparam(str))
               day = atoi(getparam(str));

            sprintf(str, "%da", i);
            show_date_selector(day, month, year, str);
            if (attr_flags[i] & AF_DATETIME) {
               rsprintf("&nbsp;&nbsp;");
               show_time_selector(-1, -1, -1, str);
            }

            rsprintf("</td></tr>\n");
            rsprintf("<tr><td width=\"1%%\">%s:<td>", loc("End"));

            year = month = day = 0;
            sprintf(str, "y%db", i);
            if (isparam(str))
               year = atoi(getparam(str));
            sprintf(str, "m%db", i);
            if (isparam(str))
               month = atoi(getparam(str));
            sprintf(str, "d%db", i);
            if (isparam(str))
               day = atoi(getparam(str));

            sprintf(str, "%db", i);
            show_date_selector(day, month, year, str);
            if (attr_flags[i] & AF_DATETIME) {
               rsprintf("&nbsp;&nbsp;");
               show_time_selector(-1, -1, -1, str);
            }

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

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

            for (j = 0;; j++) {
               if (!enum_user_line(lbs, j, login_name, sizeof(login_name)))
                  break;
               get_user_line(lbs, login_name, NULL, full_name, NULL, NULL, NULL, NULL);

               sprintf(str, "%s_%d", attr_list[i], j);

               rsprintf("<nobr><input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\"\">\n", str, str,
                        full_name);

               rsprintf("<label for=\"%s\">%s</label></nobr>\n", str, full_name);
            }

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

            for (j = 0;; j++) {
               if (!enum_user_line(lbs, j, login_name, sizeof(login_name)))
                  break;
               get_user_line(lbs, login_name, NULL, NULL, user_email, NULL, NULL, NULL);

               sprintf(str, "%s_%d", attr_list[i], j);

               rsprintf("<nobr><input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\"\">\n", str, str,
                        user_email);

               rsprintf("<label for=\"%s\">%s</label></nobr>\n", str, user_email);
            }

         } else {
            rsprintf("<input type=\"text\" size=\"30\" maxlength=\"80\" name=\"%s\" value=\"%s\">\n",
                     attr_list[i], attrib[i]);
         }

      } else {
         if (strieq(attr_options[i][0], "boolean")) {

            if (isparam(attr_list[i]) && *getparam(attr_list[i]))
               flag = atoi(getparam(attr_list[i]));
            else
               flag = -1;

            sprintf(str, "%s_0", attr_list[i]);
            rsprintf("<span style=\"white-space:nowrap;\">\n");
            if (flag == 0)
               rsprintf("<input type=radio checked id=\"%s\" name=\"%s\" value=\"0\">\n", str, attr_list[i]);
            else
               rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"0\">\n", str, attr_list[i]);
            rsprintf("<label for=\"%s\">0</label>\n", str);
            rsprintf("</span>\n");

            sprintf(str, "%s_1", attr_list[i]);
            rsprintf("<span style=\"white-space:nowrap;\">\n");
            if (flag == 1)
               rsprintf("<input type=radio checked id=\"%s\" name=\"%s\" value=\"1\">\n", str, attr_list[i]);
            else
               rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"1\">\n", str, attr_list[i]);
            rsprintf("<label for=\"%s\">1</label>\n", str);
            rsprintf("</span>\n");

            sprintf(str, "%s_2", attr_list[i]);
            rsprintf("<span style=\"white-space:nowrap;\">\n");
            if (flag == -1)
               rsprintf("<input type=radio checked id=\"%s\" name=\"%s\" value=\"\">\n", str, attr_list[i]);
            else
               rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"\">\n", str, attr_list[i]);
            rsprintf("<label for=\"%s\">%s</label>\n", str, loc("unspecified"));
            rsprintf("</span>\n");
         }

            /* display image for icon */
         else if (attr_flags[i] & AF_ICON) {
            for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
               strcpy(option, attr_options[i][j]);
               if (strchr(option, '{'))
                  *strchr(option, '{') = 0;

               sprintf(str, "Icon comment %s", option);
               getcfg(lbs->name, str, comment, sizeof(comment));

               if (comment[0] == 0)
                  strcpy(comment, option);

               rsprintf("<nobr><input type=radio name=\"%s\" value=\"%s\">", attr_list[i], comment);

               rsprintf("<img src=\"icons/%s\" alt=\"%s\" title=\"%s\"></nobr>\n", option, comment, comment);
            }
         }

            /* display check boxes (or'ed) */
         else if (attr_flags[i] & AF_MULTI) {
            for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
               sprintf(str, "%s_%d", attr_list[i], j);

               if (isparam(str))
                  rsprintf("<nobr><input type=checkbox checked id=\"%s\" name=\"%s\" value=\"%s\"\">\n",
                           str, str, attr_options[i][j]);
               else
                  rsprintf("<nobr><input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\"\">\n",
                           str, str, attr_options[i][j]);

               rsprintf("<label for=\"%s\">%s</label></nobr>\n", str, attr_options[i][j]);
            }
         } else {
            rsprintf("<select name=\"%s\"", attr_list[i]);

            if (is_cond_attr(i))
               rsprintf(" onChange=\"document.form1.jcmd.value='Find';document.form1.submit();\"");
            rsprintf(">\n");

            rsprintf("<option value=\"\">\n");
            for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
               strencode2(str, attr_options[i][j], sizeof(str));
               if (strchr(str, '{'))
                  *strchr(str, '{') = 0;
               strencode2(enc_attr, attrib[i], sizeof(enc_attr));

               sprintf(whole_attr, "^%s$", str);
               if (strieq(attr_options[i][j], attrib[i]) || strieq(str, enc_attr)
                   || strieq(whole_attr, enc_attr))
                  rsprintf("<option selected value=\"^%s$\">%s\n", str, str);
               else
                  rsprintf("<option value=\"^%s$\">%s\n", str, str);
            }
            rsprintf("</select>\n");
         }
      }
      rsprintf("</td></tr>\n");
   }

   rsprintf("<tr><td class=\"attribname\">%s:</td>", loc("Text"));
   rsprintf("<td class=\"attribvalue\">\n");
   if (isparam("subtext"))
      strlcpy(str, getparam("subtext"), sizeof(str));
   else
      str[0] = 0;
   rsprintf("<input type=\"text\" size=\"30\" maxlength=\"80\" name=\"subtext\" value=\"%s\">\n", str);

   rsprintf("<input type=checkbox id=\"sall\" name=\"sall\" value=1>\n");
   rsprintf("<label for=\"sall\">%s</label>\n", loc("Search text also in attributes"));

   rsprintf("<tr><td><td class=\"attribvalue\">\n");
   if (getcfg(lbs->name, "Case sensitive search", str, sizeof(str)) && atoi(str))
      rsprintf("<input type=checkbox id=\"sall\" name=\"casesensitive\" value=1 checked>\n");
   else
      rsprintf("<input type=checkbox id=\"sall\" name=\"casesensitive\" value=1>\n");
   rsprintf("<label for=\"casesensitive\">%s</label>\n", loc("Case sensitive"));

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