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

int elog_connect(char *host, int port) {
   int status, sock;
   struct hostent *phe;
   struct sockaddr_in bind_addr;

   /* create socket */
   if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
      perror("cannot create socket");
      return -1;
   }

   /* compose remote address */
   memset(&bind_addr, 0, sizeof(bind_addr));
   bind_addr.sin_family = AF_INET;
   bind_addr.sin_addr.s_addr = 0;
   bind_addr.sin_port = htons((unsigned short) port);

   phe = gethostbyname(host);
   if (phe == NULL) {
      perror("cannot get host name");
      return -1;
   }
   memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);

   /* connect to server */
   status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
   if (status != 0)
      return -1;

   return sock;
}

Project: elog

char *find_param(char *buf, char *group, char *param) {
   char *str, *p, *pstr, *pstart;

   /* search group */
   str = xmalloc(10000);
   p = buf;
   do {
      if (*p == '[') {
         p++;
         pstr = str;
         while (*p && *p != ']' && *p != '\n')
            *pstr++ = *p++;
         *pstr = 0;
         if (strieq(str, group)) {
            /* search parameter */
            p = strchr(p, '\n');
            if (p)
               p++;
            while (p && *p && *p != '[') {
               pstr = str;
               pstart = p;
               while (*p && *p != '=' && *p != '\n')
                  *pstr++ = *p++;
               *pstr-- = 0;
               while (pstr > str && (*pstr == ' ' || *pstr == '=' || *pstr == '\t'))
                  *pstr-- = 0;

               if (match_param(str, param, FALSE)) {
                  xfree(str);
                  return pstart;
               }

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

   xfree(str);

   /* now search if in [global] section */
   if (!strieq(group, "global"))
      return find_param(buf, "global", param);

   return NULL;
}

Project: elog

void check_config_file(BOOL force) {
   struct stat cfg_stat;

   if (force) {
      parse_config_file(config_file);
      return;
   }

   /* force re-read configuration file if changed */
   if (stat(config_file, &cfg_stat) == 0) {
      if (cfgfile_mtime < cfg_stat.st_mtime) {
         cfgfile_mtime = cfg_stat.st_mtime;
         parse_config_file(config_file);
      }
   } else
      eprintf("Cannot stat() config file \"%s\": %s\n", config_file, strerror(errno));
}

Project: elog

int getcfg_simple(char *group, char *param, char *value, int vsize, int conditional) {
   int i, j, status;
   char uparam[256];

   if (strlen(param) >= sizeof(uparam))
      return 0;

   for (i = 0; i < (int) strlen(param); i++)
      uparam[i] = toupper(param[i]);
   uparam[i] = 0;
   value[0] = 0;

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

   if (i == n_lb_config)
      return 0;

   for (j = 0; j < lb_config[i].n_params; j++)
      if (match_param(lb_config[i].config_param[j].uparam, uparam, conditional)) {
         status = strchr(lb_config[i].config_param[j].uparam, '{') ? 2 : 1;
         strlcpy(value, lb_config[i].config_param[j].value, vsize);
         return status;
      }

   return 0;
}

Project: elog

int execute_shell(LOGBOOK *lbs, int message_id, char attrib[MAX_N_ATTR][NAME_LENGTH],
                  char att_file[MAX_ATTACHMENTS][256], char *sh_cmd) {
   int i;
   char slist[MAX_N_ATTR + 10][NAME_LENGTH], svalue[MAX_N_ATTR + 10][NAME_LENGTH];
   char shell_cmd[10000], tail[1000], str[NAME_LENGTH], *p, subdir[256];

   if (!enable_execute) {
      eprintf("Shell execution not enabled via -x flag.\n");
      return SUCCESS;
   }

   strlcpy(shell_cmd, sh_cmd, sizeof(shell_cmd));

   i = build_subst_list(lbs, slist, svalue, attrib, TRUE);
   sprintf(str, "%d", message_id);
   add_subst_list(slist, svalue, "message id", str, &i);
   add_subst_list(slist, svalue, "text", getparam("text"), &i);
   strsubst_list(shell_cmd, sizeof(shell_cmd), slist, svalue, i);

   if (att_file && stristr(shell_cmd, "$attachments")) {
      /* substitute attachments */
      p = stristr(shell_cmd, "$attachments");
      strlcpy(tail, p + strlen("$attachments"), sizeof(tail));
      *p = 0;
      for (i = 0; i < MAX_ATTACHMENTS; i++) {
         generate_subdir_name(att_file[i], subdir, sizeof(subdir));
         if (att_file[i][0] && strlen(shell_cmd) + strlen(lbs->data_dir) + strlen(subdir) + strlen(att_file[i])
                               < sizeof(shell_cmd) + 1) {
            strcpy(p, "\"");
            strcat(p, lbs->data_dir);
            strlcat(str, subdir, sizeof(str));
            strlcpy(str, att_file[i], sizeof(str));
            str_escape(str, sizeof(str));
            strcat(p, str);
            strcat(p, "\" ");
            p += strlen(p);
         }
      }
      strlcat(shell_cmd, tail, sizeof(shell_cmd));
   }

   sprintf(str, "SHELL \"%s\"", shell_cmd);
   write_logfile(lbs, str);

   my_shell(shell_cmd, str, sizeof(str));

   return SUCCESS;
}

Project: elog

void show_error(char *error) {
   char str[256];

   /* header */
   rsprintf("HTTP/1.1 404 Not Found\r\n");
   rsprintf("Server: ELOG HTTP %s-%s\r\n", VERSION, git_revision());
   if (getcfg("global", "charset", str, sizeof(str)))
      rsprintf("Content-Type: text/html;charset=%s\r\n", str);
   else
      rsprintf("Content-Type: text/html;charset=%s\r\n", DEFAULT_HTTP_CHARSET);
   rsprintf("\r\n");

   rsprintf("<!DOCTYPE html>\n");
   rsprintf("<html><head>\n");
   rsprintf("<META NAME=\"ROBOTS\" CONTENT=\"NOINDEX, NOFOLLOW\">\n");
   rsprintf("<title>%s</title>\n", loc("ELOG error"));
   rsprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"elog.css\">\n");
   rsprintf("</head>\n");

   rsprintf("<body><center>\n");
   rsprintf("<table class=\"dlgframe\" width=\"50%%\" cellpadding=\"1\" cellspacing=\"0\"");
   rsprintf("<tr><td class=\"errormsg\">%s</td></tr>\n", error);

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

   rsprintf("<script language=\"javascript\" type=\"text/javascript\">\n");
   rsprintf("document.write(\"<button type=button onClick=history.back()>%s</button>\"); \n", loc("Back"));
   rsprintf("</script>\n");

   rsprintf("<noscript>\n");
   rsprintf("%s\n", loc("Please use your browser's back button to go back"));
   rsprintf("</noscript>\n");

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

Project: elog

void strencode(char *text) {
   int i;

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

            /* the translation for the search highliting */

         case '\001':
            rsprintf("<");
            break;
         case '\002':
            rsprintf(">");
            break;
         case '\003':
            rsprintf("\"");
            break;
         case '\004':
            rsprintf(" ");
            break;

         default:
            rsprintf("%c", text[i]);
      }
   }
}

Project: elog

int create_logbook(LOGBOOK *oldlbs, char *logbook, char *templ) {
   int fh, i, length, bufsize, templ_length;
   char *buf, *p1, *p2, str[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;
   }

   /* add logbook to current group */
   add_logbook_to_group(oldlbs, logbook);

   /* read previous contents */
   length = lseek(fh, 0, SEEK_END);
   lseek(fh, 0, SEEK_SET);
   bufsize = 2 * (2 * length + 1);
   buf = (char *) xmalloc(bufsize);
   read(fh, buf, length);
   buf[length] = 0;
   templ_length = 0;
   p2 = NULL;

   /* find template logbook */
   if (templ[0]) {
      p1 = (char *) find_section(buf, templ);
      p2 = (char *) find_next_section(p1 + 1);
   } else
      p1 = NULL;

   if (p1) {
      p1 = strchr(p1, ']');
      if (p1)
         while (*p1 == ']' || *p1 == '\r' || *p1 == '\n')
            p1++;

      if (p2)
         templ_length = p2 - p1;
      else
         templ_length = strlen(p1);
   }

   /* insert single blank line after last logbook */
   p2 = buf + strlen(buf) - 1;

   while (p2 > buf && (*p2 == '\r' || *p2 == '\n' || *p2 == ' ' || *p2 == '\t')) {
      *p2 = 0;
      p2--;
   }
   if (p2 > buf)
      p2++;

   strcat(p2, "\r\n\r\n[");
   strcat(p2, logbook);
   strcat(p2, "]\r\n");
   if (p1) {
      p2 = buf + strlen(buf);
      strncpy(p2, p1, templ_length);
      p2[templ_length] = 0;
   }

   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 fputs_stderr(const char *buf) {
   fputs(buf, stderr);
}

Project: elog

int enumgrp(int index, char *group) {
   if (index < n_lb_config) {
      strcpy(group, lb_config[index].section_name);
      return 1;
   }

   return 0;
}

Project: elog

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

Project: elog

void eflush(void) {
   /* Do this only for non-NULL streams (uninitiated stream or a syslog) */
   if (current_output_stream != NULL)
      fflush(current_output_stream);
}

Project: elog

BOOL is_user_allowed(LOGBOOK *lbs, char *command) {
   char str[1000], users[2000];
   char list[MAX_N_LIST][NAME_LENGTH];
   int i, n;

   /* check for user level access */
   if (!getcfg(lbs->name, "Password file", str, sizeof(str)))
      return TRUE;

   /* check for deny */
   sprintf(str, "Deny %s", command);
   if (getcfg(lbs->name, str, users, sizeof(users))) {

      if (!isparam("unm"))
         return FALSE;

      /* check if current user in list */
      n = strbreak(users, list, MAX_N_LIST, ",", FALSE);
      for (i = 0; i < n; i++)
         if (strieq(list[i], getparam("unm")))
            break;

      if (i < n)
         return FALSE;
   }

   /* check admin command */
   if (strieq(command, loc("Admin"))) {
      if (getcfg(lbs->name, "Admin user", str, sizeof(str))) {
         return is_admin_user(lbs, getparam("unm"));
      }
   }

   /* check for allow */
   sprintf(str, "Allow %s", command);
   if (!getcfg(lbs->name, str, users, sizeof(users)))
      return TRUE;

   /* check if current user in list */
   if (!isparam("unm"))
      return FALSE;

   n = strbreak(users, list, MAX_N_LIST, ",", FALSE);
   for (i = 0; i < n; i++)
      if (strieq(list[i], getparam("unm")))
         return TRUE;

   return FALSE;
}

Project: elog

void extract_path(char *str) {
   char *p, str2[256];

   p = NULL;

   if (strstr(str, "http://"))
      p = str + 7;
   if (strstr(str, "https://"))
      p = str + 8;

   if (p) {
      while (*p && *p != '/')
         p++;
      if (*p == '/')
         p++;

      strcpy(str2, p);
      strcpy(str, str2);
      if (str[strlen(str) - 1] == '/')
         str[strlen(str) - 1] = 0;
   }
}

Project: elog

void setcfg_topgroup(char *topgroup) {
   strcpy(_topgroup, topgroup);
}

Project: elog

int line_break(char *str, char *encoding) {
   if (strieq(encoding, "plain") || strieq(encoding, "ELCode")) {
      return str[0] == '\n';
   }
   // HTML encoding
   if (strncmp(str, "</p>", 4) == 0 || strncmp(str, "<br>", 4) == 0 || strncmp(str, "<br />", 6) == 0)
      return 1;

   return 0;
}

Project: elog

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

   gr = getgrnam(str);

   if (gr != NULL) {
      chown(logbook_dir, -1, gr->gr_gid);
      if (setregid(-1, 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

void *xmalloc(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(bytes + 12);
   if (temp == 0)
      memory_error_and_abort("xmalloc");

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

   return (temp + 8);
}

Project: elog

int parse_config_file(char *file_name)
/* parse whole config file and store options in sorted list */
{
   char *str, *buffer, *p, *pstr;
   int index, i, j, fh, length;

   str = xmalloc(20000);

   /* open configuration file */
   fh = open(file_name, O_RDONLY | O_BINARY);
   if (fh < 0)
      return 0;
   length = lseek(fh, 0, SEEK_END);
   lseek(fh, 0, SEEK_SET);
   buffer = xmalloc(length + 1);
   read(fh, buffer, length);
   buffer[length] = 0;
   close(fh);

   /* release previously allocated memory */
   if (lb_config)
      free_config();

   /* search group */
   p = buffer;
   index = 0;
   do {
      if (*p == '#' || *p == ';') {
         /* skip comment */
         while (*p && *p != '\n' && *p != '\r')
            p++;
      } else if (*p == '[') {
         p++;
         pstr = str;
         while (*p && *p != ']' && *p != '\n' && *p != '\r' && pstr - str < 10000)
            *pstr++ = *p++;
         *pstr = 0;

         /* allocate new group */
         if (!lb_config)
            lb_config = xmalloc(sizeof(LB_CONFIG));
         else
            lb_config = xrealloc(lb_config, sizeof(LB_CONFIG) * (n_lb_config + 1));
         lb_config[n_lb_config].section_name = xmalloc(strlen(str) + 1);
         lb_config[n_lb_config].n_params = 0;
         lb_config[n_lb_config].config_param = NULL;
         strcpy(lb_config[n_lb_config].section_name, str);

         /* enumerate parameters */
         i = 0;
         p = strchr(p, '\n');
         if (p)
            p++;
         while (p && *p && *p != '[') {
            pstr = str;
            while (*p && *p != '=' && *p != '\n' && *p != '\r' && pstr - str < 10000)
               *pstr++ = *p++;
            *pstr-- = 0;
            while (pstr > str && (*pstr == ' ' || *pstr == '\t' || *pstr == '='))
               *pstr-- = 0;

            if (*p == '=') {

               if (lb_config[n_lb_config].n_params == 0)
                  lb_config[n_lb_config].config_param = xmalloc(sizeof(CONFIG_PARAM));
               else
                  lb_config[n_lb_config].config_param = xrealloc(lb_config[n_lb_config].config_param,
                                                                 sizeof(CONFIG_PARAM) *
                                                                 (lb_config[n_lb_config].n_params + 1));
               lb_config[n_lb_config].config_param[i].param = xmalloc(strlen(str) + 1);
               lb_config[n_lb_config].config_param[i].uparam = xmalloc(strlen(str) + 1);

               strcpy(lb_config[n_lb_config].config_param[i].param, str);
               for (j = 0; j < (int) strlen(str); j++)
                  lb_config[n_lb_config].config_param[i].uparam[j] = toupper(str[j]);
               lb_config[n_lb_config].config_param[i].uparam[j] = 0;

               p++;
               while (*p == ' ' || *p == '\t')
                  p++;
               pstr = str;
               while (*p && *p != '\n' && *p != '\r' && pstr - str < 10000)
                  *pstr++ = *p++;
               *pstr-- = 0;
               while (*pstr == ' ' || *pstr == '\t')
                  *pstr-- = 0;

               lb_config[n_lb_config].config_param[i].value = xmalloc(strlen(str) + 1);
               strcpy(lb_config[n_lb_config].config_param[i].value, str);

               i++;
               lb_config[n_lb_config].n_params = i;
            }

            /* search for next line beginning */
            while (*p && *p != '\r' && *p != '\n')
               p++;
            while (*p && (*p == '\r' || *p == '\n'))
               p++;
         }

         /* sort parameter */
         // outcommented: not needed, might screw up group ordering
         //qsort(lb_config[n_lb_config].config_param, lb_config[n_lb_config].n_params, sizeof(CONFIG_PARAM),
         //      param_compare);

         n_lb_config++;
         index++;
      }

      /* search for next line beginning */
      while (*p && *p != '\r' && *p != '\n' && *p != '[')
         p++;
      while (*p && (*p == '\r' || *p == '\n'))
         p++;

   } while (*p);

   xfree(str);
   xfree(buffer);
   return 0;
}

Project: elog

int el_correct_links(LOGBOOK *lbs, int old_id, int new_id)
/* If a message gets resubmitted, the links to that message are wrong.
 This routine corrects that. */
{
   int i, i1, n, n1, size;
   char date[80], *attrib, *text, in_reply_to[80], reply_to[MAX_REPLY_TO * 10], encoding[80],
           locked_by[256], draft[256];
   char list[MAX_N_ATTR][NAME_LENGTH], list1[MAX_N_ATTR][NAME_LENGTH];
   char *att_file;

   attrib = (char *) xmalloc(MAX_N_ATTR * NAME_LENGTH);
   text = (char *) xmalloc(TEXT_SIZE);
   att_file = (char *) xmalloc(MAX_ATTACHMENTS * 256);

   el_retrieve(lbs, new_id, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, NULL, 0, in_reply_to,
               reply_to, (char (*)[256]) att_file, encoding, locked_by, draft);

   /* go through in_reply_to list */
   n = strbreak(in_reply_to, list, MAX_N_ATTR, ",", FALSE);
   for (i = 0; i < n; i++) {
      size = TEXT_SIZE;
      el_retrieve(lbs, atoi(list[i]), date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text, &size,
                  in_reply_to, reply_to, (char (*)[256]) att_file, encoding, locked_by, draft);

      n1 = strbreak(reply_to, list1, MAX_N_ATTR, ",", FALSE);
      reply_to[0] = 0;
      for (i1 = 0; i1 < n1; i1++) {
         /* replace old ID by new ID */
         if (atoi(list1[i1]) == old_id)
            sprintf(reply_to + strlen(reply_to), "%d", new_id);
         else
            strcat(reply_to, list1[i1]);

         if (i1 < n1 - 1)
            strcat(reply_to, ", ");
      }

      el_submit(lbs, atoi(list[i]), TRUE, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text,
                in_reply_to, reply_to, encoding, (char (*)[256]) att_file, TRUE, locked_by, draft);
   }

   el_retrieve(lbs, new_id, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, NULL, 0, in_reply_to,
               reply_to, (char (*)[256]) att_file, encoding, locked_by, draft);

   /* go through reply_to list */
   n = strbreak(reply_to, list, MAX_N_ATTR, ",", FALSE);
   for (i = 0; i < n; i++) {
      size = sizeof(text);
      el_retrieve(lbs, atoi(list[i]), date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text, &size,
                  in_reply_to, reply_to, (char (*)[256]) att_file, encoding, locked_by, draft);

      n1 = strbreak(in_reply_to, list1, MAX_N_ATTR, ",", FALSE);
      in_reply_to[0] = 0;
      for (i1 = 0; i1 < n1; i1++) {
         /* replace old ID by new ID */
         if (atoi(list1[i1]) == old_id)
            sprintf(in_reply_to + strlen(in_reply_to), "%d", new_id);
         else
            strcat(in_reply_to, list1[i1]);

         if (i1 < n1 - 1)
            strcat(in_reply_to, ", ");
      }

      el_submit(lbs, atoi(list[i]), TRUE, date, attr_list, (char (*)[1500]) attrib, lbs->n_attr, text,
                in_reply_to, reply_to, encoding, (char (*)[256]) att_file, TRUE, locked_by, draft);
   }

   xfree(text);
   xfree(attrib);
   xfree(att_file);

   return EL_SUCCESS;
}

Project: elog

int is_script(char *s) {
   char *str;
   int i;

   str = xstrdup(s);

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

   for (i = 0; script_tags[i] != NULL; i++) {
      if (strstr(str, script_tags[i])) {
         xfree(str);
         return TRUE;
      }
   }

   xfree(str);
   return FALSE;
}

Project: elog

int retrieve_url(LOGBOOK *lbs, const char *url, int ssl, char **buffer, BOOL send_unm) {
   char str[1000], unm[256], upwd[256], host[256], subdir[256], param[256];
   int port, bufsize;
   int i, n;
   fd_set readfds;
   struct timeval timeout;

   UNUSED(ssl);

#ifdef HAVE_SSL
   SSL *ssl_con = NULL;
#else
   void *ssl_con = NULL;
#endif
   int sock;

   *buffer = NULL;
   split_url(url, host, &port, subdir, param);

   /* create a new socket for connecting to remote server */
   sock = elog_connect(host, port);
   if (sock == -1)
      return -1;
#ifdef HAVE_SSL
   if (ssl)
      if (ssl_connect(sock, &ssl_con) < 0) {
         printf("Error initiating SSL connection\n");
         return -1;
      }
#endif

   /* compose GET request, avoid chunked data in HTTP/1.1 protocol */
   sprintf(str, "GET %s%s HTTP/1.0\r\nConnection: Close\r\n", subdir, param);

   /* add local username/password */
   if (send_unm) {
      if (isparam("unm")) {
         strlcpy(unm, getparam("unm"), sizeof(unm));
         if (isparam("upwd"))
            strlcpy(upwd, getparam("upwd"), sizeof(upwd));
         else
            get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL);

         sprintf(str + strlen(str), "Cookie: unm=%s; upwd=%s\r\n", unm, upwd);
      }
   }

   /* add host (RFC2616, Sec. 14) */
   sprintf(str + strlen(str), "Host: %s:%d\r\n", host, port);

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

   send_with_timeout(ssl_con, sock, (char *) str, strlen(str));

   bufsize = TEXT_SIZE + 1000;
   *buffer = xmalloc(bufsize);
   memset(*buffer, 0, bufsize);

   n = 0;

   do {
      FD_ZERO(&readfds);
      FD_SET(sock, &readfds);

      timeout.tv_sec = 30;      /* 30 sec. timeout */
      timeout.tv_usec = 0;

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

      if (!FD_ISSET(sock, &readfds)) {
         closesocket(sock);
         sock = 0;
         xfree(*buffer);
         *buffer = NULL;
         return -1;
      }
#ifdef HAVE_SSL
      if (ssl)
         i = SSL_read(ssl_con, *buffer + n, bufsize - n);
      else
#endif
         i = recv(sock, *buffer + n, bufsize - n, 0);

      if (i <= 0)
         break;

      n += i;

      if (n >= bufsize) {
         /* increase buffer size */
         bufsize += 10000;
         *buffer = (char *) xrealloc(*buffer, bufsize);
         if (*buffer == NULL) {
            closesocket(sock);
            return -1;
         }
      }

   } while (1);

   return n;
}

Project: elog

void show_standard_title(LOGBOOK *lbs, char *text, int printable) {
   char str[NAME_LENGTH], ref[256], sclass[32], comment[256], full_name[256], url[256], logbook[256];
   int i, j, level;
   LBLIST phier, pnode, pnext, flb;
   char slist[20][NAME_LENGTH], svalue[20][NAME_LENGTH];

   if (lbs == NULL)
      strlcpy(logbook, "global", sizeof(logbook));
   else
      strlcpy(logbook, lbs->name, sizeof(logbook));

   if (printable)
      rsprintf
              ("<table class=\"pframe\" cellpadding=\"0\" cellspacing=\"0\"><!-- show_standard_title -->\n\n");
   else
      rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\"><!-- show_standard_title -->\n\n");

   /* scan logbook hierarchy */
   phier = get_logbook_hierarchy();

   /*---- logbook selection row ----*/

   pnode = phier;               /* start at root of tree */
   pnext = NULL;

   if (!printable && (!getcfg(logbook, "logbook tabs", str, sizeof(str)) || atoi(str) == 1)) {

      for (level = 0;; level++) {
         rsprintf("<tr><td class=\"tabs\">\n");

         if (level == 0 && getcfg("global", "main tab", str, sizeof(str)) && !getcfg_topgroup()) {
            if (getcfg("global", "main tab url", url, sizeof(url)))
               rsprintf("<span class=\"ltab\"><a href=\"%s\">%s</a></span>\n", url, str);
            else
               rsprintf("<span class=\"ltab\"><a href=\"../\">%s</a></span>\n", str);
         }

         if (level == 1 && getcfg("global", "main tab", str, sizeof(str)) && getcfg_topgroup()) {
            if (getcfg("global", "main tab url", url, sizeof(url)))
               rsprintf("<span class=\"ltab\"><a href=\"%s/\">%s</a></span>\n", url, str);
            else
               rsprintf("<span class=\"ltab\"><a href=\"../%s/\">%s</a></span>\n", getcfg_topgroup(), str);
         }

         /* iterate through members of this group */
         for (i = 0; i < pnode->n_members; i++) {

            if (getcfg(pnode->member[i]->name, "Hidden", str, sizeof(str)) && atoi(str) == 1)
               continue;

            strlcpy(str, pnode->member[i]->name, sizeof(str));

            /* build reference to first logbook in group */
            comment[0] = 0;
            if (pnode->member[i]->member == NULL) {
               getcfg(pnode->member[i]->name, "Comment", comment, sizeof(comment));
               strlcpy(ref, str, sizeof(ref));  // current node is logbook
            } else {
               flb = pnode->member[i]->member[0];       // current node is group
               while (flb->member)
                  // so traverse hierarchy
                  flb = flb->member[0];
               strlcpy(ref, flb->name, sizeof(ref));
            }
            url_encode(ref, sizeof(ref));

            if (is_logbook_in_group(pnode->member[i], logbook)) {

               /* remember member list of this group for next row */
               pnext = pnode->member[i];

               if (pnode->member[i]->member == NULL)
                  /* selected logbook */
                  strcpy(sclass, "sltab");
               else
                  /* selected group */
                  strcpy(sclass, "sgtab");
            } else {
               if (pnode->member[i]->member == NULL)
                  /* unselected logbook */
                  strcpy(sclass, "ltab");
               else
                  /* unselected group */
                  strcpy(sclass, "gtab");
            }

            if (!pnode->member[i]->is_top) {

               rsprintf("<span class=\"%s\">", sclass);

               if (comment[0]) {
                  rsprintf("<a href=\"../%s/\" title=\"", ref);
                  rsputs3(comment);
                  rsprintf("\">");
               } else
                  rsprintf("<a href=\"../%s/\">", ref);

               strlcpy(str, pnode->member[i]->name, sizeof(str));

               for (j = 0; j < (int) strlen(str); j++)
                  if (str[j] == ' ')
                     rsprintf("&nbsp;");
                  else
                     rsprintf("%c", str[j]);

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

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

         pnode = pnext;
         pnext = NULL;

         if (pnode == NULL || pnode->n_members == 0)
            break;
      }
   }

   free_logbook_hierarchy(phier);

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

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

   /* left cell */
   rsprintf("<tr><td class=\"title1\">");

   /* use comment as title if available, else logbook name */
   if (!getcfg(logbook, "Comment", str, sizeof(str)))
      strcpy(str, logbook);

   rsprintf("&nbsp;&nbsp;");

   if (is_html(str))
      rsputs(str);
   else
      rsputs3(str);

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

   /* middle cell */
   if (isparam("unm")) {
      get_user_line(lbs, getparam("unm"), NULL, full_name, NULL, NULL, NULL, NULL);
      rsprintf("<td class=\"title2\">%s \"%s\"</td>\n", loc("Logged in as"), full_name);
   } else if (getcfg(lbs->name, "Guest menu commands", str, sizeof(str)))
      rsprintf("<td class=\"title2\" align=center>%s</td>\n", loc("Not logged in"));

   /* right cell */
   rsprintf("<td class=\"title3\">");

   if (getcfg(logbook, "Title image URL", str, sizeof(str)))
      rsprintf("<a href=\"%s\">\n", str);

   if (getcfg(logbook, "Title image", str, sizeof(str))) {
      // allow $short_name for example to link to personal pictures
      i = build_subst_list(lbs, slist, svalue, NULL, TRUE);
      strsubst_list(str, sizeof(str), slist, svalue, i);

      rsprintf("%s", str);
   } else
      rsprintf("<img border=0 src=\"elog.png\" alt=\"ELOG logo\" title=\"ELOG logo\">");

   if (getcfg(logbook, "Title image URL", str, sizeof(str)))
      rsprintf("</a>\n");

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

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

Project: elog

void combine_url(LOGBOOK *lbs, char *url, char *param, char *result, int size, int *ssl) {
   if (ssl)
      *ssl = 0;
   if (strstr(url, "http://"))
      strlcpy(result, url + 7, size);
   else if (strstr(url, "https://")) {
      if (ssl)
         *ssl = 1;
      strlcpy(result, url + 8, size);
   } else
      strlcpy(result, url, size);

   url_encode(result, size);

   if (result[strlen(result) - 1] != '/')
      strlcat(result, "/", size);

   if (lbs != NULL) {
      if (!strstr(result, lbs->name_enc)) {
         strlcat(result, lbs->name_enc, size);
         strlcat(result, "/", size);
      }
   }

   if (param)
      strlcat(result, param, size);
}

Project: elog

int is_draft(LOGBOOK *lbs, int message_id) {
   char draft[256];

   el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL,
               NULL, NULL, draft);
   return draft[0];
}

Project: elog

void rsputs_elcode(LOGBOOK *lbs, BOOL email_notify, const char *str) {
   int i, j, k, l, m, elcode_disabled, elcode_disabled1, ordered_list, substituted, inside_table,
           smileys_enabled;
   char *p, *pd, link[1000], link_text[1000], tmp[1000], attrib[1000], hattrib[1000], value[1000],
           subst[1000], base_url[256], param[256], *lstr, domain[256];

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

   elcode_disabled = FALSE;
   elcode_disabled1 = FALSE;
   ordered_list = FALSE;
   smileys_enabled = TRUE;
   inside_table = 0;
   j = strlen_retbuf;
   m = 0;

   /* check for smileys */
   if (getcfg(lbs->name, "Enable smileys", tmp, sizeof(tmp)) && atoi(tmp) == 0)
      smileys_enabled = FALSE;

   /* make lower case copy of str */
   lstr = xmalloc(strlen(str) + 1);
   for (pd = lstr, p = (char *) str; *p; p++, pd++)
      *pd = tolower(*p);
   *pd = 0;

   for (i = 0; i < (int) strlen(str); i++) {

      for (l = 0; key_list[l][0]; l++) {
         if (strncmp(lstr + i, key_list[l], strlen(key_list[l])) == 0) {

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

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

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

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

            /* check if link contains coloring */
            while ((p = strchr(link, '\001')) != NULL) {

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

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

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

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

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

            if (strcmp(key_list[l], "elog:") == 0) {
               strlcpy(tmp, link, sizeof(tmp));
               if (strchr(tmp, '/'))
                  *strchr(tmp, '/') = 0;

               for (m = 0; m < (int) strlen(tmp); m++)
                  if (!isdigit(tmp[m]))
                     break;

               if (m < (int) strlen(tmp) && tmp[m] != '#') {
                  /* if link contains reference to other logbook, put logbook explicitly */
                  if (email_notify)
                     compose_base_url(NULL, base_url, sizeof(base_url), TRUE);
                  else
                     strcpy(base_url, "../");
                  sprintf(return_buffer + j, "<a href=\"%s%s\">elog:%s</a>", base_url, link, link_text);
               } else if (link[0] == '/') {
                  if (email_notify)
                     compose_base_url(NULL, base_url, sizeof(base_url), TRUE);
                  else
                     base_url[0] = 0;
                  sprintf(return_buffer + j, "<a href=\"%s%s/%d%s\">elog:%s</a>", base_url, lbs->name_enc,
                          _current_message_id, link, link_text);
               } else {
                  if (email_notify)
                     compose_base_url(lbs, base_url, sizeof(base_url), TRUE);
                  else
                     base_url[0] = 0;
                  sprintf(return_buffer + j, "<a href=\"%s%s\">elog:%s</a>", base_url, link, link_text);
               }
            } else if (strcmp(key_list[l], "mailto:") == 0) {
               sprintf(return_buffer + j, "<a href=\"mailto:%s\">%s</a>", link, link_text);
            } else {
               sprintf(return_buffer + j, "<a href=\"%s", key_list[l]);
               j += strlen(return_buffer + j);
               strlen_retbuf = j;

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

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

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

            j += strlen(return_buffer + j);
            break;
         }
      }
      if (key_list[l][0])
         continue;

      substituted = FALSE;

      for (l = 0; pattern_list[l].pattern[0]; l++) {
         if (strncmp(lstr + i, pattern_list[l].pattern, strlen(pattern_list[l].pattern)) == 0) {

            if (stristr(pattern_list[l].pattern, "[/code]"))
               elcode_disabled = FALSE;
            if (stristr(pattern_list[l].pattern, "[/code1]"))
               elcode_disabled1 = FALSE;

            /* check for escape character */
            if (i > 0 && str[i - 1] == '\\') {

               j--;
               strncpy(return_buffer + j, str + i, strlen(pattern_list[l].pattern));
               j += strlen(pattern_list[l].pattern);
               i += strlen(pattern_list[l].pattern) - 1;        // 1 gets added in for loop...
               substituted = TRUE;

               break;
            }

            /* check for blank before smiley and if smileys are allowed */
            if (l <= 20 &&
                ((str[i - 1] != ' ' && str[i - 1] != '\r' && str[i - 1] != '\n') ||
                 (smileys_enabled == FALSE))) {
               strncpy(return_buffer + j, str + i, strlen(pattern_list[l].pattern));
               j += strlen(pattern_list[l].pattern);
               i += strlen(pattern_list[l].pattern) - 1;        // 1 gets added in for loop...
               substituted = TRUE;

               break;
            }

            if (!elcode_disabled && !elcode_disabled1) {

               substituted = TRUE;

               if (stristr(pattern_list[l].pattern, "[list="))
                  ordered_list = TRUE;

               if (stristr(pattern_list[l].pattern, "[table"))
                  inside_table++;
               if (stristr(pattern_list[l].pattern, "[/table]"))
                  inside_table--;

               if (stristr(pattern_list[l].pattern, "[quote")) {
                  if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] == '=') {
                     i += strlen(pattern_list[l].pattern);
                     strextract(str + i, ']', attrib, sizeof(attrib));
                     i += strlen(attrib);

                     if (attrib[0] == '\"')
                        memmove(attrib, attrib + 1, strlen(attrib + 1) + 1);
                     if (attrib[strlen(attrib) - 1] == '\"')
                        attrib[strlen(attrib) - 1] = 0;

                     sprintf(value, loc("%s wrote"), attrib);
                     if (email_notify)
                        sprintf(return_buffer + j, email_quote_table, value);
                     else
                        sprintf(return_buffer + j, pattern_list[l].subst, value);
                     j += strlen(return_buffer + j);
                  } else {
                     if (email_notify)
                        sprintf(return_buffer + j, email_quote_table, loc("Quote"));
                     else
                        sprintf(return_buffer + j, pattern_list[l].subst, loc("Quote"));
                     j += strlen(return_buffer + j);
                     i += strlen(pattern_list[l].pattern) - 1;  // 1 gets added in for loop...
                  }
               } else if (strstr(pattern_list[l].subst, "%#")) {

                  /* special substitutions */
                  if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] == '=') {

                     i += strlen(pattern_list[l].pattern);
                     strextract(str + i, ']', attrib, sizeof(attrib));
                     i += strlen(attrib) + 1;

                     if (strncmp(attrib, "elog:", 5) == 0) {    /* eval elog: */
                        strlcpy(tmp, attrib + 5, sizeof(tmp));
                        if (strchr(tmp, '/'))
                           *strchr(tmp, '/') = 0;

                        for (m = 0; m < (int) strlen(tmp); m++)
                           if (!isdigit(tmp[m]))
                              break;

                        if (m < (int) strlen(tmp))
                           /* if link contains reference to other logbook, add ".." in front */
                           sprintf(hattrib, "../%s", attrib + 5);
                        else if (attrib[5] == '/')
                           sprintf(hattrib, "%d%s", _current_message_id, attrib + 5);
                        else
                           sprintf(hattrib, "%s", attrib + 5);

                     } else if (strstr(attrib, "://") == 0 && attrib[0] != '#') {       /* add http:// if missing */
                        if (_ssl_flag)
                           sprintf(hattrib, "https://%s", attrib);
                        else
                           sprintf(hattrib, "http://%s", attrib);
                     } else
                        strlcpy(hattrib, attrib, sizeof(hattrib));

                     strextract(str + i, '[', value, sizeof(value));
                     i += strlen(value) - 1;
                     strlcpy(subst, pattern_list[l].subst, sizeof(subst));
                     *strchr(subst, '#') = 's';
                     sprintf(return_buffer + j, subst, hattrib, value);

                     j += strlen(return_buffer + j);

                  } else if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] != '=') {

                     i += strlen(pattern_list[l].pattern);
                     strextract(str + i, '[', attrib, sizeof(attrib));
                     i += strlen(attrib) - 1;
                     strlcpy(hattrib, attrib, sizeof(hattrib));

                     /* replace elog:x/x for images */
                     if (strnieq(attrib, "elog:", 5)) {
                        if (email_notify) {
                           retrieve_domain(domain, sizeof(domain));
                           sprintf(hattrib, "cid:att%d@%s", m, domain);
                        } else {
                           if (email_notify)
                              compose_base_url(lbs, hattrib, sizeof(hattrib), TRUE);
                           else
                              hattrib[0] = 0;
                           if (attrib[5] == '/') {
                              if (_current_message_id == 0) {
                                 sprintf(param, "attachment%d", atoi(attrib + 6) - 1);
                                 if (isparam(param))
                                    strlcat(hattrib, getparam(param), sizeof(hattrib));
                              } else
                                 sprintf(hattrib + strlen(hattrib), "%d%s", _current_message_id, attrib + 5);
                           } else {
                              strlcat(hattrib, attrib + 5, sizeof(hattrib));
                           }
                        }
                     }

                        /* add http:// if missing */
                     else if ((!strnieq(attrib, "http://", 7) && !strnieq(attrib, "https://", 8)) &&
                              strstr(pattern_list[l].subst, "mailto") == NULL &&
                              strstr(pattern_list[l].subst, "img") == NULL &&
                              strncmp(pattern_list[l].subst, "<a", 2) != 0) {
                        if (_ssl_flag)
                           snprintf(hattrib, sizeof(hattrib), "https://%s", attrib);
                        else
                           snprintf(hattrib, sizeof(hattrib), "http://%s", attrib);
                     }

                     strlcpy(subst, pattern_list[l].subst, sizeof(subst));
                     if (email_notify && stristr(subst, "?thumb=1"))
                        strlcpy(subst, "<a href=\"%#\"><img border=0 src=\"%#\"></a>", sizeof(subst));
                     strsubst(subst, sizeof(subst), "%#", hattrib);
                     sprintf(return_buffer + j, subst, attrib);
                     j += strlen(return_buffer + j);
                  }

               } else if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] == '=') {

                  /* extract sting after '=' and put it into '%s' of subst */
                  i += strlen(pattern_list[l].pattern);
                  strextract(str + i, ']', attrib, sizeof(attrib));
                  i += strlen(attrib);
                  sprintf(return_buffer + j, pattern_list[l].subst, attrib);
                  j += strlen(return_buffer + j);

               } else if (pattern_list[l].pattern[strlen(pattern_list[l].pattern) - 1] == ' ') {

                  /* extract sting after ' ' and put it into '%s' of subst */
                  i += strlen(pattern_list[l].pattern);
                  strextract(str + i, ']', attrib, sizeof(attrib));
                  i += strlen(attrib);
                  sprintf(return_buffer + j, pattern_list[l].subst, attrib);
                  j += strlen(return_buffer + j);

               } else if (strncmp(pattern_list[l].pattern, "[/list]", 7) == 0) {

                  if (ordered_list)
                     strcpy(subst, "</ol>");
                  else
                     strcpy(subst, "</ul>");
                  ordered_list = FALSE;

                  strcpy(return_buffer + j, subst);
                  j += strlen(subst);
                  i += strlen(pattern_list[l].pattern) - 1;     // 1 gets added in for loop...

               } else if (strncmp(pattern_list[l].pattern, "|", 1) == 0) {

                  if (inside_table) {
                     strcpy(link, pattern_list[l].subst);
                     if (strstr(link, "%s")) {
                        strcpy(tmp, link);
                        if (email_notify)
                           compose_base_url(lbs, base_url, sizeof(base_url), TRUE);
                        else
                           base_url[0] = 0;
                        sprintf(link, tmp, base_url);
                     }

                     strcpy(return_buffer + j, link);
                     j += strlen(link);
                     i += strlen(pattern_list[l].pattern) - 1;  // 1 gets added in for loop...
                  } else {
                     strcpy(return_buffer + j, pattern_list[l].pattern);
                     j += strlen(pattern_list[l].pattern);
                     i += strlen(pattern_list[l].pattern) - 1;  // 1 gets added in for loop...
                  }

               } else {

                  /* simple substitution */
                  strcpy(link, pattern_list[l].subst);
                  if (strstr(link, "%s")) {
                     strcpy(tmp, link);
                     if (email_notify)
                        compose_base_url(lbs, base_url, sizeof(base_url), TRUE);
                     else
                        base_url[0] = 0;
                     sprintf(link, tmp, base_url);
                  }

                  strcpy(return_buffer + j, link);
                  j += strlen(link);
                  i += strlen(pattern_list[l].pattern) - 1;     // 1 gets added in for loop...
               }
            }                   // !elcode_disabled && !elcode_disabled1

            else if (!elcode_disabled) {

               substituted = TRUE;

               /* simple substitution */
               strcpy(link, pattern_list[l].subst);
               if (strstr(link, "%s")) {
                  strcpy(tmp, link);
                  if (email_notify)
                     compose_base_url(lbs, base_url, sizeof(base_url), TRUE);
                  else
                     base_url[0] = 0;
                  sprintf(link, tmp, base_url);
               }

               strcpy(return_buffer + j, link);
               j += strlen(link);
               i += strlen(pattern_list[l].pattern) - 1;        // 1 gets added in for loop...
            }

            if (stristr(pattern_list[l].pattern, "[code]"))
               elcode_disabled = TRUE;
            if (stristr(pattern_list[l].pattern, "[code1]"))
               elcode_disabled1 = TRUE;

            break;
         }
      }

      /* don't output tags if already subsituted by HTML code */
      if (substituted)
         continue;

      if (strnieq(str + i, "<br>", 4)) {
         strcpy(return_buffer + j, "<br />");
         j += 6;
         i += 3;
      } else
         switch (str[i]) {
            case '\r':
               if (!elcode_disabled && !elcode_disabled1 && !inside_table) {
                  strcat(return_buffer, "<br />\r\n");
                  j += 8;
               } else {
                  strcat(return_buffer, "\r\n");
                  j += 2;
               }
               break;
            case '\n':
               break;
            case '&':
               strcat(return_buffer, "&amp;");
               j += 5;
               break;
            case '<':
               strcat(return_buffer, "&lt;");
               j += 4;
               break;
            case '>':
               strcat(return_buffer, "&gt;");
               j += 4;
               break;

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

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

   xfree(lstr);
   return_buffer[j] = 0;
   strlen_retbuf = j;
}

Project: elog

LBLIST get_logbook_hierarchy(void) {
   int i, j, n, m, flag;
   char str[1000], grpname[256], grpmembers[1000];
   LBLIST root, *pgrp;
   char grplist[MAX_N_LIST][NAME_LENGTH];

   /* allocate root node */
   root = xmalloc(sizeof(LBNODE));
   memset(root, 0, sizeof(LBNODE));

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

      /* flag indicates top group (2) or group (1) or other entry (0) */
      flag = 0;
      strlcpy(str, grpname, sizeof(str));
      str[9] = 0;
      if (strieq(str, "top group"))
         flag = 2;
      str[5] = 0;
      if (strieq(str, "group"))
         flag = 1;

      if (flag) {

         /* allocate new node, increase member pointer array by one */
         if (n == 0)
            root->member = xmalloc(sizeof(void *));
         else
            root->member = xrealloc(root->member, (n + 1) * sizeof(void *));
         root->member[n] = xmalloc(sizeof(LBNODE));

         if (strlen(grpname) < 7)
            strlcpy(root->member[n]->name, "Invalid group definition!", 256);
         else if (flag == 1)
            strlcpy(root->member[n]->name, grpname + 6, 256);
         else
            strlcpy(root->member[n]->name, grpname + 10, 256);

         m = strbreak(grpmembers, grplist, MAX_N_LIST, ",", FALSE);
         root->member[n]->n_members = m;

         root->member[n]->member = xcalloc(sizeof(void *), m);
         root->member[n]->n_members = m;
         for (j = 0; j < m; j++) {
            root->member[n]->member[j] = xcalloc(sizeof(LBNODE), 1);
            strlcpy(root->member[n]->member[j]->name, grplist[j], 256);
         }

         root->member[n]->is_top = (flag == 2);

         n++;
      }
   }

   root->n_members = n;

   /* populate nodes with logbooks or other groups */
   for (i = 0; i < root->n_members; i++)
      if (root->member[i]) {

         for (j = 0; j < root->n_members; j++) {
            if (i != j && root->member[j] != NULL && (pgrp = get_subgroup(root->member[j],
                                                                          root->member[i]->name)) != NULL) {

               /* node is allocated twice, so free one... */
               xfree(*pgrp);

               /* ... and reference the other */
               *pgrp = root->member[i];

               /* mark original pointer invalid */
               root->member[i] = NULL;

               break;
            }
         }
      }

   /* remove empty slots */
   for (i = 0; i < root->n_members; i++)
      if (root->member[i] == NULL) {
         for (j = i + 1; j < root->n_members; j++)
            if (root->member[j])
               break;

         if (j < root->n_members && root->member[j]) {
            root->member[i] = root->member[j];
            root->member[j] = NULL;
         }
      }

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

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

      /* make simple list with logbooks */
      root->member = xcalloc(n, sizeof(void *));
      root->n_members = n;

      for (i = 0; i < n; i++) {
         root->member[i] = xcalloc(1, sizeof(LBNODE));
         strlcpy(root->member[i]->name, lb_list[i].name, 256);
      }
   }

   return root;
}

Project: elog

BOOL convert_password_file(char *file_name) {
   char name[256], password[256], full_name[256], email[256], email_notify[256];
   int i, len, fh, status;
   char *buf, *p;
   PMXML_NODE root, list, node, npwd;

   printf("Converting password file \"%s\" to new XML format ... ", file_name);

   fh = open(file_name, O_RDONLY | O_BINARY);
   if (fh < 0)
      return FALSE;
   len = lseek(fh, 0, SEEK_END);
   lseek(fh, 0, SEEK_SET);
   buf = xmalloc(len + 1);
   assert(buf);
   i = my_read(fh, buf, len);
   buf[i] = 0;
   close(fh);

   /* create backup */
   strlcpy(name, file_name, sizeof(name));
   strlcat(name, "_bak", sizeof(name));
   fh = open(name, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0644);
   if (fh > 0) {
      write(fh, buf, len);
      close(fh);
   }

   p = buf;

   /* skip leading spaces or new lines */
   while (*p && isspace(*p))
      p++;

   root = mxml_create_root_node();
   list = mxml_add_node(root, "list", NULL);

   while (*p) {

      /* skip comment lines */
      if (*p != ';' && *p != '#') {
         for (i = 0; i < (int) sizeof(name) - 1 && *p && *p != ':'; i++)
            name[i] = *p++;
         name[i] = 0;
         if (*p++ != ':') {
            xfree(buf);
            return FALSE;
         }

         for (i = 0; i < (int) sizeof(password) - 1 && *p && *p != ':'; i++)
            password[i] = *p++;
         password[i] = 0;
         if (*p++ != ':') {
            xfree(buf);
            return FALSE;
         }

         for (i = 0; i < (int) sizeof(full_name) - 1 && *p && *p != ':'; i++)
            full_name[i] = *p++;
         full_name[i] = 0;
         if (*p++ != ':') {
            xfree(buf);
            return FALSE;
         }

         for (i = 0; i < (int) sizeof(email) - 1 && *p && *p != ':'; i++)
            email[i] = *p++;
         email[i] = 0;
         if (*p++ != ':') {
            xfree(buf);
            return FALSE;
         }

         for (i = 0; i < (int) sizeof(email_notify) - 1 && *p && *p != '\r' && *p != '\n'; i++)
            email_notify[i] = *p++;
         email_notify[i] = 0;
         if (*p && *p != '\n' && *p != '\r') {
            xfree(buf);
            return FALSE;
         }

         while (*p && (*p == '\r' || *p == '\n'))
            p++;

         node = mxml_add_node(list, "user", NULL);
         mxml_add_node(node, "name", name);
         npwd = mxml_add_node(node, "password", password);
         mxml_add_attribute(npwd, "encoding", "SHA256");
         mxml_add_node(node, "full_name", full_name);
         mxml_add_node(node, "last_logout", "0");
         mxml_add_node(node, "last_activity", "0");
         mxml_add_node(node, "email", email);
         mxml_add_node(node, "email_notify", email_notify);
         mxml_add_node(node, "inactive", "0");
      }

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

   status = mxml_write_tree(file_name, root);
   mxml_free_tree(root);
   xfree(buf);

   if (status)
      printf("Ok\n");
   else {
      printf("Error writing to password file\n");
      return FALSE;
   }

   return TRUE;
}

Project: elog

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

Project: elog

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

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

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

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

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

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

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

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

Project: elog

int download_config() {
   char error_str[256];
   int size, delta;
   char message[TEXT_SIZE + 1000], *buffer;

   /* return complete config file */
   load_config_section(NULL, &buffer, error_str);
   if (error_str[0]) {
      rsprintf("Error loading configuration file: %s", error_str);
      return EL_FILE_ERROR;
   }

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

   show_plain_header(size, "export.txt");

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

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

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

   return EL_SUCCESS;
}

Project: elog

void show_import_page_csv(LOGBOOK *lbs) {
   char str[256], str2[256];

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

   show_html_header(lbs, FALSE, loc("ELOG CSV 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("Field separator"));
   rsprintf("<td class=\"attribvalue\">");

   str[0] = 0;
   if (isparam("sep"))
      strlcpy(str, getparam("sep"), sizeof(str));

   if (str[0] == 0)
      rsprintf("<input type=\"radio\" checked id=\"comma\" name=\"sep\" value=\"auto\">");
   else
      rsprintf("<input type=\"radio\" id=\"comma\" name=\"sep\" value=\"auto\">");
   rsprintf("<label for=\"comma\">%s</label>\n", loc("Auto detect"));

   if (str[0] == ',')
      rsprintf("<input type=\"radio\" checked id=\"comma\" name=\"sep\" value=\",\">");
   else
      rsprintf("<input type=\"radio\" id=\"comma\" name=\"sep\" value=\",\">");
   rsprintf("<label for=\"comma\">%s (,)</label>\n", loc("Comma"));

   if (str[0] == ';')
      rsprintf("<input type=\"radio\" checked id=\"semi\" name=\"sep\" value=\";\">");
   else
      rsprintf("<input type=\"radio\" id=\"semi\" name=\"sep\" value=\";\">");
   rsprintf("<label for=\"semi\">%s (;)</label>\n", loc("Semicolon"));

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

   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 CSV file"));

   if (isparam("notignore"))
      rsprintf("<input type=checkbox checked id=\"notignore\" name=\"notignore\" value=\"1\">\n");
   else
      rsprintf("<input type=checkbox id=\"notignore\" name=\"notignore\" value=\"1\">\n");
   rsprintf("<label for=\"notignore\">%s</label><br>\n", loc("Do not ignore first line"));

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

   if (isparam("filltext"))
      rsprintf("<input type=checkbox checked id=\"filltext\" name=\"filltext\" value=\"1\">\n");
   else
      rsprintf("<input type=checkbox id=\"filltext\" name=\"filltext\" value=\"1\">\n");
   strcpy(str, loc("text"));
   sprintf(str2, loc("Column header '%s' must be present in CSV file"), str);
   rsprintf("<label for=\"filltext\">%s (%s)</label><br>\n", loc("Fill text body"), str2);

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

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

   rsprintf("<td class=\"attribvalue\">");

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

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

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

Project: elog

long my_timezone() {
#if defined(OS_MACOSX) || defined(__FreeBSD__) || defined(__OpenBSD__)
   time_t tp;
   time(&tp);
   return -localtime(&tp)->tm_gmtoff;
#elif defined(OS_WINNT)
   return _timezone;
#else
   return timezone;
#endif
}

Project: elog

BOOL enumparam(int n, char *param, char *value) {
   param[0] = value[0] = 0;

   if (n >= MAX_PARAM)
      return FALSE;

   if (_param[n][0] == 0)
      return FALSE;

   strcpy(param, _param[n]);
   strcpy(value, _value[n]);

   return TRUE;
}

Project: elog

void retrieve_domain(char *ret, int size) {
   char smtp_host[80];

   strlcpy(ret, "tmp.org", size);
   if (getcfg("global", "SMTP host", smtp_host, sizeof(smtp_host))) {
      if (strchr(smtp_host, '.'))
         strlcpy(ret, strchr(smtp_host, '.') + 1, size);
   }
}

Project: elog

void add_subst_time(LOGBOOK *lbs, char list[][NAME_LENGTH], char value[][NAME_LENGTH], char *item,
                    char *date, int *i, int flags) {
   char format[80], str[256];
   time_t ltime;
   struct tm *pts;

   if (flags & (AF_DATE | AF_DATETIME)) {
      ltime = date_to_ltime(date);
      sprintf(str, "%d", (int) ltime);
      add_subst_list(list, value, item, str, i);
   } else {
      if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
         strcpy(format, DEFAULT_TIME_FORMAT);
      ltime = date_to_ltime(date);
      pts = localtime(&ltime);
      assert(pts);
      my_strftime(str, sizeof(str), format, pts);

      add_subst_list(list, value, item, str, i);
   }
}

Project: elog

void url_decode(char *p)
/********************************************************************\
Decode the given string in-place by expanding %XX escapes
 \********************************************************************/
{
   char *pD, str[3];
   int i;

   pD = p;
   while (*p) {
      if (*p == '%') {
         /* Escape: next 2 chars are hex representation of the actual character */
         p++;
         if (isxdigit(p[0]) && isxdigit(p[1])) {
            str[0] = p[0];
            str[1] = p[1];
            str[2] = 0;
            sscanf(str, "%02X", &i);

            *pD++ = (char) i;
            p += 2;
         } else
            *pD++ = '%';
      } else if (*p == '+') {
         /* convert '+' to ' ' */
         *pD++ = ' ';
         p++;
      } else {
         *pD++ = *p++;
      }
   }
   *pD = '\0';
}

Project: elog

void show_upgrade_page(LOGBOOK *lbs) {
   char str[1000];

   show_html_header(lbs, FALSE, "ELOG Upgrade Information", TRUE, FALSE, NULL, FALSE, 0);

   rsprintf("<body>\n");

   rsprintf("<table class=\"frame\" cellpadding=\"0\" cellspacing=\"0\">\n\n");

   rsprintf("<tr><td class=\"title2\">ELog Electronic Logbook Upgrade Information</font></td></tr>\n");

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

   rsprintf("You probably use an <b>%s</b> configuration file for a ELOG version\n", CFGFILE);
   rsprintf("1.1.x, since it contains a <b><code>\"Types = ...\"</code></b> entry. From version\n");
   rsprintf("1.2.0 on, the fixed attributes <b>Type</b> and <b>Category</b> have been\n");
   rsprintf("replaced by arbitrary attributes. Please replace these two lines with the\n");
   rsprintf("following entries:<p>\n");
   rsprintf("<pre>\n");
   rsprintf("Attributes = Author, Type, Category, Subject\n");
   rsprintf("Required Attributes = Author\n");
   getcfg(lbs->name, "Types", str, sizeof(str));
   rsprintf("Options Type = %s\n", str);
   getcfg(lbs->name, "Categories", str, sizeof(str));
   rsprintf("Options Category = %s\n", str);
   rsprintf("Page title = $subject\n");
   rsprintf("</pre>\n");
   rsprintf("<p>\n");

   rsprintf("It is of course possible to change the attributes or add new ones. The new\n");
   rsprintf("options in the configuration file are described under <a href=\"\n");
   rsprintf("https://elog.psi.ch/elog/config.html\">https://elog.psi.ch/elog/config.html\n");
   rsprintf("</a>.\n");

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

   rsprintf("<hr>\n");
   rsprintf("<address>\n");
   rsprintf("<a href=\"https://www.psi.ch/ltp-muon-physics/stefan-ritt\">S. Ritt</a>, 18 October 2001");
   rsprintf("</address>");
   show_bottom_text(lbs);
   rsprintf("</body></html>\r\n");
}

Project: elog

int restructure_dir(char *dir) {
   char *file_list;
   int n1, n2, index, status;
   char old_path[MAX_PATH_LENGTH], new_path[MAX_PATH_LENGTH],
           subdir[MAX_PATH_LENGTH];
   static int first = TRUE;

   /* go through all entry files */
   n1 = ss_file_find(dir, "??????a.log", &file_list);
   for (index = 0; index < n1; index++) {
      generate_subdir_name(file_list + index * MAX_PATH_LENGTH, subdir, sizeof(subdir));

      // create new subdir
      strlcpy(new_path, dir, MAX_PATH_LENGTH);
      strlcat(new_path, subdir, MAX_PATH_LENGTH);

#ifdef OS_WINNT
      status = mkdir(new_path);
#else
      status = mkdir(new_path, 0755);
#endif

      if (status == 0) {
         if (first) {
            eprintf("\nFound old directory structure. Creating subdirectories and moving files...\n");
            first = FALSE;
         }
         eprintf("Created directory \"%s\"\n", new_path);
      } else {
         if (errno != EEXIST) {
            eprintf("generate_subdir_name: %s\n", strerror(errno));
            eprintf("Cannot create directory \"%s\"\n", new_path);
         }
      }

      strlcpy(old_path, dir, sizeof(old_path));
      strlcat(old_path, file_list + index * MAX_PATH_LENGTH, sizeof(old_path));
      strlcpy(new_path, dir, sizeof(new_path));
      strlcat(new_path, subdir, sizeof(new_path));
      strlcat(new_path, file_list + index * MAX_PATH_LENGTH, sizeof(new_path));

      rename(old_path, new_path);
   }
   if (file_list)
      xfree(file_list);

   /* go through all attachment files */
   n2 = ss_file_find(dir, "??????_??????_*", &file_list);
   for (index = 0; index < n2; index++) {
      generate_subdir_name(file_list + index * MAX_PATH_LENGTH, subdir, sizeof(subdir));

      // create new subdir
      strlcpy(new_path, dir, MAX_PATH_LENGTH);
      strlcat(new_path, subdir, MAX_PATH_LENGTH);

#ifdef OS_WINNT
      status = mkdir(new_path);
#else
      status = mkdir(new_path, 0755);
#endif

      strlcpy(old_path, dir, sizeof(old_path));
      strlcat(old_path, file_list + index * MAX_PATH_LENGTH, sizeof(old_path));
      strlcpy(new_path, dir, sizeof(new_path));
      strlcat(new_path, subdir, sizeof(new_path));
      strlcat(new_path, file_list + index * MAX_PATH_LENGTH, sizeof(new_path));

      rename(old_path, new_path);
   }
   if (file_list)
      xfree(file_list);

   return n1 + n2;
}

Project: elog

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

   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;
         }

      /* create new logbook */
      if (!create_logbook(lbs, getparam("lbname"), getparam("template")))
         return;

      strcpy(lbn, getparam("lbname"));
      url_encode(lbn, sizeof(lbn));
      sprintf(str, "../%s/?cmd=Config", lbn);
      for (i = 0; lb_list[i].name[0]; i++)
         if (strieq(lbn, lb_list[i].name))
            break;
      if (lb_list[i].name[0])
         redirect(&lb_list[i], str);
      else
         redirect(NULL, str);
      return;
   }

   show_standard_header(lbs, TRUE, loc("Create new logbook"), "", 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("Create new logbook"));
   rsprintf("%s</td></tr>\n", loc("Create new logbook"));

   rsprintf("<tr><td align=center class=\"dlgform\">\n");
   rsprintf("%s :&nbsp;&nbsp;", loc("Logbook name"));
   rsprintf("<input type=text name=lbname>\n");
   rsprintf("</td></tr>\n");

   rsprintf("<tr><td align=center class=\"dlgform\">\n");
   rsprintf("%s : \n", loc("Use existing logbook as template"));
   rsprintf("<select name=template>\n");
   rsprintf("<option value=\"\">- %s -\n", loc("none"));
   for (i = 0; lb_list[i].name[0]; i++)
      rsprintf("<option value=\"%s\">%s\n", lb_list[i].name, lb_list[i].name);
   rsprintf("</select>\n");
   rsprintf("</td></tr>\n\n");

   rsprintf("<tr><td align=center class=\"dlgform\">\n");
   rsprintf("<input type=submit name=cmd value=\"%s\">\n", loc("Create new logbook"));
   rsprintf("<input type=submit name=tmp value=\"%s\">\n", loc("Cancel"));
   rsprintf("</td></tr>\n\n");

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

Project: elog

void convert_elog_link(LOGBOOK *lbs, char *link, char *link_text, char *result, int absolute_link,
                       int message_id) {
   char str[256], base_url[256];
   int i;

   strlcpy(str, link, sizeof(str));
   if (strchr(str, '/'))
      *strchr(str, '/') = 0;

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

   if (i < (int) strlen(str)) {
      /* if link contains reference to other logbook, put logbook explicitly */
      if (absolute_link)
         compose_base_url(NULL, base_url, sizeof(base_url), FALSE);
      else
         strcpy(base_url, "../");
      sprintf(result, "<a href=\"%s%s\">elog:%s</a>", base_url, link, link_text);
   } else if (link[0] == '/') {
      if (absolute_link)
         compose_base_url(NULL, base_url, sizeof(base_url), FALSE);
      else
         base_url[0] = 0;
      sprintf(result, "<a href=\"%s%s/%d%s\">elog:%s</a>", base_url, lbs->name_enc, message_id, link,
              link_text);
   } else {
      if (absolute_link)
         compose_base_url(lbs, base_url, sizeof(base_url), FALSE);
      else
         base_url[0] = 0;
      sprintf(result, "<a href=\"%s%s\">elog:%s</a>", base_url, link, link_text);
   }
}

Project: elog

int is_full_html(char *file_name) {
   char *str, *p;
   int i, fh, length;
   unsigned char *buf;

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

   str = xstrdup((char *) buf);

   for (i = 0; i < (int) strlen((char *) buf); i++)
      str[i] = toupper(buf[i]);
   str[i] = 0;

   xfree(buf);

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

   xfree(str);
   return FALSE;
}

Project: elog

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

   for (i = 0, j = 0; j < len; i++, j += 4)
      pout[i] = ((unsigned int) pin[j]) | (((unsigned int) pin[j + 1]) << 8) | (((unsigned int) pin[j + 2])
              << 16) | (((unsigned int)
              pin[j + 3]) << 24);
}

Project: elog

char *getcfg_topgroup() {
   if (_topgroup[0])
      return _topgroup;

   return NULL;
}

Project: elog

void show_edit_form(LOGBOOK *lbs, int message_id, BOOL breply, BOOL bedit, BOOL bupload, BOOL breedit,
                    BOOL bduplicate, BOOL bpreview) {
   int i, j, n, index, aindex, size, width, height, fh, length, input_size, input_maxlen,
           format_flags[MAX_N_ATTR], year, month, day, hour, min, sec, n_attr, n_disp_attr, n_lines,
           attr_index[MAX_N_ATTR], enc_selected, show_text, n_moptions, display_inline,
           allowed_encoding, thumb_status, max_n_lines, fixed_text, autosave, new_entry, status;
   char str[2 * NAME_LENGTH], str2[NAME_LENGTH], preset[2 * NAME_LENGTH], *p, *pend, star[80],
           comment[10000], reply_string[256], list[MAX_N_ATTR][NAME_LENGTH], file_name[256], *buffer,
           format[256], date[80], script_onload[256], script_onfocus[256], script_onunload[256],
           attrib[MAX_N_ATTR][NAME_LENGTH], *text, orig_tag[80], reply_tag[MAX_REPLY_TO * 10],
           att[MAX_ATTACHMENTS][256], encoding[80], slist[MAX_N_ATTR + 10][NAME_LENGTH],
           svalue[MAX_N_ATTR + 10][NAME_LENGTH], owner[256], locked_by[256], class_value[80], class_name[80],
           ua[NAME_LENGTH], mid[80], title[256], login_name[256], full_name[256],
           orig_author[256], attr_moptions[MAX_N_LIST][NAME_LENGTH], ref[256], file_enc[256], tooltip[10000],
           enc_attr[NAME_LENGTH], user_email[256], cmd[256], thumb_name[256], thumb_ref[256], **user_list, fid[20],
           upwd[80], subdir[256], draft[256], page_title[300];
   time_t now, ltime;
   char fl[8][NAME_LENGTH];
   struct tm *pts;
   FILE *f;
   BOOL preset_text, subtable;

   for (i = 0; i < MAX_ATTACHMENTS; i++)
      att[i][0] = 0;

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

   text = xmalloc(TEXT_SIZE);
   text[0] = 0;
   orig_author[0] = 0;
   orig_tag[0] = 0;
   encoding[0] = 0;
   date[0] = 0;
   locked_by[0] = 0;
   new_entry = 0;

   if (!message_id || breply)
      new_entry = 1;
   if (isparam("new_entry"))
      new_entry = 1;

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

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

   /* check for file attachment (mhttpd) */
   if (isparam("fa")) {
      strlcpy(att[0], getparam("fa"), 256);

      /* remove any leading directory, to accept only files in the logbook directory ! */
      if (strchr(att[0], DIR_SEPARATOR)) {
         strlcpy(str, att[0], sizeof(str));
         strlcpy(att[0], strrchr(str, DIR_SEPARATOR) + 1, 256);
      }
   }

   if (breedit || bupload) {
      /* get date from parameter */
      if (isparam("entry_date"))
         strlcpy(date, getparam("entry_date"), sizeof(date));

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

      strlcpy(text, getparam("text"), TEXT_SIZE);

      for (i = 0; i < MAX_ATTACHMENTS; i++) {
         sprintf(str, "attachment%d", i);
         if (isparam(str))
            strlcpy(att[i], getparam(str), 256);
      }

      if (isparam("inlineatt")) {
         for (i = 0; i < MAX_ATTACHMENTS; i++) {
            sprintf(str, "attachment%d", i);
            if (!isparam(str) && isparam("inlineatt")) {
               strlcpy(att[i], getparam("inlineatt"), 256);
               break;
            }
         }
      }

      /* get encoding */
      strlcpy(encoding, isparam("encoding") ? getparam("encoding") : "", sizeof(encoding));
      if (!strieq(encoding, "plain") && !strieq(encoding, "ELCode") && !strieq(encoding, "HTML"))
         strcpy(encoding, "plain");
   } else {
      if (message_id) {
         /* get message for reply/edit */

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

         get_author(lbs, attrib, orig_author);

         /* strip attachments on duplicate */
         if (bduplicate)
            memset(att, 0, sizeof(att));
      } else if (isparam("nsel")) {

         /* multi edit: get all entries and check if attributes are the same */
         memset(attrib, 0, sizeof(attrib));
         for (i = n = 0; i < atoi(getparam("nsel")); i++) {
            sprintf(str, "s%d", i);
            if (isparam(str))
               compare_attributes(lbs, atoi(getparam(str)), attrib, &n);
         }
      }
   }

   if (message_id && getcfg(lbs->name, "Use Lock", str, sizeof(str)) && atoi(str) == 1 && locked_by[0]
       && !isparam("steal")) {
      sprintf(str, "%d", message_id);
      sprintf(text, "%s %s", loc("Entry is currently edited by"), locked_by);
      sprintf(cmd, "?cmd=%s&steal=1", loc("Edit"));
      show_query(lbs, loc("Entry is locked"), text, loc("Edit anyhow"), cmd, loc("Cancel"), str);
      return;
   }

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

   enc_selected = 2;            /* Default is HTML */

   if (allowed_encoding == 2)   /* select ELCode if the only one allowed */
      enc_selected = 0;
   else if (allowed_encoding == 1)      /* select plain if the only one allowed */
      enc_selected = 1;
   else if (allowed_encoding == 3)      /* select ELCode if only plain and ELCode allowed */
      enc_selected = 0;

   /* Overwrite from config file */
   if (getcfg(lbs->name, "Default Encoding", str, sizeof(str)))
      enc_selected = atoi(str);

   /* Overwrite from current entry */
   if (encoding[0]) {
      if (encoding[0] == 'E')
         enc_selected = 0;
      else if (encoding[0] == 'p')
         enc_selected = 1;
      else if (encoding[0] == 'H')
         enc_selected = 2;
   }

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

   /* check for preset attributes without any condition */
   set_condition("");
   for (index = 0; index < lbs->n_attr; index++) {

      /* check for preset string */
      sprintf(str, "Preset %s", attr_list[index]);
      if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0) {

         if ((!bedit && !breply && !bduplicate) ||      /* don't subst on edit or reply */
             (breedit && i == 2)) {     /* subst on reedit only if preset is under condition */

            /* check for index substitution */
            if (!bedit && strchr(preset, '#')) {
               /* get index */
               get_auto_index(lbs, index, preset, str, sizeof(str));
               strcpy(preset, str);
            }

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

      sprintf(str, "Preset on first reply %s", attr_list[index]);
      if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && breply) {
         if (orig_tag[0] == 0) {
            if (!breedit || (breedit && i == 2)) {      /* subst on reedit only if preset is under condition */

               /* check for index substitution */
               if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
                  /* get index */
                  get_auto_index(lbs, index, preset, str, sizeof(str));
                  strcpy(preset, str);
               }

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

      sprintf(str, "Preset on reply %s", attr_list[index]);
      if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && breply) {

         if (!breedit || (breedit && i == 2)) { /* subst on reedit only if preset is under condition */

            /* check for index substitution */
            if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
               /* get index */
               get_auto_index(lbs, index, preset, str, sizeof(str));
               strcpy(preset, str);
            }

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

      sprintf(str, "Preset on edit %s", attr_list[index]);
      if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && bedit) {

         if (!breedit || (breedit && i == 2)) { /* subst on reedit only if preset is under condition */

            /* check for index substitution */
            if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
               /* get index */
               get_auto_index(lbs, index, preset, str, sizeof(str));
               strcpy(preset, str);
            }

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

      sprintf(str, "Preset on duplicate %s", attr_list[index]);
      if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && bduplicate) {

         if (!breedit || (breedit && i == 2)) { /* subst on reedit only if preset is under condition */

            /* check for index substitution */
            if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
               /* get index */
               get_auto_index(lbs, index, preset, str, sizeof(str));
               strcpy(preset, str);
            }

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

      /* check for p<attribute> */
      sprintf(str, "p%s", attr_list[index]);
      if (isparam(str))
         strlcpy(attrib[index], getparam(str), NAME_LENGTH);
   }

   /* evaluate conditional attributes */
   evaluate_conditions(lbs, attrib);

   /* rescan attributes if condition set */
   if (_condition[0]) {
      n_attr = scan_attributes(lbs->name);
      if (breedit || bupload)
         attrib_from_param(n_attr, attrib);

      /* now check again for conditional preset */
      for (index = 0; index < lbs->n_attr; index++) {

         /* check for preset string */
         sprintf(str, "Preset %s", attr_list[index]);
         if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0) {

            if ((!bedit && !breply && !bduplicate) ||   /* don't subst on edit or reply */
                (breedit && i == 2)) {  /* subst on reedit only if preset is under condition */

               /* check for index substitution */
               if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
                  /* get index */
                  get_auto_index(lbs, index, preset, str, sizeof(str));
                  strcpy(preset, str);
               }

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

         sprintf(str, "Preset on reply %s", attr_list[index]);
         if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && breply) {

            if (!breedit || (breedit && i == 2)) {      /* subst on reedit only if preset is under condition */

               /* check for index substitution */
               if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
                  /* get index */
                  get_auto_index(lbs, index, preset, str, sizeof(str));
                  strcpy(preset, str);
               }

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

         sprintf(str, "Preset on duplicate %s", attr_list[index]);
         if ((i = getcfg(lbs->name, str, preset, sizeof(preset))) > 0 && bduplicate) {

            if (!breedit || (breedit && i == 2)) {      /* subst on reedit only if preset is under condition */

               /* check for index substitution */
               if (!bedit && (strchr(preset, '%') || strchr(preset, '#'))) {
                  /* get index */
                  get_auto_index(lbs, index, preset, str, sizeof(str));
                  strcpy(preset, str);
               }

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

   } else
      // if (_condition[0])
      n_attr = lbs->n_attr;

   /* check for maximum number of replies */
   if (breply) {
      i = 0;
      p = strtok(reply_tag, ",");
      while (p) {
         i++;
         p = strtok(NULL, ",");
      }

      if (i >= MAX_REPLY_TO) {
         sprintf(str, loc("Maximum number of replies (%d) exceeded"), MAX_REPLY_TO);
         show_error(str);
         xfree(text);
         return;
      }
   }

   /* check for non-allowed branching */
   if (breply && getcfg(lbs->name, "Allow branching", str, sizeof(str)) && atoi(str) == 0) {
      if (reply_tag[0]) {
         show_error("Branches are not allowed in this logbook");
         xfree(text);
         return;
      }
   }

   /* check for author */
   if (bedit && getcfg(lbs->name, "Restrict edit", str, sizeof(str)) && atoi(str) == 1) {
      if (!is_author(lbs, attrib, owner)) {
         strencode2(str2, owner, sizeof(str2));
         sprintf(str, loc("Only user <b>%s</b> can edit this entry"), str2);
         show_error(str);
         xfree(text);
         return;
      }
   }


   if (bedit) {
      if (isparam("nsel")) {
         for (i = n = 0; i < atoi(getparam("nsel")); i++) {
            sprintf(str, "s%d", i);
            if (isparam(str)) {
               status = check_edit_time(lbs, atoi(getparam(str)));
               if (!status) {
                  xfree(text);
                  return;
               }
            }
         }
      } else if (message_id) {
         status = check_edit_time(lbs, message_id);
         if (!status) {
            xfree(text);
            return;
         }
      }
   }

   /* check for locking */
   if (message_id && bedit && !breedit && !bupload) {
      if (getcfg(lbs->name, "Use Lock", str, sizeof(str)) && atoi(str) == 1) {
         if (isparam("unm"))
            get_full_name(lbs, getparam("unm"), str);
         else
            strlcpy(str, loc("user"), sizeof(str));

         strcat(str, " ");
         strcat(str, loc("on"));
         strcat(str, " ");
         strcat(str, rem_host);

         el_lock_message(lbs, message_id, str, TRUE);
      }
   }

   /* remove attributes for replies */
   if (breply) {
      getcfg(lbs->name, "Remove on reply", str, sizeof(str));
      n = strbreak(str, list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < n; i++)
         for (j = 0; j < n_attr; j++) {
            if (strieq(attr_list[j], list[i]))
               attrib[j][0] = 0;
         }
   }

   /* header */

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

   show_html_header(lbs, FALSE, page_title, FALSE, FALSE, NULL, FALSE, 0);

   /* java script for checking required attributes and to check for cancelled edits */
   rsprintf("<script type=\"text/javascript\">\n");
   rsprintf("<!--\n\n");
   rsprintf("var submitted = false;\n");

   if (breedit) {
      if (isparam("entry_modified") && atoi(getparam("entry_modified")) == 1) {
         rsprintf("var entry_modified = true;\n");
         rsprintf("window.status = \"%s\";\n", loc("Entry has been modified"));
      } else
         rsprintf("var entry_modified = false;\n");
   } else
      rsprintf("var entry_modified = false;\n");
   rsprintf("var draft_modified = false;\n");
   rsprintf("var last_key = 0;\n\n");
   rsprintf("var in_asend = false;\n\n");

   rsprintf("function chkform(button)\n");
   rsprintf("{\n");

   rsprintf("  if (last_key == 13) {\n");
   rsprintf("    var ret = confirm('%s');\n", loc("Really submit this entry?"));
   rsprintf("    if (!ret) {\n");
   rsprintf("      last_key = 0;\n");
   rsprintf("      return false;\n");
   rsprintf("    }\n");
   rsprintf("  }\n\n");

   for (i = 0; i < n_attr; i++) {
      if ((attr_flags[i] & AF_REQUIRED) && !(attr_flags[i] & AF_LOCKED)) {

         /* convert blanks etc. to underscores */
         strcpy(ua, attr_list[i]);
         stou(ua);

         rsprintf("  try {\n"); // try-catch block in case attributes are not there (conditional)

         if (attr_flags[i] & AF_MULTI) {
            rsprintf("  if (\n");
            for (j = 0; j < MAX_N_LIST && attr_options[i][j][0]; j++) {
               sprintf(str, "%s_%d", ua, j);
               rsprintf("      !document.form1.%s.checked", str);
               if (attr_options[i][j + 1][0])
                  rsprintf(" &&\n");
            }
            rsprintf(") {\n");
            sprintf(str, loc("Please select at least one '%s'"), attr_list[i]);
            rsprintf("      alert(\"%s\");\n", str);
            rsprintf("      document.form1.%s_0.focus();\n", ua);
            rsprintf("      return false;\n");
            rsprintf("    }\n");

         } else if (attr_flags[i] & (AF_MUSERLIST | AF_MUSEREMAIL)) {
            rsprintf("  if (\n");
            for (j = 0;; j++) {
               if (!enum_user_line(lbs, j, login_name, sizeof(login_name)))
                  break;
               get_user_line(lbs, login_name, NULL, NULL, NULL, NULL, NULL, NULL);

               sprintf(str, "%s_%d", ua, j);
               rsprintf("      !document.form1.%s.checked", str);
               if (enum_user_line(lbs, j + 1, login_name, sizeof(login_name)))
                  rsprintf(" &&\n");
            }
            rsprintf(") {\n");
            sprintf(str, loc("Please select at least one '%s'"), attr_list[i]);
            rsprintf("      alert(\"%s\");\n", str);
            rsprintf("      document.form1.%s_0.focus();\n", ua);
            rsprintf("      return false;\n");
            rsprintf("    }\n");

         } else if (attr_flags[i] & AF_RADIO) {
            rsprintf("    for (var i=0 ; i<document.form1.%s.length ; i++)\n", ua);
            rsprintf("      if (document.form1.%s[i].checked) { break }\n", ua);
            rsprintf("    if (i == document.form1.%s.length) {\n", ua);
            sprintf(str, loc("Please select a '%s'"), attr_list[i]);
            rsprintf("      alert(\"%s\");\n", str);
            rsprintf("      document.form1.%s[0].focus();\n", ua);
            rsprintf("      return false;\n");
            rsprintf("    }\n");

         } else if (attr_flags[i] & (AF_DATE | AF_DATETIME)) {
            rsprintf("    if (document.form1.m%d.value == \"\") {\n", i);
            sprintf(str, loc("Please enter month for attribute '%s'"), attr_list[i]);
            rsprintf("      alert(\"%s\");\n", str);
            rsprintf("      document.form1.m%d.focus();\n", i);
            rsprintf("      return false;\n");
            rsprintf("    }\n");
            rsprintf("    if (document.form1.d%d.value == \"\") {\n", i);
            sprintf(str, loc("Please enter day for attribute '%s'"), attr_list[i]);
            rsprintf("      alert(\"%s\");\n", str);
            rsprintf("      document.form1.d%d.focus();\n", i);
            rsprintf("      return false;\n");
            rsprintf("    }\n");
            rsprintf("    if (document.form1.y%d.value == \"\") {\n", i);
            sprintf(str, loc("Please enter year for attribute '%s'"), attr_list[i]);
            rsprintf("      alert(\"%s\");\n", str);
            rsprintf("      document.form1.y%d.focus();\n", i);
            rsprintf("      return false;\n");
            rsprintf("    }\n");

            if (attr_flags[i] & AF_DATETIME) {
               rsprintf("    if (document.form1.h%d.value == \"\") {\n", i);
               sprintf(str, loc("Please enter hour for attribute '%s'"), attr_list[i]);
               rsprintf("      alert(\"%s\");\n", str);
               rsprintf("      document.form1.h%d.focus();\n", i);
               rsprintf("      return false;\n");
               rsprintf("    }\n");
               rsprintf("    if (document.form1.n%d.value == \"\") {\n", i);
               sprintf(str, loc("Please enter minute for attribute '%s'"), attr_list[i]);
               rsprintf("      alert(\"%s\");\n", str);
               rsprintf("      document.form1.n%d.focus();\n", i);
               rsprintf("      return false;\n");
               rsprintf("    }\n");
               rsprintf("    if (document.form1.c%d.value == \"\") {\n", i);
               sprintf(str, loc("Please enter second for attribute '%s'"), attr_list[i]);
               rsprintf("      alert(\"%s\");\n", str);
               rsprintf("      document.form1.c%d.focus();\n", i);
               rsprintf("      return false;\n");
               rsprintf("    }\n");
            }

         } else {
            rsprintf("    if (document.form1.%s.value == \"\") {\n", ua);
            sprintf(str, loc("Please enter attribute '%s'"), attr_list[i]);
            rsprintf("      alert(\"%s\");\n", str);
            rsprintf("      document.form1.%s.focus();\n", ua);
            rsprintf("      return false;\n");
            rsprintf("    }\n");
         }

         rsprintf("  }\n");
         rsprintf("  catch(err) {\n");
         rsprintf("  }\n\n");
      }

      if ((attr_flags[i] & AF_NUMERIC) && !(attr_flags[i] & AF_LOCKED)) {
         /* convert blanks etc. to underscores */
         strcpy(ua, attr_list[i]);
         stou(ua);

         rsprintf("  try {\n"); // try-catch block in case attributes are not there (conditional)
         rsprintf("    if (document.form1.%s.value != \"- %s -\") {\n", ua, loc("keep original values"));
         rsprintf("      for (var i=0 ; i<document.form1.%s.value.length ; i++)\n", ua);
         rsprintf("        if (document.form1.%s.value.charAt(i) != \",\" &&\n", ua);
         rsprintf("            document.form1.%s.value.charAt(i) != \".\" &&\n", ua);
         rsprintf("            document.form1.%s.value.charAt(i) != \"-\" &&\n", ua);
         rsprintf("            (document.form1.%s.value.charAt(i) < \"0\" ||\n", ua);
         rsprintf("             document.form1.%s.value.charAt(i) > \"9\")) { break }\n", ua);
         rsprintf("      if (i<document.form1.%s.value.length) {\n", ua);
         sprintf(str, loc("Please enter numeric value for '%s'"), attr_list[i]);
         rsprintf("        alert(\"%s\");\n", str);
         rsprintf("        document.form1.%s.focus();\n", ua);
         rsprintf("        return false;\n");
         rsprintf("      }\n");
         rsprintf("    }\n");
         rsprintf("  }\n");
         rsprintf("  catch(err) {\n");
         rsprintf("  }\n\n");
      }

      if ((attr_flags[i] & (AF_DATE | AF_DATETIME)) && !(attr_flags[i] & AF_LOCKED)) {
         rsprintf("  try {\n"); // try-catch block in case attributes are not there (conditional)
         rsprintf("    for (var i=0 ; i<document.form1.y%d.value.length ; i++)\n", i);
         rsprintf("      if ((document.form1.y%d.value.charAt(i) < \"0\" ||\n", i);
         rsprintf("           document.form1.y%d.value.charAt(i) > \"9\")) { break }\n", i);
         rsprintf("    if (i<document.form1.y%d.value.length) {\n", i);
         sprintf(str, loc("Please enter numeric value for year of attribute '%s'"), attr_list[i]);
         rsprintf("      alert(\"%s\");\n", str);
         rsprintf("      document.form1.y%d.focus();\n", i);
         rsprintf("      return false;\n");
         rsprintf("    }\n");
         rsprintf("  }\n");
         rsprintf("  catch(err) {\n");
         rsprintf("  }\n\n");
      }
   }

   rsprintf("  if (autoSaveTimer != null)\n");
   rsprintf("    clearTimeout(autoSaveTimer);\n");
   rsprintf("  button.disabled = true;\n");
   rsprintf("  button.value = \"%s...\";\n", loc("Please wait"));
   rsprintf("  submitted = true;\n");
   rsprintf("  document.getElementById(\"form1\").elements['cmd'][0].value = \"%s\";\n", loc("Submit"));
   rsprintf("  document.getElementById(\"form1\").submit();\n");
   rsprintf("  return true;\n");
   rsprintf("}\n\n");

   /* mark_submitted() gets called via "Back" and "Preview" buttons */
   rsprintf("function mark_submitted()\n");
   rsprintf("{\n");
   rsprintf("  if (autoSaveTimer != null)\n");
   rsprintf("    clearTimeout(autoSaveTimer);\n");
   rsprintf("  submitted = true;\n"); // don't ask to leave that page
   rsprintf("  return true;\n");
   rsprintf("}\n\n");

   /* check_delte() gets called via "Delete" button */
   rsprintf("function check_delete()\n");
   rsprintf("{\n");
   rsprintf("  var ret = confirm('%s');\n", loc("Really delete this entry?"));
   rsprintf("  if (ret) {\n");
   rsprintf("    mark_submitted()\n");
   rsprintf("    document.form1.jcmd.value='XDelete';\n");
   rsprintf("    return true;\n");
   rsprintf("  }\n");
   rsprintf("  return false;\n");
   rsprintf("}\n\n");

   /* chkupload() gets called via "Upload" button */
   rsprintf("function chkupload()\n");
   rsprintf("{\n");
   rsprintf("  if (document.form1.attfile.value == \"\") {\n");
   rsprintf("    alert(\"%s\");\n", loc("No attachment file specified"));
   rsprintf("    return false;\n");
   rsprintf("  }\n");
   rsprintf("  mark_submitted();\n");
   rsprintf("  return true;\n");
   rsprintf("}\n\n");

   /* cond_submit() gets called via selection of new conditional attribute */
   rsprintf("function cond_submit()\n");
   rsprintf("{\n");
   rsprintf("  submitted = true;\n");
   rsprintf("  document.form1.submit();\n");
   rsprintf("}\n\n");

   /* save_draft() gets called via the "Save" button */
   rsprintf("function save_draft()\n");
   rsprintf("{\n");
   rsprintf("  if (autoSaveTimer != null)\n");
   rsprintf("    clearTimeout(autoSaveTimer);\n");
   rsprintf("  asend();\n");
   rsprintf("  draft_modified = false;\n");
   rsprintf("  return false;\n");
   rsprintf("}\n\n");

   /* beforeunload() gets called "onBeforeUnload" */
   rsprintf("function beforeunload(e)\n");
   rsprintf("{\n");
   rsprintf("  if (!submitted)\n");
   rsprintf("    e.returnValue = \"%s\";\n", loc("If you leave this page you will lose your unsaved changes"));
   rsprintf("}\n\n");

   /* unload() gets called "onUnload", issues a "Unlock" command to remove a possible lock */
   rsprintf("function unload()\n");
   rsprintf("{\n");
   rsprintf("  if (!submitted) {\n");
   rsprintf("    r = XMLHttpRequestGeneric();\n");
   rsprintf("    r.open('GET', '?jcmd=Unlock&edit_id=%d', true);\n", message_id);
   rsprintf("    r.send();\n");
   rsprintf("  }\n");
   rsprintf("}\n\n");

   /* mod() gets called via "onchange" event */
   rsprintf("var autoSaveTimer;\n");
   rsprintf("var checkTextTimer;\n");
   rsprintf("var oldText;\n\n");
   rsprintf("var initialText;\n\n");

   if (getcfg(lbs->name, "Autosave", str, sizeof(str)))
      autosave = atoi(str);
   else
      autosave = 10;

   if (getcfg(lbs->name, "Save drafts", str, sizeof(str)) && atoi(str) == 0)
      autosave = 0;

   rsprintf("function mod(e)\n");
   rsprintf("{\n");
   if (autosave) {
      rsprintf("  if (!draft_modified)\n");
      rsprintf("    autoSaveTimer = setTimeout(save_draft, %d);\n", autosave * 1000);
   }
   rsprintf("  entry_modified = true;\n");
   rsprintf("  draft_modified = true;\n");
   rsprintf("  window.status = \"%s\";\n", loc("Entry has been modified"));
   rsprintf("  document.form1.entry_modified.value = \"1\";\n");
   rsprintf("  document.title = '%s - %s';\n", page_title, loc("Edited"));
   rsprintf("  if (document.getElementById('restore') != undefined)\n");
   rsprintf("     document.getElementById('restore').disabled = false;\n");
   rsprintf("}\n\n");

   rsprintf("function checkText()\n");
   rsprintf("{\n");
   if (autosave) {
      // CKEDITOR cannot call mod(), so manually check if text has changed
      rsprintf("  if (checkTextTimer == null) {\n");
      rsprintf("    checkTextTimer = setTimeout(checkText, 1000);\n");
      rsprintf("    return;\n");
      rsprintf("  }\n");
      rsprintf("  clearTimeout(checkTextTimer);\n");
      rsprintf("  if (typeof(CKEDITOR) != 'undefined')\n");
      rsprintf("    t = CKEDITOR.instances.Text.getData();\n");
      rsprintf("   else\n");
      rsprintf("    t = document.form1.Text.value;\n");
      rsprintf("  if (oldText == null)\n");
      rsprintf("    oldText = t;\n");
      rsprintf("  if (initialText == null)\n");
      rsprintf("    initialText = t;\n");
      rsprintf("  if (oldText != t)\n");
      rsprintf("    mod();\n");
      rsprintf("  oldText = t;\n");
      rsprintf("  checkTextTimer = setTimeout(checkText, 1000);\n");
   }
   rsprintf("}\n\n");

   rsprintf("function restoreText()\n");
   rsprintf("{\n");
   rsprintf("  if (initialText != null) {\n");
   rsprintf("    if (confirm('%s'+'\\n'+'%s')) {\n", loc("Do you want to restore your original text?"),
            loc("This will overwrite your current modifications."));
   rsprintf("      if (typeof(CKEDITOR) != 'undefined')\n");
   rsprintf("        CKEDITOR.instances.Text.setData(initialText);\n");
   rsprintf("      else\n");
   rsprintf("        document.form1.Text.value = initialText;\n");
   rsprintf("    }\n");
   rsprintf("  }\n");
   rsprintf("}\n\n");

   rsprintf("function kp(e)\n");
   rsprintf("{\n");
   rsprintf("  last_key = (e.which) ? e.which : event.keyCode;\n");
   rsprintf("  mod();\n");
   rsprintf("}\n\n");

   /* switch_smileys turn on/off the smiley bar */
   rsprintf("function switch_smileys()\n");
   rsprintf("{\n");
   rsprintf("   s = document.getElementById('smileyRow');\n");
   rsprintf("   i = document.getElementById('smileyIcon');\n");
   rsprintf("   if (s.style.display == 'none') {\n");
   rsprintf("     s.style.display = 'table-row';\n");
   rsprintf("     i.src = 'icons/eld_smile.png';\n");
   rsprintf("   } else {\n");
   rsprintf("     s.style.display = 'none';\n");
   rsprintf("     i.src = 'icons/elc_smile.png';\n");
   rsprintf("   }\n");
   rsprintf("}\n\n");

   if (/*enc_selected != 2 && */ !getcfg(lbs->name, "Message height", str, sizeof(str)) &&
                                 !getcfg(lbs->name, "Message width", str, sizeof(str))) {
      /* javascript for resizing edit box */
      rsprintf("function init_resize()\n");
      rsprintf("{\n");
      rsprintf("   window.onresize = resize_textarea;\n");
      rsprintf("   resize_textarea();\n");
      rsprintf("}\n\n");
      rsprintf("function resize_textarea()\n");
      rsprintf("{\n");
      rsprintf("   p = $id('TextParent');\n");
      rsprintf("   t = p.getBoundingClientRect().top;\n");
      rsprintf("   if (!!document.documentMode) // IE\n");
      rsprintf("      height = window.innerHeight - t - 210;\n");
      rsprintf("   else\n");
      rsprintf("      height = window.innerHeight - t - 205;\n");
      rsprintf("   if (height < 300)\n");
      rsprintf("      height = 300;\n");
      rsprintf("   width = window.innerWidth;\n");
      rsprintf("   if (width < 300)\n");
      rsprintf("      width = 300;\n");
      rsprintf("   width = width - 8;\n");
      rsprintf("   if (typeof(CKEDITOR) != 'undefined')\n");
      rsprintf("      CKEDITOR.instances.Text.resize(width, height);\n");
      rsprintf("   else {\n");
      rsprintf("      document.form1.Text.style.height = height+6+\"px\";\n");
      rsprintf("      document.form1.Text.style.width = width-6+\"px\";\n");
      rsprintf("   }\n");
      rsprintf("}\n\n");
   }

   /* ToggleAll() to toggle MOptions buttons */
   rsprintf("function ToggleAll(attrib)\n");
   rsprintf("{\n");
   rsprintf("  for (var i = 0; i < document.form1.elements.length; i++) {\n");
   rsprintf(
           "    if (document.form1.elements[i].type == 'checkbox' && document.form1.elements[i].disabled == false) {\n");
   rsprintf("      a = document.form1.elements[i].name;\n");
   rsprintf("      a = a.substring(0, attrib.length);\n");
   rsprintf("      if (a == attrib)\n");
   rsprintf("        document.form1.elements[i].checked = !(document.form1.elements[i].checked);\n");
   rsprintf("    }\n");
   rsprintf("  }\n");
   rsprintf("}\n\n");

   /* language for CKEDITOR */
   if (getcfg("global", "language", str, sizeof(str))) {
      for (i = 0; lang_table[i].language[0]; i++) {
         strlcpy(str2, str, sizeof(str2));
         str2[strlen(lang_table[i].language)] = 0;
         if (stricmp(str2, lang_table[i].language) == 0)
            break;
      }
      if (lang_table[i].language[0])
         rsprintf("var CKEditorLang = '%s';\n", lang_table[i].abbrev);
      else
         rsprintf("var CKEditorLang = 'en';\n");
   } else
      rsprintf("var CKEditorLang = 'en';\n");

   /* strings for elcode.js */
   if (enc_selected == 0) {
      rsprintf("var linkText_prompt = \"%s\";\n", loc("Enter name of hyperlink"));
      rsprintf("var linkURL_prompt  = \"%s\";\n", loc("Enter URL of hyperlink"));
      rsprintf("var linkHeading_prompt  = \"%s\";\n", loc("Enter heading level (1, 2 or 3)"));
   }

   show_browser(browser);

   rsprintf("var logbook = \"%s\";\n", lbs->name_enc);
   for (i = 0; i < MAX_ATTACHMENTS; i++)
      if (!att[i][0]) {
         /* put first free attachment for uploader */
         rsprintf("var next_attachment = %d;\n", i + 1);
         break;
      }
   rsprintf("var page_title = '%s';\n", page_title);

   rsprintf("\n");
   rsprintf("window.onbeforeunload = beforeunload;\n");
   rsprintf("\n");

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

   /* optionally load ImageMagic JavaScript code */
   if (image_magick_exist)
      rsprintf("<script type=\"text/javascript\" src=\"../im.js\"></script>\n\n");

   /* optionally load ELCode JavaScript code */
   if (enc_selected == 0)
      rsprintf("<script type=\"text/javascript\" src=\"../elcode.js\"></script>\n\n");

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

   if (enc_selected == 2 && ckedit_exist && show_text && !fixed_text) {
      rsprintf("<script type=\"text/javascript\" src=\"../ckeditor/ckeditor.js\"></script>\n");
   }

   rsprintf("<script type=\"text/javascript\" src=\"../jquery-1.11.1.min.js\"></script>\n");
   rsprintf("<script type=\"text/javascript\" src=\"../progress/progress.min.js\"></script>\n");
   rsprintf("<link rel=\"stylesheet\" type=\"text/css\" href=\"../progress/progressjs.min.css\">\n");

   /* drag-and-drip script */
   rsprintf("<script type=\"text/javascript\" src=\"../dnd.js\"></script>\n");

   /* CKEDITOR */
   if (enc_selected == 2 && ckedit_exist && show_text && !fixed_text)
      rsprintf("<script type=\"text/javascript\" src=\"../load-ckeditor.js\"></script>\n");

   /* external script if requested */
   if (isparam("js")) {
      rsprintf("<script src=\"%s\" type=\"text/javascript\">\n", getparam("js"));
      rsprintf("</script>\n\n");
   }

   script_onload[0] = 0;
   script_onfocus[0] = 0;
   if ((isparam("inlineatt") && *getparam("inlineatt")) || bpreview)
      strcpy(script_onload, "document.form1.Text.focus();");
   else
      strcpy(script_onload, "i=document.getElementById('fid');if(i)i.focus();");

   if (enc_selected == 0) {
      if (!getcfg(lbs->name, "Message height", str, sizeof(str)) &&
          !getcfg(lbs->name, "Message width", str, sizeof(str))) {
         strcat(script_onload, "elKeyInit();init_resize();");
         strcat(script_onfocus, "elKeyInit();");
      } else
         strcat(script_onload, "elKeyInit();");
      strcat(script_onfocus, "elKeyInit();");
   } else if (enc_selected == 1) {
      if (!getcfg(lbs->name, "Message height", str, sizeof(str)) &&
          !getcfg(lbs->name, "Message width", str, sizeof(str)))
         strcat(script_onload, "init_resize();");
   }
   strcat(script_onload, "checkText();dndInit();");

   script_onunload[0] = 0;
   if (getcfg(lbs->name, "Use Lock", str, sizeof(str)) && atoi(str) == 1)
      strcat(script_onunload, "unload();");

   rsprintf("</head>\n\n");
   rsprintf("<body");

   if (script_onload[0])
      rsprintf(" OnLoad=\"%s\"", script_onload);
   if (script_onfocus[0])
      rsprintf(" OnFocus=\"%s\"", script_onfocus);
   if (script_onunload[0])
      rsprintf(" OnUnload=\"%s\"", script_onunload);
   rsprintf(">\n");

   show_top_text(lbs);
   rsprintf("<form name=\"form1\" id=\"form1\" method=\"POST\" action=\"./\" ");
   rsprintf("enctype=\"multipart/form-data\">\n");

   /*---- add password in case cookie expires during edit ----*/

   if (getcfg(lbs->name, "Password file", str, sizeof(str)) && isparam("unm")) {
      strencode2(str, getparam("unm"), sizeof(str));
      rsprintf("<input type=hidden name=\"unm\" value=\"%s\">\n", str);
      if (isparam("upwd"))
         strlcpy(upwd, getparam("upwd"), sizeof(upwd));
      else
         get_user_line(lbs, getparam("unm"), upwd, NULL, NULL, NULL, NULL, NULL);
      rsprintf("<input type=hidden name=\"upwd\" value=\"%s\">\n", upwd);
   }

   rsprintf("<input type=hidden name=\"jcmd\">\n");
   rsprintf("<input type=hidden name=\"smcmd\">\n");
   rsprintf("<input type=hidden name=\"inlineatt\">\n");

   if (new_entry)
      rsprintf("<input type=hidden name=\"new_entry\" value=\"1\">\n");

   if (isparam("entry_modified") && atoi(getparam("entry_modified")) == 1)
      rsprintf("<input type=hidden name=\"entry_modified\" value=\"1\">\n");
   else
      rsprintf("<input type=hidden name=\"entry_modified\" value=\"0\">\n");

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

   show_standard_title(lbs, "", 0);

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

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

   /* default cmd */
   rsprintf("<input type=hidden name=cmd value=\"%s\">\n", loc("Update"));

   rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return chkform(this);\">\n",
            loc("Submit"));

   if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1)
      rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return save_draft();\">\n",
               loc("Save"));

   if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1)
      rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
               loc("Preview"));

   if (message_id && (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1))
      rsprintf(
              "<input type=\"submit\" name=\"cmd\" id=\"restore\" value=\"%s\" disabled onClick=\"restoreText();return false;\">\n",
              loc("Restore"));

   if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1)
      rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return check_delete();\">\n",
               loc("Delete"));
   else
      rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
               loc("Back"));

   rsprintf(
           "&nbsp;&nbsp;<span id=\"saved1\" style=\"font-size:10px;font-style:italic;display:none\">%s 00:00:00</span>",
           loc("Draft saved at"));

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

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

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

   /* print required message if one of the attributes has it set */
   for (i = 0; i < n_attr; i++) {
      if (attr_flags[i] & AF_REQUIRED) {
         rsprintf("<tr><td colspan=2 class=\"attribvalue\">%s <font color=red>*</font> %s</td></tr>\n",
                  loc("Fields marked with"), loc("are required"));
         break;
      }
   }

   if (!isparam("nsel")) {
      time(&now);
      if (bedit && date[0]) {
         if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
            strcpy(format, DEFAULT_TIME_FORMAT);

         ltime = date_to_ltime(date);
         pts = localtime(&ltime);
         assert(pts);
         my_strftime(str, sizeof(str), format, pts);
      } else {
         if (!getcfg(lbs->name, "Time format", format, sizeof(format)))
            strcpy(format, DEFAULT_TIME_FORMAT);
         my_strftime(str, sizeof(str), format, localtime(&now));
         strcpy(date, ctime(&now));
         date[24] = 0;
      }

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

      strencode2(str, date, sizeof(str));
      rsprintf("<input type=hidden name=entry_date value=\"%s\"></td></tr>\n", str);
   }

   if (_condition[0])
      rsprintf("<input type=hidden name=condition value=\"%s\"></td></tr>\n", _condition);

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

   subtable = 0;

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

   strcpy(fid, "id=\"fid\" ");

   /* display attributes */
   for (aindex = 0; aindex < n_disp_attr; aindex++) {

      index = attr_index[aindex];

      strcpy(class_name, "attribname");
      strcpy(class_value, "attribvalue");
      input_size = 80;
      input_maxlen = NAME_LENGTH;
      strcpy(ua, attr_list[index]);
      stou(ua);

      sprintf(str, "Format %s", attr_list[index]);
      if (getcfg(lbs->name, str, format, sizeof(format))) {
         n = strbreak(format, fl, 8, ",", FALSE);
         if (n > 1)
            strlcpy(class_name, fl[1], sizeof(class_name));
         if (n > 2)
            strlcpy(class_value, fl[2], sizeof(class_value));
         if (n > 3 && atoi(fl[3]) > 0)
            input_size = atoi(fl[3]);
         if (n > 4 && atoi(fl[4]) > 0)
            input_maxlen = atoi(fl[4]);
      }

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

      strcpy(star, (attr_flags[index] & AF_REQUIRED) ? "<font color=red>*</font>" : "");

      /* display text box with optional tooltip */
      sprintf(str, "Tooltip %s", attr_list[index]);
      title[0] = 0;
      if (getcfg(lbs->name, str, comment, sizeof(comment)))
         sprintf(title, " title=\"%s\"", comment);

      rsprintf("<td%s nowrap class=\"%s\">", title, class_name);

      /* display attribute name */
      rsprintf("%s%s:", attr_list[index], star);

      /* show optional comment */
      sprintf(str, "Comment %s", attr_list[index]);
      if (getcfg(lbs->name, str, comment, sizeof(comment)))
         rsprintf("<br><span class=\"selcomment\"><b>%s</b></span>\n", comment);

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

      /* if attribute cannot be changed, just display text */
      if ((attr_flags[index] & AF_LOCKED) ||
          (message_id && bedit && (attr_flags[index] & AF_FIXED_EDIT)) ||
          (message_id && !bedit && (attr_flags[index] & AF_FIXED_REPLY))) {

         if (attr_flags[index] & 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[index]);
            pts = localtime(&ltime);
            assert(pts);
            if (ltime == 0)
               strcpy(str, "-");
            else
               my_strftime(str, sizeof(str), format, pts);

         } else if (attr_flags[index] & 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[index]);
            pts = localtime(&ltime);
            assert(pts);
            if (ltime == 0)
               strcpy(str, "-");
            else
               my_strftime(str, sizeof(str), format, pts);

         } else
            strlcpy(str, attrib[index], sizeof(str));

         rsprintf("<td%s class=\"%s\">\n", title, class_value);
         rsputs2(lbs, FALSE, str);
         rsprintf("&nbsp;");

         if (attr_flags[index] & AF_MULTI) {
            for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
               sprintf(str, "%s_%d", ua, i);

               if (strstr(attrib[index], attr_options[index][i]))
                  rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", str, attr_options[index][i]);
            }
         } else if (attr_flags[index] & AF_MUSERLIST) {

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

               sprintf(str, "%s_%d", ua, i);

               if (strstr(attrib[index], full_name))
                  rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", str, full_name);
            }
         } else if (attr_flags[index] & AF_MUSEREMAIL) {

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

               sprintf(str, "%s_%d", ua, i);

               if (strstr(attrib[index], user_email))
                  rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", str, user_email);
            }
         } else if (attr_flags[index] & AF_ICON) {
            for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
               sprintf(str, "%s_%d", ua, i);

               if (strstr(attrib[index], attr_options[index][i]))
                  rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", str, attr_options[index][i]);
            }
         } else {
            strencode2(str, attrib[index], sizeof(str));
            rsprintf("<input type=\"hidden\" name=\"%s\" value=\"%s\"></td>\n", ua, str);
         }
      } else {
         if (attr_options[index][0][0] == 0) {

            if (attr_flags[index] & AF_DATE) {

               year = month = day = 0;
               if (attrib[index][0]) {
                  ltime = atoi(attrib[index]);
                  if (ltime > 0) {
                     pts = localtime(&ltime);
                     assert(pts);
                     year = pts->tm_year + 1900;
                     month = pts->tm_mon + 1;
                     day = pts->tm_mday;
                  }
               }

               rsprintf("<td%s class=\"attribvalue\">", title);
               sprintf(str, "%d", index);
               show_date_selector(day, month, year, str);
               rsprintf("</td>\n");

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

               year = month = day = 0;
               hour = min = sec = -1;
               if (attrib[index][0]) {
                  ltime = atoi(attrib[index]);
                  if (ltime > 0) {
                     pts = localtime(&ltime);
                     assert(pts);
                     year = pts->tm_year + 1900;
                     month = pts->tm_mon + 1;
                     day = pts->tm_mday;
                     hour = pts->tm_hour;
                     min = pts->tm_min;
                     sec = pts->tm_sec;
                  }
               }

               rsprintf("<td%s class=\"%s\">", title, class_value);
               sprintf(str, "%d", index);
               show_date_selector(day, month, year, str);
               rsprintf("&nbsp;&nbsp;");
               show_time_selector(hour, min, sec, str);

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

            } else if (attr_flags[index] & AF_USERLIST) {

               rsprintf("<td%s class=\"%s\">\n", title, class_value);

               /* display drop-down box with list of users */
               rsprintf("<select name=\"%s\"", ua);
               rsprintf(" onChange=\"mod();\">\n");

               /* display emtpy option */
               sprintf(str, "- %s -", loc("keep original values"));
               if (strcmp(str, attrib[index]) == 0 && isparam("nsel"))
                  rsprintf("<option value=\"<keep>\">%s\n", str);
               else
                  rsprintf("<option value=\"\">- %s -\n", loc("please select"));

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

                  if (strieq(str, attrib[index]))
                     rsprintf("<option selected value=\"%s\">%s\n", str, str);
                  else
                     rsprintf("<option value=\"%s\">%s\n", str, str);
               }

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

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

            } else if (attr_flags[index] & AF_MUSERLIST) {

               /* display multiple check boxes with user names */
               rsprintf("<td%s class=\"%s\">\n", title, class_value);

               n_moptions = strbreak(attrib[index], attr_moptions, MAX_N_LIST, "|", FALSE);

               /* allocate list of users and populate it */
               for (n = 0;; n++) {
                  if (!enum_user_line(lbs, n, login_name, sizeof(login_name)))
                     break;
               }

               user_list = xcalloc(sizeof(char *), n);
               for (i = 0; i < n; i++)
                  user_list[i] = xcalloc(NAME_LENGTH, 1);

               for (i = 0; i < n; i++) {
                  enum_user_line(lbs, i, str, NAME_LENGTH);
                  get_user_line(lbs, str, NULL, user_list[i], NULL, NULL, NULL, NULL);
               }

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

               for (i = 0; i < n; i++) {
                  sprintf(str, "%s_%d", ua, i);

                  rsprintf("<span style=\"white-space:nowrap;\">\n");

                  for (j = 0; j < n_moptions; j++)
                     if (strcmp(attr_moptions[j], user_list[i]) == 0)
                        break;

                  if (j < n_moptions)
                     rsprintf
                             ("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" checked onChange=\"mod();\">\n",
                              str, str, user_list[i]);
                  else
                     rsprintf
                             ("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
                              str, str, user_list[i]);

                  rsprintf("<label for=\"%s\">%s</label>\n", str, user_list[i]);
                  rsprintf("</span>\n");

                  if (format_flags[index] & AFF_MULTI_LINE)
                     rsprintf("<br>");
               }

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

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

            } else if (attr_flags[index] & AF_MUSEREMAIL) {

               /* display multiple check boxes with user emails */
               rsprintf("<td%s class=\"%s\">\n", title, class_value);

               n_moptions = strbreak(attrib[index], attr_moptions, MAX_N_LIST, "|", FALSE);

               /* allocate list of users and populate it */
               for (n = 0;; n++) {
                  if (!enum_user_line(lbs, n, login_name, sizeof(login_name)))
                     break;
               }

               user_list = xcalloc(sizeof(char *), n);
               for (i = 0; i < n; i++)
                  user_list[i] = xcalloc(NAME_LENGTH, 1);

               for (i = 0; i < n; i++) {
                  enum_user_line(lbs, i, str, NAME_LENGTH);
                  get_user_line(lbs, str, NULL, NULL, user_list[i], NULL, NULL, NULL);
               }

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

               for (i = 0; i < n; i++) {
                  sprintf(str, "%s_%d", ua, i);

                  rsprintf("<span style=\"white-space:nowrap;\">\n");

                  for (j = 0; j < n_moptions; j++)
                     if (strcmp(attr_moptions[j], user_list[i]) == 0 ||
                         strcmp(attr_moptions[j] + 7, user_list[i]) == 0)
                        break;

                  if (j < n_moptions)
                     rsprintf
                             ("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" checked onChange=\"mod();\">\n",
                              str, str, user_list[i]);
                  else
                     rsprintf
                             ("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
                              str, str, user_list[i]);

                  rsprintf("<label for=\"%s\">%s</label>\n", str, user_list[i]);
                  rsprintf("</span>\n");

                  if (format_flags[index] & AFF_MULTI_LINE)
                     rsprintf("<br>");
               }

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

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

            } else if (attr_flags[index] & AF_USEREMAIL) {

               rsprintf("<td%s class=\"%s\">\n", title, class_value);

               /* display drop-down box with list of users */
               rsprintf("<select name=\"%s\"", ua);
               rsprintf(" onChange=\"mod();\">\n");

               /* display emtpy option */
               sprintf(str, "- %s -", loc("keep original values"));
               if (strcmp(str, attrib[index]) == 0 && isparam("nsel"))
                  rsprintf("<option value=\"<keep>\">%s\n", str);
               else
                  rsprintf("<option value=\"\">- %s -\n", loc("please select"));

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

                  if (strieq(str, attrib[index]) || strieq(str, attrib[index] + 7))
                     rsprintf("<option selected value=\"%s\">%s\n", str, str);
                  else
                     rsprintf("<option value=\"%s\">%s\n", str, str);
               }

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

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

            } else {

               /* show normal edit field */
               rsprintf("<td%s class=\"%s\">", title, class_value);

               strencode2(str, attrib[index], sizeof(str));
               rsprintf
                       ("<input type=\"text\" %ssize=%d maxlength=%d name=\"%s\" value=\"%s\" onKeyPress=\"kp(event)\" onChange=\"mod()\">\n",
                        fid, input_size, input_maxlen, ua, str);
               fid[0] = 0;

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

         } else {
            if (strieq(attr_options[index][0], "boolean")) {
               /* display three radio buttons instead of three-state check box for multi-edit */
               sprintf(str, "- %s -", loc("keep original values"));
               if (isparam("nsel") && strieq(attrib[index], str)) {
                  rsprintf("<td%s class=\"%s\">", title, class_value);
                  sprintf(str, "%s_0", ua);
                  rsprintf("<span style=\"white-space:nowrap;\">\n");
                  rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"0\" onChange=\"mod();\">\n", str,
                           ua);
                  rsprintf("<label for=\"%s\">0</label>\n", str);
                  rsprintf("</span>\n");

                  sprintf(str, "%s_1", ua);
                  rsprintf("<span style=\"white-space:nowrap;\">\n");
                  rsprintf("<input type=radio id=\"%s\" name=\"%s\" value=\"1\" onChange=\"mod();\">\n", str,
                           ua);
                  rsprintf("<label for=\"%s\">1</label>\n", str);
                  rsprintf("</span>\n");

                  sprintf(str, "%s_2", ua);
                  rsprintf("<span style=\"white-space:nowrap;\">\n");
                  rsprintf
                          ("<input type=radio id=\"%s\" name=\"%s\" value=\"<keep>\" checked onChange=\"mod();\">\n",
                           str, ua);
                  rsprintf("<label for=\"%s\">%s</label>\n", str, loc("keep original values"));
                  rsprintf("</span>\n");
               }

                  /* display checkbox */
               else if (atoi(attrib[index]) == 1)
                  rsprintf
                          ("<td%s class=\"%s\"><input type=checkbox checked name=\"%s\" value=1 onChange=\"mod();\">\n",
                           title, class_value, ua);
               else
                  rsprintf
                          ("<td%s class=\"%s\"><input type=checkbox name=\"%s\" value=1 onChange=\"mod();\">\n",
                           title, class_value, ua);
            } else {

               sprintf(str, "extend_%d", index);
               if (isparam(str)) {

                  rsprintf("<td%s class=\"%s\">\n", title, class_value);
                  rsprintf("<i>");
                  rsprintf(loc("Add new option here"), attr_list[index]);
                  rsprintf("&nbsp;:&nbsp;</i>\n");

                  if (attr_flags[index] & (AF_MULTI | AF_MUSERLIST | AF_MUSEREMAIL))
                     rsprintf
                             ("<input type=\"text\" size=20 maxlength=%d name=\"%s_0\" value=\"%s\" onChange=\"mod();\">\n",
                              input_maxlen, ua, attrib[index]);
                  else
                     rsprintf
                             ("<input type=\"text\" size=20 maxlength=%d name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
                              input_maxlen, ua, attrib[index]);

                  rsprintf("<input type=\"hidden\" name=\"extend_%d\" value=\"1\">\n", index);
                  rsprintf("</td>\n");

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

                  /* display multiple check boxes */
                  rsprintf("<td%s class=\"%s\">\n", title, class_value);

                  sprintf(str, "- %s -", loc("keep original values"));
                  if (isparam("nsel") && strieq(attrib[index], str)) {
                     rsprintf("<span style=\"white-space:nowrap;\">\n");
                     sprintf(str, "%s_keep", ua);
                     rsprintf
                             ("<input type=checkbox id=\"%s\" name=\"%s\" value=\"<keep>\" checked onChange=\"mod();\">\n",
                              str, ua);

                     rsprintf("<label for=\"%s\">%s</label>\n", str, loc("keep original values"));
                     rsprintf("</span>\n");
                  }

                  n_moptions = strbreak(attrib[index], attr_moptions, MAX_N_LIST, "|", FALSE);
                  for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {

                     /* display check box with optional tooltip */
                     sprintf(str, "Tooltip %s", attr_options[index][i]);
                     tooltip[0] = 0;
                     if (getcfg(lbs->name, str, comment, sizeof(comment)))
                        sprintf(tooltip, " title=\"%s\"", comment);

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

                     sprintf(str, "%s_%d", ua, i);
                     rsprintf("<span%s style=\"white-space:nowrap;\">\n", tooltip);

                     for (j = 0; j < n_moptions; j++)
                        if (strcmp(attr_moptions[j], attr_options[index][i]) == 0)
                           break;

                     if (j < n_moptions)
                        rsprintf
                                ("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" checked onChange=\"mod();\">\n",
                                 str, str, attr_options[index][i]);
                     else
                        rsprintf
                                ("<input type=checkbox id=\"%s\" name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
                                 str, str, attr_options[index][i]);

                     rsprintf("<label for=\"%s\">%s</label>\n", str, attr_options[index][i]);
                     rsprintf("</span>\n");

                     if (format_flags[index] & AFF_MULTI_LINE)
                        rsprintf("<br>");
                  }

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

                  if (attr_flags[index] & AF_EXTENDABLE) {
                     sprintf(str, loc("Add %s"), attr_list[index]);
                     rsprintf
                             ("<input type=submit name=\"extend_%d\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
                              index, str);
                  }

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

               } else if (attr_flags[index] & AF_RADIO) {
                  /* display radio buttons */
                  rsprintf("<td%s class=\"%s\">\n", title, class_value);

                  for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
                     /* display check box with optional tooltip */
                     sprintf(str, "Tooltip %s", attr_options[index][i]);
                     tooltip[0] = 0;
                     if (getcfg(lbs->name, str, comment, sizeof(comment)))
                        sprintf(tooltip, " title=\"%s\"", comment);

                     rsprintf("<span%s style=\"white-space:nowrap;\">\n", tooltip);

                     strencode2(str, attr_options[index][i], sizeof(str));
                     if (strchr(str, '{'))
                        *strchr(str, '{') = 0;
                     strencode2(enc_attr, attrib[index], sizeof(enc_attr));

                     if (strieq(attrib[index], attr_options[index][i]) || strieq(str, enc_attr))
                        rsprintf
                                ("<input type=radio id=\"%s\" name=\"%s\" value=\"%s\" checked onChange=\"mod();\">\n",
                                 str, ua, str);
                     else
                        rsprintf
                                ("<input type=radio id=\"%s\" name=\"%s\" value=\"%s\" onChange=\"mod();\">\n",
                                 str, ua, str);

                     rsprintf("<label for=\"%s\">%s</label>\n", str, str);

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

                     if (format_flags[index] & AFF_MULTI_LINE)
                        rsprintf("<br>");
                  }

                  if (attr_flags[index] & AF_EXTENDABLE) {
                     sprintf(str, loc("Add %s"), attr_list[index]);
                     rsprintf
                             ("<input type=submit name=\"extend_%d\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
                              index, str);
                  }

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

               } else if (attr_flags[index] & AF_ICON) {
                  /* display icons */
                  rsprintf("<td%s class=\"%s\">\n", title, class_value);
                  rsprintf("<table cellpadding=\"0\" cellspacing=\"0\"><tr>\n");

                  for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
                     if (strstr(attrib[index], attr_options[index][i]))
                        rsprintf
                                ("<td><input type=radio checked name=\"%s\" value=\"%s\" onChange=\"mod();\">",
                                 ua, attr_options[index][i]);
                     else
                        rsprintf("<td><input type=radio name=\"%s\" value=\"%s\" onChange=\"mod();\">", ua,
                                 attr_options[index][i]);

                     sprintf(str, "Icon comment %s", attr_options[index][i]);
                     getcfg(lbs->name, str, comment, sizeof(comment));

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

                     rsprintf("</td>\n");
                     if ((format_flags[index] & AFF_MULTI_LINE) && attr_options[index][i + 1][0]) {
                        rsprintf("</tr><tr>\n");
                     }
                  }

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

               } else {

                  rsprintf("<td%s class=\"%s\">\n", title, class_value);

                  /* display drop-down box */
                  rsprintf("<select name=\"%s\"", ua);

                  if (is_cond_attr(index))
                     rsprintf(" onChange=\"cond_submit()\">\n");
                  else
                     rsprintf(" onChange=\"mod();\">\n");

                  /* display emtpy option */
                  sprintf(str, "- %s -", loc("keep original values"));
                  if (strcmp(str, attrib[index]) == 0 && isparam("nsel"))
                     rsprintf("<option value=\"<keep>\">%s\n", str);
                  else
                     rsprintf("<option value=\"\">- %s -\n", loc("please select"));

                  for (i = 0; i < MAX_N_LIST && attr_options[index][i][0]; i++) {
                     strencode2(str, attr_options[index][i], sizeof(str));
                     if (strchr(str, '{'))
                        *strchr(str, '{') = 0;
                     strencode2(enc_attr, attrib[index], sizeof(enc_attr));

                     if (strieq(attr_options[index][i], attrib[index]) || strieq(str, enc_attr))
                        rsprintf("<option selected value=\"%s\">%s\n", str, str);
                     else
                        rsprintf("<option value=\"%s\">%s\n", str, str);
                  }

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

                  if (is_cond_attr(index)) {
                     /* 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 (attr_flags[index] & AF_EXTENDABLE) {
                     sprintf(str, loc("Add %s"), attr_list[index]);
                     rsprintf
                             ("<input type=submit name=\"extend_%d\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
                              index, str);
                  }

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

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

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

   if (bpreview && !fixed_text) {
      _current_message_id = message_id;
      rsprintf("<tr><td colspan=2 class=\"messageframe\">\n");

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

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

   if (enc_selected == 0 && show_text && !fixed_text) {
      rsprintf("<tr><td colspan=2 class=\"toolframe\">\n");

      ricon("bold", loc("Bold text CTRL+B"), "elcode(document.form1.Text, 'B','')");
      ricon("italic", loc("Italics text CTRL+I"), "elcode(document.form1.Text, 'I','')");
      ricon("underline", loc("Underlined text CTRL+U"), "elcode(document.form1.Text, 'U','')");

      rsprintf(" ");
      ricon("center", loc("Centered text"), "elcode(document.form1.Text, 'CENTER','')");

      rsprintf(" ");
      ricon("url", loc("Insert hyperlink"), "queryURL(document.form1.Text)");
      ricon("email", loc("Insert email"), "elcode(document.form1.Text, 'EMAIL','')");

      sprintf(str, "window.open('upload.html', '',");
      strlcat(str, "'top=280,left=350,width=500,height=120,dependent=yes,", sizeof(str));
      strlcat(str, "menubar=no,status=no,scrollbars=no,location=no,resizable=yes')", sizeof(str));
      ricon("image", loc("Insert image CTRL+M"), str);

      rsprintf(" ");
      ricon("quote", loc("Insert quote"), "elcode(document.form1.Text, 'QUOTE','')");
      ricon("list", loc("Insert list CTRL+L"), "elcode(document.form1.Text, 'LIST','')");
      ricon("table", loc("Insert table"), "elcode(document.form1.Text, 'TABLE','')");
      ricon("heading", loc("Insert heading CTRL+H"), "queryHeading(document.form1.Text)");
      ricon("line", loc("Insert horizontal line"), "elcode(document.form1.Text, 'LINE','')");
      ricon("anchor", loc("Insert anchor point"), "elcode(document.form1.Text, 'ANCHOR','')");

      rsprintf(" ");
      ricon("code", loc("Insert code CTRL+O"), "elcode(document.form1.Text, 'CODE','')");

      rsprintf(
              " <img align=\"middle\" id=\"smileyIcon\" src=\"icons/elc_smile.png\" alt=\"%s\" title=\"%s\" border=\"0\"",
              loc("Show the smiley bar"), loc("Show the smiley bar"));
      rsprintf(" onclick=\"switch_smileys()\"");
      rsprintf(" onmouseover=\"this.style.cursor='pointer';\" />\n");

      rsprintf(" <select name=\"_font_name\" ");
      rsprintf("onchange=\"elcode(document.form1.Text,'FONT',this.options[this.selectedIndex].value);");
      rsprintf("this.selectedIndex=0;\">\n");
      rsprintf("<option value=\"0\">%s</option>\n", loc("FONT"));

      if (!getcfg(lbs->name, "Fonts", str, sizeof(str)))
         strcpy(str, "Arial, Comic Sans MS, Courier New, Tahoma, Times New Roman, Verdana");

      n = strbreak(str, list, MAX_N_ATTR, ",", FALSE);
      for (i = 0; i < n; i++)
         rsprintf("<option value=\"%s\">%s</option>\n", list[i], list[i]);

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

      rsprintf(" <select name=\"_font_size\" ");
      rsprintf("onchange=\"elcode(document.form1.Text,'SIZE',this.options[this.selectedIndex].value);");
      rsprintf("this.selectedIndex=0;\">\n");
      rsprintf("<option value=\"0\">%s</option>\n", loc("SIZE"));
      rsprintf("<option value=\"1\">1</option>\n");
      rsprintf("<option value=\"2\">2</option>\n");
      rsprintf("<option value=\"3\">3</option>\n");
      rsprintf("<option value=\"4\">4</option>\n");
      rsprintf("<option value=\"5\">5</option>\n");
      rsprintf("<option value=\"6\">6</option>\n");
      rsprintf("</select>\n");

      rsprintf(" <select name=\"_font_color\" ");
      rsprintf("onchange=\"elcode(document.form1.Text,'COLOR',this.options[this.selectedIndex].value);");
      rsprintf("this.selectedIndex=0;\">\n");
      rsprintf("<option value=\"0\">%s</option>\n", loc("COLOR"));
      rsprintf("<option value=\"blue\" style=\"color:blue\">blue</option>\n");
      rsprintf("<option value=\"darkblue\" style=\"color:darkblue\">dark-blue</option>\n");
      rsprintf("<option value=\"orange\" style=\"color:orange\">orange</option>\n");
      rsprintf("<option value=\"red\" style=\"color:red\">red</option>\n");
      rsprintf("<option value=\"darkred\" style=\"color:darkred\">dark red</option>\n");
      rsprintf("<option value=\"green\" style=\"color:green\">green</option>\n");
      rsprintf("<option value=\"darkgreen\" style=\"color:darkgreen\">dark-green</option>\n");
      rsprintf("<option value=\"pink\" style=\"color:deeppink\">pink</option>\n");
      rsprintf("<option value=\"purple\" style=\"color:purple\">purple</option>\n");
      rsprintf("<option value=\"chocolate\" style=\"color:chocolate\">chocolate</option>\n");
      rsprintf("</select>");

      rsprintf(" ");
      ricon("clock", loc("Insert current time/date"), "insertTime(document.form1.Text)");

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

   if (enc_selected == 0) {

      rsprintf("<tr id=\"smileyRow\" style=\"display:none\"><td colspan=2 class=\"toolframe\">\n");
      rsicon("smile", loc("smiling"), ":)");
      rsicon("happy", loc("happy"), ":))");
      rsicon("wink", loc("winking"), ";)");
      rsicon("biggrin", loc("big grin"), ":D");
      rsicon("crying", loc("crying"), ";(");
      rsicon("cool", loc("cool"), "8-)");
      rsicon("frown", loc("frowning"), ":(");
      rsicon("confused", loc("confused"), "?-)");
      rsicon("astonished", loc("astonished"), "8o");
      rsicon("mad", loc("mad"), "X-(");
      rsicon("pleased", loc("pleased"), ":]");
      rsicon("tongue", loc("tongue"), ":P");
      rsicon("yawn", loc("yawn"), ":O");

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

   if (getcfg(lbs->name, "Message comment", comment, sizeof(comment)) && !message_id) {
      rsprintf("<tr><td colspan=2 class=\"attribvalue\">%s</td></tr>\n", comment);
   }

   if (getcfg(lbs->name, "Reply comment", comment, sizeof(comment)) && breply) {
      rsprintf("<tr><td colspan=2 class=\"attribvalue\">%s</td></tr>\n", comment);
   }

   rsprintf(
           "<tr><td colspan=2 width=\"100%%\" class=\"attribvalue\" id=\"TextParent\" style=\"padding:0\">\n");

   /* set textarea width */
   width = 112;

   if (getcfg(lbs->name, "Message width", str, sizeof(str)))
      width = atoi(str);

   /* increased width according to longest line */
   if (message_id && enc_selected == 1) {
      p = text;
      do {
         pend = strchr(p, '\n');
         if (pend == NULL)
            pend = p + strlen(p);

         if (pend - p + 1 > width)
            width = pend - p + 1;

         if (*pend == 0)
            break;

         p = pend;
         while (*p && (*p == '\r' || *p == '\n'))
            p++;
      } while (1);

      /* leave space for '> ' */
      if (!bedit && !bduplicate)
         width += 2;
   }

   /* set textarea height */
   height = 20;
   if (getcfg(lbs->name, "Message height", str, sizeof(str)))
      height = atoi(str);

   if (breply)
      /* hidden text for original message */
      rsprintf("<input type=\"hidden\" name=\"reply_to\" value=\"%d\">\n", message_id);

   if (breedit || bupload)
      /* hidden text for original message */
      if (isparam("reply_to"))
         rsprintf("<input type=\"hidden\" name=\"reply_to\" value=\"%s\">\n", getparam("reply_to"));

   if (bedit && message_id)
      rsprintf("<input type=\"hidden\" id=\"edit_id\" name=\"edit_id\" value=\"%d\">\n", message_id);

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

   preset_text = getcfg(lbs->name, "Preset text", str, sizeof(str));
   if (preset_text) {

      /* don't use preset text if editing or replying */
      if (bedit || bduplicate || breply)
         preset_text = FALSE;

      /* user preset on reedit only if preset is under condition */
      if (breedit && !bpreview && !bupload && getcfg(lbs->name, "Preset text", str, sizeof(str)) == 2)
         preset_text = TRUE;
   }

   if (show_text) {

      if (fixed_text) {

         rsprintf("<input type=hidden name=\"text\" value=\"<keep>\">\n");
         rsprintf("<input type=hidden name=\"encoding\" value=\"%s\">\n", encoding);

         _current_message_id = message_id;
         rsprintf("<tr><td colspan=2 class=\"messageframe\">\n");

         if (strieq(text, "<keep>") && message_id) {
            size = TEXT_SIZE;
            el_retrieve(lbs, message_id, NULL, NULL, NULL, 0, text, &size, NULL, NULL, NULL, NULL, NULL, NULL);
         }

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

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

         rsprintf("<tr><td colspan=2 width=\"100%%\" class=\"attribvalue\">\n");

      } else {

         if (enc_selected == 1)
            /* use hard wrapping only for plain text */
            rsprintf("<textarea rows=%d cols=%d wrap=hard name=\"Text\">\n", height, width);
         else
            rsprintf
                    ("<textarea rows=%d cols=%d name=\"Text\" style=\"width:100%%;\">\n", height, width);

         if (isparam("nsel")) {
            rsprintf("- %s -\n", loc("keep original text"));
         } else if (bedit) {
            if (!preset_text) {

               j = build_subst_list(lbs, slist, svalue, attrib, TRUE);
               sprintf(mid, "%d", message_id);
               add_subst_list(slist, svalue, "message id", mid, &j);
               add_subst_time(lbs, slist, svalue, "entry time", date, &j, 0);

               if (!bupload)
                  if (getcfg(lbs->name, "Prepend on edit", str, sizeof(str))) {
                     strsubst_list(str, sizeof(str), slist, svalue, j);
                     while (strstr(str, "\\n"))
                        memcpy(strstr(str, "\\n"), "\r\n", 2);
                     rsputs3(str);
                  }

               /* use rsputs3 which just converts "<", ">", "&", "\"" to &gt; etc. */
               /* otherwise some HTML statments would break the page syntax        */
               rsputs3(text);

               if (!bupload)
                  if (getcfg(lbs->name, "Append on edit", str, sizeof(str))) {
                     strsubst_list(str, sizeof(str), slist, svalue, j);
                     while (strstr(str, "\\n"))
                        memcpy(strstr(str, "\\n"), "\r\n", 2);
                     rsputs3(str);
                  }
            }
         } else if (bduplicate) {

            rsputs3(text);

         } else if (breply) {
            if (!getcfg(lbs->name, "Quote on reply", str, sizeof(str)) || atoi(str) > 0) {
               if (getcfg(lbs->name, "Prepend on reply", str, sizeof(str))) {
                  j = build_subst_list(lbs, slist, svalue, attrib, TRUE);
                  sprintf(mid, "%d", message_id);
                  add_subst_list(slist, svalue, "message id", mid, &j);
                  add_subst_time(lbs, slist, svalue, "entry time", date, &j, 0);

                  strsubst_list(str, sizeof(str), slist, svalue, j);
                  while (strstr(str, "\\n"))
                     memcpy(strstr(str, "\\n"), "\r\n", 2);
                  rsputs3(str);
               }

               p = text;

               if (text[0]) {
                  if (!getcfg(lbs->name, "Reply string", reply_string, sizeof(reply_string)))
                     strcpy(reply_string, "> ");

                  if (enc_selected == 0) {

                     /* check for author */
                     if (orig_author[0])
                        rsprintf("[quote=\"%s\"]", orig_author);
                     else
                        rsprintf("[quote]");
                     rsputs3(text);
                     rsprintf("[/quote]\r\n");

                  } else if (enc_selected == 2) {

                     rsprintf("<p>\n");
                     rsprintf
                             ("<table width=\"98%%\" align=\"center\" cellspacing=\"1\" style=\"border:1px solid #486090;\">\n");
                     rsprintf("<tbody>\n");
                     rsprintf("<tr>\n");
                     rsprintf
                             ("<td cellpadding=\"3px\" style=\"background-color:#486090; font-weidht:bold; color:white;\">");

                     /* check for author */
                     if (orig_author[0]) {
                        rsprintf(loc("%s wrote"), orig_author);
                     } else {
                        rsprintf(loc("Quote"));
                     }
                     rsprintf(":</td></tr>\n");
                     rsprintf("<tr>\n");
                     rsprintf("<td cellpadding=\"10px\" style=\"background-color:#FFFFB0;\">");
                     rsputs3(text);
                     rsprintf("</td>\n");
                     rsprintf("</tr>\n");
                     rsprintf("</tbody>\n");
                     rsprintf("</table>\n");
                     rsprintf("</p><p>&nbsp;</p>\n");

                  } else {
                     do {
                        if (strchr(p, '\n')) {
                           *strchr(p, '\n') = 0;

                           if (encoding[0] == 'H') {
                              rsputs3(reply_string);
                              rsprintf("%s<br>\n", p);
                           } else {
                              rsputs(reply_string);
                              rsputs3(p);
                              rsprintf("\n");
                           }

                           p += strlen(p) + 1;
                           if (*p == '\n')
                              p++;
                        } else {
                           if (encoding[0] == 'H') {
                              rsputs3(reply_string);
                              rsprintf("%s<p>\n", p);
                           } else {
                              rsputs(reply_string);
                              rsputs3(p);
                              rsprintf("\n\n");
                           }

                           break;
                        }

                     } while (TRUE);
                  }
               }

               if (getcfg(lbs->name, "Append on reply", str, sizeof(str))) {

                  j = build_subst_list(lbs, slist, svalue, attrib, TRUE);
                  sprintf(mid, "%d", message_id);
                  add_subst_list(slist, svalue, "message id", mid, &j);
                  add_subst_time(lbs, slist, svalue, "entry time", date, &j, 0);
                  strsubst_list(str, sizeof(str), slist, svalue, j);
                  while (strstr(str, "\\n"))
                     memcpy(strstr(str, "\\n"), "\r\n", 2);
                  rsputs3(str);
               }
            }
         }

         if (preset_text && !isparam("nsel")) {
            getcfg(lbs->name, "Preset text", str, sizeof(str));

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

            /* check if file exists */
            fh = open(file_name, O_RDONLY | O_BINARY);
            if (fh > 0) {
               length = lseek(fh, 0, SEEK_END);
               lseek(fh, 0, SEEK_SET);
               buffer = xmalloc(length + 1);
               read(fh, buffer, length);
               buffer[length] = 0;
               close(fh);
               rsputs3(buffer);
               xfree(buffer);
            } else {
               j = build_subst_list(lbs, slist, svalue, attrib, TRUE);
               strsubst_list(str, sizeof(str), slist, svalue, j);
               while (strstr(str, "\\n"))
                  memcpy(strstr(str, "\\n"), "\r\n", 2);

               if (strchr(str, ' ')) {
                  /* name contains blanks -> must be text */
                  rsputs3(str);
               } else {
                  /* name is probably a file -> show error */
                  rsputs("File <i>");
                  rsputs3(str);
                  rsputs("</i> cannot be found");
               }
            }
         }

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

         /* Encoding radio buttons */

         if (allowed_encoding < 1 || allowed_encoding > 7) {
            rsprintf
                    ("<h1>Invalid \"Allowed encoding\" in configuration file, value must be between 1 and 7</h1>\n");
            rsprintf("</table><!-- show_standard_title -->\n");
            show_bottom_text(lbs);
            rsprintf("</form></body></html>\r\n");
            return;
         }

         if (allowed_encoding == 1)
            rsprintf("<input type=\"hidden\" name=\"encoding\" value=\"plain\">\n");
         else if (allowed_encoding == 2)
            rsprintf("<input type=\"hidden\" name=\"encoding\" value=\"ELCode\">\n");
         else if (allowed_encoding == 4)
            rsprintf("<input type=\"hidden\" name=\"encoding\" value=\"HTML\">\n");
         else {
            if (allowed_encoding == 4)
               rsprintf("<input type=hidden name=\"encoding\" value=\"HTML\">\n");
            else if (allowed_encoding == 2)
               rsprintf("<input type=hidden name=\"encoding\" value=\"ELCode\">\n");
            else if (allowed_encoding == 1)
               rsprintf("<input type=hidden name=\"encoding\" value=\"plain\">\n");
            else {
               rsprintf("<b>%s</b>: ", loc("Encoding"));

               if (allowed_encoding & 4) {
                  if (enc_selected == 2)
                     rsprintf
                             ("<input type=radio id=\"HTML\" name=\"encoding\" value=\"HTML\" checked=\"checked\">");
                  else
                     rsprintf
                             ("<input type=radio id=\"HTML\" name=\"encoding\" value=\"HTML\" onclick=\"cond_submit()\">");
                  rsprintf("<label for=\"HTML\">HTML&nbsp;&nbsp;</label>\n");
               }

               if (allowed_encoding & 2) {
                  if (enc_selected == 0)
                     rsprintf("<input type=radio id=\"ELCode\" name=\"encoding\" value=\"ELCode\" checked>");
                  else
                     rsprintf
                             ("<input type=radio id=\"ELCode\" name=\"encoding\" value=\"ELCode\" onclick=\"cond_submit()\">");
                  rsprintf
                          ("<label for=\"ELCode\"><a target=\"_blank\" href=\"?cmd=HelpELCode\">ELCode</a>&nbsp;&nbsp;</label>\n");
               }

               if (allowed_encoding & 1) {
                  if (enc_selected == 1)
                     rsprintf("<input type=radio id=\"plain\" name=\"encoding\" value=\"plain\" checked>");
                  else
                     rsprintf
                             ("<input type=radio id=\"plain\" name=\"encoding\" value=\"plain\" onclick=\"cond_submit()\">");
                  rsprintf("<label for=\"plain\">plain&nbsp;&nbsp;</label>\n");
               }
            }
         }

         rsprintf("<br>\n");
      }
   }

   /* Suppress email check box */
   if (message_id && bedit)
      getcfg(lbs->name, "Suppress Email on edit", str, sizeof(str));
   else
      getcfg(lbs->name, "Suppress default", str, sizeof(str));

   if (atoi(str) == 0) {
      rsprintf("<input type=\"checkbox\" name=\"suppress\" id=\"suppress\" value=\"1\">");
      rsprintf("<label for=\"suppress\">%s</label>\n", loc("Suppress Email notification"));
   } else if (atoi(str) == 1) {
      rsprintf("<input type=\"checkbox\" checked name=\"suppress\" id=\"suppress\" value=\"1\">");
      rsprintf("<label for=\"suppress\">%s</label>\n", loc("Suppress Email notification"));
   } else if (atoi(str) == 2) {
      rsprintf("<input type=\"hidden\" name=\"suppress\" id=\"suppress\" value=\"2\">");
   } else if (atoi(str) == 3) {
      rsprintf("<input type=\"hidden\" name=\"suppress\" id=\"suppress\" value=\"3\">");
   }

   /* Suppress execute shell check box */
   if (!bedit && getcfg(lbs->name, "Execute new", str, sizeof(str))) {
      if (getcfg(lbs->name, "Suppress execute default", str, sizeof(str))) {
         if (atoi(str) == 0) {
            rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n");
            rsprintf("<input type=\"checkbox\" name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
            rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
         } else if (atoi(str) == 1) {
            rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n");
            rsprintf
                    ("<input type=\"checkbox\" checked name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
            rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
         }
      } else {
         rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n");
         rsprintf("<input type=\"checkbox\" name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
         rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
      }
   }

   if (bedit && getcfg(lbs->name, "Execute edit", str, sizeof(str))) {
      if (getcfg(lbs->name, "Suppress execute default", str, sizeof(str))) {
         if (atoi(str) == 0) {
            rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n");
            rsprintf("<input type=\"checkbox\" name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
            rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
         } else if (atoi(str) == 1) {
            rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n");
            rsprintf
                    ("<input type=\"checkbox\" checked name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
            rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
         }
      } else {
         rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n");
         rsprintf("<input type=\"checkbox\" name=\"shell_suppress\" id=\"shell_suppress\" value=1>");
         rsprintf("<label for=\"shell_suppress\">%s</label>\n", loc("Suppress shell execution"));
      }
   }

   /* Resubmit check box */
   if (bedit && message_id) {
      if (getcfg(lbs->name, "Resubmit default", str, sizeof(str))) {
         if (atoi(str) == 0) {
            rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n");
            rsprintf("<input type=\"checkbox\" name=\"resubmit\" id=\"resubmit\" value=1>");
            rsprintf("<label for=\"resubmit\">%s</label>\n", loc("Resubmit as new entry"));
         } else if (atoi(str) == 1) {
            rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n");
            rsprintf("<input type=\"checkbox\" checked name=\"resubmit\" id=\"resubmit\" value=1>");
            rsprintf("<label for=\"resubmit\">%s</label>\n", loc("Resubmit as new entry"));
         }
      } else {
         rsprintf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n");
         rsprintf("<input type=\"checkbox\" name=\"resubmit\" id=\"resubmit\" value=1>");
         rsprintf("<label for=\"resubmit\">%s</label>\n", loc("Resubmit as new entry"));
      }
   }

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

   for (i = 0; i < MAX_ATTACHMENTS; i++)
      if (!att[i][0]) {
         /* put first free attachment for show_uploader_finished() */
         rsprintf("<tr><td><input type=hidden name=\"next_attachment\" value=\"%d\"></td></tr>\n", i + 1);
         break;
      }

   index = 0;
   if (!getcfg(lbs->name, "Enable attachments", str, sizeof(str)) || atoi(str) > 0) {
      if (bedit || bduplicate || isparam("fa")) {
         /* show existing attachments */
         for (index = 0; index < MAX_ATTACHMENTS; index++)
            if (att[index][0]) {
               rsprintf("<tr class=\"attachment\"><td nowrap class=\"attribname\">%s %d:</td>\n",
                        loc("Attachment"), index + 1);
               sprintf(str, "attachment%d", index);
               rsprintf("<td class=\"attribvalue\">\n");
               thumb_ref[0] = 0;

               if (strlen(att[index]) < 14 || att[index][6] != '_' || att[index][13] != '_') {
                  strencode2(str, att[index], sizeof(str));
                  rsprintf("<b>Error: Invalid attachment \"%s\"</b><br>", str);
               } else {

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

                  display_inline = is_image(file_name) || is_ascii(file_name);
                  if (chkext(file_name, ".ps") || chkext(file_name, ".pdf") || chkext(file_name, ".eps")
                      || chkext(file_name, ".svg"))
                     display_inline = 0;
                  if ((chkext(file_name, ".htm") || chkext(file_name, ".html")) && is_full_html(file_name))
                     display_inline = 0;

                  thumb_status = create_thumbnail(lbs, file_name);
                  if (thumb_status)
                     display_inline = 1;

                  if (getcfg(lbs->name, "Preview attachments", str, sizeof(str)) && atoi(str) == 0)
                     display_inline = 0;

                  if (thumb_status && display_inline) {
                     get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0);
                     if (strrchr(thumb_name, DIR_SEPARATOR))
                        strlcpy(str, strrchr(thumb_name, DIR_SEPARATOR) + 1, sizeof(str));
                     else
                        strlcpy(str, thumb_name, sizeof(str));
                     strlcpy(thumb_name, str, sizeof(thumb_name));
                     if (thumb_status == 2)
                        strsubst(thumb_name, sizeof(thumb_name), "-0.png", "");

                     rsprintf("<table><tr><td class=\"toolframe\">\n");
                     sprintf(str, "im('att'+'%d','%s','%s','smaller');", index, thumb_name, att[index]);
                     ricon("smaller", loc("Make smaller"), str);
                     sprintf(str, "im('att'+'%d','%s','%s','original');", index, thumb_name, att[index]);
                     ricon("original", loc("Original size"), str);
                     sprintf(str, "im('att'+'%d','%s','%s','larger');", index, thumb_name, att[index]);
                     ricon("larger", loc("Make larger"), str);
                     rsprintf("&nbsp;\n");
                     sprintf(str, "im('att'+'%d','%s','%s','rotleft');", index, thumb_name, att[index]);
                     ricon("rotleft", loc("Rotate left"), str);
                     sprintf(str, "im('att'+'%d','%s','%s','rotright');", index, thumb_name, att[index]);
                     ricon("rotright", loc("Rotate right"), str);
                     rsprintf("&nbsp;\n");
                     sprintf(str, "deleteAtt('%d','%s')", index,
                             loc("Are you sure to delete the attachment?"));
                     ricon("delatt", loc("Delete attachment"), str);
                     rsprintf("&nbsp;&nbsp;\n");

                     /* ImageMagick available, so get image size */
                     strencode2(str, att[index], sizeof(str));
                     rsprintf("<b>%s</b>&nbsp;\n", str + 14);
                     if (chkext(file_name, ".pdf") || chkext(file_name, ".ps"))
                        sprintf(cmd, "%s -format '%%wx%%h' '%s[0]'", _identify_cmd, file_name);
                     else
                        sprintf(cmd, "%s -format '%%wx%%h' '%s'", _identify_cmd, file_name);
#ifdef OS_WINNT
                                                                                                                                             for (i = 0; i < (int) strlen(cmd); i++)
                        if (cmd[i] == '\'')
                           cmd[i] = '\"';
#endif
                     my_shell(cmd, str, sizeof(str));
                     if (atoi(str) > 0)
                        rsprintf("<span class=\"bytes\">%s: %s</span>\n", loc("Original size"), str);

                     rsprintf("</td></tr>\n");
                     rsprintf("<tr><td align=center>");
                  } else {
                     rsprintf("%s\n", att[index] + 14);
                     rsprintf("&nbsp;&nbsp;<input type=\"submit\" name=\"delatt%d\" value=\"%s\" ", index,
                              loc("Delete"));
                     rsprintf("onClick=\"return mark_submitted();\">");
                     rsprintf("<br>\n");
                  }

                  if (display_inline) {

                     if (is_image(att[index]) || thumb_status) {
                        if (thumb_status == 1) {
                           get_thumb_name(file_name, thumb_name, sizeof(thumb_name), 0);
                           strlcpy(str, att[index], sizeof(str));
                           str[13] = 0;
                           if (strrchr(thumb_name, DIR_SEPARATOR))
                              strlcpy(file_enc, strrchr(thumb_name, DIR_SEPARATOR) + 1 + 14,
                                      sizeof(file_enc));
                           else
                              strlcpy(file_enc, thumb_name + 14, sizeof(file_enc));
                           url_encode(file_enc,
                                      sizeof(file_enc));      /* for file names with special characters like "+" */
                           sprintf(ref, "%s/%s?thumb=1", str, file_enc);

                           rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\" name=\"att%d\">\n", ref,
                                    att[index] + 14, att[index] + 14, index);
                           strlcpy(thumb_ref, ref, sizeof(thumb_ref));
                        } else if (thumb_status == 2) {
                           for (i = 0;; i++) {
                              get_thumb_name(file_name, thumb_name, sizeof(thumb_name), i);
                              if (thumb_name[0]) {
                                 strlcpy(str, att[index], sizeof(str));
                                 str[13] = 0;
                                 if (strrchr(thumb_name, DIR_SEPARATOR))
                                    strlcpy(file_enc, strrchr(thumb_name, DIR_SEPARATOR) + 1 + 14,
                                            sizeof(file_enc));
                                 else
                                    strlcpy(file_enc, thumb_name + 14, sizeof(file_enc));
                                 url_encode(file_enc,
                                            sizeof(file_enc));        /* for file names with special characters like "+" */
                                 sprintf(ref, "%s/%s?thumb=1", str, file_enc);

                                 rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\" name=\"att%d_%d\">\n",
                                          ref, att[index] + 14, att[index] + 14, index, i);
                                 strlcpy(thumb_ref, ref, sizeof(thumb_ref));
                              } else
                                 break;
                           }

                        } else {
                           strlcpy(str, att[index], sizeof(str));
                           str[13] = 0;
                           strcpy(file_enc, att[index] + 14);
                           url_encode(file_enc,
                                      sizeof(file_enc));      /* for file names with special characters like "+" */
                           sprintf(ref, "%s/%s", str, file_enc);

                           rsprintf("<img src=\"%s\" alt=\"%s\" title=\"%s\" name=\"att%d\">\n", ref,
                                    att[index] + 14, att[index] + 14, index);
                           strlcpy(thumb_ref, ref, sizeof(thumb_ref));
                        }
                     } else {
                        if (is_ascii(file_name)) {
                           if (!chkext(att[index], ".HTML"))
                              rsprintf("<pre class=\"messagepre\">");

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

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

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

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

                  if (thumb_status && display_inline)
                     rsprintf("</td></tr></table>\n");
               }

               strencode2(str, att[index], sizeof(str));
               if (thumb_ref[0])
                  rsprintf("<input type=hidden name=\"attachment%d\" alt=\"%s\" value=\"%s\">\n", index,
                           thumb_ref, str);
               else
                  rsprintf("<input type=hidden name=\"attachment%d\" value=\"%s\">\n", index, str);

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

      /* optional attachment comment */
      if (getcfg(lbs->name, "Attachment comment", comment, sizeof(comment))) {
         rsprintf("<tr><td colspan=2 class=\"attribvalue\">\n");
         rsputs(comment);
         rsputs("</td></tr>\n");
      }

      /* field for add attachment */
      if (att[MAX_ATTACHMENTS - 1][0]) {
         rsprintf("<tr><td colspan=2 class=\"attribname\">%s</td>\n",
                  loc("Maximum number of attachments reached"));
         rsprintf("</td></tr>\n");
      } else {
         rsprintf("<tr id=\"attachment_upload\"><td nowrap class=\"attribname\">%s %d:</td>\n",
                  loc("Attachment"), index + 1);
         rsprintf(
                 "<td class=\"attribvalue\"><input type=\"file\" size=\"60\" maxlength=\"200\" name=\"attfile\">\n");
         rsprintf(
                 "&nbsp;&nbsp;<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return chkupload();\">\n",
                 loc("Upload"));
         rsprintf("</td></tr>\n");

         // print the holder for dropping attachments
         rsprintf("<tr>\n");
         rsprintf("<td style=\"background: white;\" colspan=2>\n");
         rsprintf("<div id=\"holder\" class=\"holder\">%s</div>", loc("Drop attachments here..."));
         rsprintf("</td></tr>");
      }
   }

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

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

   rsprintf("<tr><td class=\"menuframe\"><span class=\"menu1\">\n");
   rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return chkform(this);\">\n",
            loc("Submit"));

   if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1)
      rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return save_draft();\">\n",
               loc("Save"));

   if (!getcfg(lbs->name, "Show text", str, sizeof(str)) || atoi(str) == 1)
      rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
               loc("Preview"));

   if (!getcfg(lbs->name, "Save drafts", str, sizeof(str)) || atoi(str) == 1)
      rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return check_delete();\">\n",
               loc("Delete"));
   else
      rsprintf("<input type=\"submit\" name=\"cmd\" value=\"%s\" onClick=\"return mark_submitted();\">\n",
               loc("Back"));

   rsprintf(
           "&nbsp;&nbsp;<span id=\"saved2\" style=\"font-size:10px;font-style:italic;display:none\">%s 00:00:00</span>",
           loc("Draft saved at"));

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

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

   /* rescan unconditional attributes */
   if (_condition[0])
      scan_attributes(lbs->name);

   xfree(text);
}

Project: elog

void receive_pwdfile(LOGBOOK *lbs, char *server, char *error_str) {
   char str[256], url[256], pwd[256], *buffer, *buf, *p;
   int i, status, version, fh, ssl;

   error_str[0] = 0;

   do {

      combine_url(lbs, server, "", url, sizeof(url), &ssl);
      strlcpy(str, url, sizeof(str));
      strcat(str, "?cmd=GetPwdFile");   // request password file

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

      /* check version */
      p = strstr(buffer, "ELOG HTTP ");
      if (!p) {
         sprintf(error_str, "Remote server is not an ELOG server");
         xfree(buffer);
         return;
      }
      version = atoi(p + 10) * 100 + atoi(p + 12) * 10 + atoi(p + 14);
      if (version < 254) {
         strlcpy(str, p + 10, 10);
         if (strchr(str, '\r'))
            *strchr(str, '\r') = 0;

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

      /* evaluate status */
      p = strchr(buffer, ' ');
      if (p == NULL) {
         xfree(buffer);
         *strchr(str, '?') = 0;
         sprintf(error_str, "Received invalid response from elogd server at http://%s", str);
         xfree(buffer);
         return;
      }
      p++;
      status = atoi(p);
      if (status != 200 && status != 302 && status != 404) {
         xfree(buffer);
         *strchr(str, '?') = 0;
         sprintf(error_str, "Received invalid response from elogd server at http://%s", str);
         return;
      }

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

      /* check for logbook access */
      if (strstr(p, loc("Please login")) || strstr(p, "GetPwdFile") || status == 302) {

         if (strstr(buffer, "?fail="))
            eprintf("\nInvalid username or password.");

         if (strstr(p, loc("Please login")) == NULL && strstr(p, "GetPwdFile") && isparam("unm"))
            eprintf("\nUser \"%s\" has no admin rights on remote server.", getparam("unm"));

         /* ask for username and password */
         eprintf("\nPlease enter admin username to access\n%s: ", url);
         fgets(str, sizeof(str), stdin);
         while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n')
            str[strlen(str) - 1] = 0;
         setparam("unm", str);

         eprintf("\nPlease enter admin password to access\n%s: ", url);
         read_password(str, sizeof(str));
         eprintf("\n");
         while (str[strlen(str) - 1] == '\r' || str[strlen(str) - 1] == '\n')
            str[strlen(str) - 1] = 0;
         do_crypt(str, pwd, sizeof(pwd));
         setparam("upwd", pwd);
         status = 0;
      }

   } while (status != 200);

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

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

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

   TRUNCATE(fh);

   close(fh);

   xfree(buf);
   xfree(buffer);
}

Project: elog

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

Project: elog

void set_sid_cookie(LOGBOOK *lbs, char *sid, char *full_name) {
   char str[256], lb_name[256], exp[80];
   BOOL global;

   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");
   }

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

   /* get optional expriation from configuration file */
   if (getcfg(lb_name, "Login expiration", str, sizeof(str)) || atof(str) > 0)
      strcpy(exp, str);
   else if (isparam("remember")) {
      strcpy(exp, "744");       /* one month by default = 31*24 */
   } else
      exp[0] = 0;

   /* check if cookies should be global */
   global = getcfg("global", "Password file", str, sizeof(str));

   /* set the session ID cookie */
   set_cookie(lbs, "sid", sid, global, exp);

   /* set the use full name cookie */
   set_cookie(lbs, "ufnm", full_name, global, exp);

   /* set "remember me" cookie on login */
   if (isparam("remember"))
      set_cookie(lbs, "urem", "1", global, "8760");     /* one year = 24*365 */
   else
      set_cookie(lbs, "urem", "0", global, "8760");

   set_redir(lbs, isparam("redir") ? getparam("redir") : "");
}

Project: elog

void xmlencode(char *text) {
   int i;

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

         default:
            rsprintf("%c", text[i]);
      }
   }
}

Project: elog

BOOL logged_in(LOGBOOK *lbs) {
   if (isparam("unm")) {
      if (check_login_user(lbs, getparam("unm")) && check_login(lbs, getparam("sid")))
         return TRUE;
   }
   return FALSE;
}