move all source files to src/
authorWilfried Göesgens <willi@citadel.org>
Sun, 12 Mar 2006 12:00:48 +0000 (12:00 +0000)
committerWilfried Göesgens <willi@citadel.org>
Sun, 12 Mar 2006 12:00:48 +0000 (12:00 +0000)
122 files changed:
webcit/auth.c [deleted file]
webcit/autocompletion.c [deleted file]
webcit/availability.c [deleted file]
webcit/calendar.c [deleted file]
webcit/calendar_tools.c [deleted file]
webcit/calendar_view.c [deleted file]
webcit/context_loop.c [deleted file]
webcit/cookie_conversion.c [deleted file]
webcit/crypto.c [deleted file]
webcit/doxygen_groups.c [deleted file]
webcit/event.c [deleted file]
webcit/floors.c [deleted file]
webcit/fmt_date.c [deleted file]
webcit/gettext.c [deleted file]
webcit/graphics.c [deleted file]
webcit/groupdav.h [deleted file]
webcit/groupdav_delete.c [deleted file]
webcit/groupdav_get.c [deleted file]
webcit/groupdav_main.c [deleted file]
webcit/groupdav_options.c [deleted file]
webcit/groupdav_propfind.c [deleted file]
webcit/groupdav_put.c [deleted file]
webcit/html2html.c [deleted file]
webcit/http_datestring.c [deleted file]
webcit/ical_dezonify.c [deleted file]
webcit/iconbar.c [deleted file]
webcit/inetconf.c [deleted file]
webcit/listsub.c [deleted file]
webcit/locate_host.c [deleted file]
webcit/mainmenu.c [deleted file]
webcit/messages.c [deleted file]
webcit/mime_parser.c [deleted file]
webcit/mime_parser.h [deleted file]
webcit/netconf.c [deleted file]
webcit/notes.c [deleted file]
webcit/paging.c [deleted file]
webcit/preferences.c [deleted file]
webcit/roomops.c [deleted file]
webcit/rss.c [deleted file]
webcit/serv_func.c [deleted file]
webcit/setup.c [deleted file]
webcit/setup_wizard.c [deleted file]
webcit/siteconfig.c [deleted file]
webcit/snprintf.c [deleted file]
webcit/src/auth.c [new file with mode: 0644]
webcit/src/autocompletion.c [new file with mode: 0644]
webcit/src/availability.c [new file with mode: 0644]
webcit/src/calendar.c [new file with mode: 0644]
webcit/src/calendar_tools.c [new file with mode: 0644]
webcit/src/calendar_view.c [new file with mode: 0644]
webcit/src/context_loop.c [new file with mode: 0644]
webcit/src/cookie_conversion.c [new file with mode: 0644]
webcit/src/crypto.c [new file with mode: 0644]
webcit/src/doxygen_groups.c [new file with mode: 0644]
webcit/src/event.c [new file with mode: 0644]
webcit/src/floors.c [new file with mode: 0644]
webcit/src/fmt_date.c [new file with mode: 0644]
webcit/src/gettext.c [new file with mode: 0644]
webcit/src/graphics.c [new file with mode: 0644]
webcit/src/groupdav.h [new file with mode: 0644]
webcit/src/groupdav_delete.c [new file with mode: 0644]
webcit/src/groupdav_get.c [new file with mode: 0644]
webcit/src/groupdav_main.c [new file with mode: 0644]
webcit/src/groupdav_options.c [new file with mode: 0644]
webcit/src/groupdav_propfind.c [new file with mode: 0644]
webcit/src/groupdav_put.c [new file with mode: 0644]
webcit/src/html2html.c [new file with mode: 0644]
webcit/src/http_datestring.c [new file with mode: 0644]
webcit/src/ical_dezonify.c [new file with mode: 0644]
webcit/src/iconbar.c [new file with mode: 0644]
webcit/src/inetconf.c [new file with mode: 0644]
webcit/src/listsub.c [new file with mode: 0644]
webcit/src/locate_host.c [new file with mode: 0644]
webcit/src/mainmenu.c [new file with mode: 0644]
webcit/src/messages.c [new file with mode: 0644]
webcit/src/mime_parser.c [new file with mode: 0644]
webcit/src/mime_parser.h [new file with mode: 0644]
webcit/src/netconf.c [new file with mode: 0644]
webcit/src/notes.c [new file with mode: 0644]
webcit/src/paging.c [new file with mode: 0644]
webcit/src/preferences.c [new file with mode: 0644]
webcit/src/roomops.c [new file with mode: 0644]
webcit/src/rss.c [new file with mode: 0644]
webcit/src/serv_func.c [new file with mode: 0644]
webcit/src/setup.c [new file with mode: 0644]
webcit/src/setup_wizard.c [new file with mode: 0644]
webcit/src/siteconfig.c [new file with mode: 0644]
webcit/src/snprintf.c [new file with mode: 0644]
webcit/src/subst.c [new file with mode: 0644]
webcit/src/summary.c [new file with mode: 0644]
webcit/src/sysmsgs.c [new file with mode: 0644]
webcit/src/tabs.c [new file with mode: 0644]
webcit/src/tcp_sockets.c [new file with mode: 0644]
webcit/src/tools.c [new file with mode: 0644]
webcit/src/useredit.c [new file with mode: 0644]
webcit/src/userlist.c [new file with mode: 0644]
webcit/src/vcard.c [new file with mode: 0644]
webcit/src/vcard.h [new file with mode: 0644]
webcit/src/vcard_edit.c [new file with mode: 0644]
webcit/src/webcit.c [new file with mode: 0644]
webcit/src/webcit.h [new file with mode: 0644]
webcit/src/webserver.c [new file with mode: 0644]
webcit/src/webserver.h [new file with mode: 0644]
webcit/src/who.c [new file with mode: 0644]
webcit/src/wiki.c [new file with mode: 0644]
webcit/subst.c [deleted file]
webcit/summary.c [deleted file]
webcit/sysmsgs.c [deleted file]
webcit/tabs.c [deleted file]
webcit/tcp_sockets.c [deleted file]
webcit/tools.c [deleted file]
webcit/useredit.c [deleted file]
webcit/userlist.c [deleted file]
webcit/vcard.c [deleted file]
webcit/vcard.h [deleted file]
webcit/vcard_edit.c [deleted file]
webcit/webcit.c [deleted file]
webcit/webcit.h [deleted file]
webcit/webserver.c [deleted file]
webcit/webserver.h [deleted file]
webcit/who.c [deleted file]
webcit/wiki.c [deleted file]

diff --git a/webcit/auth.c b/webcit/auth.c
deleted file mode 100644 (file)
index 0f594e1..0000000
+++ /dev/null
@@ -1,562 +0,0 @@
-/*
- * $Id$
- */
-/**
- *
- * \defgroup WebcitAuth WebcitAuth; Handles authentication of users to a Citadel server.
- * \ingroup CitadelConfig
- */
-
-/*@{*/
-#include "webcit.h"
-
-
-
-/**
- * \brief  user states
- * the plain text states of a user. filled in at \ function TODO initialize_ax_defs()
- * due to NLS
- */
-char *axdefs[7]; 
-
-void initialize_axdefs(void) {
-       axdefs[0] = _("Deleted");       /*!0: an erased user */
-       axdefs[1] = _("New User");      /*!1: a new user */
-       axdefs[2] = _("Problem User");  /*!2: a trouble maker */
-       axdefs[3] = _("Local User");    /*!3: user with normal privileges */
-       axdefs[4] = _("Network User");  /*!4: a user that may access network resources */
-       axdefs[5] = _("Preferred User");/*!5: a moderator */
-       axdefs[6] = _("Aide");          /*!6: chief */
-}
-
-
-
-
-/** 
- * \brief Display the login screen
- * \param mesg The error message if last attempt failed.
- */
-void display_login(char *mesg)
-{
-       char buf[SIZ];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div style=\"position:absolute; top:20px; left:20px; right:20px\">\n");
-
-       if (mesg != NULL) if (strlen(mesg) > 0) {
-               stresc(buf, mesg, 0, 0);
-               svprintf("mesg", WCS_STRING, "%s", buf);
-       }
-
-       svprintf("LOGIN_INSTRUCTIONS", WCS_STRING,
-               _("<ul>"
-               "<li><b>If you already have an account on %s</b>, "
-               "enter your user name and password and click &quot;Login.&quot; "
-               "<li><b>If you are a new user</b>, enter the name and password "
-               "you wish to use, "
-               "and click &quot;New User.&quot; "
-               "<li>Please log off properly when finished. "
-               "<li>You must use a browser that supports <i>frames</i> and "
-               "<i>cookies</i>. "
-               "<li>Also keep in mind that if your browser is "
-               "configured to block pop-up windows, you will not be able "
-               "to receive any instant messages.<br />"
-               "</ul>"),
-               serv_info.serv_humannode
-       );
-
-       svprintf("USERNAME_BOX", WCS_STRING, "%s", _("User name:"));
-       svprintf("PASSWORD_BOX", WCS_STRING, "%s", _("Password:"));
-       svprintf("LANGUAGE_BOX", WCS_STRING, "%s", _("Language:"));
-       svprintf("LOGIN_BUTTON", WCS_STRING, "%s", _("Login"));
-       svprintf("NEWUSER_BUTTON", WCS_STRING, "%s", _("New User"));
-       svprintf("EXIT_BUTTON", WCS_STRING, "%s", _("Exit"));
-       svprintf("hello", WCS_SERVCMD, "MESG hello");
-       svprintf("BOXTITLE", WCS_STRING, _("%s - powered by Citadel"),
-               serv_info.serv_humannode);
-       svcallback("DO_LANGUAGE_BOX", offer_languages);
-       if (serv_info.serv_newuser_disabled) {
-               svprintf("NEWUSER_BUTTON_PRE", WCS_STRING, "<div style=\"display:none;\">");
-               svprintf("NEWUSER_BUTTON_POST", WCS_STRING, "</div>");
-       }
-       else {
-               svprintf("NEWUSER_BUTTON_PRE", WCS_STRING, "");
-               svprintf("NEWUSER_BUTTON_POST", WCS_STRING, "");
-       }
-
-       do_template("login");
-
-       wDumpContent(2);
-}
-
-
-
-
-/** \brief Initialize the session
- * This function needs to get called whenever the session changes from
- * not-logged-in to logged-in, either by an explicit login by the user or
- * by a timed-out session automatically re-establishing with a little help
- * from the browser cookie.  Either way, we need to load access controls and
- * preferences from the server.
- *
- * \param user the username
- * \param pass his password
- * \param serv_response The parameters returned from a Citadel USER or NEWU command
- */
-void become_logged_in(char *user, char *pass, char *serv_response)
-{
-       char buf[SIZ];
-
-       WC->logged_in = 1;
-       extract_token(WC->wc_fullname, &serv_response[4], 0, '|', sizeof WC->wc_fullname);
-       safestrncpy(WC->wc_username, user, sizeof WC->wc_username);
-       safestrncpy(WC->wc_password, pass, sizeof WC->wc_password);
-       WC->axlevel = extract_int(&serv_response[4], 1);
-       if (WC->axlevel >= 6) {
-               WC->is_aide = 1;
-       }
-
-       load_preferences();
-
-       serv_puts("CHEK");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               WC->new_mail = extract_int(&buf[4], 0);
-               WC->need_regi = extract_int(&buf[4], 1);
-               WC->need_vali = extract_int(&buf[4], 2);
-               extract_token(WC->cs_inet_email, &buf[4], 3, '|', sizeof WC->cs_inet_email);
-       }
-
-       get_preference("current_iconbar", buf, sizeof buf);
-       WC->current_iconbar = atoi(buf);
-
-       get_preference("floordiv_expanded", WC->floordiv_expanded, sizeof WC->floordiv_expanded);
-}
-
-
-/** 
- * \brief Login Checks
- * the logics to detect invalid passwords not to get on citservers nerves
- */
-void do_login(void)
-{
-       char buf[SIZ];
-
-       if (strlen(bstr("language")) > 0) {
-               set_selected_language(bstr("language"));
-               go_selected_language();
-       }
-
-       if (strlen(bstr("exit_action")) > 0) {
-               do_logout();
-               return;
-       }
-       if (strlen(bstr("login_action")) > 0) {
-               serv_printf("USER %s", bstr("name"));
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '3') {
-                       serv_printf("PASS %s", bstr("pass"));
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] == '2') {
-                               become_logged_in(bstr("name"),
-                                                bstr("pass"), buf);
-                       } else {
-                               display_login(&buf[4]);
-                               return;
-                       }
-               } else {
-                       display_login(&buf[4]);
-                       return;
-               }
-       }
-       if (strlen(bstr("newuser_action")) > 0) {
-               if (strlen(bstr("pass")) == 0) {
-                       display_login(_("Blank passwords are not allowed."));
-                       return;
-               }
-               serv_printf("NEWU %s", bstr("name"));
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       become_logged_in(bstr("name"), bstr("pass"), buf);
-                       serv_printf("SETP %s", bstr("pass"));
-                       serv_getln(buf, sizeof buf);
-               } else {
-                       display_login(&buf[4]);
-                       return;
-               }
-       }
-       if (WC->logged_in) {
-               if (WC->need_regi) {
-                       display_reg(1);
-               } else {
-                       do_welcome();
-               }
-       } else {
-               display_login(_("Your password was not accepted."));
-       }
-
-}
-
-/**
- * \brief display the user a welcome screen. 
- * if this is the first time login, and the web based setup is enabled, 
- * lead the user through the setup routines
- */
-void do_welcome(void)
-{
-       char buf[SIZ];
-#ifdef XXX_NOT_FINISHED_YET_XXX
-       FILE *fp;
-       int i;
-
-       /**
-        * See if we have to run the first-time setup wizard
-        */
-       if (WC->is_aide) {
-               if (!setup_wizard) {
-                       sprintf(wizard_filename, "setupwiz.%s.%s",
-                               ctdlhost, ctdlport);
-                       for (i=0; i<strlen(wizard_filename); ++i) {
-                               if (    (wizard_filename[i]==' ')
-                                       || (wizard_filename[i] == '/')
-                               ) {
-                                       wizard_filename[i] = '_';
-                               }
-                       }
-       
-                       fp = fopen(wizard_filename, "r");
-                       if (fp != NULL) {
-                               fgets(buf, sizeof buf, fp);
-                               buf[strlen(buf)-1] = 0;
-                               fclose(fp);
-                               if (atoi(buf) == serv_info.serv_rev_level) {
-                                       setup_wizard = 1; /**< already run */
-                               }
-                       }
-               }
-
-               if (!setup_wizard) {
-                       http_redirect("setup_wizard");
-               }
-       }
-#endif
-
-       /**
-        * Go to the user's preferred start page
-        */
-       get_preference("startpage", buf, sizeof buf);
-       if (strlen(buf)==0) {
-               safestrncpy(buf, "dotskip&room=_BASEROOM_", sizeof buf);
-               set_preference("startpage", buf, 1);
-       }
-       if (buf[0] == '/') {
-               strcpy(buf, &buf[1]);
-       }
-       http_redirect(buf);
-}
-
-
-/**
- * Disconnect from the Citadel server, and end this WebCit session
- */
-void end_webcit_session(void) {
-       char buf[256];
-
-       if (WC->logged_in) {
-               sprintf(buf, "%d", WC->current_iconbar);
-               set_preference("current_iconbar", buf, 0);
-               set_preference("floordiv_expanded", WC->floordiv_expanded, 1);
-       }
-
-       serv_puts("QUIT");
-       WC->killthis = 1;
-       /* close() of citadel socket will be done by do_housekeeping() */
-}
-
-/** 
- * execute the logout
- */
-void do_logout(void)
-{
-       char buf[SIZ];
-
-       safestrncpy(WC->wc_username, "", sizeof WC->wc_username);
-       safestrncpy(WC->wc_password, "", sizeof WC->wc_password);
-       safestrncpy(WC->wc_roomname, "", sizeof WC->wc_roomname);
-       safestrncpy(WC->wc_fullname, "", sizeof WC->wc_fullname);
-
-       /** Calling output_headers() this way causes the cookies to be un-set */
-       output_headers(1, 1, 0, 1, 0, 0);
-
-       wprintf("<center>");
-       serv_puts("MESG goodbye");
-       serv_getln(buf, sizeof buf);
-
-       if (WC->serv_sock >= 0) {
-               if (buf[0] == '1') {
-                       fmout("CENTER");
-               } else {
-                       wprintf("Goodbye\n");
-               }
-       }
-       else {
-               wprintf(_("This program was unable to connect or stay "
-                       "connected to the Citadel server.  Please report "
-                       "this problem to your system administrator.")
-               );
-       }
-
-       wprintf("<hr /><a href=\".\">");
-       wprintf(_("Log in again"));
-       wprintf("</A>&nbsp;&nbsp;&nbsp;"
-               "<a href=\"javascript:window.close();\">");
-       wprintf(_("Close window"));
-       wprintf("</a></center>\n");
-       wDumpContent(2);
-       end_webcit_session();
-}
-
-
-/* *
- * validate new users
- */
-void validate(void)
-{
-       char cmd[SIZ];
-       char user[SIZ];
-       char buf[SIZ];
-       int a;
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Validate new users"));
-       wprintf("</SPAN></TD></TR></TABLE>\n</div>\n<div id=\"content\">\n");
-
-       /** If the user just submitted a validation, process it... */
-       safestrncpy(buf, bstr("user"), sizeof buf);
-       if (strlen(buf) > 0) {
-               if (strlen(bstr("axlevel")) > 0) {
-                       serv_printf("VALI %s|%s", buf, bstr("axlevel"));
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] != '2') {
-                               wprintf("<b>%s</b><br />\n", &buf[4]);
-                       }
-               }
-       }
-
-       /** Now see if any more users require validation. */
-       serv_puts("GNUR");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               wprintf("<b>");
-               wprintf(_("No users require validation at this time."));
-               wprintf("</b><br />\n");
-               wDumpContent(1);
-               return;
-       }
-       if (buf[0] != '3') {
-               wprintf("<b>%s</b><br />\n", &buf[4]);
-               wDumpContent(1);
-               return;
-       }
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-       wprintf("<center>");
-
-       safestrncpy(user, &buf[4], sizeof user);
-       serv_printf("GREG %s", user);
-       serv_getln(cmd, sizeof cmd);
-       if (cmd[0] == '1') {
-               a = 0;
-               do {
-                       serv_getln(buf, sizeof buf);
-                       ++a;
-                       if (a == 1)
-                               wprintf("#%s<br /><H1>%s</H1>",
-                                       buf, &cmd[4]);
-                       if (a == 2)
-                               wprintf("PW: %s<br />\n", buf);
-                       if (a == 3)
-                               wprintf("%s<br />\n", buf);
-                       if (a == 4)
-                               wprintf("%s<br />\n", buf);
-                       if (a == 5)
-                               wprintf("%s, ", buf);
-                       if (a == 6)
-                               wprintf("%s ", buf);
-                       if (a == 7)
-                               wprintf("%s<br />\n", buf);
-                       if (a == 8)
-                               wprintf("%s<br />\n", buf);
-                       if (a == 9)
-                               wprintf(_("Current access level: %d (%s)\n"),
-                                       atoi(buf), axdefs[atoi(buf)]);
-               } while (strcmp(buf, "000"));
-       } else {
-               wprintf("<H1>%s</H1>%s<br />\n", user, &cmd[4]);
-       }
-
-       wprintf("<hr />");
-       wprintf(_("Select access level for this user:"));
-       wprintf("<br />\n");
-       for (a = 0; a <= 6; ++a) {
-               wprintf("<a href=\"validate&user=");
-               urlescputs(user);
-               wprintf("&axlevel=%d\">%s</A>&nbsp;&nbsp;&nbsp;\n",
-                       a, axdefs[a]);
-       }
-       wprintf("<br />\n");
-
-       wprintf("</CENTER>\n");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-
-
-/** 
- * \brief Display form for registration.
- * (Set during_login to 1 if this registration is being performed during
- * new user login and will require chaining to the proper screen.)
- * \param during_login are we just in the login phase?
- */
-void display_reg(int during_login)
-{
-       long vcard_msgnum;
-
-       if (goto_config_room() != 0) {
-               if (during_login) do_welcome();
-               else display_main_menu();
-               return;
-       }
-
-       vcard_msgnum = locate_user_vcard(WC->wc_fullname, -1);
-       if (vcard_msgnum < 0L) {
-               if (during_login) do_welcome();
-               else display_main_menu();
-               return;
-       }
-
-       if (during_login) {
-               do_edit_vcard(vcard_msgnum, "1", "do_welcome");
-       }
-       else {
-               do_edit_vcard(vcard_msgnum, "1", "display_main_menu");
-       }
-
-}
-
-
-
-
-/** 
- * display form for changing your password
- */
-void display_changepw(void)
-{
-       char buf[SIZ];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Change your password"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       if (strlen(WC->ImportantMessage) > 0) {
-               do_template("beginbox_nt");
-               wprintf("<SPAN CLASS=\"errormsg\">"
-                       "%s</SPAN><br />\n", WC->ImportantMessage);
-               do_template("endbox");
-               safestrncpy(WC->ImportantMessage, "", sizeof WC->ImportantMessage);
-       }
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       wprintf("<CENTER><br />");
-       serv_puts("MESG changepw");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               fmout("CENTER");
-       }
-
-       wprintf("<form name=\"changepwform\" action=\"changepw\" method=\"post\">\n");
-       wprintf("<CENTER>"
-               "<table border=\"0\" cellspacing=\"5\" cellpadding=\"5\" "
-               "BGCOLOR=\"#EEEEEE\">"
-               "<TR><TD>");
-       wprintf(_("Enter new password:"));
-       wprintf("</TD>\n");
-       wprintf("<TD><INPUT TYPE=\"password\" NAME=\"newpass1\" VALUE=\"\" MAXLENGTH=\"20\"></TD></TR>\n");
-       wprintf("<TR><TD>");
-       wprintf(_("Enter it again to confirm:"));
-       wprintf("</TD>\n");
-       wprintf("<TD><INPUT TYPE=\"password\" NAME=\"newpass2\" VALUE=\"\" MAXLENGTH=\"20\"></TD></TR>\n");
-
-       wprintf("</TABLE><br />\n");
-       wprintf("<INPUT type=\"submit\" name=\"change_action\" value=\"%s\">", _("Change password"));
-       wprintf("&nbsp;");
-       wprintf("<INPUT type=\"submit\" name=\"cancel_action\" value=\"%s\">\n", _("Cancel"));
-       wprintf("</form></center>\n");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-/**
- * \brief change password
- * if passwords match, propagate it to citserver.
- */
-void changepw(void)
-{
-       char buf[SIZ];
-       char newpass1[32], newpass2[32];
-
-       if (strlen(bstr("change_action")) == 0) {
-               safestrncpy(WC->ImportantMessage, 
-                       _("Cancelled.  Password was not changed."),
-                       sizeof WC->ImportantMessage);
-               display_main_menu();
-               return;
-       }
-
-       safestrncpy(newpass1, bstr("newpass1"), sizeof newpass1);
-       safestrncpy(newpass2, bstr("newpass2"), sizeof newpass2);
-
-       if (strcasecmp(newpass1, newpass2)) {
-               safestrncpy(WC->ImportantMessage, 
-                       _("They don't match.  Password was not changed."),
-                       sizeof WC->ImportantMessage);
-               display_changepw();
-               return;
-       }
-
-       if (strlen(newpass1) == 0) {
-               safestrncpy(WC->ImportantMessage, 
-                       _("Blank passwords are not allowed."),
-                       sizeof WC->ImportantMessage);
-               display_changepw();
-               return;
-       }
-
-       serv_printf("SETP %s", newpass1);
-       serv_getln(buf, sizeof buf);
-       sprintf(WC->ImportantMessage, "%s", &buf[4]);
-       if (buf[0] == '2') {
-               safestrncpy(WC->wc_password, buf, sizeof WC->wc_password);
-               display_main_menu();
-       }
-       else {
-               display_changepw();
-       }
-}
-
-
-
-/** @} */
diff --git a/webcit/autocompletion.c b/webcit/autocompletion.c
deleted file mode 100644 (file)
index 0b9373e..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * $Id$
- *//**
- * \defgroup AjaxAutoCompletion ajax-powered autocompletion...
- * \ingroup ClientPower
- */
-
-/*@{*/
-#include "webcit.h"
-
-/**
- * \brief Recipient autocompletion results
- * \param partial the account to search for ??????
- */
-void recp_autocomplete(char *partial) {
-       char buf[1024];
-       char name[128];
-
-       output_headers(0, 0, 0, 0, 0, 0);
-
-       wprintf("Content-type: text/html\r\n"
-               "Server: %s\r\n"
-               "Connection: close\r\n"
-               "Pragma: no-cache\r\n"
-               "Cache-Control: no-store\r\n",
-               SERVER);
-       begin_burst();
-
-       wprintf("<ul>");
-
-       serv_printf("AUTO %s", partial);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       extract_token(name, buf, 0, '|', sizeof name);
-                       wprintf("<li>");
-                       escputs(name);
-                       wprintf("</li>");
-               }
-       }
-
-       wprintf("</ul>");
-
-       wprintf("\r\n\r\n");
-       wDumpContent(0);
-}
-
-
-/** @} */
diff --git a/webcit/availability.c b/webcit/availability.c
deleted file mode 100644 (file)
index 0c1198a..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * $Id$
- */
-/**
- *
- * \defgroup CalendarAv  Check attendee availability for scheduling a meeting.
- * \ingroup Calendaring
- */
-/*@{*/
-
-
-#include "webcit.h"
-#include "webserver.h"
-
-/** only available if we have calendaring */
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-
-
-
-/**
- * \brief verify users avaiability
- * Utility function to fetch a VFREEBUSY type of thing for
- * any specified user.
- * \param who string of the user to search
- */
-icalcomponent *get_freebusy_for_user(char *who) {
-       char buf[SIZ];
-       char *serialized_fb = NULL;
-       icalcomponent *fb = NULL;
-
-       serv_printf("ICAL freebusy|%s", who);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               serialized_fb = read_server_text();
-       }
-
-       if (serialized_fb == NULL) {
-               return NULL;
-       }
-       
-       fb = icalcomponent_new_from_string(serialized_fb);
-       free(serialized_fb);
-       if (fb == NULL) {
-               return NULL;
-       }
-
-       return(fb);
-}
-
-
-
-
-/**
- * \brief Check if dates are overlapping
- * Check to see if two events overlap.  
- * (This function is used in both Citadel and WebCit.  If you change it in
- * one place, change it in the other.  Better yet, put it in a library.)
- * \param t1start date one start
- * \param t1end  date one end
- * \param t2start date one start
- * \param t2end date two end
- * \returns nonzero if they do.
- */
-int ical_ctdl_is_overlap(
-                       struct icaltimetype t1start,
-                       struct icaltimetype t1end,
-                       struct icaltimetype t2start,
-                       struct icaltimetype t2end
-) {
-
-       if (icaltime_is_null_time(t1start)) return(0);
-       if (icaltime_is_null_time(t2start)) return(0);
-
-       /** First, check for all-day events */
-       if (t1start.is_date) {
-               if (!icaltime_compare_date_only(t1start, t2start)) {
-                       return(1);
-               }
-               if (!icaltime_is_null_time(t2end)) {
-                       if (!icaltime_compare_date_only(t1start, t2end)) {
-                               return(1);
-                       }
-               }
-       }
-
-       if (t2start.is_date) {
-               if (!icaltime_compare_date_only(t2start, t1start)) {
-                       return(1);
-               }
-               if (!icaltime_is_null_time(t1end)) {
-                       if (!icaltime_compare_date_only(t2start, t1end)) {
-                               return(1);
-                       }
-               }
-       }
-
-       /** Now check for overlaps using date *and* time. */
-
-       /** First, bail out if either event 1 or event 2 is missing end time. */
-       if (icaltime_is_null_time(t1end)) return(0);
-       if (icaltime_is_null_time(t2end)) return(0);
-
-       /** If event 1 ends before event 2 starts, we're in the clear. */
-       if (icaltime_compare(t1end, t2start) <= 0) return(0);
-
-       /** If event 2 ends before event 1 starts, we're also ok. */
-       if (icaltime_compare(t2end, t1start) <= 0) return(0);
-
-       /** Otherwise, they overlap. */
-       return(1);
-}
-
-
-
-/*
- * \brief dig availability on citserver
- * Back end function for check_attendee_availability()
- * This one checks an individual attendee against a supplied
- * event start and end time.  All these fields have already been
- * broken out.  
- * \param attendee_string name of the attendee
- * \param event_start starttime of the event to check
- * \param event_end endtime of the event to check
- * \return The result is placed in 'annotation'.
- */
-void check_individual_attendee(char *attendee_string,
-                               struct icaltimetype event_start,
-                               struct icaltimetype event_end,
-                               char *annotation) {
-
-       icalcomponent *fbc = NULL;
-       icalcomponent *fb = NULL;
-       icalproperty *thisfb = NULL;
-       struct icalperiodtype period;
-
-       /**
-        * Set to 'unknown' right from the beginning.  Unless we learn
-        * something else, that's what we'll go with.
-        */
-       strcpy(annotation, _("availability unknown"));
-
-       fbc = get_freebusy_for_user(attendee_string);
-       if (fbc == NULL) {
-               return;
-       }
-
-       /**
-        * Make sure we're looking at a VFREEBUSY by itself.  What we're probably
-        * looking at initially is a VFREEBUSY encapsulated in a VCALENDAR.
-        */
-       if (icalcomponent_isa(fbc) == ICAL_VCALENDAR_COMPONENT) {
-               fb = icalcomponent_get_first_component(fbc, ICAL_VFREEBUSY_COMPONENT);
-       }
-       else if (icalcomponent_isa(fbc) == ICAL_VFREEBUSY_COMPONENT) {
-               fb = fbc;
-       }
-
-       /** Iterate through all FREEBUSY's looking for conflicts. */
-       if (fb != NULL) {
-
-               strcpy(annotation, _("free"));
-
-               for (thisfb = icalcomponent_get_first_property(fb, ICAL_FREEBUSY_PROPERTY);
-                   thisfb != NULL;
-                   thisfb = icalcomponent_get_next_property(fb, ICAL_FREEBUSY_PROPERTY) ) {
-
-                       /** Do the check */
-                       period = icalproperty_get_freebusy(thisfb);
-                       if (ical_ctdl_is_overlap(period.start, period.end,
-                          event_start, event_end)) {
-                               strcpy(annotation, _("BUSY"));
-                       }
-
-               }
-       }
-
-       icalcomponent_free(fbc);
-}
-
-
-
-
-/**
- * \brief check attendees availability
- * Check the availability of all attendees for an event (when possible)
- * and annotate accordingly.
- * \param vevent the event which should be compared with attendees calendar
- */
-void check_attendee_availability(icalcomponent *vevent) {
-       icalproperty *attendee = NULL;
-       icalproperty *dtstart_p = NULL;
-       icalproperty *dtend_p = NULL;
-       struct icaltimetype dtstart_t;
-       struct icaltimetype dtend_t;
-       char attendee_string[SIZ];
-       char annotated_attendee_string[SIZ];
-       char annotation[SIZ];
-
-       if (vevent == NULL) {
-               return;
-       }
-
-       /**
-        * If we're looking at a fully encapsulated VCALENDAR
-        * rather than a VEVENT component, attempt to use the first
-        * relevant VEVENT subcomponent.  If there is none, the
-        * NULL returned by icalcomponent_get_first_component() will
-        * tell the next iteration of this function to create a
-        * new one.
-        */
-       if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) {
-               check_attendee_availability(
-                       icalcomponent_get_first_component(
-                               vevent, ICAL_VEVENT_COMPONENT
-                       )
-               );
-               return;
-       }
-
-       ical_dezonify(vevent);          /**< Convert everything to UTC */
-
-       /**
-        * Learn the start and end times.
-        */
-       dtstart_p = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY);
-       if (dtstart_p != NULL) dtstart_t = icalproperty_get_dtstart(dtstart_p);
-
-       dtend_p = icalcomponent_get_first_property(vevent, ICAL_DTEND_PROPERTY);
-       if (dtend_p != NULL) dtend_t = icalproperty_get_dtend(dtend_p);
-
-       /**
-        * Iterate through attendees.
-        */
-       for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY);
-           attendee != NULL;
-           attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
-
-               strcpy(attendee_string, icalproperty_get_attendee(attendee));
-               if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
-
-                       /** screen name or email address */
-                       strcpy(attendee_string, &attendee_string[7]);
-                       striplt(attendee_string);
-
-                       check_individual_attendee(attendee_string,
-                                               dtstart_t, dtend_t,
-                                               annotation);
-
-                       /** Replace the attendee name with an annotated one. */
-                       snprintf(annotated_attendee_string, sizeof annotated_attendee_string,
-                               "MAILTO:%s (%s)", attendee_string, annotation);
-                       icalproperty_set_attendee(attendee, annotated_attendee_string);
-
-               }
-       }
-
-}
-
-
-#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
-
-/** @} */
diff --git a/webcit/calendar.c b/webcit/calendar.c
deleted file mode 100644 (file)
index 5353dfa..0000000
+++ /dev/null
@@ -1,1008 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup calav Functions which handle calendar objects and their processing/display.
- * \ingroup Calendaring
- */
-/* @{ */
-
-#include "webcit.h"
-#include "webserver.h"
-
-#ifndef WEBCIT_WITH_CALENDAR_SERVICE
-
-/**
- * \brief get around non existing types
- * Handler stubs for builds with no calendar library available
- * \param part_source dummy pointer to the source
- * \param msgnum number of the mesage in the db
- * \param cal_partnum number of the calendar part
- */
-void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) {
-
-       wprintf(_("<I>This message contains calendaring/scheduling information,"
-               " but support for calendars is not available on this "
-               "particular system.  Please ask your system administrator to "
-               "install a new version of the Citadel web service with "
-               "calendaring enabled.</I><br />\n")
-       );
-
-}
-
-/**
- * \brief say we can't display calendar items
- * \param msgnum number of the mesage in our db
- */
-void display_calendar(long msgnum) {
-       wprintf(_("<i>"
-               "Cannot display calendar item.  You are seeing this error "
-               "because your WebCit service has not been installed with "
-               "calendar support.  Please contact your system administrator."
-               "</i><br />\n"));
-}
-
-/**
- * \brief say we can't display task items
- * \param msgnum number of the mesage in our db
- */
-void display_task(long msgnum) {
-       wprintf(_("<i>"
-               "Cannot display to-do item.  You are seeing this error "
-               "because your WebCit service has not been installed with "
-               "calendar support.  Please contact your system administrator."
-               "</i><br />\n"));
-}
-/** ok, we have calendaring available */
-#else /* WEBCIT_WITH_CALENDAR_SERVICE */
-
-
-/******   End of handler stubs.  Everything below this line is real.   ******/
-
-
-
-
-/**
- * \brief Process a calendar object
- * ...at this point it's already been deserialized by cal_process_attachment()
- * \param cal teh calendar object
- * \param recursion_level call stack depth ??????
- * \param msgnum number of the mesage in our db
- * \param cal_partnum of the calendar object ???? 
- */
-void cal_process_object(icalcomponent *cal,
-                       int recursion_level,
-                       long msgnum,
-                       char *cal_partnum
-) {
-       icalcomponent *c;
-       icalproperty *method = NULL;
-       icalproperty_method the_method = ICAL_METHOD_NONE;
-       icalproperty *p;
-       struct icaltimetype t;
-       time_t tt;
-       char buf[256];
-       char conflict_name[256];
-       char conflict_message[256];
-       int is_update = 0;
-
-       /** Leading HTML for the display of this object */
-       if (recursion_level == 0) {
-               wprintf("<CENTER><TABLE border=0>\n");
-       }
-
-       /** Look for a method */
-       method = icalcomponent_get_first_property(cal, ICAL_METHOD_PROPERTY);
-
-       /** See what we need to do with this */
-       if (method != NULL) {
-               the_method = icalproperty_get_method(method);
-               switch(the_method) {
-                   case ICAL_METHOD_REQUEST:
-                       wprintf("<tr><td colspan=\"2\">\n"
-                               "<img align=\"center\" "
-                               "src=\"static/calarea_48x.gif\">"
-                               "&nbsp;&nbsp;"  
-                               "<B>");
-                       wprintf(_("Meeting invitation"));
-                       wprintf("</B></TD></TR>\n");
-                       break;
-                   case ICAL_METHOD_REPLY:
-                       wprintf("<TR><TD COLSPAN=2>\n"
-                               "<IMG ALIGN=CENTER "
-                               "src=\"static/calarea_48x.gif\">"
-                               "&nbsp;&nbsp;"  
-                               "<B>");
-                       wprintf(_("Attendee's reply to your invitation"));
-                       wprintf("</B></TD></TR>\n");
-                       break;
-                   case ICAL_METHOD_PUBLISH:
-                       wprintf("<TR><TD COLSPAN=2>\n"
-                               "<IMG ALIGN=CENTER "
-                               "src=\"static/calarea_48x.gif\">"
-                               "&nbsp;&nbsp;"  
-                               "<B>");
-                       wprintf(_("Published event"));
-                       wprintf("</B></TD></TR>\n");
-                       break;
-                   default:
-                       wprintf("<TR><TD COLSPAN=2>");
-                       wprintf(_("This is an unknown type of calendar item."));
-                       wprintf("</TD></TR>\n");
-                       break;
-               }
-       }
-
-       p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY);
-       if (p != NULL) {
-               wprintf("<TR><TD><B>");
-               wprintf(_("Summary:"));
-               wprintf("</B></TD><TD>");
-               escputs((char *)icalproperty_get_comment(p));
-               wprintf("</TD></TR>\n");
-       }
-
-       p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY);
-       if (p != NULL) {
-               wprintf("<TR><TD><B>");
-               wprintf(_("Location:"));
-               wprintf("</B></TD><TD>");
-               escputs((char *)icalproperty_get_comment(p));
-               wprintf("</TD></TR>\n");
-       }
-
-       /**
-        * Only show start/end times if we're actually looking at the VEVENT
-        * component.  Otherwise it shows bogus dates for things like timezone.
-        */
-       if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) {
-
-               p = icalcomponent_get_first_property(cal,
-                                               ICAL_DTSTART_PROPERTY);
-               if (p != NULL) {
-                       t = icalproperty_get_dtstart(p);
-
-                       if (t.is_date) {
-                               struct tm d_tm;
-                               char d_str[32];
-                               memset(&d_tm, 0, sizeof d_tm);
-                               d_tm.tm_year = t.year - 1900;
-                               d_tm.tm_mon = t.month - 1;
-                               d_tm.tm_mday = t.day;
-                               wc_strftime(d_str, sizeof d_str, "%x", &d_tm);
-                               wprintf("<TR><TD><B>");
-                               wprintf(_("Date:"));
-                               wprintf("</B></TD><TD>%s</TD></TR>", d_str);
-                       }
-                       else {
-                               tt = icaltime_as_timet(t);
-                               fmt_date(buf, tt, 0);
-                               wprintf("<TR><TD><B>");
-                               wprintf(_("Starting date/time:"));
-                               wprintf("</B></TD><TD>%s</TD></TR>", buf);
-                       }
-               }
-       
-               p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
-               if (p != NULL) {
-                       t = icalproperty_get_dtend(p);
-                       tt = icaltime_as_timet(t);
-                       fmt_date(buf, tt, 0);
-                       wprintf("<TR><TD><B>");
-                       wprintf(_("Ending date/time:"));
-                       wprintf("</B></TD><TD>%s</TD></TR>", buf);
-               }
-
-       }
-
-       p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY);
-       if (p != NULL) {
-               wprintf("<TR><TD><B>");
-               wprintf(_("Description:"));
-               wprintf("</B></TD><TD>");
-               escputs((char *)icalproperty_get_comment(p));
-               wprintf("</TD></TR>\n");
-       }
-
-       /** If the component has attendees, iterate through them. */
-       for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY); (p != NULL); p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) {
-               wprintf("<TR><TD><B>");
-               wprintf(_("Attendee:"));
-               wprintf("</B></TD><TD>");
-               safestrncpy(buf, icalproperty_get_attendee(p), sizeof buf);
-               if (!strncasecmp(buf, "MAILTO:", 7)) {
-
-                       /** screen name or email address */
-                       strcpy(buf, &buf[7]);
-                       striplt(buf);
-                       escputs(buf);
-                       wprintf(" ");
-
-                       /** participant status */
-                       partstat_as_string(buf, p);
-                       escputs(buf);
-               }
-               wprintf("</TD></TR>\n");
-       }
-
-       /** If the component has subcomponents, recurse through them. */
-       for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
-           (c != 0);
-           c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
-               /* Recursively process subcomponent */
-               cal_process_object(c, recursion_level+1, msgnum, cal_partnum);
-       }
-
-       /** If this is a REQUEST, display conflicts and buttons */
-       if (the_method == ICAL_METHOD_REQUEST) {
-
-               /* Check for conflicts */
-               lprintf(9, "Checking server calendar for conflicts...\n");
-               serv_printf("ICAL conflicts|%ld|%s|", msgnum, cal_partnum);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '1') {
-                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                               extract_token(conflict_name, buf, 3, '|', sizeof conflict_name);
-                               is_update = extract_int(buf, 4);
-
-                               if (is_update) {
-                                       snprintf(conflict_message, sizeof conflict_message,
-                                               _("This is an update of '%s' which is already in your calendar."), conflict_name);
-                               }
-                               else {
-                                       snprintf(conflict_message, sizeof conflict_message,
-                                               _("This event would conflict with '%s' which is already in your calendar."), conflict_name);
-                               }
-
-                               wprintf("<TR><TD><B><I>%s</I></B></TD><td>",
-                                       (is_update ?
-                                               _("Update:") :
-                                               _("CONFLICT:")
-                                       )
-                               );
-                               escputs(conflict_message);
-                               wprintf("</TD></TR>\n");
-                       }
-               }
-               lprintf(9, "...done.\n");
-
-               /** Display the Accept/Decline buttons */
-               wprintf("<tr><td>%s</td>"
-                       "<td><font size=+1>"
-                       "<a href=\"respond_to_request?msgnum=%ld&cal_partnum=%s&sc=Accept\">%s</a>"
-                       " | "
-                       "<a href=\"respond_to_request?msgnum=%ld&cal_partnum=%s&sc=Tentative\">%s</a>"
-                       " | "
-                       "<a href=\"respond_to_request?msgnum=%ld&cal_partnum=%s&sc=Decline\">%s</a>"
-                       "</FONT></TD></TR>\n",
-                       _("How would you like to respond to this invitation?"),
-                       msgnum, cal_partnum, _("Accept"),
-                       msgnum, cal_partnum, _("Tentative"),
-                       msgnum, cal_partnum, _("Decline")
-               );
-
-       }
-
-       /** If this is a REPLY, display update button */
-       if (the_method == ICAL_METHOD_REPLY) {
-
-               /** \todo  In the future, if we want to validate this object before \
-                * continuing, we can do it this way:
-               serv_printf("ICAL whatever|%ld|%s|", msgnum, cal_partnum);
-               serv_getln(buf, sizeof buf);
-               }
-                ***********/
-
-               /** Display the update buttons */
-               wprintf("<TR><TD>"
-                       "%s"
-                       "</td><td><font size=+1>"
-                       "<a href=\"handle_rsvp?msgnum=%ld&cal_partnum=%s&sc=Update\">%s</a>"
-                       " | "
-                       "<a href=\"handle_rsvp?msgnum=%ld&cal_partnum=%s&sc=Ignore\">%s</a>"
-                       "</font>"
-                       "</TD></TR>\n",
-                       _("Click <i>Update</i> to accept this reply and update your calendar."),
-                       msgnum, cal_partnum, _("Update"),
-                       msgnum, cal_partnum, _("Ignore")
-               );
-
-       }
-
-       /** Trailing HTML for the display of this object */
-       if (recursion_level == 0) {
-
-               wprintf("</TR></TABLE></CENTER>\n");
-       }
-}
-
-
-/**
- * \brief process calendar mail atachment
- * Deserialize a calendar object in a message so it can be processed.
- * (This is the main entry point for these things)
- * \param part_source the part of the message we want to parse
- * \param msgnum number of the mesage in our db
- * \param cal_partnum the number of the calendar item
- */
-void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) {
-       icalcomponent *cal;
-
-       cal = icalcomponent_new_from_string(part_source);
-
-       if (cal == NULL) {
-               wprintf(_("There was an error parsing this calendar item."));
-               wprintf("<br />\n");
-               return;
-       }
-
-       ical_dezonify(cal);
-       cal_process_object(cal, 0, msgnum, cal_partnum);
-
-       /* Free the memory we obtained from libical's constructor */
-       icalcomponent_free(cal);
-}
-
-
-
-
-/**
- * \brief accept/decline meeting
- * Respond to a meeting request
- */
-void respond_to_request(void) {
-       char buf[SIZ];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Respond to meeting request"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-       );
-       wprintf("</div>\n<div id=\"content\">\n");
-
-       serv_printf("ICAL respond|%s|%s|%s|",
-               bstr("msgnum"),
-               bstr("cal_partnum"),
-               bstr("sc")
-       );
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] == '2') {
-               wprintf("<TABLE BORDER=0><TR><TD>"
-                       "<img src=\"static/calarea_48x.gif\" ALIGN=CENTER>"
-                       "</TD><TD>"
-               );
-               if (!strcasecmp(bstr("sc"), "accept")) {
-                       wprintf(_("You have accepted this meeting invitation.  "
-                               "It has been entered into your calendar.")
-                       );
-               } else if (!strcasecmp(bstr("sc"), "tentative")) {
-                       wprintf(_("You have tentatively accepted this meeting invitation.  "
-                               "It has been 'pencilled in' to your calendar.")
-                       );
-               } else if (!strcasecmp(bstr("sc"), "decline")) {
-                       wprintf(_("You have declined this meeting invitation.  "
-                               "It has <b>not</b> been entered into your calendar.")
-                       );
-               }
-               wprintf(" ");
-               wprintf(_("A reply has been sent to the meeting organizer."));
-               wprintf("</TD></TR></TABLE>\n");
-       } else {
-               wprintf("<img src=\"static/error.gif\" ALIGN=CENTER>"
-                       "%s\n", &buf[4]);
-       }
-
-       wprintf("<a href=\"dotskip?room=");
-       urlescputs(WC->wc_roomname);
-       wprintf("\"><br />");
-       wprintf(_("Return to messages"));
-       wprintf("</A><br />\n");
-
-       wDumpContent(1);
-}
-
-
-
-/**
- * \brief Handle an incoming RSVP
- */
-void handle_rsvp(void) {
-       char buf[SIZ];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Update your calendar with this RSVP"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       serv_printf("ICAL handle_rsvp|%s|%s|%s|",
-               bstr("msgnum"),
-               bstr("cal_partnum"),
-               bstr("sc")
-       );
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] == '2') {
-               wprintf("<TABLE BORDER=0><TR><TD>"
-                       "<img src=\"static/calarea_48x.gif\" ALIGN=CENTER>"
-                       "</TD><TD>"
-               );
-               if (!strcasecmp(bstr("sc"), "update")) {
-                       wprintf(_("Your calendar has been updated to reflect this RSVP."));
-               } else if (!strcasecmp(bstr("sc"), "ignore")) {
-                       wprintf(_("You have chosen to ignore this RSVP. "
-                               "Your calendar has <b>not</b> been updated.")
-                       );
-               }
-               wprintf("</TD></TR></TABLE>\n"
-               );
-       } else {
-               wprintf("<img src=\"static/error.gif\" ALIGN=CENTER>"
-                       "%s\n", &buf[4]);
-       }
-
-       wprintf("<a href=\"dotskip?room=");
-       urlescputs(WC->wc_roomname);
-       wprintf("\"><br />");
-       wprintf(_("Return to messages"));
-       wprintf("</A><br />\n");
-
-       wDumpContent(1);
-}
-
-
-
-/*@}*/
-/*-----------------------------------------------------------------------**/
-
-
-
-/**
- * \defgroup MsgDisplayHandlers Display handlers for message reading 
- * \ingroup Calendaring
- */
-
-/*@{*/
-
-
-
-/**
- * \brief get items, keep them.
- * If we're reading calendar items, just store them for now.  We have to
- * sort and re-output them later when we draw the calendar.
- * \param cal Our calendar to process
- * \param msgnum number of the mesage in our db
- */
-void display_individual_cal(icalcomponent *cal, long msgnum) {
-
-       WC->num_cal += 1;
-
-       WC->disp_cal = realloc(WC->disp_cal,
-                       (sizeof(struct disp_cal) * WC->num_cal) );
-       WC->disp_cal[WC->num_cal - 1].cal = icalcomponent_new_clone(cal);
-
-       WC->disp_cal[WC->num_cal - 1].cal_msgnum = msgnum;
-}
-
-
-
-/*
- * \brief edit a task
- * Display a task by itself (for editing)
- * \param supplied_vtodo the todo item we want to edit
- * \param msgnum number of the mesage in our db
- */
-void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
-       icalcomponent *vtodo;
-       icalproperty *p;
-       struct icaltimetype t;
-       time_t now;
-       int created_new_vtodo = 0;
-
-       now = time(NULL);
-
-       if (supplied_vtodo != NULL) {
-               vtodo = supplied_vtodo;
-
-               /**
-                * If we're looking at a fully encapsulated VCALENDAR
-                * rather than a VTODO component, attempt to use the first
-                * relevant VTODO subcomponent.  If there is none, the
-                * NULL returned by icalcomponent_get_first_component() will
-                * tell the next iteration of this function to create a
-                * new one.
-                */
-               if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
-                       display_edit_individual_task(
-                               icalcomponent_get_first_component(
-                                       vtodo, ICAL_VTODO_COMPONENT
-                               ), msgnum
-                       );
-                       return;
-               }
-       }
-       else {
-               vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
-               created_new_vtodo = 1;
-       }
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR>"
-               "<TD><img src=\"static/taskmanag_48x.gif\"></TD>"
-               "<td><SPAN CLASS=\"titlebar\">");
-       wprintf(_("Edit task"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>");
-       
-       wprintf("<FORM METHOD=\"POST\" action=\"save_task\">\n");
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
-               msgnum);
-
-       wprintf("<TABLE border=0>\n");
-
-       wprintf("<TR><TD>");
-       wprintf(_("Summary:"));
-       wprintf("</TD><TD>"
-               "<INPUT TYPE=\"text\" NAME=\"summary\" "
-               "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
-       p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
-       if (p != NULL) {
-               escputs((char *)icalproperty_get_comment(p));
-       }
-       wprintf("\"></TD></TR>\n");
-
-       wprintf("<TR><TD>");
-       wprintf(_("Start date:"));
-       wprintf("</TD><TD>");
-       p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY);
-       if (p != NULL) {
-               t = icalproperty_get_dtstart(p);
-       }
-       else {
-               t = icaltime_from_timet(now, 0);
-       }
-       display_icaltimetype_as_webform(&t, "dtstart");
-       wprintf("</TD></TR>\n");
-
-       wprintf("<TR><TD>");
-       wprintf(_("Due date:"));
-       wprintf("</TD><TD>");
-       p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
-       if (p != NULL) {
-               t = icalproperty_get_due(p);
-       }
-       else {
-               t = icaltime_from_timet(now, 0);
-       }
-       display_icaltimetype_as_webform(&t, "due");
-       wprintf("</TD></TR>\n");
-       wprintf("<TR><TD>");
-       wprintf(_("Description:"));
-       wprintf("</TD><TD>");
-       wprintf("<TEXTAREA NAME=\"description\" wrap=soft "
-               "ROWS=10 COLS=80 WIDTH=80>\n"
-       );
-       p = icalcomponent_get_first_property(vtodo, ICAL_DESCRIPTION_PROPERTY);
-       if (p != NULL) {
-               escputs((char *)icalproperty_get_comment(p));
-       }
-       wprintf("</TEXTAREA></TD></TR></TABLE>\n");
-
-       wprintf("<CENTER>"
-               "<INPUT TYPE=\"submit\" NAME=\"save_button\" VALUE=\"%s\">"
-               "&nbsp;&nbsp;"
-               "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
-               "&nbsp;&nbsp;"
-               "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">\n"
-               "</CENTER>\n",
-               _("Save"),
-               _("Delete"),
-               _("Cancel")
-       );
-
-       wprintf("</FORM>\n");
-
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-
-       if (created_new_vtodo) {
-               icalcomponent_free(vtodo);
-       }
-}
-
-/*
- * \brief Save an edited task
- * \param supplied_vtodo the task to save
- * \param msgnum number of the mesage in our db
- */
-void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
-       char buf[SIZ];
-       int delete_existing = 0;
-       icalproperty *prop;
-       icalcomponent *vtodo, *encaps;
-       int created_new_vtodo = 0;
-       int i;
-       int sequence = 0;
-       struct icaltimetype t;
-
-       if (supplied_vtodo != NULL) {
-               vtodo = supplied_vtodo;
-               /**
-                * If we're looking at a fully encapsulated VCALENDAR
-                * rather than a VTODO component, attempt to use the first
-                * relevant VTODO subcomponent.  If there is none, the
-                * NULL returned by icalcomponent_get_first_component() will
-                * tell the next iteration of this function to create a
-                * new one.
-                */
-               if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
-                       save_individual_task(
-                               icalcomponent_get_first_component(
-                                       vtodo, ICAL_VTODO_COMPONENT
-                               ), msgnum
-                       );
-                       return;
-               }
-       }
-       else {
-               vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
-               created_new_vtodo = 1;
-       }
-
-       if (strlen(bstr("save_button")) > 0) {
-
-               /** Replace values in the component with ones from the form */
-
-               while (prop = icalcomponent_get_first_property(vtodo,
-                     ICAL_SUMMARY_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vtodo, prop);
-                       icalproperty_free(prop);
-               }
-               icalcomponent_add_property(vtodo,
-                       icalproperty_new_summary(bstr("summary")));
-               
-               while (prop = icalcomponent_get_first_property(vtodo,
-                     ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vtodo, prop);
-                       icalproperty_free(prop);
-               }
-               icalcomponent_add_property(vtodo,
-                       icalproperty_new_description(bstr("description")));
-       
-               while (prop = icalcomponent_get_first_property(vtodo,
-                     ICAL_DTSTART_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vtodo, prop);
-                       icalproperty_free(prop);
-               }
-               icaltime_from_webform(&t, "dtstart");
-               icalcomponent_add_property(vtodo,
-                       icalproperty_new_dtstart(t)
-               );
-       
-               while (prop = icalcomponent_get_first_property(vtodo,
-                     ICAL_DUE_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vtodo, prop);
-                       icalproperty_free(prop);
-               }
-               icaltime_from_webform(&t, "due");
-               icalcomponent_add_property(vtodo,
-                       icalproperty_new_due(t)
-               );
-
-               /** Give this task a UID if it doesn't have one. */
-               lprintf(9, "Give this task a UID if it doesn't have one.\n");
-               if (icalcomponent_get_first_property(vtodo,
-                  ICAL_UID_PROPERTY) == NULL) {
-                       generate_uuid(buf);
-                       icalcomponent_add_property(vtodo,
-                               icalproperty_new_uid(buf)
-                       );
-               }
-
-               /** Increment the sequence ID */
-               lprintf(9, "Increment the sequence ID\n");
-               while (prop = icalcomponent_get_first_property(vtodo,
-                     ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
-                       i = icalproperty_get_sequence(prop);
-                       lprintf(9, "Sequence was %d\n", i);
-                       if (i > sequence) sequence = i;
-                       icalcomponent_remove_property(vtodo, prop);
-                       icalproperty_free(prop);
-               }
-               ++sequence;
-               lprintf(9, "New sequence is %d.  Adding...\n", sequence);
-               icalcomponent_add_property(vtodo,
-                       icalproperty_new_sequence(sequence)
-               );
-
-               /**
-                * Encapsulate event into full VCALENDAR component.  Clone it first,
-                * for two reasons: one, it's easier to just free the whole thing
-                * when we're done instead of unbundling, but more importantly, we
-                * can't encapsulate something that may already be encapsulated
-                * somewhere else.
-                */
-               lprintf(9, "Encapsulating into full VCALENDAR component\n");
-               encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo));
-
-               /* Serialize it and save it to the message base */
-               serv_puts("ENT0 1|||4");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '4') {
-                       serv_puts("Content-type: text/calendar");
-                       serv_puts("");
-                       serv_puts(icalcomponent_as_ical_string(encaps));
-                       serv_puts("000");
-
-                       /**
-                        * Probably not necessary; the server will see the UID
-                        * of the object and delete the old one anyway, but
-                        * just in case...
-                        */
-                       delete_existing = 1;
-               }
-               icalcomponent_free(encaps);
-       }
-
-       /**
-        * If the user clicked 'Delete' then explicitly delete the message.
-        */
-       if (strlen(bstr("delete_button")) > 0) {
-               delete_existing = 1;
-       }
-
-       if ( (delete_existing) && (msgnum > 0L) ) {
-               serv_printf("DELE %ld", atol(bstr("msgnum")));
-               serv_getln(buf, sizeof buf);
-       }
-
-       if (created_new_vtodo) {
-               icalcomponent_free(vtodo);
-       }
-
-       /** Go back to the task list */
-       readloop("readfwd");
-}
-
-
-
-/**
- * \brief generic item handler
- * Code common to all display handlers.  Given a message number and a MIME
- * type, we load the message and hunt for that MIME type.  If found, we load
- * the relevant part, deserialize it into a libical component, filter it for
- * the requested object type, and feed it to the specified handler.
- * \param mimetype mimetyp of our object
- * \param which_kind sort of ical type
- * \param msgnum number of the mesage in our db
- * \param callback a funcion \todo
- *
- */
-void display_using_handler(long msgnum,
-                       char *mimetype,
-                       icalcomponent_kind which_kind,
-                       void (*callback)(icalcomponent *, long)
-       ) {
-       char buf[SIZ];
-       char mime_partnum[SIZ];
-       char mime_filename[SIZ];
-       char mime_content_type[SIZ];
-       char mime_disposition[SIZ];
-       int mime_length;
-       char relevant_partnum[SIZ];
-       char *relevant_source = NULL;
-       icalcomponent *cal, *c;
-
-       sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
-       serv_puts(buf);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') return;
-
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               if (!strncasecmp(buf, "part=", 5)) {
-                       extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
-                       extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
-                       extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
-                       extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
-                       mime_length = extract_int(&buf[5], 5);
-
-                       if (!strcasecmp(mime_content_type, "text/calendar")) {
-                               strcpy(relevant_partnum, mime_partnum);
-                       }
-
-               }
-       }
-
-       if (strlen(relevant_partnum) > 0) {
-               relevant_source = load_mimepart(msgnum, relevant_partnum);
-               if (relevant_source != NULL) {
-
-                       cal = icalcomponent_new_from_string(relevant_source);
-                       if (cal != NULL) {
-
-                               ical_dezonify(cal);
-
-                               /** Simple components of desired type */
-                               if (icalcomponent_isa(cal) == which_kind) {
-                                       callback(cal, msgnum);
-                               }
-
-                               /** Subcomponents of desired type */
-                               for (c = icalcomponent_get_first_component(cal,
-                                   which_kind);
-                                   (c != 0);
-                                   c = icalcomponent_get_next_component(cal,
-                                   which_kind)) {
-                                       callback(c, msgnum);
-                               }
-                               icalcomponent_free(cal);
-                       }
-                       free(relevant_source);
-               }
-       }
-
-}
-
-/**
- * \brief display whole calendar
- * \param msgnum number of the mesage in our db
- */
-void display_calendar(long msgnum) {
-       display_using_handler(msgnum, "text/calendar",
-                               ICAL_VEVENT_COMPONENT,
-                               display_individual_cal);
-}
-
-/**
- * \brief display whole taksview
- * \param msgnum number of the mesage in our db
- */
-void display_task(long msgnum) {
-       display_using_handler(msgnum, "text/calendar",
-                               ICAL_VTODO_COMPONENT,
-                               display_individual_cal);
-}
-
-/**
- * \brief display the editor component for a task
- */
-void display_edit_task(void) {
-       long msgnum = 0L;
-
-       /** Force change the room if we have to */
-       if (strlen(bstr("taskrm")) > 0) {
-               gotoroom(bstr("taskrm"));
-       }
-
-       msgnum = atol(bstr("msgnum"));
-       if (msgnum > 0L) {
-               /** existing task */
-               display_using_handler(msgnum, "text/calendar",
-                               ICAL_VTODO_COMPONENT,
-                               display_edit_individual_task);
-       }
-       else {
-               /** new task */
-               display_edit_individual_task(NULL, 0L);
-       }
-}
-
-/**
- *\brief save an edited task
- */
-void save_task(void) {
-       long msgnum = 0L;
-
-       msgnum = atol(bstr("msgnum"));
-       if (msgnum > 0L) {
-               display_using_handler(msgnum, "text/calendar",
-                               ICAL_VTODO_COMPONENT,
-                               save_individual_task);
-       }
-       else {
-               save_individual_task(NULL, 0L);
-       }
-}
-
-/**
- * \brief display the editor component for an event
- */
-void display_edit_event(void) {
-       long msgnum = 0L;
-
-       msgnum = atol(bstr("msgnum"));
-       if (msgnum > 0L) {
-               /* existing event */
-               display_using_handler(msgnum, "text/calendar",
-                               ICAL_VEVENT_COMPONENT,
-                               display_edit_individual_event);
-       }
-       else {
-               /* new event */
-               display_edit_individual_event(NULL, 0L);
-       }
-}
-
-/**
- * \brief save an edited event
- */
-void save_event(void) {
-       long msgnum = 0L;
-
-       msgnum = atol(bstr("msgnum"));
-
-       if (msgnum > 0L) {
-               display_using_handler(msgnum, "text/calendar",
-                               ICAL_VEVENT_COMPONENT,
-                               save_individual_event);
-       }
-       else {
-               save_individual_event(NULL, 0L);
-       }
-}
-
-
-
-
-
-/**
- * \brief freebusy display (for client software)
- * \param req dunno. ?????
- */
-void do_freebusy(char *req) {
-       char who[SIZ];
-       char buf[SIZ];
-       char *fb;
-
-       extract_token(who, req, 1, ' ', sizeof who);
-       if (!strncasecmp(who, "/freebusy/", 10)) {
-               strcpy(who, &who[10]);
-       }
-       unescape_input(who);
-
-       if ( (!strcasecmp(&who[strlen(who)-4], ".vcf"))
-          || (!strcasecmp(&who[strlen(who)-4], ".ifb"))
-          || (!strcasecmp(&who[strlen(who)-4], ".vfb")) ) {
-               who[strlen(who)-4] = 0;
-       }
-
-       lprintf(9, "freebusy requested for <%s>\n", who);
-       serv_printf("ICAL freebusy|%s", who);
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] != '1') {
-               wprintf("HTTP/1.1 404 %s\n", &buf[4]);
-               output_headers(0, 0, 0, 0, 0, 0);
-               wprintf("Content-Type: text/plain\r\n");
-               wprintf("\r\n");
-               wprintf("%s\n", &buf[4]);
-               return;
-       }
-
-       fb = read_server_text();
-       http_transmit_thing(fb, strlen(fb), "text/calendar", 0);
-       free(fb);
-}
-
-
-
-#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
-
-
-/*@}*/
diff --git a/webcit/calendar_tools.c b/webcit/calendar_tools.c
deleted file mode 100644 (file)
index d62cb82..0000000
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup MiscCal Miscellaneous functions which handle calendar components.
- * \ingroup Calendaring
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-
-/** Hour strings */
-char *hourname[] = {
-       "12am", "1am", "2am", "3am", "4am", "5am", "6am",
-       "7am", "8am", "9am", "10am", "11am", "12pm",
-       "1pm", "2pm", "3pm", "4pm", "5pm", "6pm",
-       "7pm", "8pm", "9pm", "10pm", "11pm"
-};
-
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-
-/**
- * \brief display and edit date/time
- * The display_icaltimetype_as_webform() and icaltime_from_webform() functions
- * handle the display and editing of date/time properties in web pages.  The
- * first one converts an icaltimetype into valid HTML markup -- a series of form
- * fields for editing the date and time.  When the user submits the form, the
- * results can be fed back into the second function, which turns it back into
- * an icaltimetype.  The "prefix" string required by both functions is prepended
- * to all field names.  This allows a form to contain more than one date/time
- * property (for example, a start and end time) by ensuring the field names are
- * unique within the form.
- *
- * \todo NOTE: These functions assume that the icaltimetype being edited is in UTC, and
- * will convert to/from local time for editing.  "local" in this case is assumed
- * to be the time zone in which the WebCit server is running.  A future improvement
- * might be to allow the user to specify his/her timezone.
- * \param t the time we want to parse
- * \param prefix ???? \todo
- */
-
-
-void display_icaltimetype_as_webform(struct icaltimetype *t, char *prefix) {
-       int i;
-       time_t now;
-       struct tm tm_now;
-       int this_year;
-       time_t tt;
-       struct tm tm;
-       const int span = 10;
-       int all_day_event = 0;
-       time_t monthselect_time;
-       struct tm monthselect_tm;
-       char monthselect_str[32];
-       char calhourformat[16];
-
-       get_preference("calhourformat", calhourformat, sizeof calhourformat);
-
-       now = time(NULL);
-       localtime_r(&now, &tm_now);
-       this_year = tm_now.tm_year + 1900;
-
-       if (t == NULL) return;
-       if (t->is_date) all_day_event = 1;
-       tt = icaltime_as_timet(*t);
-       if (all_day_event) {
-               gmtime_r(&tt, &tm);
-       }
-       else {
-               localtime_r(&tt, &tm);
-       }
-
-       wprintf(_("Month: "));
-       wprintf("<SELECT NAME=\"%s_month\" SIZE=\"1\">\n", prefix);
-       for (i=0; i<=11; ++i) {
-               monthselect_time = 1137997451 + (i * 2592000);
-               localtime_r(&monthselect_time, &monthselect_tm);
-               wc_strftime(monthselect_str, sizeof monthselect_str, "%B", &monthselect_tm);
-               wprintf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
-                       ((tm.tm_mon == i) ? "SELECTED" : ""),
-                       i+1,
-                       monthselect_str
-               );
-       }
-       wprintf("</SELECT>\n");
-
-       wprintf(_("Day: "));
-       wprintf("<SELECT NAME=\"%s_day\" SIZE=\"1\">\n", prefix);
-       for (i=1; i<=31; ++i) {
-               wprintf("<OPTION %s VALUE=\"%d\">%d</OPTION>\n",
-                       ((tm.tm_mday == i) ? "SELECTED" : ""),
-                       i, i
-               );
-       }
-       wprintf("</SELECT>\n");
-
-       wprintf(_("Year: "));
-       wprintf("<SELECT NAME=\"%s_year\" SIZE=\"1\">\n", prefix);
-       if ((this_year - t->year) > span) {
-               wprintf("<OPTION SELECTED VALUE=\"%d\">%d</OPTION>\n",
-                       t->year, t->year);
-       }
-       for (i=(this_year-span); i<=(this_year+span); ++i) {
-               wprintf("<OPTION %s VALUE=\"%d\">%d</OPTION>\n",
-                       ((t->year == i) ? "SELECTED" : ""),
-                       i, i
-               );
-       }
-       if ((t->year - this_year) > span) {
-               wprintf("<OPTION SELECTED VALUE=\"%d\">%d</OPTION>\n",
-                       t->year, t->year);
-       }
-       wprintf("</SELECT>\n");
-
-       wprintf(_("Hour: "));
-       wprintf("<SELECT NAME=\"%s_hour\" SIZE=\"1\">\n", prefix);
-       for (i=0; i<=23; ++i) {
-
-               if (!strcasecmp(calhourformat, "24")) {
-                       wprintf("<OPTION %s VALUE=\"%d\">%d</OPTION>\n",
-                               ((tm.tm_hour == i) ? "SELECTED" : ""),
-                               i, i
-                       );
-               }
-               else {
-                       wprintf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
-                               ((tm.tm_hour == i) ? "SELECTED" : ""),
-                               i, hourname[i]
-                       );
-               }
-
-       }
-       wprintf("</SELECT>\n");
-
-       wprintf(_("Minute: "));
-       wprintf("<SELECT NAME=\"%s_minute\" SIZE=\"1\">\n", prefix);
-       for (i=0; i<=59; ++i) {
-               if ( (i % 5 == 0) || (tm.tm_min == i) ) {
-                       wprintf("<OPTION %s VALUE=\"%d\">:%02d</OPTION>\n",
-                               ((tm.tm_min == i) ? "SELECTED" : ""),
-                               i, i
-                       );
-               }
-       }
-       wprintf("</SELECT>\n");
-}
-
-/**
- *\brief Get time from form
- * get the time back from the user and convert it into internal structs.
- * \param t our time element
- * \param prefix whats that\todo ????
- */
-void icaltime_from_webform(struct icaltimetype *t, char *prefix) {
-       char vname[32];
-        time_t tt;
-        struct tm tm;
-       struct icaltimetype t2;
-
-        tt = time(NULL);
-        localtime_r(&tt, &tm);
-
-        sprintf(vname, "%s_month", prefix);     tm.tm_mon = atoi(bstr(vname)) - 1;
-        sprintf(vname, "%s_day", prefix);       tm.tm_mday = atoi(bstr(vname));
-        sprintf(vname, "%s_year", prefix);      tm.tm_year = atoi(bstr(vname)) - 1900;
-        sprintf(vname, "%s_hour", prefix);      tm.tm_hour = atoi(bstr(vname));
-        sprintf(vname, "%s_minute", prefix);    tm.tm_min = atoi(bstr(vname));
-
-        tt = mktime(&tm);
-        t2 = icaltime_from_timet(tt, 0);
-       memcpy(t, &t2, sizeof(struct icaltimetype));
-}
-
-/**
- *\brief Get time from form
- * get the time back from the user and convert it into internal structs.
- * \param t our time element
- * \param prefix whats that\todo ????
- */
-
-void icaltime_from_webform_dateonly(struct icaltimetype *t, char *prefix) {
-       char vname[32];
-
-       memset(t, 0, sizeof(struct icaltimetype));
-
-        sprintf(vname, "%s_month", prefix);     t->month = atoi(bstr(vname));
-        sprintf(vname, "%s_day", prefix);       t->day = atoi(bstr(vname));
-        sprintf(vname, "%s_year", prefix);      t->year = atoi(bstr(vname));
-       t->is_utc = 1;
-       t->is_date = 1;
-}
-
-
-/**
- * \brief Render PAPSTAT
- * Render a PARTSTAT parameter as a string (and put it in parentheses)
- * \param buf the string to put it to
- * \param attendee the attendee to textify
- */
-void partstat_as_string(char *buf, icalproperty *attendee) {
-       icalparameter *partstat_param;
-       icalparameter_partstat partstat;
-
-       strcpy(buf, _("(status unknown)"));
-
-       partstat_param = icalproperty_get_first_parameter(
-                               attendee,
-                               ICAL_PARTSTAT_PARAMETER
-       );
-       if (partstat_param == NULL) {
-               return;
-       }
-
-       partstat = icalparameter_get_partstat(partstat_param);
-       switch(partstat) {
-               case ICAL_PARTSTAT_X:
-                       strcpy(buf, "(x)");
-                       break;
-               case ICAL_PARTSTAT_NEEDSACTION:
-                       strcpy(buf, _("(needs action)"));
-                       break;
-               case ICAL_PARTSTAT_ACCEPTED:
-                       strcpy(buf, _("(accepted)"));
-                       break;
-               case ICAL_PARTSTAT_DECLINED:
-                       strcpy(buf, _("(declined)"));
-                       break;
-               case ICAL_PARTSTAT_TENTATIVE:
-                       strcpy(buf, _("(tenative)"));
-                       break;
-               case ICAL_PARTSTAT_DELEGATED:
-                       strcpy(buf, _("(delegated)"));
-                       break;
-               case ICAL_PARTSTAT_COMPLETED:
-                       strcpy(buf, _("(completed)"));
-                       break;
-               case ICAL_PARTSTAT_INPROCESS:
-                       strcpy(buf, _("(in process)"));
-                       break;
-               case ICAL_PARTSTAT_NONE:
-                       strcpy(buf, _("(none)"));
-                       break;
-       }
-}
-
-
-/**
- * \brief embedd
- * Utility function to encapsulate a subcomponent into a full VCALENDAR
- * \param subcomp the component to encapsulate
- * \returns the meta object ???
- */
-icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp) {
-       icalcomponent *encaps;
-
-       /* lprintf(9, "ical_encapsulate_subcomponent() called\n"); */
-
-       if (subcomp == NULL) {
-               lprintf(3, "ERROR: called with NULL argument!\n");
-               return NULL;
-       }
-
-       /**
-        * If we're already looking at a full VCALENDAR component,
-        * don't bother ... just return itself.
-        */
-       if (icalcomponent_isa(subcomp) == ICAL_VCALENDAR_COMPONENT) {
-               return subcomp;
-       }
-
-       /** Encapsulate the VEVENT component into a complete VCALENDAR */
-       encaps = icalcomponent_new(ICAL_VCALENDAR_COMPONENT);
-       if (encaps == NULL) {
-               lprintf(3, "%s:%d: Error - could not allocate component!\n",
-                       __FILE__, __LINE__);
-               return NULL;
-       }
-
-       /** Set the Product ID */
-       icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID));
-
-       /** Set the Version Number */
-       icalcomponent_add_property(encaps, icalproperty_new_version("2.0"));
-
-       /** Encapsulate the subcomponent inside */
-       /* lprintf(9, "Doing the encapsulation\n"); */
-       icalcomponent_add_component(encaps, subcomp);
-
-       /** Convert all timestamps to UTC so we don't have to deal with
-        * stupid VTIMEZONE crap.
-        */
-       ical_dezonify(encaps);
-
-       /** Return the object we just created. */
-       return(encaps);
-}
-
-
-
-
-#endif
-/*@}*/
diff --git a/webcit/calendar_view.c b/webcit/calendar_view.c
deleted file mode 100644 (file)
index dde6f21..0000000
+++ /dev/null
@@ -1,984 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup CalHtmlHandles Handles the HTML display of calendar items.
- * \ingroup Calendaring
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-
-#ifndef WEBCIT_WITH_CALENDAR_SERVICE
-
-/**\brief stub for non-libical builds */
-void do_calendar_view(void) {
-       wprintf("<center><i>");
-       wprintf(_("The calendar view is not available."));
-       wprintf("</i></center><br />\n");
-}
-
-/**\brief stub for non-libical builds */
-void do_tasks_view(void) {     
-       wprintf("<center><I>");
-       wprintf(_("The tasks view is not available."));
-       wprintf("</i></center><br />\n");
-}
-
-#else  /* WEBCIT_WITH_CALENDAR_SERVICE */
-
-/****************************************************************************/
-
-/**
- * \brief Display one day of a whole month view of a calendar
- * \param thetime the month we want to see 
- */
-void calendar_month_view_display_events(time_t thetime) {
-       int i;
-       time_t event_tt;
-       struct tm event_tm;
-       struct tm today_tm;
-       icalproperty *p;
-       struct icaltimetype t;
-       int month, day, year;
-       int all_day_event = 0;
-
-       if (WC->num_cal == 0) {
-               wprintf("<br /><br /><br />\n");
-               return;
-       }
-
-       localtime_r(&thetime, &today_tm);
-       month = today_tm.tm_mon + 1;
-       day = today_tm.tm_mday;
-       year = today_tm.tm_year + 1900;
-
-       for (i=0; i<(WC->num_cal); ++i) {
-               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
-                                               ICAL_DTSTART_PROPERTY);
-               if (p != NULL) {
-                       t = icalproperty_get_dtstart(p);
-                       event_tt = icaltime_as_timet(t);
-
-                       if (t.is_date) all_day_event = 1;
-                       else all_day_event = 0;
-
-                       if (all_day_event) {
-                               gmtime_r(&event_tt, &event_tm);
-                       }
-                       else {
-                               localtime_r(&event_tt, &event_tm);
-                       }
-
-                       if ((event_tm.tm_year == today_tm.tm_year)
-                          && (event_tm.tm_mon == today_tm.tm_mon)
-                          && (event_tm.tm_mday == today_tm.tm_mday)) {
-
-                               p = icalcomponent_get_first_property(
-                                                       WC->disp_cal[i].cal,
-                                                       ICAL_SUMMARY_PROPERTY);
-                               if (p != NULL) {
-
-                                       if (all_day_event) {
-                                               wprintf("<table border=0 cellpadding=2><TR>"
-                                                       "<td bgcolor=\"#CCCCDD\">"
-                                               );
-                                       }
-
-                                       wprintf("<font size=-1>"
-                                               "<a href=\"display_edit_event?msgnum=%ld&calview=%s&year=%s&month=%s&day=%s\">",
-                                               WC->disp_cal[i].cal_msgnum,
-                                               bstr("calview"),
-                                               bstr("year"),
-                                               bstr("month"),
-                                               bstr("day")
-                                       );
-                                       escputs((char *)
-                                               icalproperty_get_comment(p));
-                                       wprintf("</a></font><br />\n");
-
-                                       if (all_day_event) {
-                                               wprintf("</td></tr></table>");
-                                       }
-
-                               }
-
-                       }
-
-
-               }
-       }
-}
-
-
-/**
- * \brief Display one day of a whole month view of a calendar
- * \param thetime the month we want to see 
- */
-void calendar_month_view_brief_events(time_t thetime, const char *daycolor) {
-       int i;
-       time_t event_tt;
-       time_t event_tts;
-       time_t event_tte;
-       struct tm event_tms;
-       struct tm event_tme;
-       struct tm today_tm;
-       icalproperty *p;
-       icalproperty *e;
-       struct icaltimetype t;
-       int month, day, year;
-       int all_day_event = 0;
-       char calhourformat[16];
-       char *timeformat;
-
-       get_preference("calhourformat", calhourformat, sizeof calhourformat);
-       if (!strcasecmp(calhourformat, "24"))   timeformat="%k:%M";
-       else timeformat="%I:%M %p";
-
-       localtime_r(&thetime, &today_tm);
-       month = today_tm.tm_mon + 1;
-       day = today_tm.tm_mday;
-       year = today_tm.tm_year + 1900;
-
-       for (i=0; i<(WC->num_cal); ++i) {
-               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
-                                               ICAL_DTSTART_PROPERTY);
-               if (p != NULL) {
-                       t = icalproperty_get_dtstart(p);
-                       event_tt = icaltime_as_timet(t);
-                       event_tts=event_tt;
-                       if (t.is_date) all_day_event = 1;
-                       else all_day_event = 0;
-
-                       if (all_day_event) {
-                               gmtime_r(&event_tts, &event_tms);
-                       }
-                       else {
-                               localtime_r(&event_tts, &event_tms);
-                       }
-                       /** \todo epoch &! daymask */
-                       if ((event_tms.tm_year == today_tm.tm_year)
-                          && (event_tms.tm_mon == today_tm.tm_mon)
-                          && (event_tms.tm_mday == today_tm.tm_mday)) {
-                               
-                               
-                               char sbuf[255];
-                               char ebuf[255];
-
-                               p = icalcomponent_get_first_property(
-                                                       WC->disp_cal[i].cal,
-                                                       ICAL_SUMMARY_PROPERTY);
-                               e = icalcomponent_get_first_property(
-                                                       WC->disp_cal[i].cal, 
-                                                       ICAL_DTEND_PROPERTY);
-                               if ((p != NULL) && (e != NULL)) {
-                                       time_t difftime;
-                                       int hours, minutes;
-                                       t = icalproperty_get_dtend(e);
-                                       event_tte = icaltime_as_timet(t);
-                                       localtime_r(&event_tte, &event_tme);
-                                       difftime=(event_tte-event_tts)/60;
-                                       hours=(int)(difftime / 60);
-                                       minutes=difftime % 60;
-                                       wprintf("<tr><td bgcolor='%s'>%i:%2i</td><td bgcolor='%s'>"
-                                                       "<font size=-1>"
-                                                       "<a href=\"display_edit_event?msgnum=%ld&calview=%s&year=%s&month=%s&day=%s\">",
-                                                       daycolor,
-                                                       hours, minutes,
-                                                       daycolor,
-                                                       WC->disp_cal[i].cal_msgnum,
-                                                       bstr("calview"),
-                                                       bstr("year"),
-                                                       bstr("month"),
-                                                       bstr("day")
-                                                       );
-
-                                       escputs((char *)
-                                                       icalproperty_get_comment(p));
-                                       /** \todo: allso ammitime format */
-                                       wc_strftime(&sbuf[0], sizeof(sbuf), timeformat, &event_tms);
-                                       wc_strftime(&ebuf[0], sizeof(sbuf), timeformat, &event_tme);
-
-                                       wprintf("</a></font></td>"
-                                                       "<td bgcolor='%s'>%s</td><td bgcolor='%s'>%s</td></tr>",
-                                                       daycolor,
-                                                       sbuf,
-                                                       daycolor,
-                                                       ebuf);
-                                       
-                               }
-                               
-                       }
-                       
-                       
-               }
-       }
-}
-
-
-/**
- * \brief view one month. pretty view
- * \param year the year
- * \param month the month
- * \param day the actual day we want to see
- */
-void calendar_month_view(int year, int month, int day) {
-       struct tm starting_tm;
-       struct tm tm;
-       time_t thetime;
-       int i;
-       time_t previous_month;
-       time_t next_month;
-       time_t colheader_time;
-       struct tm colheader_tm;
-       char colheader_label[32];
-
-       /** Determine what day to start.
-        * First, back up to the 1st of the month...
-        */
-       memset(&starting_tm, 0, sizeof(struct tm));
-       starting_tm.tm_year = year - 1900;
-       starting_tm.tm_mon = month - 1;
-       starting_tm.tm_mday = day;
-       thetime = mktime(&starting_tm);
-
-       memcpy(&tm, &starting_tm, sizeof(struct tm));
-       while (tm.tm_mday != 1) {
-               thetime = thetime - (time_t)86400;      /* go back 24 hours */
-               localtime_r(&thetime, &tm);
-       }
-
-       /** Determine previous and next months ... for links */
-       previous_month = thetime - (time_t)864000L;     /* back 10 days */
-       next_month = thetime + (time_t)(31L * 86400L);  /* ahead 31 days */
-
-       /** Now back up until we're on a Sunday */
-       localtime_r(&thetime, &tm);
-       while (tm.tm_wday != 0) {
-               thetime = thetime - (time_t)86400;      /* go back 24 hours */
-               localtime_r(&thetime, &tm);
-       }
-
-       /** Outer table (to get the background color) */
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table width=100%% border=0 cellpadding=0 cellspacing=0 "
-               "bgcolor=#204B78><TR><TD>\n");
-
-       wprintf("<table width=100%% border=0 cellpadding=0 cellspacing=0><tr>\n");
-
-       wprintf("<td align=center>");
-
-       localtime_r(&previous_month, &tm);
-       wprintf("<a href=\"readfwd?calview=month&year=%d&month=%d&day=1\">",
-               (int)(tm.tm_year)+1900, tm.tm_mon + 1);
-       wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>\n");
-
-       wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm);
-       wprintf("&nbsp;&nbsp;"
-               "<font size=+1 color=\"#FFFFFF\">"
-               "%s %d"
-               "</font>"
-               "&nbsp;&nbsp;", colheader_label, year);
-
-       localtime_r(&next_month, &tm);
-       wprintf("<a href=\"readfwd?calview=month&year=%d&month=%d&day=1\">",
-               (int)(tm.tm_year)+1900, tm.tm_mon + 1);
-       wprintf("<img align=middle src=\"static/nextdate_32x.gif\" border=0></A>\n");
-
-       wprintf("</td></tr></table>\n");
-
-       /** Inner table (the real one) */
-       wprintf("<table width=100%% border=0 cellpadding=1 cellspacing=1 "
-               "bgcolor=#204B78><tr>");
-       colheader_time = thetime;
-       for (i=0; i<7; ++i) {
-               colheader_time = thetime + (i * 86400) ;
-               localtime_r(&colheader_time, &colheader_tm);
-               wc_strftime(colheader_label, sizeof colheader_label, "%A", &colheader_tm);
-               wprintf("<td align=center width=14%%>"
-                       "<font color=\"#FFFFFF\">%s</font></th>", colheader_label);
-
-       }
-       wprintf("</tr>\n");
-
-       /** Now do 35 days */
-       for (i = 0; i < 35; ++i) {
-               localtime_r(&thetime, &tm);
-
-               /** Before displaying Sunday, start a new row */
-               if ((i % 7) == 0) {
-                       wprintf("<tr>");
-               }
-
-               wprintf("<td bgcolor=\"#%s\" width=14%% height=60 align=left valign=top><b>",
-                       ((tm.tm_mon != month-1) ? "DDDDDD" :
-                       ((tm.tm_wday==0 || tm.tm_wday==6) ? "EEEECC" :
-                       "FFFFFF"))
-               );
-               if ((i==0) || (tm.tm_mday == 1)) {
-                       wc_strftime(colheader_label, sizeof colheader_label, "%B", &tm);
-                       wprintf("%s ", colheader_label);
-               }
-               wprintf("<a href=\"readfwd?calview=day&year=%d&month=%d&day=%d\">"
-                       "%d</a></b><br />",
-                       tm.tm_year + 1900,
-                       tm.tm_mon + 1,
-                       tm.tm_mday,
-                       tm.tm_mday);
-
-               /** put the data here, stupid */
-               calendar_month_view_display_events(thetime);
-
-               wprintf("</td>");
-
-               /** After displaying Saturday, end the row */
-               if ((i % 7) == 6) {
-                       wprintf("</tr>\n");
-               }
-
-               thetime += (time_t)86400;               /** ahead 24 hours */
-       }
-
-       wprintf("</table>"                      /** end of inner table */
-               "</td></tr></table>"            /** end of outer table */
-               "</div>\n");
-}
-
-/**
- * \brief view one month. brief view
- * \param year the year
- * \param month the month
- * \param day the actual day we want to see
- */
-void calendar_brief_month_view(int year, int month, int day) {
-       struct tm starting_tm;
-       struct tm tm;
-       time_t thetime;
-       int i;
-       time_t previous_month;
-       time_t next_month;
-       char month_label[32];
-
-       /** Determine what day to start.
-        * First, back up to the 1st of the month...
-        */
-       memset(&starting_tm, 0, sizeof(struct tm));
-       starting_tm.tm_year = year - 1900;
-       starting_tm.tm_mon = month - 1;
-       starting_tm.tm_mday = day;
-       thetime = mktime(&starting_tm);
-
-       memcpy(&tm, &starting_tm, sizeof(struct tm));
-       while (tm.tm_mday != 1) {
-               thetime = thetime - (time_t)86400;      /* go back 24 hours */
-               localtime_r(&thetime, &tm);
-       }
-
-       /** Determine previous and next months ... for links */
-       previous_month = thetime - (time_t)864000L;     /* back 10 days */
-       next_month = thetime + (time_t)(31L * 86400L);  /* ahead 31 days */
-
-       /** Now back up until we're on a Sunday */
-       localtime_r(&thetime, &tm);
-       while (tm.tm_wday != 0) {
-               thetime = thetime - (time_t)86400;      /* go back 24 hours */
-               localtime_r(&thetime, &tm);
-       }
-
-       /** Outer table (to get the background color) */
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table width=100%% border=0 cellpadding=0 cellspacing=0 "
-               "bgcolor=#204B78><TR><TD>\n");
-
-       wprintf("<table width=100%% border=0 cellpadding=0 cellspacing=0><tr>\n");
-
-       wprintf("<td align=center>");
-
-       localtime_r(&previous_month, &tm);
-       wprintf("<a href=\"readfwd?calview=month&year=%d&month=%d&day=1\">",
-               (int)(tm.tm_year)+1900, tm.tm_mon + 1);
-       wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>\n");
-
-       wc_strftime(month_label, sizeof month_label, "%B", &tm);
-       wprintf("&nbsp;&nbsp;"
-               "<font size=+1 color=\"#FFFFFF\">"
-               "%s %d"
-               "</font>"
-               "&nbsp;&nbsp;", month_label, year);
-
-       localtime_r(&next_month, &tm);
-       wprintf("<a href=\"readfwd?calview=month&year=%d&month=%d&day=1\">",
-               (int)(tm.tm_year)+1900, tm.tm_mon + 1);
-       wprintf("<img align=middle src=\"static/nextdate_32x.gif\" border=0></A>\n");
-
-       wprintf("</td></tr></table>\n");
-
-       /** Inner table (the real one) */
-       wprintf("<table width=100%% border=0 cellpadding=1 cellspacing=1 "
-               "bgcolor=#EEEECC><TR>");
-       wprintf("</tr>\n");
-       wprintf("<tr><td colspan=\"100%\">\n");
-
-       /** Now do 35 days */
-       for (i = 0; i < 35; ++i) {
-               char weeknumber[255];
-               char weekday_name[32];
-               char *daycolor;
-               localtime_r(&thetime, &tm);
-
-
-               /** Before displaying Sunday, start a new CELL */
-               if ((i % 7) == 0) {
-                       wc_strftime(&weeknumber[0], sizeof(weeknumber), "%U", &tm);
-                       wprintf("<table border='0' bgcolor=\"#EEEECC\" width='100%'> <tr><th colspan='4'>%s %s</th></tr>"
-                                       "   <tr><td>%s</td><td width='70%'>%s</td><td>%s</td><td>%s</td></tr>\n",
-                                       _("Week"), 
-                                       weeknumber,
-                                       _("Hours"),
-                                       _("Subject"),
-                                       _("Start"),
-                                       _("End")
-                                       );
-               }
-               
-               daycolor=((tm.tm_mon != month-1) ? "DDDDDD" :
-                                 ((tm.tm_wday==0 || tm.tm_wday==6) ? "EEEECC" :
-                                  "FFFFFF"));
-               
-               /** Day Header */
-               wc_strftime(weekday_name, sizeof weekday_name, "%A", &tm);
-               wprintf("<tr><td bgcolor='%s' colspan='1' align='left'> %s,%i."
-                               "</td><td bgcolor='%s' colspan='3'><hr></td></tr>\n",
-                               daycolor,
-                               weekday_name,tm.tm_mday,
-                               daycolor);
-
-               /** put the data of one day  here, stupid */
-               calendar_month_view_brief_events(thetime, daycolor);
-
-
-               /** After displaying Saturday, end the row */
-               if ((i % 7) == 6) {
-                       wprintf("</td></tr></table>\n");
-               }
-
-               thetime += (time_t)86400;               /** ahead 24 hours */
-       }
-
-       wprintf("</table>"                      /** end of inner table */
-               "</td></tr></table>"            /** end of outer table */
-               "</div>\n");
-}
-
-/** 
- * \brief view one week
- * this should view just one week, but it's not here yet.
- * \todo ny implemented
- * \param year the year
- * \param month the month
- * \param day the day which we want to see the week around
- */
-void calendar_week_view(int year, int month, int day) {
-       wprintf("<center><i>week view FIXME</i></center><br />\n");
-}
-
-
-/**
- * \brief display one day
- * Display events for a particular hour of a particular day.
- * (Specify hour < 0 to show "all day" events)
- * \param year the year
- * \param month the month
- * \param day the day
- * \param hour the hour we want to start displaying?????
- */
-void calendar_day_view_display_events(int year, int month,
-                                       int day, int hour) {
-       int i;
-       icalproperty *p;
-       struct icaltimetype t;
-       time_t event_tt;
-       struct tm *event_tm;
-       int all_day_event = 0;
-
-       if (WC->num_cal == 0) {
-               // \todo FIXME wprintf("<br /><br /><br />\n");
-               return;
-       }
-
-       for (i=0; i<(WC->num_cal); ++i) {
-               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
-                                               ICAL_DTSTART_PROPERTY);
-               if (p != NULL) {
-                       t = icalproperty_get_dtstart(p);
-                       event_tt = icaltime_as_timet(t);
-                       if (t.is_date) {
-                               all_day_event = 1;
-                       }
-                       else {
-                               all_day_event = 0;
-                       }
-
-                       if (all_day_event) {
-                               event_tm = gmtime(&event_tt);
-                       }
-                       else {
-                               event_tm = localtime(&event_tt);
-                       }
-
-                       if ((event_tm->tm_year == (year-1900))
-                          && (event_tm->tm_mon == (month-1))
-                          && (event_tm->tm_mday == day)
-                          && ( ((event_tm->tm_hour == hour)&&(!t.is_date)) || ((hour<0)&&(t.is_date)) )
-                          ) {
-
-
-                               p = icalcomponent_get_first_property(
-                                                       WC->disp_cal[i].cal,
-                                                       ICAL_SUMMARY_PROPERTY);
-                               if (p != NULL) {
-
-                                       if (all_day_event) {
-                                               wprintf("<table border=1 cellpadding=2><TR>"
-                                                       "<td bgcolor=\"#CCCCCC\">"
-                                               );
-                                       }
-
-                                       wprintf("<font size=-1>"
-                                               "<a href=\"display_edit_event?msgnum=%ld&calview=day&year=%d&month=%d&day=%d\">",
-                                               WC->disp_cal[i].cal_msgnum,
-                                               year, month, day
-                                       );
-                                       escputs((char *)
-                                               icalproperty_get_comment(p));
-                                       wprintf("</a></font><br />\n");
-
-                                       if (all_day_event) {
-                                               wprintf("</td></tr></table>");
-                                       }
-                               }
-
-                       }
-
-
-               }
-       }
-}
-
-
-/**
- * \brief view one day
- * \param year the year
- * \param month the month 
- * \param day the day we want to display
- */
-void calendar_day_view(int year, int month, int day) {
-       int hour;
-       struct icaltimetype today, yesterday, tomorrow;
-       char calhourformat[16];
-       int daystart = 8;
-       int dayend = 17;
-       char daystart_str[16], dayend_str[16];
-       struct tm d_tm;
-       char d_str[128];
-
-       get_preference("calhourformat", calhourformat, sizeof calhourformat);
-       get_preference("daystart", daystart_str, sizeof daystart_str);
-       if (strlen(daystart_str) > 0) daystart = atoi(daystart_str);
-       get_preference("dayend", dayend_str, sizeof dayend_str);
-       if (strlen(dayend_str) > 0) dayend = atoi(dayend_str);
-       
-
-       /** Figure out the dates for "yesterday" and "tomorrow" links */
-
-       memset(&today, 0, sizeof(struct icaltimetype));
-       today.year = year;
-       today.month = month;
-       today.day = day;
-       today.is_date = 1;
-
-       memcpy(&yesterday, &today, sizeof(struct icaltimetype));
-       --yesterday.day;
-       yesterday = icaltime_normalize(yesterday);
-
-       memcpy(&tomorrow, &today, sizeof(struct icaltimetype));
-       ++tomorrow.day;
-       tomorrow = icaltime_normalize(tomorrow);
-
-
-       /** Outer table (to get the background color) */
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table width=100%% border=0 cellpadding=0 cellspacing=0 "
-               "bgcolor=#204B78><tr><td>\n");
-
-       /** Inner table (the real one) */
-       wprintf("<table width=100%% border=0 cellpadding=1 cellspacing=1 "
-               "bgcolor=#204B78><tr>\n");
-
-       /** Innermost table (contains hours etc.) */
-       wprintf("<td width=80%%>"
-               "<table width=100%% border=0 cellpadding=1 cellspacing=1 "
-               "bgcolor=#204B78>\n");
-
-       /** Display events before 8:00 (hour=-1 is all-day events) */
-       wprintf("<tr>"
-               "<td bgcolor=\"#CCCCDD\" valign=middle width=10%%></td>"
-               "<td bgcolor=\"#FFFFFF\" valign=top>");
-       for (hour = (-1); hour <= (daystart-1); ++hour) {
-               calendar_day_view_display_events(year, month, day, hour);
-       }
-       wprintf("</td></tr>\n");
-
-       /** Now the middle of the day... */     
-       for (hour = daystart; hour <= dayend; ++hour) { /* could do HEIGHT=xx */
-               wprintf("<tr height=30><td bgcolor=\"#CCCCDD\" align=middle "
-                       "valign=middle width=10%%>");
-               wprintf("<a href=\"display_edit_event?msgnum=0"
-                       "&year=%d&month=%d&day=%d&hour=%d&minute=0\">",
-                       year, month, day, hour
-               );
-
-               if (!strcasecmp(calhourformat, "24")) {
-                       wprintf("%2d:00</a> ", hour);
-               }
-               else {
-                       wprintf("%d:00%s</a> ",
-                               (hour <= 12 ? hour : hour-12),
-                               (hour < 12 ? "am" : "pm")
-                       );
-               }
-
-               wprintf("</td><td bgcolor=\"#FFFFFF\" valign=top>");
-
-               /* put the data here, stupid */
-               calendar_day_view_display_events(year, month, day, hour);
-
-               wprintf("</td></tr>\n");
-       }
-
-       /** Display events after 5:00... */
-       wprintf("<tr>"
-               "<td bgcolor=\"#CCCCDD\" valign=middle width=10%%></td>"
-               "<td bgcolor=\"#FFFFFF\" valign=top>");
-       for (hour = (dayend+1); hour <= 23; ++hour) {
-               calendar_day_view_display_events(year, month, day, hour);
-       }
-       wprintf("</td></tr>\n");
-
-
-       wprintf("</table>"                      /* end of innermost table */
-               "</td>"
-       );
-
-       wprintf("<td width=20%% valign=top>");  /* begin stuff-on-the-right */
-
-
-       /** Begin todays-date-with-left-and-right-arrows */
-       wprintf("<table border=0 width=100%% "
-               "cellspacing=0 cellpadding=0 bgcolor=\"#FFFFFF\">\n");
-       wprintf("<tr>");
-
-       /** Left arrow */       
-       wprintf("<td align=center>");
-       wprintf("<a href=\"readfwd?calview=day&year=%d&month=%d&day=%d\">",
-               yesterday.year, yesterday.month, yesterday.day);
-       wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>");
-       wprintf("</td>");
-
-       /** Today's date */
-       memset(&d_tm, 0, sizeof d_tm);
-       d_tm.tm_year = year - 1900;
-       d_tm.tm_mon = month - 1;
-       d_tm.tm_mday = day;
-       wc_strftime(d_str, sizeof d_str,
-               "<td align=center>"
-               "<font size=+2>%B</font><br />"
-               "<font size=+3>%d</font><br />"
-               "<font size=+2>%Y</font><br />"
-               "</td>",
-               &d_tm
-       );
-       wprintf("%s", d_str);
-
-       /** Right arrow */
-       wprintf("<td align=center>");
-       wprintf("<a href=\"readfwd?calview=day&year=%d&month=%d&day=%d\">",
-               tomorrow.year, tomorrow.month, tomorrow.day);
-       wprintf("<img align=middle src=\"static/nextdate_32x.gif\""
-               " border=0></A>\n");
-       wprintf("</td>");
-
-       wprintf("</tr></table>\n");
-       /** End todays-date-with-left-and-right-arrows */
-
-       /** \todo In the future we might want to put a month-o-matic here */
-
-       wprintf("</font></center>\n");
-
-       wprintf("</td>");                       /** end stuff-on-the-right */
-
-
-
-       wprintf("</tr></table>"                 /** end of inner table */
-               "</td></tr></table></div>"      /** end of outer table */
-       );
-
-
-
-}
-
-/**
- * \brief Display today's events.
- */
-void calendar_summary_view(void) {
-       int i;
-       icalproperty *p;
-       struct icaltimetype t;
-       time_t event_tt;
-       struct tm event_tm;
-       struct tm today_tm;
-       time_t now;
-       int all_day_event = 0;
-       char timestring[SIZ];
-
-       if (WC->num_cal == 0) {
-               return;
-       }
-
-       now = time(NULL);
-       localtime_r(&now, &today_tm);
-
-       for (i=0; i<(WC->num_cal); ++i) {
-               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
-                                               ICAL_DTSTART_PROPERTY);
-               if (p != NULL) {
-                       t = icalproperty_get_dtstart(p);
-                       event_tt = icaltime_as_timet(t);
-                       if (t.is_date) {
-                               all_day_event = 1;
-                       }
-                       else {
-                               all_day_event = 0;
-                       }
-                       fmt_time(timestring, event_tt);
-
-                       if (all_day_event) {
-                               gmtime_r(&event_tt, &event_tm);
-                       }
-                       else {
-                               localtime_r(&event_tt, &event_tm);
-                       }
-
-                       if ( (event_tm.tm_year == today_tm.tm_year)
-                          && (event_tm.tm_mon == today_tm.tm_mon)
-                          && (event_tm.tm_mday == today_tm.tm_mday)
-                          ) {
-
-
-                               p = icalcomponent_get_first_property(
-                                                       WC->disp_cal[i].cal,
-                                                       ICAL_SUMMARY_PROPERTY);
-                               if (p != NULL) {
-                                       escputs((char *)
-                                               icalproperty_get_comment(p));
-                                       wprintf(" (%s)<br />\n", timestring);
-                               }
-                       }
-               }
-       }
-       free_calendar_buffer();
-}
-
-
-/**
- * \brief clean up ical memory
- * \todo this could get troubel with future ical versions
- */
-void free_calendar_buffer(void) {
-       int i;
-       if (WC->num_cal) for (i=0; i<(WC->num_cal); ++i) {
-               icalcomponent_free(WC->disp_cal[i].cal);
-       }
-       WC->num_cal = 0;
-       free(WC->disp_cal);
-       WC->disp_cal = NULL;
-}
-
-
-
-/**
- * \brief do the whole calendar page
- * view any part of the calender. decide which way, etc.
- */
-void do_calendar_view(void) {
-       time_t now;
-       struct tm tm;
-       int year, month, day;
-       char calview[SIZ];
-
-       /** In case no date was specified, go with today */
-       now = time(NULL);
-       localtime_r(&now, &tm);
-       year = tm.tm_year + 1900;
-       month = tm.tm_mon + 1;
-       day = tm.tm_mday;
-
-       /** Now see if a date was specified */
-       if (strlen(bstr("year")) > 0) year = atoi(bstr("year"));
-       if (strlen(bstr("month")) > 0) month = atoi(bstr("month"));
-       if (strlen(bstr("day")) > 0) day = atoi(bstr("day"));
-
-       /** How would you like that cooked? */
-       if (strlen(bstr("calview")) > 0) {
-               strcpy(calview, bstr("calview"));
-       }
-       else {
-               strcpy(calview, "month");
-       }
-
-       /** Display the selected view */
-       if (!strcasecmp(calview, "day")) {
-               calendar_day_view(year, month, day);
-       }
-       else if (!strcasecmp(calview, "week")) {
-               calendar_week_view(year, month, day);
-       }
-       else {
-               if (WC->wc_view == VIEW_CALBRIEF) {
-                       calendar_brief_month_view(year, month, day);
-               }
-               else {
-                       calendar_month_view(year, month, day);
-               }
-       }
-
-       /** Free the calendar stuff */
-       free_calendar_buffer();
-
-}
-
-
-/**
- * \brief get task due date
- * Helper function for do_tasks_view().  
- * \param vtodo a task to get the due date
- * \return the date/time due.
- */
-time_t get_task_due_date(icalcomponent *vtodo) {
-       icalproperty *p;
-
-       if (vtodo == NULL) {
-               return(0L);
-       }
-
-       /**
-        * If we're looking at a fully encapsulated VCALENDAR
-        * rather than a VTODO component, recurse into the data
-        * structure until we get a VTODO.
-        */
-       if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
-               return get_task_due_date(
-                       icalcomponent_get_first_component(
-                               vtodo, ICAL_VTODO_COMPONENT
-                       )
-               );
-       }
-
-       p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
-       if (p != NULL) {
-               return(icaltime_as_timet(icalproperty_get_due(p)));
-       }
-       else {
-               return(0L);
-       }
-}
-
-
-/**
- * \brief Compare the due dates of two tasks (this is for sorting)
- * \param task1 first task to compare
- * \param task2 second task to compare
- */
-int task_due_cmp(const void *task1, const void *task2) {
-       time_t t1;
-       time_t t2;
-
-       t1 =  get_task_due_date(((struct disp_cal *)task1)->cal);
-       t2 =  get_task_due_date(((struct disp_cal *)task2)->cal);
-
-       if (t1 < t2) return(-1);
-       if (t1 > t2) return(1);
-       return(0);
-}
-
-
-
-
-/**
- * \brief do the whole task view stuff
- */
-void do_tasks_view(void) {
-       int i;
-       time_t due;
-       int bg = 0;
-       char buf[SIZ];
-       icalproperty *p;
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 cellspacing=0 width=100%% bgcolor=\"#FFFFFF\">\n<tr>\n"
-               "<th>");
-       wprintf(_("Name of task"));
-       wprintf("</th><th>");
-       wprintf(_("Date due"));
-       wprintf("</th></tr>\n"
-       );
-
-       /** Sort them if necessary */
-       if (WC->num_cal > 1) {
-               qsort(WC->disp_cal,
-                       WC->num_cal,
-                       sizeof(struct disp_cal),
-                       task_due_cmp
-               );
-       }
-
-       if (WC->num_cal) for (i=0; i<(WC->num_cal); ++i) {
-
-               bg = 1 - bg;
-               wprintf("<tr bgcolor=\"#%s\"><td>",
-                       (bg ? "DDDDDD" : "FFFFFF")
-               );
-
-               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
-                                                       ICAL_SUMMARY_PROPERTY);
-               wprintf("<a href=\"display_edit_task?msgnum=%ld&taskrm=",
-                       WC->disp_cal[i].cal_msgnum );
-               urlescputs(WC->wc_roomname);
-               wprintf("\">");
-               wprintf("<img align=middle "
-                       "src=\"static/taskmanag_16x.gif\" border=0>&nbsp;");
-               if (p != NULL) {
-                       escputs((char *)icalproperty_get_comment(p));
-               }
-               wprintf("</a>\n");
-               wprintf("</td>\n");
-
-               due = get_task_due_date(WC->disp_cal[i].cal);
-               fmt_date(buf, due, 0);
-               wprintf("<td><font");
-               if (due < time(NULL)) {
-                       wprintf(" color=\"#FF0000\"");
-               }
-               wprintf(">%s</font></td></tr>\n", buf);
-       }
-
-       wprintf("</table></div>\n");
-
-       /** Free the list */
-       free_calendar_buffer();
-
-}
-
-#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
-
-/** @} */
diff --git a/webcit/context_loop.c b/webcit/context_loop.c
deleted file mode 100644 (file)
index e276383..0000000
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup WebServerII some of the webserver stuff.
- * This is the other half of the webserver.  It handles the task of hooking
- * up HTTP requests with the sessions they belong to, using HTTP cookies to
- * keep track of things.  If the HTTP request doesn't belong to any currently
- * active session, a new session is started.
- * \ingroup WebcitHttpServer 
- *
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-
-/** Only one thread may manipulate SessionList at a time... */
-pthread_mutex_t SessionListMutex;
-
-struct wcsession *SessionList = NULL; /**< our sessions ????*/
-
-pthread_key_t MyConKey;         /**< TSD key for MySession() */
-
-
-/**
- * \brief free the memory used for viewing atachments
- * \param sess the session object to destroy
- */
-void free_attachments(struct wcsession *sess) {
-       struct wc_attachment *att;
-
-       while (sess->first_attachment != NULL) {
-               att = sess->first_attachment;
-               sess->first_attachment = sess->first_attachment->next;
-               free(att->data);
-               free(att);
-       }
-}
-
-/**
- * \brief what??????
- */
-void do_housekeeping(void)
-{
-       struct wcsession *sptr, *ss;
-       struct wcsession *sessions_to_kill = NULL;
-       int num_sessions = 0;
-       static int num_threads = MIN_WORKER_THREADS;
-
-       /**
-        * Lock the session list, moving any candidates for euthanasia into
-        * a separate list.
-        */
-       pthread_mutex_lock(&SessionListMutex);
-       num_sessions = 0;
-       for (sptr = SessionList; sptr != NULL; sptr = sptr->next) {
-               ++num_sessions;
-
-               /** Kill idle sessions */
-               if ((time(NULL) - (sptr->lastreq)) >
-                  (time_t) WEBCIT_TIMEOUT) {
-                       sptr->killthis = 1;
-               }
-
-               /** Remove sessions flagged for kill */
-               if (sptr->killthis) {
-
-                       /** remove session from linked list */
-                       if (sptr == SessionList) {
-                               SessionList = SessionList->next;
-                       }
-                       else for (ss=SessionList;ss!=NULL;ss=ss->next) {
-                               if (ss->next == sptr) {
-                                       ss->next = ss->next->next;
-                               }
-                       }
-
-                       sptr->next = sessions_to_kill;
-                       sessions_to_kill = sptr;
-               }
-       }
-       pthread_mutex_unlock(&SessionListMutex);
-
-       /**
-        * Now free up and destroy the culled sessions.
-        */
-       while (sessions_to_kill != NULL) {
-               lprintf(3, "Destroying session %d\n", sessions_to_kill->wc_session);
-               pthread_mutex_lock(&sessions_to_kill->SessionMutex);
-               close(sessions_to_kill->serv_sock);
-               close(sessions_to_kill->chat_sock);
-               if (sessions_to_kill->preferences != NULL) {
-                       free(sessions_to_kill->preferences);
-               }
-               if (sessions_to_kill->cache_fold != NULL) {
-                       free(sessions_to_kill->cache_fold);
-               }
-               free_attachments(sessions_to_kill);
-               free_march_list(sessions_to_kill);
-               pthread_mutex_unlock(&sessions_to_kill->SessionMutex);
-               sptr = sessions_to_kill->next;
-               free(sessions_to_kill);
-               sessions_to_kill = sptr;
-               --num_sessions;
-       }
-
-       /**
-        * If there are more sessions than threads, then we should spawn
-        * more threads ... up to a predefined maximum.
-        */
-       while ( (num_sessions > num_threads)
-             && (num_threads <= MAX_WORKER_THREADS) ) {
-               spawn_another_worker_thread();
-               ++num_threads;
-               lprintf(3, "There are %d sessions and %d threads active.\n",
-                       num_sessions, num_threads);
-       }
-}
-
-
-/**
- * \brief Wake up occasionally and clean house
- */
-void housekeeping_loop(void)
-{
-       while (1) {
-               sleeeeeeeeeep(HOUSEKEEPING);
-               do_housekeeping();
-       }
-}
-
-
-/**
- * \brief Create a Session id
- * Generate a unique WebCit session ID (which is not the same thing as the
- * Citadel session ID).
- *
- * \todo FIXME ... ensure that session number is truly unique
- *
- */
-int GenerateSessionID(void)
-{
-       static int seq = (-1);
-
-       if (seq < 0) {
-               seq = (int) time(NULL);
-       }
-               
-       return ++seq;
-}
-
-
-/**
- * \brief Collapse multiple cookies on one line
- * \param sock a socket?
- * \param buf some bunch of chars?
- * \param hold hold what?
- */
-int req_gets(int sock, char *buf, char *hold)
-{
-       int a;
-
-       if (strlen(hold) == 0) {
-               strcpy(buf, "");
-               a = client_getln(sock, buf, SIZ);
-               if (a<1) return(-1);
-       } else {
-               safestrncpy(buf, hold, SIZ);
-       }
-       strcpy(hold, "");
-
-       if (!strncasecmp(buf, "Cookie: ", 8)) {
-               for (a = 0; a < strlen(buf); ++a)
-                       if (buf[a] == ';') {
-                               sprintf(hold, "Cookie: %s", &buf[a + 1]);
-                               buf[a] = 0;
-                               while (isspace(hold[8]))
-                                       strcpy(&hold[8], &hold[9]);
-                               return(0);
-                       }
-       }
-
-       return(0);
-}
-
-/**
- * \brief close some fd for some reason???
- * \param fd the fd to close??????
- * lingering_close() a`la Apache. see
- * http://www.apache.org/docs/misc/fin_wait_2.html for rationale
- */
-
-int lingering_close(int fd)
-{
-       char buf[SIZ];
-       int i;
-       fd_set set;
-       struct timeval tv, start;
-
-       gettimeofday(&start, NULL);
-       shutdown(fd, 1);
-       do {
-               do {
-                       gettimeofday(&tv, NULL);
-                       tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec);
-                       tv.tv_usec = start.tv_usec - tv.tv_usec;
-                       if (tv.tv_usec < 0) {
-                               tv.tv_sec--;
-                               tv.tv_usec += 1000000;
-                       }
-                       FD_ZERO(&set);
-                       FD_SET(fd, &set);
-                       i = select(fd + 1, &set, NULL, NULL, &tv);
-               } while (i == -1 && errno == EINTR);
-
-               if (i <= 0)
-                       break;
-
-               i = read(fd, buf, sizeof buf);
-       } while (i != 0 && (i != -1 || errno == EINTR));
-
-       return close(fd);
-}
-
-
-
-/**
- * \brief      sanity requests
- *             Check for bogus requests coming from brain-dead Windows boxes.
- *
- * \param      http_cmd        The HTTP request to check
- */
-int is_bogus(char *http_cmd) {
-       char *url;
-
-       url = strstr(http_cmd, " ");
-       if (url == NULL) return(1);
-       ++url;
-
-       /** Worms and trojans and viruses, oh my! */
-       if (!strncasecmp(url, "/scripts/root.exe", 17)) return(2);
-       if (!strncasecmp(url, "/c/winnt", 8)) return(2);
-       if (!strncasecmp(url, "/MSADC/", 7)) return(2);
-
-       /** Broken Microsoft DAV implementation */
-       if (!strncasecmp(url, "/_vti", 5)) return(3);
-
-       return(0);      /* probably ok */
-}
-
-
-
-/**
- * \brief handle one request
- * This loop gets called once for every HTTP connection made to WebCit.  At
- * this entry point we have an HTTP socket with a browser allegedly on the
- * other end, but we have not yet bound to a WebCit session.
- *
- * The job of this function is to locate the correct session and bind to it,
- * or create a session if necessary and bind to it, then run the WebCit
- * transaction loop.  Afterwards, we unbind from the session.  When this
- * function returns, the worker thread is then free to handle another
- * transaction.
- * \param sock the socket we will put our answer to
- */
-void context_loop(int sock)
-{
-       struct httprequest *req = NULL;
-       struct httprequest *last = NULL;
-       struct httprequest *hptr;
-       char buf[SIZ], hold[SIZ];
-       int desired_session = 0;
-       int got_cookie = 0;
-       int gzip_ok = 0;
-       struct wcsession *TheSession, *sptr;
-       char httpauth_string[1024];
-       char httpauth_user[1024];
-       char httpauth_pass[1024];
-       char accept_language[256];
-       char *ptr = NULL;
-       int session_is_new = 0;
-
-       strcpy(httpauth_string, "");
-       strcpy(httpauth_user, DEFAULT_HTTPAUTH_USER);
-       strcpy(httpauth_pass, DEFAULT_HTTPAUTH_PASS);
-
-       /**
-        * Find out what it is that the web browser is asking for
-        */
-       memset(hold, 0, sizeof(hold));
-       do {
-               if (req_gets(sock, buf, hold) < 0) return;
-
-               /**
-                * Can we compress?
-                */
-               if (!strncasecmp(buf, "Accept-encoding:", 16)) {
-                       if (strstr(&buf[16], "gzip")) {
-                               gzip_ok = 1;
-                       }
-               }
-
-               /**
-                * Browser-based sessions use cookies for session authentication
-                */
-               if (!strncasecmp(buf, "Cookie: webcit=", 15)) {
-                       cookie_to_stuff(&buf[15], &desired_session,
-                               NULL, 0, NULL, 0, NULL, 0);
-                       got_cookie = 1;
-               }
-
-               /**
-                * GroupDAV-based sessions use HTTP authentication
-                */
-               if (!strncasecmp(buf, "Authorization: Basic ", 21)) {
-                       CtdlDecodeBase64(httpauth_string, &buf[21], strlen(&buf[21]));
-                       extract_token(httpauth_user, httpauth_string, 0, ':', sizeof httpauth_user);
-                       extract_token(httpauth_pass, httpauth_string, 1, ':', sizeof httpauth_pass);
-               }
-
-               if (!strncasecmp(buf, "If-Modified-Since: ", 19)) {
-                       if_modified_since = httpdate_to_timestamp(&buf[19]);
-               }
-
-               if (!strncasecmp(buf, "Accept-Language: ", 17)) {
-                       safestrncpy(accept_language, &buf[17], sizeof accept_language);
-               }
-
-               /**
-                * Read in the request
-                */
-               hptr = (struct httprequest *)
-                       malloc(sizeof(struct httprequest));
-               if (req == NULL)
-                       req = hptr;
-               else
-                       last->next = hptr;
-               hptr->next = NULL;
-               last = hptr;
-
-               safestrncpy(hptr->line, buf, sizeof hptr->line);
-
-       } while (strlen(buf) > 0);
-
-       /**
-        * If the request is prefixed by "/webcit" then chop that off.  This
-        * allows a front end web server to forward all /webcit requests to us
-        * while still using the same web server port for other things.
-        */
-       
-       ptr = strstr(req->line, " /webcit ");   /*< Handle "/webcit" */
-       if (ptr != NULL) {
-               strcpy(ptr+2, ptr+8);
-       }
-
-       ptr = strstr(req->line, " /webcit");    /*< Handle "/webcit/" */
-       if (ptr != NULL) {
-               strcpy(ptr+1, ptr+8);
-       }
-
-       /** Begin parsing the request. */
-
-       safestrncpy(buf, req->line, sizeof buf);
-       lprintf(5, "HTTP: %s\n", buf);
-
-       /** Check for bogus requests */
-       if (is_bogus(buf)) {
-               strcpy(req->line, "GET /404 HTTP/1.1");
-               strcpy(buf, "GET /404 HTTP/1.1");
-       }
-
-       /**
-        * Strip out the method, leaving the URL up front...
-        */
-       remove_token(buf, 0, ' ');
-       if (buf[1]==' ') buf[1]=0;
-
-       /**
-        * While we're at it, gracefully handle requests for the
-        * robots.txt and favicon.ico files.
-        */
-       if (!strncasecmp(buf, "/robots.txt", 11)) {
-               strcpy(req->line, "GET /static/robots.txt"
-                               "?force_close_session=yes HTTP/1.1");
-       }
-       else if (!strncasecmp(buf, "/favicon.ico", 12)) {
-               strcpy(req->line, "GET /static/favicon.ico");
-       }
-
-       /**
-        * These are the URL's which may be executed without a
-        * session cookie already set.  If it's not one of these,
-        * force the session to close because cookies are
-        * probably disabled on the client browser.
-        */
-       else if ( (strcmp(buf, "/"))
-               && (strncasecmp(buf, "/listsub", 8))
-               && (strncasecmp(buf, "/freebusy", 9))
-               && (strncasecmp(buf, "/do_logout", 10))
-               && (strncasecmp(buf, "/groupdav", 9))
-               && (strncasecmp(buf, "/static", 7))
-               && (strncasecmp(buf, "/rss", 4))
-               && (strncasecmp(buf, "/404", 4))
-               && (got_cookie == 0)) {
-               strcpy(req->line, "GET /static/nocookies.html"
-                               "?force_close_session=yes HTTP/1.1");
-       }
-
-       /**
-        * See if there's an existing session open with the desired ID or user/pass
-        */
-       TheSession = NULL;
-
-       if (TheSession == NULL) {
-               pthread_mutex_lock(&SessionListMutex);
-               for (sptr = SessionList; sptr != NULL; sptr = sptr->next) {
-
-                       /** If HTTP-AUTH, look for a session with matching credentials */
-                       if ( (strlen(httpauth_user) > 0)
-                          &&(!strcasecmp(sptr->httpauth_user, httpauth_user))
-                          &&(!strcasecmp(sptr->httpauth_pass, httpauth_pass)) ) {
-                               TheSession = sptr;
-                       }
-
-                       /** If cookie-session, look for a session with matching session ID */
-                       if ( (desired_session != 0) && (sptr->wc_session == desired_session)) {
-                               TheSession = sptr;
-                       }
-
-               }
-               pthread_mutex_unlock(&SessionListMutex);
-       }
-
-       /**
-        * Create a new session if we have to
-        */
-       if (TheSession == NULL) {
-               lprintf(3, "Creating a new session\n");
-               TheSession = (struct wcsession *)
-                       malloc(sizeof(struct wcsession));
-               memset(TheSession, 0, sizeof(struct wcsession));
-               TheSession->serv_sock = (-1);
-               TheSession->chat_sock = (-1);
-               TheSession->wc_session = GenerateSessionID();
-               strcpy(TheSession->httpauth_user, httpauth_user);
-               strcpy(TheSession->httpauth_pass, httpauth_pass);
-               pthread_mutex_init(&TheSession->SessionMutex, NULL);
-               pthread_mutex_lock(&SessionListMutex);
-               TheSession->next = SessionList;
-               SessionList = TheSession;
-               pthread_mutex_unlock(&SessionListMutex);
-               session_is_new = 1;
-       }
-
-       /**
-        * A future improvement might be to check the session integrity
-        * at this point before continuing.
-        */
-
-       /**
-        * Bind to the session and perform the transaction
-        */
-       pthread_mutex_lock(&TheSession->SessionMutex);          /*< bind */
-       pthread_setspecific(MyConKey, (void *)TheSession);
-       TheSession->http_sock = sock;
-       TheSession->lastreq = time(NULL);                       /*< log */
-       TheSession->gzip_ok = gzip_ok;
-#ifdef ENABLE_NLS
-       if (session_is_new) {
-               httplang_to_locale(accept_language);
-       }
-       go_selected_language();                         /*< set locale */
-#endif
-       session_loop(req);                              /*< do transaction */
-#ifdef ENABLE_NLS
-       stop_selected_language();                       /*< unset locale */
-#endif
-       pthread_mutex_unlock(&TheSession->SessionMutex);        /*< unbind */
-
-       /** Free the request buffer */
-       while (req != NULL) {
-               hptr = req->next;
-               free(req);
-               req = hptr;
-       }
-
-       /**
-        * Free up any session-local substitution variables which
-        * were set during this transaction
-        */
-       clear_local_substs();
-}
-/*@}*/
diff --git a/webcit/cookie_conversion.c b/webcit/cookie_conversion.c
deleted file mode 100644 (file)
index 24e29ce..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup CookieConversion Grep Cookies
- * Utility functions which convert the HTTP cookie format we use to and
- * from user/password/room strings.
- *
- * \ingroup WebcitHttpServer 
- */
-/*@{*/
-#include "webcit.h"
-
-
-#define TRUE  1    /**< for sure? */
-#define FALSE 0    /**< nope. */
-
-typedef unsigned char byte;          /**< Byte type */
-
-/**
- * \brief find cookie
- * Pack all session info into one easy-to-digest cookie. Healthy and delicious!
- * \param cookie cookie string to create???
- * \param session the session we want to convert into a cookie
- * \param user the user to be associated with the cookie
- * \param pass his passphrase
- * \param room the room he wants to enter
- */
-void stuff_to_cookie(char *cookie, int session,
-               char *user, char *pass, char *room)
-{
-       char buf[SIZ];
-       int i;
-
-       sprintf(buf, "%d|%s|%s|%s|", session, user, pass, room);
-       strcpy(cookie, "");
-       for (i=0; i<strlen(buf); ++i) {
-               sprintf(&cookie[i*2], "%02X", buf[i]);
-       }
-}
-
-/**
- * \brief      Convert unpacked hex string to an integer
- * \param      in      Input hex string
- * \param      len     the length of the string
- * \return     the corrosponding integer value
- */
-int xtoi(char *in, size_t len)
-{
-       int val = 0;
-       char c = 0;
-       while (isxdigit((byte) *in) && (len-- > 0))
-       {
-               c = *in++;
-               val <<= 4;
-               val += isdigit((unsigned char)c)
-               ? (c - '0')
-               : (tolower((unsigned char)c) - 'a' + 10);
-       }
-       return val;
-}
-
-/**
- * \brief Extract all that fun stuff out of the cookie.
- * \param cookie the cookie string
- * \param session the corrosponding session to return
- * \param user the user string
- * \param user_len the user stringlength
- * \param pass the passphrase
- * \param pass_len length of the passphrase string 
- * \param room the room he is in
- * \param room_len the length of the room string
- */
-void cookie_to_stuff(char *cookie, int *session,
-               char *user, size_t user_len,
-               char *pass, size_t pass_len,
-               char *room, size_t room_len)
-{
-       char buf[SIZ];
-       int i, len;
-
-       strcpy(buf, "");
-       len = strlen(cookie) * 2 ;
-       for (i=0; i<len; ++i) {
-               buf[i] = xtoi(&cookie[i*2], 2);
-               buf[i+1] = 0;
-       }
-
-       if (session != NULL)
-               *session = extract_int(buf, 0);
-       if (user != NULL)
-               extract_token(user, buf, 1, '|', user_len);
-       if (pass != NULL)
-               extract_token(pass, buf, 2, '|', pass_len);
-       if (room != NULL)
-               extract_token(room, buf, 3, '|', room_len);
-}
-/*@}*/
diff --git a/webcit/crypto.c b/webcit/crypto.c
deleted file mode 100644 (file)
index ae3e417..0000000
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup https  Provides HTTPS, when the OpenSSL library is available.
- * \ingroup WebcitHttpServer 
- */
-
-/*@{*/
-#ifdef HAVE_OPENSSL
-
-#include "webcit.h"
-#include "webserver.h"
-/** \todo dirify */
-/** where to find the keys */
-#define        CTDL_CRYPTO_DIR         "./keys" 
-#define CTDL_KEY_PATH          CTDL_CRYPTO_DIR "/citadel.key" /**< the key */
-#define CTDL_CSR_PATH          CTDL_CRYPTO_DIR "/citadel.csr" /**< the csr file */
-#define CTDL_CER_PATH          CTDL_CRYPTO_DIR "/citadel.cer" /**< the cer file */
-#define SIGN_DAYS              365 /**< how long our certificate should live */
-
-SSL_CTX *ssl_ctx;              /**< SSL context */
-pthread_mutex_t **SSLCritters; /**< Things needing locking */
-
-pthread_key_t ThreadSSL;       /**< Per-thread SSL context */
-
-/**
- * \brief what?????
- * \return thread id??? 
- */
-static unsigned long id_callback(void)
-{
-       return (unsigned long) pthread_self();
-}
-
-/**
- * \brief initialize ssl engine
- * load certs and initialize openssl internals
- */
-void init_ssl(void)
-{
-       SSL_METHOD *ssl_method;
-       RSA *rsa=NULL;
-       X509_REQ *req = NULL;
-       X509 *cer = NULL;
-       EVP_PKEY *pk = NULL;
-       EVP_PKEY *req_pkey = NULL;
-       X509_NAME *name = NULL;
-       FILE *fp;
-       char buf[SIZ];
-
-       if (!access("/var/run/egd-pool", F_OK))
-               RAND_egd("/var/run/egd-pool");
-
-       if (!RAND_status()) {
-               lprintf(3,
-                       "PRNG not adequately seeded, won't do SSL/TLS\n");
-               return;
-       }
-       SSLCritters =
-           malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t *));
-       if (!SSLCritters) {
-               lprintf(1, "citserver: can't allocate memory!!\n");
-               /* Nothing's been initialized, just die */
-               exit(1);
-       } else {
-               int a;
-
-               for (a = 0; a < CRYPTO_num_locks(); a++) {
-                       SSLCritters[a] = malloc(sizeof(pthread_mutex_t));
-                       if (!SSLCritters[a]) {
-                               lprintf(1,
-                                       "citserver: can't allocate memory!!\n");
-                               /** Nothing's been initialized, just die */
-                               exit(1);
-                       }
-                       pthread_mutex_init(SSLCritters[a], NULL);
-               }
-       }
-
-       /**
-        * Initialize SSL transport layer
-        */
-       SSL_library_init();
-       SSL_load_error_strings();
-       ssl_method = SSLv23_server_method();
-       if (!(ssl_ctx = SSL_CTX_new(ssl_method))) {
-               lprintf(3, "SSL_CTX_new failed: %s\n",
-                       ERR_reason_error_string(ERR_get_error()));
-               return;
-       }
-
-       CRYPTO_set_locking_callback(ssl_lock);
-       CRYPTO_set_id_callback(id_callback);
-
-       /**
-        * Get our certificates in order. \todo dirify. this is a setup job.
-        * First, create the key/cert directory if it's not there already...
-        */
-       mkdir(CTDL_CRYPTO_DIR, 0700);
-
-       /**
-        * Before attempting to generate keys/certificates, first try
-        * link to them from the Citadel server if it's on the same host.
-        * We ignore any error return because it either meant that there
-        * was nothing in Citadel to link from (in which case we just
-        * generate new files) or the target files already exist (which
-        * is not fatal either). \todo dirify
-        */
-       if (!strcasecmp(ctdlhost, "uds")) {
-               sprintf(buf, "%s/keys/citadel.key", ctdlport);
-               symlink(buf, CTDL_KEY_PATH);
-               sprintf(buf, "%s/keys/citadel.csr", ctdlport);
-               symlink(buf, CTDL_CSR_PATH);
-               sprintf(buf, "%s/keys/citadel.cer", ctdlport);
-               symlink(buf, CTDL_CER_PATH);
-       }
-
-       /**
-        * If we still don't have a private key, generate one.
-        */
-       if (access(CTDL_KEY_PATH, R_OK) != 0) {
-               lprintf(5, "Generating RSA key pair.\n");
-               rsa = RSA_generate_key(1024,    /**< modulus size */
-                                       65537,  /**< exponent */
-                                       NULL,   /**< no callback */
-                                       NULL);  /**< no callback */
-               if (rsa == NULL) {
-                       lprintf(3, "Key generation failed: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               }
-               if (rsa != NULL) {
-                       fp = fopen(CTDL_KEY_PATH, "w");
-                       if (fp != NULL) {
-                               chmod(CTDL_KEY_PATH, 0600);
-                               if (PEM_write_RSAPrivateKey(fp, /**< the file */
-                                                       rsa,    /**< the key */
-                                                       NULL,   /**< no enc */
-                                                       NULL,   /**< no passphr */
-                                                       0,      /**< no passphr */
-                                                       NULL,   /**< no callbk */
-                                                       NULL    /**< no callbk */
-                               ) != 1) {
-                                       lprintf(3, "Cannot write key: %s\n",
-                                               ERR_reason_error_string(ERR_get_error()));
-                                       unlink(CTDL_KEY_PATH);
-                               }
-                               fclose(fp);
-                       }
-                       RSA_free(rsa);
-               }
-       }
-
-       /**
-        * Generate a CSR if we don't have one.
-        */
-       if (access(CTDL_CSR_PATH, R_OK) != 0) {
-               lprintf(5, "Generating a certificate signing request.\n");
-
-               /**
-                * Read our key from the file.  No, we don't just keep this
-                * in memory from the above key-generation function, because
-                * there is the possibility that the key was already on disk
-                * and we didn't just generate it now.
-                */
-               fp = fopen(CTDL_KEY_PATH, "r");
-               if (fp) {
-                       rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
-                       fclose(fp);
-               }
-
-               if (rsa) {
-
-                       /** Create a public key from the private key */
-                       if (pk=EVP_PKEY_new(), pk != NULL) {
-                               EVP_PKEY_assign_RSA(pk, rsa);
-                               if (req = X509_REQ_new(), req != NULL) {
-
-                                       /** Set the public key */
-                                       X509_REQ_set_pubkey(req, pk);
-                                       X509_REQ_set_version(req, 0L);
-
-                                       name = X509_REQ_get_subject_name(req);
-
-                                       /** Tell it who we are */
-
-                                       /* \todo whats this?
-                                       X509_NAME_add_entry_by_txt(name, "C",
-                                               MBSTRING_ASC, "US", -1, -1, 0);
-
-                                       X509_NAME_add_entry_by_txt(name, "ST",
-                                               MBSTRING_ASC, "New York", -1, -1, 0);
-
-                                       X509_NAME_add_entry_by_txt(name, "L",
-                                               MBSTRING_ASC, "Mount Kisco", -1, -1, 0);
-                                       */
-
-                                       X509_NAME_add_entry_by_txt(name, "O",
-                                               MBSTRING_ASC, "FIXME.FIXME.org", -1, -1, 0);
-
-                                       X509_NAME_add_entry_by_txt(name, "OU",
-                                               MBSTRING_ASC, "Citadel server", -1, -1, 0);
-
-                                       X509_NAME_add_entry_by_txt(name, "CN",
-                                               MBSTRING_ASC, "FIXME.FIXME.org", -1, -1, 0);
-                               
-                                       X509_REQ_set_subject_name(req, name);
-
-                                       /** Sign the CSR */
-                                       if (!X509_REQ_sign(req, pk, EVP_md5())) {
-                                               lprintf(3, "X509_REQ_sign(): error\n");
-                                       }
-                                       else {
-                                               /** Write it to disk. */        
-                                               fp = fopen(CTDL_CSR_PATH, "w");
-                                               if (fp != NULL) {
-                                                       chmod(CTDL_CSR_PATH, 0600);
-                                                       PEM_write_X509_REQ(fp, req);
-                                                       fclose(fp);
-                                               }
-                                       }
-
-                                       X509_REQ_free(req);
-                               }
-                       }
-
-                       RSA_free(rsa);
-               }
-
-               else {
-                       lprintf(3, "Unable to read private key.\n");
-               }
-       }
-
-
-
-       /**
-        * Generate a self-signed certificate if we don't have one.
-        */
-       if (access(CTDL_CER_PATH, R_OK) != 0) {
-               lprintf(5, "Generating a self-signed certificate.\n");
-
-               /** Same deal as before: always read the key from disk because
-                * it may or may not have just been generated.
-                */
-               fp = fopen(CTDL_KEY_PATH, "r");
-               if (fp) {
-                       rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
-                       fclose(fp);
-               }
-
-               /** This also holds true for the CSR. */
-               req = NULL;
-               cer = NULL;
-               pk = NULL;
-               if (rsa) {
-                       if (pk=EVP_PKEY_new(), pk != NULL) {
-                               EVP_PKEY_assign_RSA(pk, rsa);
-                       }
-
-                       fp = fopen(CTDL_CSR_PATH, "r");
-                       if (fp) {
-                               req = PEM_read_X509_REQ(fp, NULL, NULL, NULL);
-                               fclose(fp);
-                       }
-
-                       if (req) {
-                               if (cer = X509_new(), cer != NULL) {
-
-                                       ASN1_INTEGER_set(X509_get_serialNumber(cer), 0);
-                                       X509_set_issuer_name(cer, req->req_info->subject);
-                                       X509_set_subject_name(cer, req->req_info->subject);
-                                       X509_gmtime_adj(X509_get_notBefore(cer), 0);
-                                       X509_gmtime_adj(X509_get_notAfter(cer),(long)60*60*24*SIGN_DAYS);
-
-                                       req_pkey = X509_REQ_get_pubkey(req);
-                                       X509_set_pubkey(cer, req_pkey);
-                                       EVP_PKEY_free(req_pkey);
-                                       
-                                       /** Sign the cert */
-                                       if (!X509_sign(cer, pk, EVP_md5())) {
-                                               lprintf(3, "X509_sign(): error\n");
-                                       }
-                                       else {
-                                               /** Write it to disk. */        
-                                               fp = fopen(CTDL_CER_PATH, "w");
-                                               if (fp != NULL) {
-                                                       chmod(CTDL_CER_PATH, 0600);
-                                                       PEM_write_X509(fp, cer);
-                                                       fclose(fp);
-                                               }
-                                       }
-                                       X509_free(cer);
-                               }
-                       }
-
-                       RSA_free(rsa);
-               }
-       }
-
-       /**
-        * Now try to bind to the key and certificate.
-        * Note that we use SSL_CTX_use_certificate_chain_file() which allows
-        * the certificate file to contain intermediate certificates.
-        */
-       SSL_CTX_use_certificate_chain_file(ssl_ctx, CTDL_CER_PATH);
-       SSL_CTX_use_PrivateKey_file(ssl_ctx, CTDL_KEY_PATH, SSL_FILETYPE_PEM);
-       if ( !SSL_CTX_check_private_key(ssl_ctx) ) {
-               lprintf(3, "Cannot install certificate: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-       }
-       
-}
-
-
-/**
- * \brief starts SSL/TLS encryption for the current session.
- * \param sock the socket connection
- * \return Zero if the SSL/TLS handshake succeeded, non-zero otherwise.
- */
-int starttls(int sock) {
-       int retval, bits, alg_bits;
-       SSL *newssl;
-
-       pthread_setspecific(ThreadSSL, NULL);
-
-       if (!ssl_ctx) {
-               return(1);
-       }
-       if (!(newssl = SSL_new(ssl_ctx))) {
-               lprintf(3, "SSL_new failed: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               return(2);
-       }
-       if (!(SSL_set_fd(newssl, sock))) {
-               lprintf(3, "SSL_set_fd failed: %s\n",
-                       ERR_reason_error_string(ERR_get_error()));
-               SSL_free(newssl);
-               return(3);
-       }
-       retval = SSL_accept(newssl);
-       if (retval < 1) {
-               /**
-                * Can't notify the client of an error here; they will
-                * discover the problem at the SSL layer and should
-                * revert to unencrypted communications.
-                */
-               long errval;
-
-               errval = SSL_get_error(newssl, retval);
-               lprintf(3, "SSL_accept failed: %s\n",
-                       ERR_reason_error_string(ERR_get_error()));
-               SSL_free(newssl);
-               newssl = NULL;
-               return(4);
-       }
-       BIO_set_close(newssl->rbio, BIO_NOCLOSE);
-       bits =
-           SSL_CIPHER_get_bits(SSL_get_current_cipher(newssl),
-                               &alg_bits);
-       lprintf(5, "SSL/TLS using %s on %s (%d of %d bits)\n",
-               SSL_CIPHER_get_name(SSL_get_current_cipher(newssl)),
-               SSL_CIPHER_get_version(SSL_get_current_cipher(newssl)),
-               bits, alg_bits);
-
-       pthread_setspecific(ThreadSSL, newssl);
-       return(0);
-}
-
-
-
-/**
- * \brief shuts down the TLS connection
- *
- * WARNING:  This may make your session vulnerable to a known plaintext
- * attack in the current implmentation.
- */
-void endtls(void)
-{
-       if (THREADSSL == NULL) return;
-
-       lprintf(5, "Ending SSL/TLS\n");
-       SSL_shutdown(THREADSSL);
-       SSL_free(THREADSSL);
-       pthread_setspecific(ThreadSSL, NULL);
-}
-
-
-/**
- * \brief callback for OpenSSL mutex locks
- * \param mode which mode??????
- * \param n  how many???
- * \param file which filename ???
- * \param line what line????
- */
-void ssl_lock(int mode, int n, const char *file, int line)
-{
-       if (mode & CRYPTO_LOCK)
-               pthread_mutex_lock(SSLCritters[n]);
-       else
-               pthread_mutex_unlock(SSLCritters[n]);
-}
-
-/**
- * \brief Send binary data to the client encrypted.
- * \param buf chars to send to the client
- * \param nbytes how many chars
- */
-void client_write_ssl(char *buf, int nbytes)
-{
-       int retval;
-       int nremain;
-       char junk[1];
-
-       if (THREADSSL == NULL) return;
-
-       nremain = nbytes;
-
-       while (nremain > 0) {
-               if (SSL_want_write(THREADSSL)) {
-                       if ((SSL_read(THREADSSL, junk, 0)) < 1) {
-                               lprintf(9, "SSL_read in client_write: %s\n",
-                                               ERR_reason_error_string(ERR_get_error()));
-                       }
-               }
-               retval = SSL_write(THREADSSL, &buf[nbytes - nremain], nremain);
-               if (retval < 1) {
-                       long errval;
-
-                       errval = SSL_get_error(THREADSSL, retval);
-                       if (errval == SSL_ERROR_WANT_READ ||
-                           errval == SSL_ERROR_WANT_WRITE) {
-                               sleep(1);
-                               continue;
-                       }
-                       lprintf(9, "SSL_write got error %ld, ret %d\n", errval, retval);
-                       if (retval == -1) {
-                               lprintf(9, "errno is %d\n", errno);
-                       }
-                       endtls();
-                       return;
-               }
-               nremain -= retval;
-       }
-}
-
-
-/**
- * \brief read data from the encrypted layer.
- * \param buf charbuffer to read to 
- * \param bytes how many
- * \param timeout how long should we wait?
- * \returns what???
- */
-int client_read_ssl(char *buf, int bytes, int timeout)
-{
-#if 0
-       fd_set rfds;
-       struct timeval tv;
-       int retval;
-       int s;
-#endif
-       int len, rlen;
-       char junk[1];
-
-       if (THREADSSL == NULL) return(0);
-
-       len = 0;
-       while (len < bytes) {
-#if 0
-               /**
-                * This code is disabled because we don't need it when
-                * using blocking reads (which we are). -IO
-                */
-               FD_ZERO(&rfds);
-               s = BIO_get_fd(THREADSSL->rbio, NULL);
-               FD_SET(s, &rfds);
-               tv.tv_sec = timeout;
-               tv.tv_usec = 0;
-
-               retval = select(s + 1, &rfds, NULL, NULL, &tv);
-
-               if (FD_ISSET(s, &rfds) == 0) {
-                       return (0);
-               }
-
-#endif
-               if (SSL_want_read(THREADSSL)) {
-                       if ((SSL_write(THREADSSL, junk, 0)) < 1) {
-                               lprintf(9, "SSL_write in client_read: %s\n", ERR_reason_error_string(ERR_get_error()));
-                       }
-               }
-               rlen = SSL_read(THREADSSL, &buf[len], bytes - len);
-               if (rlen < 1) {
-                       long errval;
-
-                       errval = SSL_get_error(THREADSSL, rlen);
-                       if (errval == SSL_ERROR_WANT_READ ||
-                           errval == SSL_ERROR_WANT_WRITE) {
-                               sleep(1);
-                               continue;
-                       }
-                       lprintf(9, "SSL_read got error %ld\n", errval);
-                       endtls();
-                       return (0);
-               }
-               len += rlen;
-       }
-       return (1);
-}
-
-
-#endif                         /* HAVE_OPENSSL */
-/*@}*/
diff --git a/webcit/doxygen_groups.c b/webcit/doxygen_groups.c
deleted file mode 100644 (file)
index 3fed3e6..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * ok, hacky, but gets us nice groups. so we define sub parts to join from other 
- * files here. NO CODE IN HERE! This is comment shouldn't appear in doxygen.
- * we have: 
- * CitadelConfig; WebcitDisplayItems; WebcitHttpServer; WebcitHttpServerGDav;
- * ClientPower; Calendaring; MenuInfrastructure; CitadelCommunitacion;
- * VCards
- * WebcitHttpServerRSS; tools;
- */
-
-
-/**
- * \defgroup CitadelConfig Configuration Mechanisms
- * \brief Functions about configuring citadel / webcit
- */
-
-/*@{*/
-/*@}*/
-
-/**
- * \defgroup tools  Utility Functions
- * \brief Functions that aren't related to webcit topics
- */
-
-/*@{*/
-/*@}*/
-
-
-/**
- * \defgroup WebcitDisplayItems Display some mime types through webcit
- * \brief Functions that format mime types into HTML to the user
- */
-
-/*@{*/
-/*@}*/
-
-/**
- * \defgroup WebcitHttpServer the Webserver part
- * \brief Functions that run the HTTP-Deamon
- */
-
-/*@{*/
-/*@}*/
-
-/**
- * \defgroup WebcitHttpServerGDav Groupdav Mechanisms
- * \ingroup WebcitHttpServer
- * \brief Functions that handle groupdav requests
- */
-/*@{*/
-/*@}*/
-
-
-/**
- * \defgroup WebcitHttpServerRSS RSS Mechanisms
- * \ingroup WebcitHttpServer
- * \brief Functions that handle RSS requests
- */
-
-/*@{*/
-/*@}*/
-
-/**
- * \defgroup ClientPower Client powered Functionality
- * \brief Functions that spawn things on the webbrowser
- */
-
-/*@{*/
-/*@}*/
-
-/**
- * \defgroup Calendaring Calendaring background
- * \brief Functions that make the Business-logic of the calendaring items
- * \ingroup WebcitDisplayItems
- */
-
-/*@{*/
-/*@}*/
-
-/**
- * \defgroup VCards showing / editing VCards
- * \brief Functions that make the Business-logic of the vcard stuff
- * \ingroup WebcitDisplayItems
- */
-
-/*@{*/
-/*@}*/
-
-/**
- * \defgroup MenuInfrastructure Things that guide you through the webcit parts
- * \brief Functions that display menues, trees etc. to connect the parts of the 
- *        ui to a whole thing
- * \ingroup WebcitDisplayItems
- */
-
-/*@{*/
-/*@}*/
-
-/**
- * \defgroup CitadelCommunitacion Talk to the citadel server
- * \brief Functions that talk to the citadel server and process reviewed entities
- * \ingroup WebcitDisplayItems
- */
-
-/*@{*/
-/*@}*/
-
-
-
diff --git a/webcit/event.c b/webcit/event.c
deleted file mode 100644 (file)
index 94fac68..0000000
+++ /dev/null
@@ -1,721 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup EditCal Editing calendar events.
- * \ingroup Calendaring
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-
-
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-
-/**
- * \brief Display an event by itself (for editing)
- * \param supplied_vevent the event to edit
- * \param msgnum reference on the citserver
- */
-void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum) {
-       icalcomponent *vevent;
-       icalproperty *p;
-       icalvalue *v;
-       struct icaltimetype t_start, t_end;
-       time_t now;
-       struct tm tm_now;
-       int created_new_vevent = 0;
-       icalproperty *organizer = NULL;
-       char organizer_string[SIZ];
-       icalproperty *attendee = NULL;
-       char attendee_string[SIZ];
-       char buf[SIZ];
-       int organizer_is_me = 0;
-       int i;
-       int sequence = 0;
-
-       now = time(NULL);
-       strcpy(organizer_string, "");
-       strcpy(attendee_string, "");
-
-       if (supplied_vevent != NULL) {
-               vevent = supplied_vevent;
-               /**
-                * If we're looking at a fully encapsulated VCALENDAR
-                * rather than a VEVENT component, attempt to use the first
-                * relevant VEVENT subcomponent.  If there is none, the
-                * NULL returned by icalcomponent_get_first_component() will
-                * tell the next iteration of this function to create a
-                * new one.
-                */
-               if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) {
-                       display_edit_individual_event(
-                               icalcomponent_get_first_component(
-                                       vevent, ICAL_VEVENT_COMPONENT
-                               ), msgnum
-                       );
-                       return;
-               }
-       }
-       else {
-               vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
-               created_new_vevent = 1;
-       }
-
-       /** Learn the sequence */
-       p = icalcomponent_get_first_property(vevent, ICAL_SEQUENCE_PROPERTY);
-       if (p != NULL) {
-               sequence = icalproperty_get_sequence(p);
-       }
-
-       /** Begin output */
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Add or edit an event"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<script type=\"text/javascript\">"
-               "function grey_all_day() { "
-                       "if (document.EventForm.alldayevent.checked) {"
-                               "document.EventForm.dtstart_hour.value='0';"
-                               "document.EventForm.dtstart_hour.disabled = true;"
-                               "document.EventForm.dtstart_minute.value='0';"
-                               "document.EventForm.dtstart_minute.disabled = true;"
-                               "document.EventForm.dtend_hour.value='0';"
-                               "document.EventForm.dtend_hour.disabled = true;"
-                               "document.EventForm.dtend_minute.value='0';"
-                               "document.EventForm.dtend_minute.disabled = true;"
-                               "document.EventForm.dtend_month.disabled = true;"
-                               "document.EventForm.dtend_day.disabled = true;"
-                               "document.EventForm.dtend_year.disabled = true;"
-                       "}"
-                       "else {"
-                               "document.EventForm.dtstart_hour.disabled = false;"
-                               "document.EventForm.dtstart_minute.disabled = false;"
-                               "document.EventForm.dtend_hour.disabled = false;"
-                               "document.EventForm.dtend_minute.disabled = false;"
-                               "document.EventForm.dtend_month.disabled = false;"
-                               "document.EventForm.dtend_day.disabled = false;"
-                               "document.EventForm.dtend_year.disabled = false;"
-                       "}"
-               "}"
-               "</script>\n"
-       );
-
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       /************************************************************
-        * Uncomment this to see the UID in calendar events for debugging
-       wprintf("UID == ");
-       p = icalcomponent_get_first_property(vevent, ICAL_UID_PROPERTY);
-       if (p != NULL) {
-               escputs((char *)icalproperty_get_comment(p));
-       }
-       wprintf("<br />\n");
-       wprintf("SEQUENCE == %d<br />\n", sequence);
-       *************************************************************/
-
-       wprintf("<FORM NAME=\"EventForm\" METHOD=\"POST\" action=\"save_event\">\n");
-
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
-               msgnum);
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"calview\" VALUE=\"%s\">\n",
-               bstr("calview"));
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"year\" VALUE=\"%s\">\n",
-               bstr("year"));
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"month\" VALUE=\"%s\">\n",
-               bstr("month"));
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"day\" VALUE=\"%s\">\n",
-               bstr("day"));
-
-       /** Put it in a borderless table so it lines up nicely */
-       wprintf("<TABLE border=0 width=100%%>\n");
-
-       wprintf("<TR><TD><B>");
-       wprintf(_("Summary"));
-       wprintf("</B></TD><TD>\n"
-               "<INPUT TYPE=\"text\" NAME=\"summary\" "
-               "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
-       p = icalcomponent_get_first_property(vevent, ICAL_SUMMARY_PROPERTY);
-       if (p != NULL) {
-               escputs((char *)icalproperty_get_comment(p));
-       }
-       wprintf("\"></TD></TR>\n");
-
-       wprintf("<TR><TD><B>");
-       wprintf(_("Location"));
-       wprintf("</B></TD><TD>\n"
-               "<INPUT TYPE=\"text\" NAME=\"location\" "
-               "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
-       p = icalcomponent_get_first_property(vevent, ICAL_LOCATION_PROPERTY);
-       if (p != NULL) {
-               escputs((char *)icalproperty_get_comment(p));
-       }
-       wprintf("\"></TD></TR>\n");
-
-       wprintf("<TR><TD><B>");
-       wprintf(_("Start"));
-       wprintf("</B></TD><TD>\n");
-       p = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY);
-       if (p != NULL) {
-               t_start = icalproperty_get_dtstart(p);
-               if (t_start.is_date) {
-                       t_start.hour = 0;
-                       t_start.minute = 0;
-                       t_start.second = 0;
-               }
-       }
-       else {
-               localtime_r(&now, &tm_now);
-               if (strlen(bstr("year")) > 0) {
-                       tm_now.tm_year = atoi(bstr("year")) - 1900;
-                       tm_now.tm_mon = atoi(bstr("month")) - 1;
-                       tm_now.tm_mday = atoi(bstr("day"));
-               }
-               if (strlen(bstr("hour")) > 0) {
-                       tm_now.tm_hour = atoi(bstr("hour"));
-                       tm_now.tm_min = atoi(bstr("minute"));
-                       tm_now.tm_sec = 0;
-               }
-               else {
-                       tm_now.tm_hour = 9;
-                       tm_now.tm_min = 0;
-                       tm_now.tm_sec = 0;
-               }
-
-               t_start = icaltime_from_timet_with_zone(
-                       mktime(&tm_now),
-                       ((!strcasecmp(bstr("alldayevent"), "yes")) ? 1 : 0),
-                       icaltimezone_get_utc_timezone()
-               );
-               t_start.is_utc = 1;
-
-       }
-       display_icaltimetype_as_webform(&t_start, "dtstart");
-
-       wprintf("<INPUT TYPE=\"checkbox\" NAME=\"alldayevent\" "
-               "VALUE=\"yes\" onClick=\"grey_all_day();\""
-               " %s >%s",
-               (t_start.is_date ? "CHECKED" : "" ),
-               _("All day event")
-       );
-
-       wprintf("</TD></TR>\n");
-
-       /**
-        * If this is an all-day-event, set the end time to be identical to
-        * the start time (the hour/minute/second will be set to midnight).
-        * Otherwise extract or create it.
-        */
-       wprintf("<TR><TD><B>");
-       wprintf(_("End"));
-       wprintf("</B></TD><TD>\n");
-       if (t_start.is_date) {
-               t_end = t_start;
-       }
-       else {
-               p = icalcomponent_get_first_property(vevent,
-                                                       ICAL_DTEND_PROPERTY);
-               if (p != NULL) {
-                       t_end = icalproperty_get_dtend(p);
-               }
-               else {
-                       /**
-                        * If this is not an all-day event and there is no
-                        * end time specified, make the default one hour
-                        * from the start time.
-                        */
-                       t_end = t_start;
-                       t_end.hour += 1;
-                       t_end.second = 0;
-                       t_end = icaltime_normalize(t_end);
-                       /* t_end = icaltime_from_timet(now, 0); */
-               }
-       }
-       display_icaltimetype_as_webform(&t_end, "dtend");
-       wprintf("</TD></TR>\n");
-
-       wprintf("<TR><TD><B>");
-       wprintf(_("Notes"));
-       wprintf("</B></TD><TD>\n"
-               "<TEXTAREA NAME=\"description\" wrap=soft "
-               "ROWS=5 COLS=80 WIDTH=80>\n"
-       );
-       p = icalcomponent_get_first_property(vevent, ICAL_DESCRIPTION_PROPERTY);
-       if (p != NULL) {
-               escputs((char *)icalproperty_get_comment(p));
-       }
-       wprintf("</TEXTAREA></TD></TR>");
-
-       /**
-        * For a new event, the user creating the event should be the
-        * organizer.  Set this field accordingly.
-        */
-       if (icalcomponent_get_first_property(vevent, ICAL_ORGANIZER_PROPERTY)
-          == NULL) {
-               sprintf(organizer_string, "MAILTO:%s", WC->cs_inet_email);
-               icalcomponent_add_property(vevent,
-                       icalproperty_new_organizer(organizer_string)
-               );
-       }
-
-       /**
-        * Determine who is the organizer of this event.
-        * We need to determine "me" or "not me."
-        */
-       organizer = icalcomponent_get_first_property(vevent, ICAL_ORGANIZER_PROPERTY);
-       if (organizer != NULL) {
-               strcpy(organizer_string, icalproperty_get_organizer(organizer));
-               if (!strncasecmp(organizer_string, "MAILTO:", 7)) {
-                       strcpy(organizer_string, &organizer_string[7]);
-                       striplt(organizer_string);
-                       serv_printf("ISME %s", organizer_string);
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] == '2') {
-                               organizer_is_me = 1;
-                       }
-               }
-       }
-
-       wprintf("<TR><TD><B>");
-       wprintf(_("Organizer"));
-       wprintf("</B></TD><TD>");
-       escputs(organizer_string);
-       if (organizer_is_me) {
-               wprintf(" <FONT SIZE=-1><I>");
-               wprintf(_("(you are the organizer)"));
-               wprintf("</I></FONT>\n");
-       }
-
-       /**
-        * Transmit the organizer as a hidden field.   We don't want the user
-        * to be able to change it, but we do want it fed back to the server,
-        * especially if this is a new event and there is no organizer already
-        * in the calendar object.
-        */
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"organizer\" VALUE=\"");
-       escputs(organizer_string);
-       wprintf("\">");
-
-       wprintf("</TD></TR>\n");
-
-       /** Transparency */
-       wprintf("<TR><TD><B>");
-       wprintf(_("Show time as:"));
-       wprintf("</B></TD><TD>");
-
-       p = icalcomponent_get_first_property(vevent, ICAL_TRANSP_PROPERTY);
-       if (p == NULL) {
-               /** No transparency found.  Default to opaque (busy). */
-               p = icalproperty_new_transp(ICAL_TRANSP_OPAQUE);
-               if (p != NULL) {
-                       icalcomponent_add_property(vevent, p);
-               }
-       }
-       if (p != NULL) {
-               v = icalproperty_get_value(p);
-       }
-       else {
-               v = NULL;
-       }
-
-       wprintf("<INPUT TYPE=\"radio\" NAME=\"transp\" VALUE=\"transparent\"");
-       if (v != NULL) if (icalvalue_get_transp(v) == ICAL_TRANSP_TRANSPARENT)
-               wprintf(" CHECKED");
-       wprintf(">");
-       wprintf(_("Free"));
-       wprintf("&nbsp;&nbsp;");
-
-       wprintf("<INPUT TYPE=\"radio\" NAME=\"transp\" VALUE=\"opaque\"");
-       if (v != NULL) if (icalvalue_get_transp(v) == ICAL_TRANSP_OPAQUE)
-               wprintf(" CHECKED");
-       wprintf(">");
-       wprintf(_("Busy"));
-
-       wprintf("</TD></TR>\n");
-
-       /** Attendees */
-       wprintf("<TR><TD><B>");
-       wprintf(_("Attendees"));
-       wprintf("</B><br />"
-               "<FONT SIZE=-2>");
-       wprintf(_("(One per line)"));
-       wprintf("</FONT></TD><TD>"
-               "<TEXTAREA %s NAME=\"attendees\" wrap=soft "
-               "ROWS=3 COLS=80 WIDTH=80>\n",
-               (organizer_is_me ? "" : "DISABLED ")
-       );
-       i = 0;
-       for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY);
-           attendee != NULL;
-           attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
-               strcpy(attendee_string, icalproperty_get_attendee(attendee));
-               if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
-
-                       /** screen name or email address */
-                       strcpy(attendee_string, &attendee_string[7]);
-                       striplt(attendee_string);
-                       if (i++) wprintf("\n");
-                       escputs(attendee_string);
-                       wprintf(" ");
-
-                       /** participant status */
-                       partstat_as_string(buf, attendee);
-                       escputs(buf);
-               }
-       }
-       wprintf("</TEXTAREA></TD></TR>\n");
-
-       /** Done with properties. */
-       wprintf("</TABLE>\n<CENTER>"
-               "<INPUT TYPE=\"submit\" NAME=\"save_button\" VALUE=\"%s\">"
-               "&nbsp;&nbsp;"
-               "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
-               "&nbsp;&nbsp;"
-               "<INPUT TYPE=\"submit\" NAME=\"check_button\" "
-                               "VALUE=\"%s\">\n"
-               "&nbsp;&nbsp;"
-               "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">\n"
-               "</CENTER>\n",
-               _("Save"),
-               _("Delete"),
-               _("Check attendee availability"),
-               _("Cancel")
-       );
-
-       wprintf("</FORM>\n");
-       
-       wprintf("</td></tr></table></div>\n");
-       wprintf("<script type=\"text/javascript\">"
-               "grey_all_day();"
-               "</script>\n"
-       );
-       wDumpContent(1);
-
-       if (created_new_vevent) {
-               icalcomponent_free(vevent);
-       }
-}
-
-/**
- * \brief Save an edited event
- * \param supplied_vevent the event to save
- * \param msgnum the index on the citserver
- */
-void save_individual_event(icalcomponent *supplied_vevent, long msgnum) {
-       char buf[SIZ];
-       icalproperty *prop;
-       icalcomponent *vevent, *encaps;
-       int created_new_vevent = 0;
-       int all_day_event = 0;
-       struct icaltimetype event_start, t;
-       icalproperty *attendee = NULL;
-       char attendee_string[SIZ];
-       int i;
-       int foundit;
-       char form_attendees[SIZ];
-       char organizer_string[SIZ];
-       int sequence = 0;
-       enum icalproperty_transp formtransp = ICAL_TRANSP_NONE;
-
-       if (supplied_vevent != NULL) {
-               vevent = supplied_vevent;
-               /**
-                * If we're looking at a fully encapsulated VCALENDAR
-                * rather than a VEVENT component, attempt to use the first
-                * relevant VEVENT subcomponent.  If there is none, the
-                * NULL returned by icalcomponent_get_first_component() will
-                * tell the next iteration of this function to create a
-                * new one.
-                */
-               if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) {
-                       save_individual_event(
-                               icalcomponent_get_first_component(
-                                       vevent, ICAL_VEVENT_COMPONENT
-                               ), msgnum
-                       );
-                       return;
-               }
-       }
-       else {
-               vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
-               created_new_vevent = 1;
-       }
-
-       if ( (strlen(bstr("save_button")) > 0)
-          || (strlen(bstr("check_button")) > 0) ) {
-
-               /** Replace values in the component with ones from the form */
-
-               while (prop = icalcomponent_get_first_property(vevent,
-                     ICAL_SUMMARY_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vevent, prop);
-                       icalproperty_free(prop);
-               }
-               icalcomponent_add_property(vevent,
-                       icalproperty_new_summary(bstr("summary")));
-
-               while (prop = icalcomponent_get_first_property(vevent,
-                     ICAL_LOCATION_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vevent, prop);
-                       icalproperty_free(prop);
-               }
-               icalcomponent_add_property(vevent,
-                       icalproperty_new_location(bstr("location")));
-               
-               while (prop = icalcomponent_get_first_property(vevent,
-                     ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vevent, prop);
-                       icalproperty_free(prop);
-               }
-               icalcomponent_add_property(vevent,
-                       icalproperty_new_description(bstr("description")));
-       
-               while (prop = icalcomponent_get_first_property(vevent,
-                     ICAL_DTSTART_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vevent, prop);
-                       icalproperty_free(prop);
-               }
-
-               if (!strcmp(bstr("alldayevent"), "yes")) {
-                       all_day_event = 1;
-               }
-               else {
-                       all_day_event = 0;
-               }
-
-               if (all_day_event) {
-                       icaltime_from_webform_dateonly(&event_start, "dtstart");
-               }
-               else {
-                       icaltime_from_webform(&event_start, "dtstart");
-               }
-
-               /**
-                * The following odd-looking snippet of code looks like it
-                * takes some unnecessary steps.  It is done this way because
-                * libical incorrectly turns an "all day event" into a normal
-                * event starting at midnight (i.e. it serializes as date/time
-                * instead of just date) unless icalvalue_new_date() is used.
-                * So we force it, if this is an all day event.
-                */
-               prop = icalproperty_new_dtstart(event_start);
-               if (all_day_event) {
-                       icalproperty_set_value(prop,
-                               icalvalue_new_date(event_start)
-                       );
-               }
-
-               if (prop) icalcomponent_add_property(vevent, prop);
-               else icalproperty_free(prop);
-
-               while (prop = icalcomponent_get_first_property(vevent,
-                     ICAL_DTEND_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vevent, prop);
-                       icalproperty_free(prop);
-               }
-               while (prop = icalcomponent_get_first_property(vevent,
-                     ICAL_DURATION_PROPERTY), prop != NULL) {
-                       icalcomponent_remove_property(vevent, prop);
-                       icalproperty_free(prop);
-               }
-
-               if (all_day_event == 0) {
-                       icaltime_from_webform(&t, "dtend");     
-                       icalcomponent_add_property(vevent,
-                               icalproperty_new_dtend(icaltime_normalize(t)
-                               )
-                       );
-               }
-
-               /** See if transparency is indicated */
-               if (strlen(bstr("transp")) > 0) {
-                       if (!strcasecmp(bstr("transp"), "opaque")) {
-                               formtransp = ICAL_TRANSP_OPAQUE;
-                       }
-                       else if (!strcasecmp(bstr("transp"), "transparent")) {
-                               formtransp = ICAL_TRANSP_TRANSPARENT;
-                       }
-
-                       while (prop = icalcomponent_get_first_property(vevent, ICAL_TRANSP_PROPERTY),
-                             (prop != NULL)) {
-                               icalcomponent_remove_property(vevent, prop);
-                               icalproperty_free(prop);
-                       }
-
-                       lprintf(9, "adding new property...\n");
-                       icalcomponent_add_property(vevent, icalproperty_new_transp(formtransp));
-                       lprintf(9, "...added it.\n");
-               }
-
-               /** Give this event a UID if it doesn't have one. */
-               lprintf(9, "Give this event a UID if it doesn't have one.\n");
-               if (icalcomponent_get_first_property(vevent,
-                  ICAL_UID_PROPERTY) == NULL) {
-                       generate_uuid(buf);
-                       icalcomponent_add_property(vevent,
-                               icalproperty_new_uid(buf)
-                       );
-               }
-
-               /** Increment the sequence ID */
-               lprintf(9, "Increment the sequence ID\n");
-               while (prop = icalcomponent_get_first_property(vevent,
-                     ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
-                       i = icalproperty_get_sequence(prop);
-                       lprintf(9, "Sequence was %d\n", i);
-                       if (i > sequence) sequence = i;
-                       icalcomponent_remove_property(vevent, prop);
-                       icalproperty_free(prop);
-               }
-               ++sequence;
-               lprintf(9, "New sequence is %d.  Adding...\n", sequence);
-               icalcomponent_add_property(vevent,
-                       icalproperty_new_sequence(sequence)
-               );
-               
-               /**
-                * Set the organizer, only if one does not already exist *and*
-                * the form is supplying one
-                */
-               lprintf(9, "Setting the organizer...\n");
-               strcpy(buf, bstr("organizer"));
-               if ( (icalcomponent_get_first_property(vevent,
-                  ICAL_ORGANIZER_PROPERTY) == NULL) 
-                  && (strlen(buf) > 0) ) {
-
-                       /** set new organizer */
-                       sprintf(organizer_string, "MAILTO:%s", buf);
-                       icalcomponent_add_property(vevent,
-                               icalproperty_new_organizer(organizer_string)
-                       );
-
-               }
-
-               /**
-                * Add any new attendees listed in the web form
-                */
-               lprintf(9, "Add any new attendees\n");
-
-               /* First, strip out the parenthesized partstats.  */
-               strcpy(form_attendees, bstr("attendees"));
-               stripout(form_attendees, '(', ')');
-
-               /** Now iterate! */
-               for (i=0; i<num_tokens(form_attendees, '\n'); ++i) {
-                       extract_token(buf, form_attendees, i, '\n', sizeof buf);
-                       striplt(buf);
-                       if (strlen(buf) > 0) {
-                               lprintf(9, "Attendee: <%s>\n", buf);
-                               sprintf(attendee_string, "MAILTO:%s", buf);
-                               foundit = 0;
-
-                               for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
-                                       if (!strcasecmp(attendee_string,
-                                          icalproperty_get_attendee(attendee)))
-                                               ++foundit;
-                               }
-
-
-                               if (foundit == 0) {
-                                       icalcomponent_add_property(vevent,
-                                               icalproperty_new_attendee(attendee_string)
-                                       );
-                               }
-                       }
-               }
-
-               /**
-                * Remove any attendees *not* listed in the web form
-                */
-STARTOVER:     lprintf(9, "Remove unlisted attendees\n");
-               for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
-                       strcpy(attendee_string, icalproperty_get_attendee(attendee));
-                       if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
-                               strcpy(attendee_string, &attendee_string[7]);
-                               striplt(attendee_string);
-                               foundit = 0;
-                               for (i=0; i<num_tokens(form_attendees, '\n'); ++i) {
-                                       extract_token(buf, form_attendees, i, '\n', sizeof buf);
-                                       striplt(buf);
-                                       if (!strcasecmp(buf, attendee_string)) ++foundit;
-                               }
-                               if (foundit == 0) {
-                                       icalcomponent_remove_property(vevent, attendee);
-                                       icalproperty_free(attendee);
-                                       goto STARTOVER;
-                               }
-                       }
-               }
-
-               /**
-                * Encapsulate event into full VCALENDAR component.  Clone it first,
-                * for two reasons: one, it's easier to just free the whole thing
-                * when we're done instead of unbundling, but more importantly, we
-                * can't encapsulate something that may already be encapsulated
-                * somewhere else.
-                */
-               lprintf(9, "Encapsulating into full VCALENDAR component\n");
-               encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vevent));
-
-               /** If the user clicked 'Save' then save it to the server. */
-               lprintf(9, "Serializing it for saving\n");
-               if ( (encaps != NULL) && (strlen(bstr("save_button")) > 0) ) {
-                       serv_puts("ENT0 1|||4|||1|");
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] == '8') {
-                               serv_puts("Content-type: text/calendar");
-                               serv_puts("");
-                               serv_puts(icalcomponent_as_ical_string(encaps));
-                               serv_puts("000");
-                       }
-                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                               lprintf(9, "ENT0 REPLY: %s\n", buf);
-                       }
-                       icalcomponent_free(encaps);
-               }
-
-               /** Or, check attendee availability if the user asked for that. */
-               if ( (encaps != NULL) && (strlen(bstr("check_button")) > 0) ) {
-
-                       /** Call this function, which does the real work */
-                       check_attendee_availability(encaps);
-
-                       /** This displays the form again, with our annotations */
-                       display_edit_individual_event(encaps, msgnum);
-
-                       icalcomponent_free(encaps);
-               }
-
-       }
-
-       /**
-        * If the user clicked 'Delete' then delete it.
-        */
-       lprintf(9, "Checking to see if we have to delete an old event\n");
-       if ( (strlen(bstr("delete_button")) > 0) && (msgnum > 0L) ) {
-               serv_printf("DELE %ld", atol(bstr("msgnum")));
-               serv_getln(buf, sizeof buf);
-       }
-
-       if (created_new_vevent) {
-               icalcomponent_free(vevent);
-       }
-
-       /** If this was a save or deelete, go back to the calendar view. */
-       if (strlen(bstr("check_button")) == 0) {
-               readloop("readfwd");
-       }
-}
-
-
-#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
-
-/*@}*/
diff --git a/webcit/floors.c b/webcit/floors.c
deleted file mode 100644 (file)
index d919285..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup AdminFloor Administrative screens for floor maintenance
- * \ingroup CitadelConfig
- */
-/*@{*/
-
-#include "webcit.h"
-#include "webserver.h"
-
-
-
-
-/**
- * \brief Display floor config
- * Display floor configuration.  If prepend_html is not NULL, its contents
- * will be displayed at the top of the screen.
- * \param prepend_html pagetitle to prepend
- */
-void display_floorconfig(char *prepend_html)
-{
-       char buf[SIZ];
-
-       int floornum;
-       char floorname[SIZ];
-       int refcount;
-
-        output_headers(1, 1, 2, 0, 0, 0);
-        wprintf("<div id=\"banner\">\n"
-                "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-                "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Add/change/delete floors"));
-       wprintf("</SPAN>"
-                "</TD></TR></TABLE>\n"
-                "</div>\n<div id=\"content\">\n"
-        );
-                                                                                                                             
-       if (prepend_html != NULL) {
-               wprintf("<br /><b><i>");
-               client_write(prepend_html, strlen(prepend_html));
-               wprintf("</i></b><br /><br />\n");
-       }
-
-       serv_printf("LFLR");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#770000\"><TR><TD>");
-               wprintf("<SPAN CLASS=\"titlebar\">");
-               wprintf(_("Error"));
-               wprintf("</SPAN>\n");
-               wprintf("</TD></TR></TABLE>\n");
-               wprintf("%s<br />\n", &buf[4]);
-               wDumpContent(1);
-               return;
-       }
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<TABLE BORDER=1 WIDTH=100%% bgcolor=\"#ffffff\">\n"
-               "<TR><TH>");
-       wprintf(_("Floor number"));
-       wprintf("</TH><TH>");
-       wprintf(_("Floor name"));
-       wprintf("</TH><TH>");
-       wprintf(_("Number of rooms"));
-       wprintf("</TH><TH>");
-       wprintf(_("Floor CSS"));
-       wprintf("</TH></TR>\n");
-
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               floornum = extract_int(buf, 0);
-               extract_token(floorname, buf, 1, '|', sizeof floorname);
-               refcount = extract_int(buf, 2);
-
-               wprintf("<TR><TD><TABLE border=0><TR><TD>%d", floornum);
-               if (refcount == 0) {
-                       wprintf("</TD><TD>"
-                               "<a href=\"delete_floor?floornum=%d\">"
-                               "<FONT SIZE=-1>", floornum);
-                       wprintf(_("(delete floor)"));
-                       wprintf("</A></FONT><br />");
-               }
-               wprintf("<FONT SIZE=-1>"
-                       "<a href=\"display_editfloorpic&"
-                       "which_floor=%d\">", floornum);
-               wprintf(_("(edit graphic)"));
-               wprintf("</A></TD></TR></TABLE>");
-               wprintf("</TD>");
-
-               wprintf("<TD>"
-                       "<FORM METHOD=\"POST\" action=\"rename_floor\">"
-                       "<INPUT TYPE=\"hidden\" NAME=\"floornum\" "
-                       "VALUE=\"%d\">"
-                       "<INPUT TYPE=\"text\" NAME=\"floorname\" "
-                       "VALUE=\"%s\" MAXLENGTH=\"250\">\n",
-                       floornum, floorname);
-               wprintf("<INPUT TYPE=\"SUBMIT\" NAME=\"sc\" "
-                       "VALUE=\"%s\">"
-                       "</FORM></TD>", _("Change name"));
-
-               wprintf("<TD>%d</TD>\n", refcount);
-
-               wprintf("<TD>"
-                       "<FORM METHOD=\"POST\" action=\"set_floor_css\">"
-                       "<INPUT TYPE=\"hidden\" NAME=\"floornum\" "
-                       "VALUE=\"%d\">"
-                       "<INPUT TYPE=\"text\" NAME=\"floorcss\" "
-                       "VALUE=\"%s\" MAXLENGTH=\"250\">\n",
-                       floornum, floorname);
-               wprintf("<INPUT TYPE=\"SUBMIT\" NAME=\"sc\" "
-                       "VALUE=\"%s\">"
-                       "</FORM></TD>", _("Change CSS"));
-
-               wprintf("</TR>\n");
-       }
-
-       wprintf("<TR><TD>&nbsp;</TD>"
-               "<TD><FORM METHOD=\"POST\" action=\"create_floor\">"
-               "<INPUT TYPE=\"text\" NAME=\"floorname\" "
-               "MAXLENGTH=\"250\">\n"
-               "<INPUT TYPE=\"SUBMIT\" NAME=\"sc\" "
-               "VALUE=\"%s\">"
-               "</FORM></TD>"
-               "<TD>&nbsp;</TD></TR>\n", _("Create new floor"));
-
-       wprintf("</table></div>\n");
-       wDumpContent(1);
-}
-
-
-/**
- * \brief delete the actual floor
- */
-void delete_floor(void) {
-       int floornum;
-       char buf[SIZ];
-       char message[SIZ];
-
-       floornum = atoi(bstr("floornum"));
-
-       serv_printf("KFLR %d|1", floornum);
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] == '2') {
-               sprintf(message, _("Floor has been deleted."));
-       }
-       else {
-               sprintf(message, "%s", &buf[4]);
-       }
-
-       display_floorconfig(message);
-}
-
-/**
- * \brief tart creating a new floor
- */
-void create_floor(void) {
-       char buf[SIZ];
-       char message[SIZ];
-       char floorname[SIZ];
-
-       strcpy(floorname, bstr("floorname"));
-
-       serv_printf("CFLR %s|1", floorname);
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] == '2') {
-               sprintf(message, _("New floor has been created."));
-       } else {
-               sprintf(message, "%s", &buf[4]);
-       }
-
-       display_floorconfig(message);
-}
-
-/**
- * \brief rename this floor
- */
-void rename_floor(void) {
-       int floornum;
-       char buf[SIZ];
-       char message[SIZ];
-       char floorname[SIZ];
-
-       floornum = atoi(bstr("floornum"));
-       strcpy(floorname, bstr("floorname"));
-
-       serv_printf("EFLR %d|%s", floornum, floorname);
-       serv_getln(buf, sizeof buf);
-
-       sprintf(message, "%s", &buf[4]);
-
-       display_floorconfig(message);
-}
-
-
-/*@}*/
diff --git a/webcit/fmt_date.c b/webcit/fmt_date.c
deleted file mode 100644 (file)
index 2c7c9f6..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup FormatDates Miscellaneous routines formating dates
- * \ingroup Calendaring
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-
-typedef unsigned char byte; /**< a byte. */
-
-#define FALSE 0 /**< no. */
-#define TRUE 1 /**< yes. */
-
-/**
- * \brief      Wrapper around strftime() or strftime_l()
- *             depending upon how our build is configured.
- *
- * \param      s       String target buffer
- * \param      max     Maximum size of string target buffer
- * \param      format  strftime() format
- * \param      tm      Input date/time
- */
-size_t wc_strftime(char *s, size_t max, const char *format, const struct tm *tm)
-{
-#ifdef ENABLE_NLS
-       if (wc_locales[WC->selected_language] == NULL) {
-               return strftime(s, max, format, tm);
-       }
-       else {
-               return strftime_l(s, max, format, tm, wc_locales[WC->selected_language]);
-       }
-#else
-       return strftime(s, max, format, tm);
-#endif
-}
-
-
-/**
- * \brief Format a date/time stamp for output 
- * \param buf the output buffer
- * \param thetime time to convert to string 
- * \param brief do we want compact view?????
- */
-void fmt_date(char *buf, time_t thetime, int brief)
-{
-       struct tm tm;
-       struct tm today_tm;
-       time_t today_timet;
-       int hour;
-       char calhourformat[16];
-
-       get_preference("calhourformat", calhourformat, sizeof calhourformat);
-
-       today_timet = time(NULL);
-       localtime_r(&today_timet, &today_tm);
-
-       localtime_r(&thetime, &tm);
-       hour = tm.tm_hour;
-       if (hour == 0)
-               hour = 12;
-       else if (hour > 12)
-               hour = hour - 12;
-
-       buf[0] = 0;
-
-       if (brief) {
-
-               /** If date == today, show only the time */
-               if ((tm.tm_year == today_tm.tm_year)
-                 &&(tm.tm_mon == today_tm.tm_mon)
-                 &&(tm.tm_mday == today_tm.tm_mday)) {
-                       wc_strftime(buf, 32, "%l:%M%p", &tm);
-               }
-               /** Otherwise, for messages up to 6 months old, show the
-                * month and day, and the time */
-               else if (today_timet - thetime < 15552000) {
-                       wc_strftime(buf, 32, "%b %d %l:%M%p", &tm);
-               }
-               /** older than 6 months, show only the date */
-               else {
-                       wc_strftime(buf, 32, "%b %d %Y", &tm);
-               }
-       }
-       else {
-               wc_strftime(buf, 32, "%c", &tm);
-       }
-}
-
-
-/**
- * \brief Format TIME ONLY for output 
- * \param buf the output buffer
- * \param thetime time to format into buf
- */
-void fmt_time(char *buf, time_t thetime)
-{
-       struct tm *tm;
-       int hour;
-       char calhourformat[16];
-
-       get_preference("calhourformat", calhourformat, sizeof calhourformat);
-
-       buf[0] = 0;
-       tm = localtime(&thetime);
-       hour = tm->tm_hour;
-       if (hour == 0)
-               hour = 12;
-       else if (hour > 12)
-               hour = hour - 12;
-
-       if (!strcasecmp(calhourformat, "24")) {
-               sprintf(buf, "%2d:%02d",
-                       tm->tm_hour, tm->tm_min
-               );
-       }
-       else {
-               sprintf(buf, "%d:%02d%s",
-                       hour, tm->tm_min, ((tm->tm_hour > 12) ? "pm" : "am")
-               );
-       }
-}
-
-
-
-
-/**
- * \brief Break down the timestamp used in HTTP headers
- * Should read rfc1123 and rfc850 dates OK
- * \todo FIXME won't read asctime
- * Doesn't understand timezone, but we only should be using GMT/UTC anyway
- * \param buf time to parse
- * \return the time found in buf
- */
-time_t httpdate_to_timestamp(char *buf)
-{
-       time_t t = 0;
-       struct tm tt;
-       char *c;
-       char tz[256];
-
-       /** Skip day of week, to number */
-       for (c = buf; *c != ' '; c++)
-               ;
-       c++;
-
-       /* Get day of month */
-       tt.tm_mday = atoi(c);
-       for (; *c != ' ' && *c != '-'; c++);
-       c++;
-
-       /** Get month */
-       switch (*c) {
-       case 'A':       /** April, August */
-               tt.tm_mon = (c[1] == 'p') ? 3 : 7;
-               break;
-       case 'D':       /** December */
-               tt.tm_mon = 11;
-               break;
-       case 'F':       /** February */
-               tt.tm_mon = 1;
-               break;
-       case 'M':       /** March, May */
-               tt.tm_mon = (c[2] == 'r') ? 2 : 4;
-               break;
-       case 'J':       /** January, June, July */
-               tt.tm_mon = (c[2] == 'n') ? ((c[1] == 'a') ? 0 : 5) : 6;
-               break;
-       case 'N':       /** November */
-               tt.tm_mon = 10;
-               break;
-       case 'O':       /** October */
-               tt.tm_mon = 9;
-               break;
-       case 'S':       /** September */
-               tt.tm_mon = 8;
-               break;
-       default:
-               return 42;
-               break;  /** NOTREACHED */
-       }
-       c += 4;
-
-       tt.tm_year = 0;
-       /** Get year */
-       tt.tm_year = atoi(c);
-       for (; *c != ' '; c++);
-       c++;
-       if (tt.tm_year >= 1900)
-               tt.tm_year -= 1900;
-
-       /** Get hour */
-       tt.tm_hour = atoi(c);
-       for (; *c != ':'; c++);
-       c++;
-
-       /** Get minute */
-       tt.tm_min = atoi(c);
-       for (; *c != ':'; c++);
-       c++;
-
-       /** Get second */
-       tt.tm_sec = atoi(c);
-       for (; *c && *c != ' '; c++);
-
-       /** Got everything; let's go */
-       /** First, change to UTC */
-       if (getenv("TZ"))
-               sprintf(tz, "TZ=%s", getenv("TZ"));
-       else
-               strcpy(tz, "TZ=");
-       putenv("TZ=UTC");
-       tzset();
-       t = mktime(&tt);
-       putenv(tz);
-       tzset();
-       return t;
-}
-
-
-
-
-/*@}*/
diff --git a/webcit/gettext.c b/webcit/gettext.c
deleted file mode 100644 (file)
index ddee2f8..0000000
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * $Id
- */
-/**
- * \defgroup LocaleHeaderParser Parse the browser http locale headers and set the NLS stuff.
- * \ingroup WebcitHttpServer 
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-
-#ifdef ENABLE_NLS
-
-#define NUM_LANGS 5 /**< how many different locales do we know? */
-#define SEARCH_LANG 20 /**< how many langs should we parse? */
-
-/** actual supported locales */
-char *AvailLang[NUM_LANGS] = {
-       "C",
-       "en_US",
-       "de_DE",
-       "it_IT",
-       "en_GB"
-};
-
-locale_t wc_locales[NUM_LANGS]; /**< here we keep the parsed stuff */
-
-/** Keep information about one locale */
-typedef struct _lang_pref{
-       char lang[16];          /**< the language locale string */
-       char region[16];        /**< the region locale string */
-       long priority;          /**< which priority does it have */
-       int availability;       /**< do we know it? */
-       int selectedlang;       /**< is this the selected language? */
-} LangStruct;
-
-/* \brief parse browser locale header 
- * seems as most browsers just do a one after coma value even if more than 10 locales are available. Sample strings:
- * opera: 
- * Accept-Language: sq;q=1.0,de;q=0.9,as;q=0.8,ar;q=0.7,bn;q=0.6,zh-cn;q=0.5,kn;q=0.4,ch;q=0.3,fo;q=0.2,gn;q=0.1,ce;q=0.1,ie;q=0.1 
- * Firefox 
- * Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' 
- * Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 
- * Accept-Language: de,en-us;q=0.9,it;q=0.9,de-de;q=0.8,en-ph;q=0.7,de-at;q=0.7,zh-cn;q=0.6,cy;q=0.5,ar-om;q=0.5,en-tt;q=0.4,xh;q=0.3,nl-be;q=0.3,cs;q=0.2,sv;q=0.1,tk;q=0.1 
- * \param LocaleString the string from the browser http headers
- */
-
-void httplang_to_locale(char *LocaleString)
-{
-       LangStruct wanted_locales[SEARCH_LANG];
-       LangStruct *ls;
-
-       int i = 0;
-       int j = 0;
-       size_t len = strlen(LocaleString);
-       long prio;
-       int av;
-       int nBest;
-       int nParts;
-       char *search = (char *) malloc(len);
-       
-       memcpy(search, LocaleString, len);
-       search[len] = '\0';
-       nParts=num_tokens(search,',');
-       for (i=0; ((i<nParts)&&(i<SEARCH_LANG)); i++)
-        {
-                       char buf[16];
-                       char sbuf[16];
-                       char lbuf[16];
-                       int blen;
-                       
-                       ls=&wanted_locales[i];
-
-                       extract_token(&buf[0],search, i,',',16);
-                       /** we are searching, if this list item has something like ;q=n*/
-                       if (num_tokens(&buf[0],'=')>1) {
-                               int sbuflen, k;
-                               extract_token(&sbuf[0],&buf[0], 1,'=',16);
-                               sbuflen=strlen(&sbuf[0]);
-                               for (k=0; k<sbuflen; k++) if (sbuf[k]=='.') sbuf[k]='0';
-                               ls->priority=atol(&sbuf[0]);
-                       }
-                       else {
-                               ls->priority=1000;
-                       }
-                       /** get the locale part */
-                       extract_token(&sbuf[0],&buf[0],0,';',16);
-                       /** get the lang part, which should be allways there */
-                       extract_token(&ls->lang[0],&sbuf[0],0,'-',16);
-                       /** get the area code if any. */
-                       if (num_tokens(&sbuf[0],'-')>1) {
-                               extract_token(&ls->region[0],&sbuf[0],1,'-',16);
-                       }
-                       else { /** no ara code? use lang code */
-                               blen=strlen(&ls->lang[0]);
-                               memcpy(&ls->region[0], ls->lang,blen);
-                               ls->region[blen]='\0';
-                       } /** area codes are uppercase */
-                       blen=strlen(&ls->region[0]);
-                       for (j=0; j<blen; j++)
-                               {
-                                       int chars=toupper(ls->region[j]);
-                                       ls->region[j]=(char)chars;/** \todo ?! */
-                               }
-                       sprintf(&lbuf[0],"%s_%s",&ls->lang[0],&ls->region[0]);
-                       
-                       /** check if we have this lang */
-                       ls->availability=1;
-                       ls->selectedlang=-1;
-                       for (j=0; j<NUM_LANGS; j++) {
-                               int result;
-                               /** match against the LANG part */
-                               result=strcasecmp(&ls->lang[0], AvailLang[j]);
-                               if ((result<0)&&(result<ls->availability)){
-                                       ls->availability=result;
-                                       ls->selectedlang=j;
-                               }
-                               /** match against lang and locale */
-                               if (0==strcasecmp(&lbuf[0], AvailLang[j])){
-                                       ls->availability=0;
-                                       ls->selectedlang=j;
-                                       j=NUM_LANGS;
-                               }
-                       }
-        }
-       
-       prio=0;
-       av=-1000;
-       nBest=-1;
-       for (i=0; ((i<nParts)&&(i<SEARCH_LANG)); i++)
-               {
-                       ls=&wanted_locales[i];
-                       if ((ls->availability<=0)&& 
-                               (av<ls->availability)&&
-                               (prio<ls->priority)&&
-                               (ls->selectedlang!=-1)){
-                               nBest=ls->selectedlang;
-                               av=ls->availability;
-                               prio=ls->priority;
-                       }
-               }
-       if (nBest==-1) /** fall back to C */
-               nBest=0;
-       WC->selected_language=nBest;
-       lprintf(9, "language found: %s\n", AvailLang[WC->selected_language]);
-       if (search != NULL) {
-               free(search);
-       }
-}
-
-/* TODO: we skip the language weightening so far. */
-/* Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' */
-/* Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 */
-//void httplang_to_locale(char *LocaleString)
-//{
-//     char selected_locale[16];
-//     int i, j;
-//     char lang[64];
-//     int num_accept = 0;
-//
-//     lprintf(9, "languageAccept: %s\n", LocaleString);
-//
-//     strcpy(selected_locale, "C");
-//     num_accept = num_tokens(LocaleString, ',');
-//
-//     for (i=num_accept-1; i>=0; --i) {
-//             extract_token(lang, LocaleString, i, ',', sizeof lang);
-//
-//             /* Strip out the weights; we don't use them.  Also convert
-//              * hyphens to underscores.
-//              */
-//             for (j=0; j<strlen(lang); ++j) {
-//                     if (lang[j] == '-') lang[j] = '_';
-//                     if (lang[j] == ';') lang[j] = 0;
-//             }
-//
-//             for (j=0; j<NUM_LANGS; ++j) {
-//                     if (!strncasecmp(lang, AvailLang[j], strlen(lang))) {
-//                             strcpy(selected_locale, AvailLang[j]);
-//                     }
-//             }
-//     }
-//
-//     lprintf(9, "language found: %s\n", selected_locale);
-//     set_selected_language(selected_locale);
-//}
-
-
-/**
- * \brief show the language chooser on the login dialog
- * depending on the browser locale change the sequence of the 
- * language chooser.
- */
-void offer_languages(void) {
-       int i;
-
-       wprintf("<select name=\"language\" size=\"1\">\n");
-
-       for (i=0; i < NUM_LANGS; ++i) {
-               wprintf("<option %s value=%s>%s</option>\n",
-                       ((WC->selected_language == i) ? "selected" : ""),
-                       AvailLang[i],
-                       AvailLang[i]
-               );
-       }
-
-       wprintf("</select>\n");
-}
-
-/**
- * \brief Set the selected language for this session.
- * \param lang the locale to set.
- */
-void set_selected_language(char *lang) {
-       int i;
-
-       for (i=0; i<NUM_LANGS; ++i) {
-               if (!strcasecmp(lang, AvailLang[i])) {
-                       WC->selected_language = i;
-               }
-       }
-}
-
-/**
- * \brief Activate the selected language for this session.
- */
-void go_selected_language(void) {
-       if (WC->selected_language < 0) return;
-       uselocale(wc_locales[WC->selected_language]);   /** switch locales */
-       textdomain(textdomain(NULL));                   /** clear the cache */
-}
-
-/**
- * \brief Deactivate the selected language for this session.
- */
-void stop_selected_language(void) {
-       uselocale(LC_GLOBAL_LOCALE);                    /** switch locales */
-       textdomain(textdomain(NULL));                   /** clear the cache */
-}
-
-
-/**
- * \brief Create a locale_t for each available language
- */
-void initialize_locales(void) {
-       int i;
-       locale_t Empty_Locale;
-       char buf[32];
-
-       /* create default locale */
-       Empty_Locale = newlocale(LC_ALL_MASK, NULL, NULL);
-
-       for (i = 0; i < NUM_LANGS; ++i) {
-               if (i == 0) {
-                       sprintf(buf, "%s", AvailLang[i]);       // locale 0 (C) is ascii, not utf-8
-               }
-               else {
-                       sprintf(buf, "%s.UTF8", AvailLang[i]);
-               }
-               wc_locales[i] = newlocale(
-                       (LC_MESSAGES_MASK|LC_TIME_MASK),
-                       buf,
-                       (((i > 0) && (wc_locales[0] != NULL)) ? wc_locales[0] : Empty_Locale)
-               );
-               if (wc_locales[i] == NULL) {
-                       lprintf(1, "Error configuring locale for %s: %s\n",
-                               buf,
-                               strerror(errno)
-                       );
-               }
-               else {
-                       lprintf(3, "Configured available locale: %s\n", buf);
-               }
-       }
-}
-
-
-#else  /* ENABLE_NLS */
-/** \brief dummy for non NLS enabled systems */
-void offer_languages(void) {
-       wprintf("English (US)");
-}
-
-/** \brief dummy for non NLS enabled systems */
-void set_selected_language(char *lang) {
-}
-
-/** \brief dummy for non NLS enabled systems */
-void go_selected_language(void) {
-}
-
-/** \brief dummy for non NLS enabled systems */
-void stop_selected_language(void) {
-}
-
-#endif /* ENABLE_NLS */
-
-
-/*@}*/
diff --git a/webcit/graphics.c b/webcit/graphics.c
deleted file mode 100644 (file)
index 00f256b..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * $Id$
- *
- * Handles HTTP upload of graphics files into the system.
- * \ingroup WebcitHttpServer
- */
-
-#include "webcit.h"
-
-void display_graphics_upload(char *description, char *check_cmd, char *uplurl)
-{
-       char buf[SIZ];
-
-       serv_puts(check_cmd);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               strcpy(WC->ImportantMessage, &buf[4]);
-               display_main_menu();
-               return;
-       }
-       output_headers(1, 1, 0, 0, 0, 0);
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Image upload"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       wprintf("<CENTER>\n");
-
-       wprintf("<FORM ENCTYPE=\"multipart/form-data\" action=\"%s\" "
-               "METHOD=\"POST\" NAME=\"graphicsupload\">\n", uplurl);
-
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"which_room\" VALUE=\"");
-       urlescputs(bstr("which_room"));
-       wprintf("\">\n");
-
-       wprintf(_("You can upload any image directly from your computer, "
-               "as long as it is in GIF format (JPEG, PNG, etc. won't "
-               "work)."));
-       wprintf("<br /><br />\n");
-
-       wprintf(_("Please select a file to upload:"));
-       wprintf("<br /><br />\n");
-       wprintf("<INPUT TYPE=\"FILE\" NAME=\"filename\" SIZE=\"35\">\n");
-       wprintf("<br /><br />");
-       wprintf("<INPUT TYPE=\"SUBMIT\" NAME=\"upload_button\" VALUE=\"%s\">\n", _("Upload"));
-       wprintf("&nbsp;");
-       wprintf("<INPUT TYPE=\"RESET\" VALUE=\"%s\">\n", _("Reset form"));
-       wprintf("&nbsp;");
-       wprintf("<INPUT TYPE=\"SUBMIT\" NAME=\"cancel_button\" VALUE=\"%s\">\n", _("Cancel"));
-       wprintf("</FORM>\n");
-       wprintf("</CENTER>\n");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-void do_graphics_upload(char *upl_cmd)
-{
-       char buf[SIZ];
-       int bytes_remaining;
-       int pos = 0;
-       int thisblock;
-
-       if (strlen(bstr("cancel_button")) > 0) {
-               strcpy(WC->ImportantMessage,
-                       _("Graphics upload has been cancelled."));
-               display_main_menu();
-               return;
-       }
-
-       if (WC->upload_length == 0) {
-               strcpy(WC->ImportantMessage,
-                       _("You didn't upload a file."));
-               display_main_menu();
-               return;
-       }
-       serv_puts(upl_cmd);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               strcpy(WC->ImportantMessage, &buf[4]);
-               display_main_menu();
-               return;
-       }
-       bytes_remaining = WC->upload_length;
-       while (bytes_remaining) {
-               thisblock = ((bytes_remaining > 4096) ? 4096 : bytes_remaining);
-               serv_printf("WRIT %d", thisblock);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '7') {
-                       strcpy(WC->ImportantMessage, &buf[4]);
-                       serv_puts("UCLS 0");
-                       serv_getln(buf, sizeof buf);
-                       display_main_menu();
-                       return;
-               }
-               thisblock = extract_int(&buf[4], 0);
-               serv_write(&WC->upload[pos], thisblock);
-               pos = pos + thisblock;
-               bytes_remaining = bytes_remaining - thisblock;
-       }
-
-       serv_puts("UCLS 1");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != 'x') {
-               display_success(&buf[4]);
-               return;
-       }
-}
diff --git a/webcit/groupdav.h b/webcit/groupdav.h
deleted file mode 100644 (file)
index 2a933ad..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/* $Id$ */
-
-void groupdav_common_headers(void);
-void groupdav_main(struct httprequest *, char *, int, char *);
-void groupdav_get(char *);
-void groupdav_put(char *, char *, char *, char *, int);
-void groupdav_delete(char *, char *);
-void groupdav_propfind(char *, int, char *, char *);
-void groupdav_options(char *);
-long locate_message_by_uid(char *);
-void groupdav_folder_list(void);
-void euid_escapize(char *, char *);
-void euid_unescapize(char *, char *);
-void groupdav_identify_host(void);
diff --git a/webcit/groupdav_delete.c b/webcit/groupdav_delete.c
deleted file mode 100644 (file)
index 2d44b8f..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * $Id$
- *
- * Handles GroupDAV DELETE requests.
- *
- */
-
-#include "webcit.h"
-#include "webserver.h"
-#include "groupdav.h"
-
-
-/*
- * The pathname is always going to be /groupdav/room_name/euid
- */
-void groupdav_delete(char *dav_pathname, char *dav_ifmatch) {
-       char dav_roomname[SIZ];
-       char dav_uid[SIZ];
-       long dav_msgnum = (-1);
-       char buf[SIZ];
-       int n = 0;
-
-       /* First, break off the "/groupdav/" prefix */
-       remove_token(dav_pathname, 0, '/');
-       remove_token(dav_pathname, 0, '/');
-
-       /* Now extract the message euid */
-       n = num_tokens(dav_pathname, '/');
-       extract_token(dav_uid, dav_pathname, n-1, '/', sizeof dav_uid);
-       remove_token(dav_pathname, n-1, '/');
-
-       /* What's left is the room name.  Remove trailing slashes. */
-       if (dav_pathname[strlen(dav_pathname)-1] == '/') {
-               dav_pathname[strlen(dav_pathname)-1] = 0;
-       }
-       strcpy(dav_roomname, dav_pathname);
-
-       /* Go to the correct room. */
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               gotoroom(dav_roomname);
-       }
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               wprintf("HTTP/1.1 404 not found\r\n");
-               groupdav_common_headers();
-               wprintf("Content-Length: 0\r\n\r\n");
-               return;
-       }
-
-       dav_msgnum = locate_message_by_uid(dav_uid);
-
-       /*
-        * If no item exists with the requested uid ... simple error.
-        */
-       if (dav_msgnum < 0L) {
-               wprintf("HTTP/1.1 404 Not Found\r\n");
-               groupdav_common_headers();
-               wprintf("Content-Length: 0\r\n\r\n");
-               return;
-       }
-
-       /*
-        * It's there ... check the ETag and make sure it matches
-        * the message number.
-        */
-       if (strlen(dav_ifmatch) > 0) {
-               if (atol(dav_ifmatch) != dav_msgnum) {
-                       wprintf("HTTP/1.1 412 Precondition Failed\r\n");
-                       groupdav_common_headers();
-                       wprintf("Content-Length: 0\r\n\r\n");
-                       return;
-               }
-       }
-
-       /*
-        * Ok, attempt to delete the item.
-        */
-       serv_printf("DELE %ld", dav_msgnum);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               wprintf("HTTP/1.1 204 No Content\r\n"); /* success */
-               groupdav_common_headers();
-               wprintf("Content-Length: 0\r\n\r\n");
-       }
-       else {
-               wprintf("HTTP/1.1 403 Forbidden\r\n");  /* access denied */
-               groupdav_common_headers();
-               wprintf("Content-Length: 0\r\n\r\n");
-       }
-       return;
-}
diff --git a/webcit/groupdav_get.c b/webcit/groupdav_get.c
deleted file mode 100644 (file)
index d773fe2..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * $Id$
- *
- * Handles GroupDAV GET requests.
- *
- */
-
-#include "webcit.h"
-#include "webserver.h"
-#include "groupdav.h"
-
-
-/*
- * Fetch the entire contents of the room as one big ics file.
- * This is for "webcal://" type access.
- */    
-void groupdav_get_big_ics(void) {
-       char buf[1024];
-
-       serv_puts("ICAL getics");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               wprintf("HTTP/1.1 404 not found\r\n");
-               groupdav_common_headers();
-               wprintf(
-                       "Content-Type: text/plain\r\n"
-                       "\r\n"
-                       "%s\r\n",
-                       &buf[4]
-               );
-               return;
-       }
-
-       wprintf("HTTP/1.1 200 OK\r\n");
-       groupdav_common_headers();
-       wprintf("Content-type: text/calendar; charset=UTF-8\r\n");
-       begin_burst();
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               wprintf("%s\r\n", buf);
-       }
-       end_burst();
-}
-
-
-/*
- * The pathname is always going to take one of two formats:
- * /groupdav/room_name/euid    (GroupDAV)
- * /groupdav/room_name         (webcal)
- */
-void groupdav_get(char *dav_pathname) {
-       char dav_roomname[1024];
-       char dav_uid[1024];
-       long dav_msgnum = (-1);
-       char buf[1024];
-       int in_body = 0;
-       int found_content_type = 0;
-
-       if (num_tokens(dav_pathname, '/') < 3) {
-               wprintf("HTTP/1.1 404 not found\r\n");
-               groupdav_common_headers();
-               wprintf(
-                       "Content-Type: text/plain\r\n"
-                       "\r\n"
-                       "The object you requested was not found.\r\n"
-               );
-               return;
-       }
-
-       extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
-       extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
-       if ((!strcasecmp(dav_uid, "ics")) || (!strcasecmp(dav_uid, "calendar.ics"))) {
-               strcpy(dav_uid, "");
-       }
-
-       /* Go to the correct room. */
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               gotoroom(dav_roomname);
-       }
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               wprintf("HTTP/1.1 404 not found\r\n");
-               groupdav_common_headers();
-               wprintf(
-                       "Content-Type: text/plain\r\n"
-                       "\r\n"
-                       "There is no folder called \"%s\" on this server.\r\n",
-                       dav_roomname
-               );
-               return;
-       }
-
-       /** GET on the collection itself returns an ICS of the entire collection.
-        */
-       if (!strcasecmp(dav_uid, "")) {
-               groupdav_get_big_ics();
-               return;
-       }
-
-       dav_msgnum = locate_message_by_uid(dav_uid);
-       serv_printf("MSG2 %ld", dav_msgnum);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               wprintf("HTTP/1.1 404 not found\r\n");
-               groupdav_common_headers();
-               wprintf(
-                       "Content-Type: text/plain\r\n"
-                       "\r\n"
-                       "Object \"%s\" was not found in the \"%s\" folder.\r\n",
-                       dav_uid,
-                       dav_roomname
-               );
-               return;
-       }
-
-       wprintf("HTTP/1.1 200 OK\r\n");
-       groupdav_common_headers();
-       wprintf("etag: \"%ld\"\r\n", dav_msgnum);
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               if (in_body) {
-                       wprintf("%s\r\n", buf);
-               }
-               else if (!strncasecmp(buf, "Date: ", 6)) {
-                       wprintf("%s\r\n", buf);
-               }
-               else if (!strncasecmp(buf, "Content-type: ", 14)) {
-                       wprintf("%s", buf);
-                       if (bmstrcasestr(buf, "charset=")) {
-                               wprintf("%s\r\n", buf);
-                       }
-                       else {
-                               wprintf("%s;charset=UTF-8\r\n", buf);
-                       }
-                       found_content_type = 1;
-               }
-               else if ((strlen(buf) == 0) && (in_body == 0)) {
-                       if (!found_content_type) {
-                               wprintf("Content-type: text/plain\r\n");
-                       }
-                       in_body = 1;
-                       begin_burst();
-               }
-       }
-       end_burst();
-}
diff --git a/webcit/groupdav_main.c b/webcit/groupdav_main.c
deleted file mode 100644 (file)
index ca31fe2..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * $Id$
- *
- * Entry point for GroupDAV functions
- *
- */
-
-#include "webcit.h"
-#include "webserver.h"
-#include "groupdav.h"
-
-
-/*
- * Output HTTP headers which are common to all requests.
- *
- * Please observe that we don't use the usual output_headers()
- * and wDumpContent() functions in the GroupDAV subsystem, so we
- * do our own header stuff here.
- *
- */
-void groupdav_common_headers(void) {
-       wprintf(
-               "Server: %s / %s\r\n"
-               "Connection: close\r\n",
-               SERVER, serv_info.serv_software
-       );
-}
-
-
-
-/*
- * string conversion function
- */
-void euid_escapize(char *target, char *source) {
-       int i;
-       int target_length = 0;
-
-       strcpy(target, "");
-       for (i=0; i<strlen(source); ++i) {
-               if ( (isalnum(source[i])) || (source[i]=='-') || (source[i]=='_') ) {
-                       target[target_length] = source[i];
-                       target[++target_length] = 0;
-               }
-               else {
-                       sprintf(&target[target_length], "=%02X", source[i]);
-                       target_length += 3;
-               }
-       }
-}
-
-/*
- * string conversion function
- */
-void euid_unescapize(char *target, char *source) {
-       int a, b;
-       char hex[3];
-       int target_length = 0;
-
-       strcpy(target, "");
-
-       for (a = 0; a < strlen(source); ++a) {
-               if (source[a] == '=') {
-                       hex[0] = source[a + 1];
-                       hex[1] = source[a + 2];
-                       hex[2] = 0;
-                       b = 0;
-                       sscanf(hex, "%02x", &b);
-                       target[target_length] = b;
-                       target[++target_length] = 0;
-                       a += 2;
-               }
-               else {
-                       target[target_length] = source[a];
-                       target[++target_length] = 0;
-               }
-       }
-}
-
-
-
-
-/*
- * Main entry point for GroupDAV requests
- */
-void groupdav_main(struct httprequest *req,
-                       char *dav_content_type,
-                       int dav_content_length,
-                       char *dav_content
-) {
-       struct httprequest *rptr;
-       char dav_method[256];
-       char dav_pathname[256];
-       char dav_ifmatch[256];
-       int dav_depth;
-       char *ds;
-       int i;
-
-       strcpy(dav_method, "");
-       strcpy(dav_pathname, "");
-       strcpy(dav_ifmatch, "");
-       dav_depth = 0;
-
-       for (rptr=req; rptr!=NULL; rptr=rptr->next) {
-               if (!strncasecmp(rptr->line, "Host: ", 6)) {
-                       if (strlen(WC->http_host) == 0) {
-                               safestrncpy(WC->http_host, &rptr->line[6],
-                                       sizeof WC->http_host);
-                       }
-                }
-               if (!strncasecmp(rptr->line, "If-Match: ", 10)) {
-                        safestrncpy(dav_ifmatch, &rptr->line[10],
-                               sizeof dav_ifmatch);
-                }
-               if (!strncasecmp(rptr->line, "Depth: ", 7)) {
-                       if (!strcasecmp(&rptr->line[7], "infinity")) {
-                               dav_depth = 32767;
-                       }
-                       else if (!strcmp(&rptr->line[7], "0")) {
-                               dav_depth = 0;
-                       }
-                       else if (!strcmp(&rptr->line[7], "1")) {
-                               dav_depth = 1;
-                       }
-                }
-       }
-
-       if (!WC->logged_in) {
-               wprintf("HTTP/1.1 401 Unauthorized\r\n");
-               groupdav_common_headers();
-               wprintf("WWW-Authenticate: Basic realm=\"%s\"\r\n",
-                       serv_info.serv_humannode);
-               wprintf("Content-Length: 0\r\n\r\n");
-               return;
-       }
-
-       extract_token(dav_method, req->line, 0, ' ', sizeof dav_method);
-       extract_token(dav_pathname, req->line, 1, ' ', sizeof dav_pathname);
-       unescape_input(dav_pathname);
-
-       /* If the request does not begin with "/groupdav", prepend it.  If
-        * we happen to introduce a double-slash, that's ok; we'll strip it
-        * in the next step.
-        * 
-        * (THIS IS DISABLED BECAUSE WE ARE NOW TRYING TO DO REAL DAV.)
-        *
-       if (strncasecmp(dav_pathname, "/groupdav", 9)) {
-               char buf[512];
-               snprintf(buf, sizeof buf, "/groupdav/%s", dav_pathname);
-               safestrncpy(dav_pathname, buf, sizeof dav_pathname);
-       }
-        *
-        */
-       
-       /* Remove any stray double-slashes in pathname */
-       while (ds=strstr(dav_pathname, "//"), ds != NULL) {
-               strcpy(ds, ds+1);
-       }
-
-       /*
-        * If there's an If-Match: header, strip out the quotes if present, and
-        * then if all that's left is an asterisk, make it go away entirely.
-        */
-       if (strlen(dav_ifmatch) > 0) {
-               striplt(dav_ifmatch);
-               if (dav_ifmatch[0] == '\"') {
-                       strcpy(dav_ifmatch, &dav_ifmatch[1]);
-                       for (i=0; i<strlen(dav_ifmatch); ++i) {
-                               if (dav_ifmatch[i] == '\"') {
-                                       dav_ifmatch[i] = 0;
-                               }
-                       }
-               }
-               if (!strcmp(dav_ifmatch, "*")) {
-                       strcpy(dav_ifmatch, "");
-               }
-       }
-
-       /*
-        * The OPTIONS method is not required by GroupDAV.  This is an
-        * experiment to determine what might be involved in supporting
-        * other variants of DAV in the future.
-        */
-       if (!strcasecmp(dav_method, "OPTIONS")) {
-               groupdav_options(dav_pathname);
-               return;
-       }
-
-       /*
-        * The PROPFIND method is basically used to list all objects in a
-        * room, or to list all relevant rooms on the server.
-        */
-       if (!strcasecmp(dav_method, "PROPFIND")) {
-               groupdav_propfind(dav_pathname, dav_depth,
-                               dav_content_type, dav_content);
-               return;
-       }
-
-       /*
-        * The GET method is used for fetching individual items.
-        */
-       if (!strcasecmp(dav_method, "GET")) {
-               groupdav_get(dav_pathname);
-               return;
-       }
-
-       /*
-        * The PUT method is used to add or modify items.
-        */
-       if (!strcasecmp(dav_method, "PUT")) {
-               groupdav_put(dav_pathname, dav_ifmatch,
-                               dav_content_type, dav_content,
-                               dav_content_length);
-               return;
-       }
-
-       /*
-        * The DELETE method kills, maims, and destroys.
-        */
-       if (!strcasecmp(dav_method, "DELETE")) {
-               groupdav_delete(dav_pathname, dav_ifmatch);
-               return;
-       }
-
-       /*
-        * Couldn't find what we were looking for.  Die in a car fire.
-        */
-       wprintf("HTTP/1.1 501 Method not implemented\r\n");
-       groupdav_common_headers();
-       wprintf("Content-Type: text/plain\r\n"
-               "\r\n"
-               "GroupDAV method \"%s\" is not implemented.\r\n",
-               dav_method
-       );
-}
-
-
-/*
- * Output our host prefix for globally absolute URL's.
- */  
-void groupdav_identify_host(void) {
-       if (strlen(WC->http_host) > 0) {
-               wprintf("%s://%s",
-                       (is_https ? "https" : "http"),
-                       WC->http_host);
-       }
-}
diff --git a/webcit/groupdav_options.c b/webcit/groupdav_options.c
deleted file mode 100644 (file)
index b92b794..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * $Id$
- *
- * Handles DAV OPTIONS requests (experimental -- not required by GroupDAV)
- *
- */
-
-#include "webcit.h"
-#include "webserver.h"
-#include "groupdav.h"
-
-/*
- * The pathname is always going to be /groupdav/room_name/msg_num
- */
-void groupdav_options(char *dav_pathname) {
-       char dav_roomname[256];
-       char dav_uid[256];
-       long dav_msgnum = (-1);
-       char datestring[256];
-       time_t now;
-
-       now = time(NULL);
-       http_datestring(datestring, sizeof datestring, now);
-
-       extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
-       extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
-
-       /*
-        * If the room name is blank, the client is doing a top-level OPTIONS.
-        */
-       if (strlen(dav_roomname) == 0) {
-               wprintf("HTTP/1.1 200 OK\r\n");
-               groupdav_common_headers();
-               wprintf("Date: %s\r\n", datestring);
-               wprintf("DAV: 1\r\n");
-               wprintf("Allow: OPTIONS, PROPFIND\r\n");
-               wprintf("\r\n");
-               return;
-       }
-
-       /* Go to the correct room. */
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               gotoroom(dav_roomname);
-       }
-
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               wprintf("HTTP/1.1 404 not found\r\n");
-               groupdav_common_headers();
-               wprintf("Date: %s\r\n", datestring);
-               wprintf(
-                       "Content-Type: text/plain\r\n"
-                       "\r\n"
-                       "There is no folder called \"%s\" on this server.\r\n",
-                       dav_roomname
-               );
-               return;
-       }
-
-       /* If dav_uid is non-empty, client is requesting an OPTIONS on
-        * a specific item in the room.
-        */
-       if (strlen(dav_uid) > 0) {
-
-               dav_msgnum = locate_message_by_uid(dav_uid);
-               if (dav_msgnum < 0) {
-                       wprintf("HTTP/1.1 404 not found\r\n");
-                       groupdav_common_headers();
-                       wprintf(
-                               "Content-Type: text/plain\r\n"
-                               "\r\n"
-                               "Object \"%s\" was not found in the \"%s\" folder.\r\n",
-                               dav_uid,
-                               dav_roomname
-                       );
-                       return;
-               }
-
-               wprintf("HTTP/1.1 200 OK\r\n");
-               groupdav_common_headers();
-               wprintf("Date: %s\r\n", datestring);
-               wprintf("DAV: 1\r\n");
-               wprintf("Allow: OPTIONS, PROPFIND, GET, PUT, DELETE\r\n");
-               wprintf("\r\n");
-               return;
-       }
-
-       /*
-        * We got to this point, which means that the client is requesting
-        * an OPTIONS on the room itself.
-        */
-       wprintf("HTTP/1.1 200 OK\r\n");
-       groupdav_common_headers();
-       wprintf("Date: %s\r\n", datestring);
-       wprintf("DAV: 1\r\n");
-       wprintf("Allow: OPTIONS, PROPFIND, GET, PUT\r\n");
-       wprintf("\r\n");
-}
diff --git a/webcit/groupdav_propfind.c b/webcit/groupdav_propfind.c
deleted file mode 100644 (file)
index 8e6154d..0000000
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * $Id$
- *
- * Handles GroupDAV PROPFIND requests.
- *
- * A few notes about our XML output:
- *
- * --> Yes, we are spewing tags directly instead of using an XML library.
- *     If you would like to rewrite this using libxml2, code it up and submit
- *     a patch.  Whining will be summarily ignored.
- *
- * --> XML is deliberately output with no whitespace/newlines between tags.
- *     This makes it difficult to read, but we have discovered clients which
- *     crash when you try to pretty it up.
- *
- */
-
-#include "webcit.h"
-#include "webserver.h"
-#include "groupdav.h"
-
-/*
- * Given an encoded UID, translate that to an unencoded Citadel EUID and
- * then search for it in the current room.  Return a message number or -1
- * if not found.
- *
- */
-long locate_message_by_uid(char *uid) {
-       char buf[256];
-       char decoded_uid[1024];
-       long retval = (-1L);
-
-       /* Decode the uid */
-       euid_unescapize(decoded_uid, uid);
-
-/**************  THE NEW WAY ***********************/
-       serv_printf("EUID %s", decoded_uid);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               retval = atol(&buf[4]);
-       }
-/***************************************************/
-
-/**************  THE OLD WAY ***********************
-       serv_puts("MSGS ALL|0|1");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '8') {
-               serv_printf("exti|%s", decoded_uid);
-               serv_puts("000");
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       retval = atol(buf);
-               }
-       }
- ***************************************************/
-
-       return(retval);
-}
-
-
-
-/*
- * List rooms (or "collections" in DAV terminology) which contain
- * interesting groupware objects.
- */
-void groupdav_collection_list(char *dav_pathname, int dav_depth)
-{
-       char buf[256];
-       char roomname[256];
-       int view;
-       char datestring[256];
-       time_t now;
-       time_t mtime;
-       int is_groupware_collection = 0;
-       int starting_point = 1;         /**< 0 for /, 1 for /groupdav/ */
-
-       if (!strcmp(dav_pathname, "/")) {
-               starting_point = 0;
-       }
-       else if (!strcasecmp(dav_pathname, "/groupdav")) {
-               starting_point = 1;
-       }
-       else if (!strcasecmp(dav_pathname, "/groupdav/")) {
-               starting_point = 1;
-       }
-       else if ( (!strncasecmp(dav_pathname, "/groupdav/", 10)) && (strlen(dav_pathname) > 10) ) {
-               starting_point = 2;
-       }
-
-       now = time(NULL);
-       http_datestring(datestring, sizeof datestring, now);
-
-       /**
-        * Be rude.  Completely ignore the XML request and simply send them
-        * everything we know about.  Let the client sort it out.
-        */
-       wprintf("HTTP/1.0 207 Multi-Status\r\n");
-       groupdav_common_headers();
-       wprintf("Date: %s\r\n", datestring);
-       wprintf("Content-type: text/xml\r\n");
-       wprintf("Content-encoding: identity\r\n");
-
-       begin_burst();
-
-       wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
-               "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
-       );
-
-       /**
-        *      If the client is requesting the root, show a root node.
-        */
-       if (starting_point == 0) {
-               wprintf("<response>");
-                       wprintf("<href>");
-                               groupdav_identify_host();
-                               wprintf("/");
-                       wprintf("</href>");
-                       wprintf("<propstat>");
-                               wprintf("<status>HTTP/1.1 200 OK</status>");
-                               wprintf("<prop>");
-                                       wprintf("<displayname>/</displayname>");
-                                       wprintf("<resourcetype><collection/></resourcetype>");
-                                       wprintf("<getlastmodified>");
-                                               escputs(datestring);
-                                       wprintf("</getlastmodified>");
-                               wprintf("</prop>");
-                       wprintf("</propstat>");
-               wprintf("</response>");
-       }
-
-       /**
-        *      If the client is requesting "/groupdav", show a /groupdav subdirectory.
-        */
-       if ((starting_point + dav_depth) >= 1) {
-               wprintf("<response>");
-                       wprintf("<href>");
-                               groupdav_identify_host();
-                               wprintf("/groupdav");
-                       wprintf("</href>");
-                       wprintf("<propstat>");
-                               wprintf("<status>HTTP/1.1 200 OK</status>");
-                               wprintf("<prop>");
-                                       wprintf("<displayname>GroupDAV</displayname>");
-                                       wprintf("<resourcetype><collection/></resourcetype>");
-                                       wprintf("<getlastmodified>");
-                                               escputs(datestring);
-                                       wprintf("</getlastmodified>");
-                               wprintf("</prop>");
-                       wprintf("</propstat>");
-               wprintf("</response>");
-       }
-
-       /**
-        *      Now go through the list and make it look like a DAV collection
-        */
-       serv_puts("LKRA");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-
-               extract_token(roomname, buf, 0, '|', sizeof roomname);
-               view = extract_int(buf, 7);
-               mtime = extract_long(buf, 8);
-               http_datestring(datestring, sizeof datestring, mtime);
-
-               /*
-                * For now, only list rooms that we know a GroupDAV client
-                * might be interested in.  In the future we may add
-                * the rest.
-                *
-                * We determine the type of objects which are stored in each
-                * room by looking at the *default* view for the room.  This
-                * allows, for example, a Calendar room to appear as a
-                * GroupDAV calendar even if the user has switched it to a
-                * Calendar List view.
-                */
-               if ((view == VIEW_CALENDAR) || (view == VIEW_TASKS) || (view == VIEW_ADDRESSBOOK) ) {
-                       is_groupware_collection = 1;
-               }
-               else {
-                       is_groupware_collection = 0;
-               }
-
-               if ( (is_groupware_collection) && ((starting_point + dav_depth) >= 2) ) {
-                       wprintf("<response>");
-
-                       wprintf("<href>");
-                       groupdav_identify_host();
-                       wprintf("/groupdav/");
-                       urlescputs(roomname);
-                       wprintf("/</href>");
-
-                       wprintf("<propstat>");
-                       wprintf("<status>HTTP/1.1 200 OK</status>");
-                       wprintf("<prop>");
-                       wprintf("<displayname>");
-                       escputs(roomname);
-                       wprintf("</displayname>");
-                       wprintf("<resourcetype><collection/>");
-
-                       switch(view) {
-                               case VIEW_CALENDAR:
-                                       wprintf("<G:vevent-collection />");
-                                       break;
-                               case VIEW_TASKS:
-                                       wprintf("<G:vtodo-collection />");
-                                       break;
-                               case VIEW_ADDRESSBOOK:
-                                       wprintf("<G:vcard-collection />");
-                                       break;
-                       }
-
-                       wprintf("</resourcetype>");
-                       wprintf("<getlastmodified>");
-                               escputs(datestring);
-                       wprintf("</getlastmodified>");
-                       wprintf("</prop>");
-                       wprintf("</propstat>");
-                       wprintf("</response>");
-               }
-       }
-       wprintf("</multistatus>\n");
-
-       end_burst();
-}
-
-
-
-/*
- * The pathname is always going to be /groupdav/room_name/msg_num
- */
-void groupdav_propfind(char *dav_pathname, int dav_depth, char *dav_content_type, char *dav_content) {
-       char dav_roomname[256];
-       char dav_uid[256];
-       char msgnum[256];
-       long dav_msgnum = (-1);
-       char buf[256];
-       char uid[256];
-       char encoded_uid[256];
-       long *msgs = NULL;
-       int num_msgs = 0;
-       int i;
-       char datestring[256];
-       time_t now;
-
-       now = time(NULL);
-       http_datestring(datestring, sizeof datestring, now);
-
-       extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
-       extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
-
-       /*
-        * If the room name is blank, the client is requesting a
-        * folder list.
-        */
-       if (strlen(dav_roomname) == 0) {
-               groupdav_collection_list(dav_pathname, dav_depth);
-               return;
-       }
-
-       /* Go to the correct room. */
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               gotoroom(dav_roomname);
-       }
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               wprintf("HTTP/1.1 404 not found\r\n");
-               groupdav_common_headers();
-               wprintf("Date: %s\r\n", datestring);
-               wprintf(
-                       "Content-Type: text/plain\r\n"
-                       "\r\n"
-                       "There is no folder called \"%s\" on this server.\r\n",
-                       dav_roomname
-               );
-               return;
-       }
-
-       /* If dav_uid is non-empty, client is requesting a PROPFIND on
-        * a specific item in the room.  This is not valid GroupDAV, but
-        * it is valid WebDAV.
-        */
-       if (strlen(dav_uid) > 0) {
-
-               dav_msgnum = locate_message_by_uid(dav_uid);
-               if (dav_msgnum < 0) {
-                       wprintf("HTTP/1.1 404 not found\r\n");
-                       groupdav_common_headers();
-                       wprintf(
-                               "Content-Type: text/plain\r\n"
-                               "\r\n"
-                               "Object \"%s\" was not found in the \"%s\" folder.\r\n",
-                               dav_uid,
-                               dav_roomname
-                       );
-                       return;
-               }
-
-               /* Be rude.  Completely ignore the XML request and simply send them
-                * everything we know about (which is going to simply be the ETag and
-                * nothing else).  Let the client-side parser sort it out.
-                */
-               wprintf("HTTP/1.0 207 Multi-Status\r\n");
-               groupdav_common_headers();
-               wprintf("Date: %s\r\n", datestring);
-               wprintf("Content-type: text/xml\r\n");
-               wprintf("Content-encoding: identity\r\n");
-       
-               begin_burst();
-       
-               wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
-                       "<multistatus xmlns=\"DAV:\">"
-               );
-
-               wprintf("<response>");
-
-               wprintf("<href>");
-               groupdav_identify_host();
-               wprintf("/groupdav/");
-               urlescputs(WC->wc_roomname);
-               euid_escapize(encoded_uid, dav_uid);
-               wprintf("/%s", encoded_uid);
-               wprintf("</href>");
-               wprintf("<propstat>");
-               wprintf("<status>HTTP/1.1 200 OK</status>");
-               wprintf("<prop><getetag>\"%ld\"</getetag></prop>", dav_msgnum);
-               wprintf("</propstat>");
-
-               wprintf("</response>\n");
-               wprintf("</multistatus>\n");
-               end_burst();
-               return;
-       }
-
-
-       /*
-        * We got to this point, which means that the client is requesting
-        * a 'collection' (i.e. a list of all items in the room).
-        *
-        * Be rude.  Completely ignore the XML request and simply send them
-        * everything we know about (which is going to simply be the ETag and
-        * nothing else).  Let the client-side parser sort it out.
-        */
-       wprintf("HTTP/1.0 207 Multi-Status\r\n");
-       groupdav_common_headers();
-       wprintf("Date: %s\r\n", datestring);
-       wprintf("Content-type: text/xml\r\n");
-       wprintf("Content-encoding: identity\r\n");
-
-       begin_burst();
-
-       wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
-               "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
-       );
-
-
-       /** Transmit the collection resource (FIXME check depth and starting point) */
-       wprintf("<response>");
-
-       wprintf("<href>");
-               groupdav_identify_host();
-               wprintf("/groupdav/");
-               urlescputs(WC->wc_roomname);
-       wprintf("</href>");
-
-       wprintf("<propstat>");
-       wprintf("<status>HTTP/1.1 200 OK</status>");
-       wprintf("<prop>");
-       wprintf("<displayname>");
-       escputs(WC->wc_roomname);
-       wprintf("</displayname>");
-       wprintf("<resourcetype><collection/>");
-
-       switch(WC->wc_default_view) {
-               case VIEW_CALENDAR:
-                       wprintf("<G:vevent-collection />");
-                       break;
-               case VIEW_TASKS:
-                       wprintf("<G:vtodo-collection />");
-                       break;
-               case VIEW_ADDRESSBOOK:
-                       wprintf("<G:vcard-collection />");
-                       break;
-       }
-
-       wprintf("</resourcetype>");
-       /* FIXME get the mtime
-       wprintf("<getlastmodified>");
-               escputs(datestring);
-       wprintf("</getlastmodified>");
-       */
-       wprintf("</prop>");
-       wprintf("</propstat>");
-       wprintf("</response>");
-
-       /** Transmit the collection listing (FIXME check depth and starting point) */
-
-       serv_puts("MSGS ALL");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') while (serv_getln(msgnum, sizeof msgnum), strcmp(msgnum, "000")) {
-               msgs = realloc(msgs, ++num_msgs * sizeof(long));
-               msgs[num_msgs-1] = atol(msgnum);
-       }
-
-       if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
-
-               strcpy(uid, "");
-               serv_printf("MSG0 %ld|3", msgs[i]);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       if (!strncasecmp(buf, "exti=", 5)) {
-                               strcpy(uid, &buf[5]);
-                       }
-               }
-
-               if (strlen(uid) > 0) {
-                       wprintf("<response>");
-                               wprintf("<href>");
-                                       groupdav_identify_host();
-                                       wprintf("/groupdav/");
-                                       urlescputs(WC->wc_roomname);
-                                       euid_escapize(encoded_uid, uid);
-                                       wprintf("/%s", encoded_uid);
-                               wprintf("</href>");
-                               wprintf("<propstat>");
-                                       wprintf("<status>HTTP/1.1 200 OK</status>");
-                                       wprintf("<prop>");
-                                               wprintf("<getetag>\"%ld\"</getetag>", msgs[i]);
-                                       wprintf("</prop>");
-                               wprintf("</propstat>");
-                       wprintf("</response>");
-               }
-       }
-
-       wprintf("</multistatus>\n");
-       end_burst();
-
-       if (msgs != NULL) {
-               free(msgs);
-       }
-}
diff --git a/webcit/groupdav_put.c b/webcit/groupdav_put.c
deleted file mode 100644 (file)
index 21eaefb..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * $Id$
- *
- * Handles GroupDAV PUT requests.
- *
- */
-
-#include "webcit.h"
-#include "webserver.h"
-#include "groupdav.h"
-
-
-/*
- * This function is for uploading an ENTIRE calendar, not just one
- * component.  This would be for webcal:// 'publish' operations, not
- * for GroupDAV.
- */
-void groupdav_put_bigics(char *dav_content, int dav_content_length)
-{
-       char buf[1024];
-
-       serv_puts("ICAL putics");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '4') {
-               wprintf("HTTP/1.1 502 Bad Gateway\r\n");
-               groupdav_common_headers();
-               wprintf("Content-type: text/plain\r\n"
-                       "\r\n"
-                       "%s\r\n", &buf[4]
-               );
-               return;
-       }
-
-       serv_write(dav_content, dav_content_length);
-       serv_printf("\n000");
-
-       /* Report success and not much else. */
-       wprintf("HTTP/1.1 204 No Content\r\n");
-       lprintf(9, "HTTP/1.1 204 No Content\r\n");
-       groupdav_common_headers();
-       wprintf("Content-Length: 0\r\n\r\n");
-}
-
-
-
-/*
- * The pathname is always going to take one of two formats:
- * /groupdav/room_name/euid    (GroupDAV)
- * /groupdav/room_name         (webcal)
- */
-void groupdav_put(char *dav_pathname, char *dav_ifmatch,
-               char *dav_content_type, char *dav_content,
-               int dav_content_length
-) {
-       char dav_roomname[1024];
-       char dav_uid[1024];
-       long new_msgnum = (-2L);
-       long old_msgnum = (-1L);
-       char buf[SIZ];
-       int n = 0;
-
-       if (num_tokens(dav_pathname, '/') < 3) {
-               wprintf("HTTP/1.1 404 not found\r\n");
-               groupdav_common_headers();
-               wprintf(
-                       "Content-Type: text/plain\r\n"
-                       "\r\n"
-                       "The object you requested was not found.\r\n"
-               );
-               return;
-       }
-
-       extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
-       extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
-       if ((!strcasecmp(dav_uid, "ics")) || (!strcasecmp(dav_uid, "calendar.ics"))) {
-               strcpy(dav_uid, "");
-       }
-
-       /* Go to the correct room. */
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               gotoroom(dav_roomname);
-       }
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               wprintf("HTTP/1.1 404 not found\r\n");
-               groupdav_common_headers();
-               wprintf(
-                       "Content-Type: text/plain\r\n"
-                       "\r\n"
-                       "There is no folder called \"%s\" on this server.\r\n",
-                       dav_roomname
-               );
-               return;
-       }
-
-       /*
-        * If an HTTP If-Match: header is present, the client is attempting
-        * to replace an existing item.  We have to check to see if the
-        * message number associated with the supplied uid matches what the
-        * client is expecting.  If not, the server probably contains a newer
-        * version, so we fail...
-        */
-       if (strlen(dav_ifmatch) > 0) {
-               lprintf(9, "dav_ifmatch: %s\n", dav_ifmatch);
-               old_msgnum = locate_message_by_uid(dav_uid);
-               lprintf(9, "old_msgnum:  %ld\n", old_msgnum);
-               if (atol(dav_ifmatch) != old_msgnum) {
-                       wprintf("HTTP/1.1 412 Precondition Failed\r\n");
-                       lprintf(9, "HTTP/1.1 412 Precondition Failed (ifmatch=%ld, old_msgnum=%ld)\r\n",
-                               atol(dav_ifmatch), old_msgnum);
-                       groupdav_common_headers();
-                       wprintf("Content-Length: 0\r\n\r\n");
-                       return;
-               }
-       }
-
-       /** PUT on the collection itself uploads an ICS of the entire collection.
-        */
-       if (!strcasecmp(dav_uid, "")) {
-               groupdav_put_bigics(dav_content, dav_content_length);
-               return;
-       }
-
-       /*
-        * We are cleared for upload!  We use the new calling syntax for ENT0
-        * which allows a confirmation to be sent back to us.  That's how we
-        * extract the message ID.
-        */
-       serv_puts("ENT0 1|||4|||1|");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '8') {
-               wprintf("HTTP/1.1 502 Bad Gateway\r\n");
-               groupdav_common_headers();
-               wprintf("Content-type: text/plain\r\n"
-                       "\r\n"
-                       "%s\r\n", &buf[4]
-               );
-               return;
-       }
-
-       /* Send the content to the Citadel server */
-       serv_printf("Content-type: %s\n\n", dav_content_type);
-       serv_puts(dav_content);
-       serv_puts("\n000");
-
-       /* Fetch the reply from the Citadel server */
-       n = 0;
-       strcpy(dav_uid, "");
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               switch(n++) {
-                       case 0: new_msgnum = atol(buf);
-                               break;
-                       case 1: lprintf(9, "new_msgnum=%ld (%s)\n", new_msgnum, buf);
-                               break;
-                       case 2: strcpy(dav_uid, buf);
-                               break;
-                       default:
-                               break;
-               }
-       }
-
-       /* Tell the client what happened. */
-
-       /* Citadel failed in some way? */
-       if (new_msgnum < 0L) {
-               wprintf("HTTP/1.1 502 Bad Gateway\r\n");
-               groupdav_common_headers();
-               wprintf("Content-type: text/plain\r\n"
-                       "\r\n"
-                       "new_msgnum is %ld\r\n"
-                       "\r\n", new_msgnum
-               );
-               return;
-       }
-
-       /* We created this item for the first time. */
-       if (old_msgnum < 0L) {
-               wprintf("HTTP/1.1 201 Created\r\n");
-               lprintf(9, "HTTP/1.1 201 Created\r\n");
-               groupdav_common_headers();
-               wprintf("etag: \"%ld\"\r\n", new_msgnum);
-               wprintf("Content-Length: 0\r\n");
-               wprintf("Location: ");
-               groupdav_identify_host();
-               wprintf("/groupdav/");
-               urlescputs(dav_roomname);
-               wprintf("/%s\r\n", dav_uid);
-               wprintf("\r\n");
-               return;
-       }
-
-       /* We modified an existing item. */
-       wprintf("HTTP/1.1 204 No Content\r\n");
-       lprintf(9, "HTTP/1.1 204 No Content\r\n");
-       groupdav_common_headers();
-       wprintf("etag: \"%ld\"\r\n", new_msgnum);
-       wprintf("Content-Length: 0\r\n\r\n");
-
-       /* The item we replaced has probably already been deleted by
-        * the Citadel server, but we'll do this anyway, just in case.
-        */
-       serv_printf("DELE %ld", old_msgnum);
-       serv_getln(buf, sizeof buf);
-
-       return;
-}
diff --git a/webcit/html2html.c b/webcit/html2html.c
deleted file mode 100644 (file)
index ca8804c..0000000
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup HTML2HTML Output an HTML message, modifying it slightly to make sure it plays nice
- * with the rest of our web framework.
- * \ingroup WebcitHttpServer
- */
-/*@{*/
-#include "webcit.h"
-#include "vcard.h"
-#include "webserver.h"
-
-
-/**
- * \brief      Strip surrounding single or double quotes from a string.
- *
- * \param s    String to be stripped.
- */
-void stripquotes(char *s)
-{
-       int len;
-
-       if (!s) return;
-
-       len = strlen(s);
-       if (len < 2) return;
-
-       if ( ( (s[0] == '\"') && (s[len-1] == '\"') ) || ( (s[0] == '\'') && (s[len-1] == '\'') ) ) {
-               s[len-1] = 0;
-               strcpy(s, &s[1]);
-       }
-}
-
-
-/**
- * \brief Check to see if a META tag has overridden the declared MIME character set.
- *
- * \param charset              Character set name (left unchanged if we don't do anything)
- * \param meta_http_equiv      Content of the "http-equiv" portion of the META tag
- * \param meta_content         Content of the "content" portion of the META tag
- */
-void extract_charset_from_meta(char *charset, char *meta_http_equiv, char *meta_content)
-{
-       char *ptr;
-       char buf[64];
-
-       if (!charset) return;
-       if (!meta_http_equiv) return;
-       if (!meta_content) return;
-
-
-       if (strcasecmp(meta_http_equiv, "Content-type")) return;
-
-       ptr = strchr(meta_content, ';');
-       if (!ptr) return;
-
-       safestrncpy(buf, ++ptr, sizeof buf);
-       striplt(buf);
-       if (!strncasecmp(buf, "charset=", 8)) {
-               strcpy(charset, &buf[8]);
-       }
-}
-
-
-
-/**
- * \brief Sanitize and enhance an HTML message for display.
- *        Also convert weird character sets to UTF-8 if necessary.
- *
- * \param supplied_charset the input charset as declared in the MIME headers
- */
-void output_html(char *supplied_charset, int treat_as_wiki) {
-       char buf[SIZ];
-       char *msg;
-       char *ptr;
-       char *msgstart;
-       char *msgend;
-       char *converted_msg;
-       int buffer_length = 1;
-       int line_length = 0;
-       int content_length = 0;
-       int output_length = 0;
-       char new_window[SIZ];
-       int brak = 0;
-       int alevel = 0;
-       int i;
-       int linklen;
-       char charset[128];
-#ifdef HAVE_ICONV
-       iconv_t ic = (iconv_t)(-1) ;
-       char *ibuf;                   /**< Buffer of characters to be converted */
-       char *obuf;                   /**< Buffer for converted characters      */
-       size_t ibuflen;               /**< Length of input buffer               */
-       size_t obuflen;               /**< Length of output buffer              */
-       char *osav;                   /**< Saved pointer to output buffer       */
-#endif
-
-       safestrncpy(charset, supplied_charset, sizeof charset);
-       msg = strdup("");
-       sprintf(new_window, "<a target=\"%s\" href=", TARGET);
-
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               line_length = strlen(buf);
-               buffer_length = content_length + line_length + 2;
-               ptr = realloc(msg, buffer_length);
-               if (ptr == NULL) {
-                       wprintf("<b>");
-                       wprintf(_("realloc() error! couldn't get %d bytes: %s"),
-                               buffer_length + 1,
-                               strerror(errno));
-                       wprintf("</b><br /><br />\n");
-                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                               /** flush */
-                       }
-                       free(msg);
-                       return;
-               }
-               msg = ptr;
-               strcpy(&msg[content_length], buf);
-               content_length += line_length;
-               strcpy(&msg[content_length], "\n");
-               content_length += 1;
-       }
-
-       /** Do a first pass to isolate the message body */
-       ptr = msg;
-       msgstart = msg;
-       msgend = &msg[content_length];
-
-       while (ptr < msgend) {
-
-               /** Advance to next tag */
-               ptr = strchr(ptr, '<');
-               if ((ptr == NULL) || (ptr >= msgend)) break;
-               ++ptr;
-               if ((ptr == NULL) || (ptr >= msgend)) break;
-
-               /**
-                *  Look for META tags.  Some messages (particularly in
-                *  Asian locales) illegally declare a message's character
-                *  set in the HTML instead of in the MIME headers.  This
-                *  is wrong but we have to work around it anyway.
-                */
-               if (!strncasecmp(ptr, "META", 4)) {
-
-                       char *meta_start;
-                       char *meta_end;
-                       int meta_length;
-                       char *meta;
-                       char *meta_http_equiv;
-                       char *meta_content;
-                       char *spaceptr;
-
-                       meta_start = &ptr[4];
-                       meta_end = strchr(ptr, '>');
-                       if ((meta_end != NULL) && (meta_end <= msgend)) {
-                               meta_length = meta_end - meta_start + 1;
-                               meta = malloc(meta_length + 1);
-                               safestrncpy(meta, meta_start, meta_length);
-                               meta[meta_length] = 0;
-                               striplt(meta);
-                               if (!strncasecmp(meta, "HTTP-EQUIV=", 11)) {
-                                       meta_http_equiv = strdup(&meta[11]);
-                                       spaceptr = strchr(meta_http_equiv, ' ');
-                                       if (spaceptr != NULL) {
-                                               *spaceptr = 0;
-                                               meta_content = strdup(++spaceptr);
-                                               if (!strncasecmp(meta_content, "content=", 8)) {
-                                                       strcpy(meta_content, &meta_content[8]);
-                                                       stripquotes(meta_http_equiv);
-                                                       stripquotes(meta_content);
-                                                       extract_charset_from_meta(charset,
-                                                               meta_http_equiv, meta_content);
-                                               }
-                                               free(meta_content);
-                                       }
-                                       free(meta_http_equiv);
-                               }
-                               free(meta);
-                       }
-               }
-
-               /**
-                * Any of these tags cause everything up to and including
-                * the tag to be removed.
-                */     
-               if ( (!strncasecmp(ptr, "HTML", 4))
-                  ||(!strncasecmp(ptr, "HEAD", 4))
-                  ||(!strncasecmp(ptr, "/HEAD", 5))
-                  ||(!strncasecmp(ptr, "BODY", 4)) ) {
-                       ptr = strchr(ptr, '>');
-                       if ((ptr == NULL) || (ptr >= msgend)) break;
-                       ++ptr;
-                       if ((ptr == NULL) || (ptr >= msgend)) break;
-                       msgstart = ptr;
-               }
-
-               /**
-                * Any of these tags cause everything including and following
-                * the tag to be removed.
-                */
-               if ( (!strncasecmp(ptr, "/HTML", 5))
-                  ||(!strncasecmp(ptr, "/BODY", 5)) ) {
-                       --ptr;
-                       msgend = ptr;
-                       strcpy(ptr, "");
-                       
-               }
-
-               ++ptr;
-       }
-       if (msgstart > msg) {
-               strcpy(msg, msgstart);
-       }
-
-       /** Convert foreign character sets to UTF-8 if necessary. */
-#ifdef HAVE_ICONV
-       if ( (strcasecmp(charset, "us-ascii"))
-          && (strcasecmp(charset, "UTF-8"))
-          && (strcasecmp(charset, ""))
-       ) {
-               lprintf(9, "Converting %s to UTF-8\n", charset);
-               ic = ctdl_iconv_open("UTF-8", charset);
-               if (ic == (iconv_t)(-1) ) {
-                       lprintf(5, "%s:%d iconv_open() failed: %s\n",
-                               __FILE__, __LINE__, strerror(errno));
-               }
-       }
-       if (ic != (iconv_t)(-1) ) {
-               ibuf = msg;
-               ibuflen = content_length;
-               obuflen = content_length + (content_length / 2) ;
-               obuf = (char *) malloc(obuflen);
-               osav = obuf;
-               iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
-               content_length = content_length + (content_length / 2) - obuflen;
-               osav[content_length] = 0;
-               free(msg);
-               msg = osav;
-               iconv_close(ic);
-       }
-#endif
-
-       /**
-        *      At this point, the message has been stripped down to
-        *      only the content inside the <BODY></BODY> tags, and has
-        *      been converted to UTF-8 if it was originally in a foreign
-        *      character set.  The text is also guaranteed to be null
-        *      terminated now.
-        */
-
-       /** Now go through the message, parsing tags as necessary. */
-       converted_msg = malloc(content_length);
-       strcpy(converted_msg, "");
-       ptr = msg;
-       msgend = strchr(msg, 0);
-       while (ptr < msgend) {
-
-               /**
-                * Change mailto: links to WebCit mail, by replacing the
-                * link with one that points back to our mail room.  Due to
-                * the way we parse URL's, it'll even handle mailto: links
-                * that have "?subject=" in them.
-                */
-               if (!strncasecmp(ptr, "<a href=\"mailto:", 16)) {
-                       content_length += 64;
-                       converted_msg = realloc(converted_msg, content_length);
-                       sprintf(&converted_msg[output_length],
-                               "<a href=\"display_enter"
-                               "?force_room=_MAIL_&recp=");
-                       output_length += 47;
-                       ptr = &ptr[16];
-                       ++alevel;
-               }
-               /** Make external links open in a separate window */
-               else if (!strncasecmp(ptr, "<a href=\"", 9)) {
-                       ++alevel;
-                       if ( ((strchr(ptr, ':') < strchr(ptr, '/')))
-                            &&  ((strchr(ptr, '/') < strchr(ptr, '>'))) 
-                            ) {
-                               /* open external links to new window */
-                               content_length += 64;
-                               converted_msg = realloc(converted_msg, content_length);
-                               sprintf(&converted_msg[output_length], new_window);
-                               output_length += strlen(new_window);
-                               ptr = &ptr[8];
-                       }
-                       else if ( (treat_as_wiki) && (strncasecmp(ptr, "<a href=\"wiki?", 14)) ) {
-                               content_length += 64;
-                               converted_msg = realloc(converted_msg, content_length);
-                               sprintf(&converted_msg[output_length], "<a href=\"wiki?page=");
-                               output_length += 19;
-                               ptr = &ptr[9];
-                       }
-                       else {
-                               sprintf(&converted_msg[output_length], "<a href=\"");
-                               output_length += 9;
-                               ptr = &ptr[9];
-                       }
-               }
-               /**
-                * Turn anything that looks like a URL into a real link, as long
-                * as it's not inside a tag already
-                */
-               else if ( (brak == 0) && (alevel == 0)
-                    && (!strncasecmp(ptr, "http://", 7))) {
-                               linklen = 0;
-                               /** Find the end of the link */
-                               for (i=0; i<=strlen(ptr); ++i) {
-                                       if ((ptr[i]==0)
-                                          ||(isspace(ptr[i]))
-                                          ||(ptr[i]==10)
-                                          ||(ptr[i]==13)
-                                          ||(ptr[i]=='(')
-                                          ||(ptr[i]==')')
-                                          ||(ptr[i]=='<')
-                                          ||(ptr[i]=='>')
-                                          ||(ptr[i]=='[')
-                                          ||(ptr[i]==']')
-                                       ) linklen = i;
-                                       if (linklen > 0) break;
-                               }
-                               if (linklen > 0) {
-                                       content_length += (32 + linklen);
-                                       converted_msg = realloc(converted_msg, content_length);
-                                       sprintf(&converted_msg[output_length], new_window);
-                                       output_length += strlen(new_window);
-                                       converted_msg[output_length] = '\"';
-                                       converted_msg[++output_length] = 0;
-                                       for (i=0; i<linklen; ++i) {
-                                               converted_msg[output_length] = ptr[i];
-                                               converted_msg[++output_length] = 0;
-                                       }
-                                       sprintf(&converted_msg[output_length], "\">");
-                                       output_length += 2;
-                                       for (i=0; i<linklen; ++i) {
-                                               converted_msg[output_length] = *ptr++;
-                                               converted_msg[++output_length] = 0;
-                                       }
-                                       sprintf(&converted_msg[output_length], "</A>");
-                                       output_length += 4;
-                               }
-               }
-               else {
-                       /**
-                        * We need to know when we're inside a tag,
-                        * so we don't turn things that look like URL's into
-                        * links, when they're already links - or image sources.
-                        */
-                       if (*ptr == '<') ++brak;
-                       if (*ptr == '>') --brak;
-                       if (!strncasecmp(ptr, "</A>", 3)) --alevel;
-                       converted_msg[output_length] = *ptr++;
-                       converted_msg[++output_length] = 0;
-               }
-       }
-
-       /**     uncomment these two lines to override conversion        */
-       /**     memcpy(converted_msg, msg, content_length);             */
-       /**     output_length = content_length;                         */
-
-       /** Output our big pile of markup */
-       client_write(converted_msg, output_length);
-
-       /** A little trailing vertical whitespace... */
-       wprintf("<br /><br />\n");
-
-       /** Now give back the memory */
-       free(converted_msg);
-       free(msg);
-}
-
-/*@}*/
diff --git a/webcit/http_datestring.c b/webcit/http_datestring.c
deleted file mode 100644 (file)
index 33f5784..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup HTTPDateTime Function to generate HTTP-compliant textual time/date stamp
- * (This module was lifted directly from the Citadel server source)
- *
- * \ingroup WebcitHttpServer
- */
-/*@{*/
-#include "webcit.h"
-
-/** HTTP Months - do not translate - these are not for human consumption */
-static char *httpdate_months[] = {
-       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-/** HTTP Weekdays - do not translate - these are not for human consumption */
-static char *httpdate_weekdays[] = {
-       "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-};
-
-
-/**
- * \brief Supplied with a unix timestamp, generate a textual time/date stamp
- * \param buf the return buffer
- * \param n the size of the buffer
- * \param xtime the time to format as string
- */
-void http_datestring(char *buf, size_t n, time_t xtime) {
-       struct tm t;
-
-       long offset;
-       char offsign;
-
-       localtime_r(&xtime, &t);
-
-       /** Convert "seconds west of GMT" to "hours/minutes offset" */
-#ifdef HAVE_STRUCT_TM_TM_GMTOFF
-       offset = t.tm_gmtoff;
-#else
-       offset = timezone;
-#endif
-       if (offset > 0) {
-               offsign = '+';
-       }
-       else {
-               offset = 0L - offset;
-               offsign = '-';
-       }
-       offset = ( (offset / 3600) * 100 ) + ( offset % 60 );
-
-       snprintf(buf, n, "%s, %02d %s %04d %02d:%02d:%02d %c%04ld",
-               httpdate_weekdays[t.tm_wday],
-               t.tm_mday,
-               httpdate_months[t.tm_mon],
-               t.tm_year + 1900,
-               t.tm_hour,
-               t.tm_min,
-               t.tm_sec,
-               offsign, offset
-       );
-}
-
-
-/*@}*/
diff --git a/webcit/ical_dezonify.c b/webcit/ical_dezonify.c
deleted file mode 100644 (file)
index c2042fd..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/* 
- * $Id$ 
- */
-/**
- * \defgroup IcalDezonify normalize ical dates to UTC
- * Function to go through an ical component set and convert all non-UTC
- * date/time properties to UTC.  It also strips out any VTIMEZONE
- * subcomponents afterwards, because they're irrelevant.
- *
- * Everything here will work on both a fully encapsulated VCALENDAR component
- * or any type of subcomponent.
- *
- * \ingroup Calendaring
- */
-/*@{*/
-
-#include "webcit.h"
-#include "webserver.h"
-
-
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-
-
-/**
- * \brief Back end function for ical_dezonify()
- *
- * We supply this with the master component, the relevant component,
- * and the property (which will be a DTSTART, DTEND, etc.)
- * which we want to convert to UTC.
- * \param cal dunno ???
- * \param rcal dunno ???
- * \param prop dunno ???
- */
-void ical_dezonify_backend(icalcomponent *cal,
-                       icalcomponent *rcal,
-                       icalproperty *prop) {
-
-       icaltimezone *t = NULL;
-       icalparameter *param;
-       const char *tzid;
-       struct icaltimetype TheTime;
-
-       /** Give me nothing and I will give you nothing in return. */
-       if (cal == NULL) return;
-
-       /** Hunt for a TZID parameter in this property. */
-       param = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER);
-
-       /** Get the stringish name of this TZID. */
-       if (param != NULL) {
-               tzid = icalparameter_get_tzid(param);
-
-               /** Convert it to an icaltimezone type. */
-               if (tzid != NULL) {
-                       t = icalcomponent_get_timezone(cal, tzid);
-               }
-
-       }
-
-       /** Now we know the timezone.  Convert to UTC. */
-
-       if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) {
-               TheTime = icalproperty_get_dtstart(prop);
-       }
-       else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) {
-               TheTime = icalproperty_get_dtend(prop);
-       }
-       else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) {
-               TheTime = icalproperty_get_due(prop);
-       }
-       else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) {
-               TheTime = icalproperty_get_exdate(prop);
-       }
-       else {
-               return;
-       }
-
-       /** Do the conversion. */
-       if (t != NULL) {
-               icaltimezone_convert_time(&TheTime,
-                                       t,
-                                       icaltimezone_get_utc_timezone()
-               );
-       }
-       TheTime.is_utc = 1;
-       icalproperty_remove_parameter_by_kind(prop, ICAL_TZID_PARAMETER);
-
-       /** Now add the converted property back in. */
-       if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) {
-               icalproperty_set_dtstart(prop, TheTime);
-       }
-       else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) {
-               icalproperty_set_dtend(prop, TheTime);
-       }
-       else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) {
-               icalproperty_set_due(prop, TheTime);
-       }
-       else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) {
-               icalproperty_set_exdate(prop, TheTime);
-       }
-}
-
-
-/**
- * \brief Recursive portion of ical_dezonify()
- * \param cal dunno ???
- * \param rcal dunno ???
- */
-void ical_dezonify_recur(icalcomponent *cal, icalcomponent *rcal) {
-       icalcomponent *c;
-       icalproperty *p;
-
-       /**
-        * Recurse through all subcomponents *except* VTIMEZONE ones.
-        */
-       for (c=icalcomponent_get_first_component(
-                                       rcal, ICAL_ANY_COMPONENT);
-               c != NULL;
-               c = icalcomponent_get_next_component(
-                                       rcal, ICAL_ANY_COMPONENT)
-       ) {
-               if (icalcomponent_isa(c) != ICAL_VTIMEZONE_COMPONENT) {
-                       ical_dezonify_recur(cal, c);
-               }
-       }
-
-       /**
-        * Now look for DTSTART and DTEND properties
-        */
-       for (p=icalcomponent_get_first_property(
-                               rcal, ICAL_ANY_PROPERTY);
-               p != NULL;
-               p = icalcomponent_get_next_property(
-                               rcal, ICAL_ANY_PROPERTY)
-       ) {
-               if (
-                       (icalproperty_isa(p) == ICAL_DTSTART_PROPERTY)
-                       || (icalproperty_isa(p) == ICAL_DTEND_PROPERTY)
-                       || (icalproperty_isa(p) == ICAL_DUE_PROPERTY)
-                       || (icalproperty_isa(p) == ICAL_EXDATE_PROPERTY)
-                  ) {
-                       ical_dezonify_backend(cal, rcal, p);
-               }
-       }
-}
-
-
-/**
- * \brief Convert all DTSTART and DTEND properties in all subcomponents to UTC.
- * This function will search any VTIMEZONE subcomponents to learn the
- * relevant timezone information.
- * \param cal item to process
- */
-void ical_dezonify(icalcomponent *cal) {
-       icalcomponent *vt = NULL;
-
-       /** Convert all times to UTC */
-       ical_dezonify_recur(cal, cal);
-
-       /** Strip out VTIMEZONE subcomponents -- we don't need them anymore */
-       while (vt = icalcomponent_get_first_component(
-                       cal, ICAL_VTIMEZONE_COMPONENT), vt != NULL) {
-               icalcomponent_remove_component(cal, vt);
-               icalcomponent_free(vt);
-       }
-
-}
-
-
-#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
-/*@}*/
diff --git a/webcit/iconbar.c b/webcit/iconbar.c
deleted file mode 100644 (file)
index bb9a4e4..0000000
+++ /dev/null
@@ -1,774 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup IconBar Displays and customizes the iconbar.
- * \ingroup MenuInfrastructure
- */
-/*@{*/
-#include "webcit.h"
-
-
-/** Values for ib_displayas */
-#define IB_PICTEXT     0 /**< picture and text */
-#define IB_PICONLY     1 /**< just a picture */
-#define IB_TEXTONLY    2 /**< just text */
-
-
-/**
- * \brief draw the icon bar?????
- */
-void do_selected_iconbar(void) {
-       if (WC->current_iconbar == current_iconbar_roomlist) {
-               do_iconbar_roomlist();
-       }
-       else {
-               do_iconbar();
-       }
-}
-
-/**
- * \brief draw the icon bar???
- */
-void do_iconbar(void) {
-       char iconbar[SIZ];
-       char buf[SIZ];
-       char key[SIZ], value[SIZ];
-       int i;
-
-       WC->current_iconbar = current_iconbar_menu;
-
-       /**
-        * The initialized values of these variables also happen to
-        * specify the default values for users who haven't customized
-        * their iconbars.  These should probably be set in a master
-        * configuration somewhere.
-        */
-       int ib_displayas = 0;   /**< pictures and text, pictures, text */
-       int ib_logo = 0;        /**< Site logo */
-       int ib_summary = 1;     /**< Summary page icon */
-       int ib_inbox = 1;       /**< Inbox icon */
-       int ib_calendar = 1;    /**< Calendar icon */
-       int ib_contacts = 1;    /**< Contacts icon */
-       int ib_notes = 1;       /**< Notes icon */
-       int ib_tasks = 1;       /**< Tasks icon */
-       int ib_rooms = 1;       /**< Rooms icon */
-       int ib_users = 1;       /**< Users icon */
-       int ib_chat = 1;        /**< Chat icon */
-       int ib_advanced = 1;    /**< Advanced Options icon */
-       int ib_citadel = 1;     /**< 'Powered by Citadel' logo */
-       /*
-        */
-
-       get_preference("iconbar", iconbar, sizeof iconbar);
-       for (i=0; i<num_tokens(iconbar, ','); ++i) {
-               extract_token(buf, iconbar, i, ',', sizeof buf);
-               extract_token(key, buf, 0, '=', sizeof key);
-               extract_token(value, buf, 1, '=', sizeof value);
-
-               if (!strcasecmp(key, "ib_displayas")) ib_displayas = atoi(value);
-               if (!strcasecmp(key, "ib_logo")) ib_logo = atoi(value);
-               if (!strcasecmp(key, "ib_summary")) ib_summary = atoi(value);
-               if (!strcasecmp(key, "ib_inbox")) ib_inbox = atoi(value);
-               if (!strcasecmp(key, "ib_calendar")) ib_calendar = atoi(value);
-               if (!strcasecmp(key, "ib_contacts")) ib_contacts = atoi(value);
-               if (!strcasecmp(key, "ib_notes")) ib_notes = atoi(value);
-               if (!strcasecmp(key, "ib_tasks")) ib_tasks = atoi(value);
-               if (!strcasecmp(key, "ib_rooms")) ib_rooms = atoi(value);
-               if (!strcasecmp(key, "ib_users")) ib_users = atoi(value);
-               if (!strcasecmp(key, "ib_chat")) ib_chat = atoi(value);
-               if (!strcasecmp(key, "ib_advanced")) ib_advanced = atoi(value);
-               if (!strcasecmp(key, "ib_citadel")) ib_citadel = atoi(value);
-       }
-
-       wprintf("<div id=\"button\">\n"
-               "<ul>\n"
-       );
-
-       if (ib_logo) {
-               wprintf("<li>");
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" "
-                               "HEIGHT=\"32\" src=\"image&name=hello\" ALT=\"&nbsp;\">\n"
-                       );
-               }
-               wprintf("</li>\n");
-       }
-
-       if (ib_citadel) if (ib_displayas != IB_TEXTONLY) wprintf(
-               "<li><div align=\"center\">"
-               "<a href=\"http://www.citadel.org\" "
-               "title=\"%s\" target=\"aboutcit\">"
-               "<img border=\"0\" "
-               "src=\"static/citadel-logo.gif\" ALT=\"%s\"></a>"
-               "</div></li>\n",
-               _("Find out more about Citadel"),
-               _("CITADEL")
-       );
-
-       wprintf("<li><div align=\"center\"><a href=\"javascript:switch_to_room_list()\">");
-       wprintf(_("switch to room list"));
-       wprintf("</a></div>");
-
-       if (ib_summary) {
-               wprintf("<li><a href=\"summary\" "
-                       "TITLE=\"%s\" "
-                       ">", _("Your summary page")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                               "src=\"static/summscreen_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Summary"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if (ib_inbox) {
-               wprintf("<li>"
-                       "<a href=\"dotgoto?room=_MAIL_\" "
-                       "TITLE=\"%s\" "
-                       ">",
-                       _("Go to your email inbox")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                               "src=\"static/privatemess_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Mail"));
-                       if (WC->new_mail != WC->remember_new_mail) {
-/*
-                               if (WC->new_mail > 0) {
-                                       wprintf(" <b>(%d)</b>", WC->new_mail);
-                               }
-*/
-                               WC->remember_new_mail = WC->new_mail;
-                       }
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if (ib_calendar) {
-               wprintf("<li>"
-                       "<a href=\"dotgoto?room=_CALENDAR_\" "
-                       "TITLE=\"%s\" "
-                       ">",
-                       _("Go to your personal calendar")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                       "src=\"static/calarea_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Calendar"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if (ib_contacts) {
-               wprintf("<li>"
-                       "<a href=\"dotgoto?room=_CONTACTS_\" "
-                       "TITLE=\"%s\" "
-                       ">",
-                       _("Go to your personal address book")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                       "src=\"static/viewcontacts_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Contacts"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if (ib_notes) {
-               wprintf("<li>"
-                       "<a href=\"dotgoto?room=_NOTES_\" "
-                       "TITLE=\"%s\" "
-                       ">",
-                       _("Go to your personal notes")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                       "src=\"static/storenotes_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Notes"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if (ib_tasks)  {
-               wprintf("<li>"
-                       "<a href=\"dotgoto?room=_TASKS_\" "
-                       "TITLE=\"%s\" "
-                       ">",
-                       _("Go to your personal task list")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                       "src=\"static/taskmanag_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Tasks"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if (ib_rooms) {
-               wprintf("<li>"
-                       "<a href=\"knrooms\" TITLE=\"%s\" >",
-                       _("List all of your accessible rooms")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                       "src=\"static/chatrooms_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Rooms"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if (ib_users) {
-               wprintf("<li>"
-                       "<a href=\"who\" TITLE=\"%s\" "
-                       ">",
-                       _("See who is online right now")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                       "src=\"static/usermanag_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Who is online?"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if (ib_chat) {
-               wprintf("<li>"
-                       "<a href=\"#\" onClick=\"window.open('chat', "
-                       "'ctdl_chat_window', "
-                       "'toolbar=no,location=no,directories=no,copyhistory=no,"
-                       "status=no,scrollbars=yes,resizable=yes');\""
-                       ">"
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                       "src=\"static/citadelchat_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Chat"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if (ib_advanced) {
-               wprintf("<li>"
-                       "<a href=\"display_main_menu\" "
-                       "TITLE=\"%s\" "
-                       ">",
-                       _("Advanced Options Menu: Advanced Room commands, Account Info, and Chat")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                       "src=\"static/advanpage2_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Advanced"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       if ((WC->axlevel >= 6) || (WC->is_room_aide)) {
-               wprintf("<li>"
-                       "<a href=\"display_aide_menu\" "
-                       "TITLE=\"%s\" "
-                       ">",
-                       _("Room and system administration functions")
-               );
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-                       "src=\"static/advanpage2_32x.gif\">");
-               }
-               if (ib_displayas != IB_PICONLY) {
-                       wprintf(_("Administration"));
-               }
-               wprintf("</A></li>\n");
-       }
-
-       wprintf("<li>"
-               "<a href=\"termquit\" TITLE=\"%s\" "
-               "onClick=\"return confirm('%s');\">",
-               _("Log off"),
-               _("Log off now?")
-               
-       );
-       if (ib_displayas != IB_TEXTONLY) {
-       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-               "src=\"static/logoff_32x.gif\">");
-       }
-       if (ib_displayas != IB_PICONLY) {
-               wprintf(_("Log off"));
-       }
-       wprintf("</A></li>\n");
-
-       wprintf(
-               "<li><div align=\"center\">"
-               "<a href=\"display_customize_iconbar\" "
-               "TITLE=\"%s\" "
-               ">%s"
-               "</A></div></li>\n",
-               _("Customize this menu"),
-               _("customize this menu")
-       );
-
-       wprintf("</ul></div>\n");
-}
-
-
-/**
- * \brief roomtree view of the iconbar
- * If the user has toggled the icon bar over to a room list, here's where
- * we generate its innerHTML...
- */
-void do_iconbar_roomlist(void) {
-       char iconbar[SIZ];
-       char buf[SIZ];
-       char key[SIZ], value[SIZ];
-       int i;
-
-       WC->current_iconbar = current_iconbar_roomlist;
-
-       /**
-        * The initialized values of these variables also happen to
-        * specify the default values for users who haven't customized
-        * their iconbars.  These should probably be set in a master
-        * configuration somewhere.
-        */
-       int ib_displayas = 0;   /* pictures and text, pictures, text */
-       int ib_logo = 0;        /* Site logo */
-       int ib_citadel = 1;     /* 'Powered by Citadel' logo */
-       /*
-        */
-
-       get_preference("iconbar", iconbar, sizeof iconbar);
-       for (i=0; i<num_tokens(iconbar, ','); ++i) {
-               extract_token(buf, iconbar, i, ',', sizeof buf);
-               extract_token(key, buf, 0, '=', sizeof key);
-               extract_token(value, buf, 1, '=', sizeof value);
-
-               if (!strcasecmp(key, "ib_displayas")) ib_displayas = atoi(value);
-               if (!strcasecmp(key, "ib_logo")) ib_logo = atoi(value);
-               if (!strcasecmp(key, "ib_citadel")) ib_citadel = atoi(value);
-       }
-
-       wprintf("<div id=\"button\">\n"
-               "<ul>\n"
-       );
-
-       if (ib_logo) {
-               wprintf("<li>");
-               if (ib_displayas != IB_TEXTONLY) {
-                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" "
-                               "HEIGHT=\"32\" src=\"image&name=hello\" ALT=\"&nbsp;\">\n"
-                       );
-               }
-               wprintf("</li>\n");
-       }
-
-       if (ib_citadel) if (ib_displayas != IB_TEXTONLY) wprintf(
-               "<li><div align=\"center\">"
-               "<a href=\"http://www.citadel.org\" "
-               "title=\"%s\" target=\"aboutcit\">"
-               "<img border=\"0\" "
-               "src=\"static/citadel-logo.gif\" ALT=\"%s\"></a>"
-               "</div></li>\n",
-               _("Find out more about Citadel"),
-               _("CITADEL")
-       );
-
-       wprintf("<li><div align=\"center\"><a href=\"javascript:switch_to_menu_buttons()\">");
-       wprintf(_("switch to menu"));
-       wprintf("</a></div>");
-
-       wprintf("<li>"
-               "<a href=\"termquit\" TITLE=\"%s\" "
-               "onClick=\"return confirm('%s');\">",
-               _("Log off"),
-               _("Log off now?")
-               
-       );
-       if (ib_displayas != IB_TEXTONLY) {
-       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
-               "src=\"static/logoff_32x.gif\">");
-       }
-       if (ib_displayas != IB_PICONLY) {
-               wprintf(_("Log off"));
-       }
-       wprintf("</A></li>\n");
-
-       wprintf("</ul></div>\n");
-
-       /** embed the room list */
-       list_all_rooms_by_floor("iconbar");
-
-       wprintf("</div>\n");
-}
-
-
-/**
- * \brief display a customized version of the iconbar
- */
-void display_customize_iconbar(void) {
-       char iconbar[SIZ];
-       char buf[SIZ];
-       char key[SIZ], value[SIZ];
-       int i;
-       int bar = 0;
-
-       /**
-        * The initialized values of these variables also happen to
-        * specify the default values for users who haven't customized
-        * their iconbars.  These should probably be set in a master
-        * configuration somewhere.
-        */
-       int ib_displayas = IB_PICTEXT;  /**< pictures and text, pictures, text */
-       int ib_logo = 0;        /**< Site logo */
-       int ib_summary = 1;     /**< Summary page icon */
-       int ib_inbox = 1;       /**< Inbox icon */
-       int ib_calendar = 1;    /**< Calendar icon */
-       int ib_contacts = 1;    /**< Contacts icon */
-       int ib_notes = 1;       /**< Notes icon */
-       int ib_tasks = 1;       /**< Tasks icon */
-       int ib_rooms = 1;       /**< Rooms icon */
-       int ib_users = 1;       /**< Users icon */
-       int ib_chat = 1;        /**< Chat icon */
-       int ib_advanced = 1;    /**< Advanced Options icon */
-       int ib_citadel = 1;     /**< 'Powered by Citadel' logo */
-       /*
-        */
-
-       get_preference("iconbar", iconbar, sizeof iconbar);
-       for (i=0; i<num_tokens(iconbar, ','); ++i) {
-               extract_token(buf, iconbar, i, ',', sizeof buf);
-               extract_token(key, buf, 0, '=', sizeof key);
-               extract_token(value, buf, 1, '=', sizeof value);
-
-               if (!strcasecmp(key, "ib_displayas")) ib_displayas = atoi(value);
-               if (!strcasecmp(key, "ib_logo")) ib_logo = atoi(value);
-               if (!strcasecmp(key, "ib_summary")) ib_summary = atoi(value);
-               if (!strcasecmp(key, "ib_inbox")) ib_inbox = atoi(value);
-               if (!strcasecmp(key, "ib_calendar")) ib_calendar = atoi(value);
-               if (!strcasecmp(key, "ib_contacts")) ib_contacts = atoi(value);
-               if (!strcasecmp(key, "ib_notes")) ib_notes = atoi(value);
-               if (!strcasecmp(key, "ib_tasks")) ib_tasks = atoi(value);
-               if (!strcasecmp(key, "ib_rooms")) ib_rooms = atoi(value);
-               if (!strcasecmp(key, "ib_users")) ib_users = atoi(value);
-               if (!strcasecmp(key, "ib_chat")) ib_chat = atoi(value);
-               if (!strcasecmp(key, "ib_advanced")) ib_advanced = atoi(value);
-               if (!strcasecmp(key, "ib_citadel")) ib_citadel = atoi(value);
-       }
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Customize the icon bar"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>");
-
-       wprintf("<FORM METHOD=\"POST\" action=\"commit_iconbar\">\n");
-
-       wprintf("<CENTER>");
-       wprintf(_("Display icons as:"));
-       wprintf(" ");
-       for (i=0; i<=2; ++i) {
-               wprintf("<INPUT TYPE=\"radio\" NAME=\"ib_displayas\" VALUE=\"%d\"", i);
-               if (ib_displayas == i) wprintf(" CHECKED");
-               wprintf(">");
-               if (i == IB_PICTEXT)    wprintf(_("pictures and text"));
-               if (i == IB_PICONLY)    wprintf(_("pictures only"));
-               if (i == IB_TEXTONLY)   wprintf(_("text only"));
-               wprintf("\n");
-       }
-       wprintf("<br /><br />\n");
-
-       wprintf(_("Select the icons you would like to see displayed "
-               "in the 'icon bar' menu on the left side of the "
-               "screen."));
-       wprintf("</CENTER><br />\n");
-
-       wprintf("<TABLE border=0 cellspacing=0 cellpadding=3 width=100%%>\n");
-
-       wprintf("<TR BGCOLOR=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_logo\" VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"image&name=hello\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_logo ? "CHECKED" : ""),
-               _("Site logo"),
-               _("An icon describing this site")
-       );
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_summary\" VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/summscreen_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_summary ? "CHECKED" : ""),
-               _("Summary"),
-               _("Your summary page")
-       );
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_inbox\" VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/privatemess_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_inbox ? "CHECKED" : ""),
-               _("Mail (inbox)"),
-               _("A shortcut to your email Inbox")
-       );
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_contacts\" "
-               "VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/viewcontacts_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_contacts ? "CHECKED" : ""),
-               _("Contacts"),
-               _("Your personal address book")
-       );
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_notes\" "
-               "VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/storenotes_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_notes ? "CHECKED" : ""),
-               _("Notes"),
-               _("Your personal notes")
-       );
-
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_calendar\" "
-               "VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/calarea_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_calendar ? "CHECKED" : ""),
-               _("Calendar"),
-               _("A shortcut to your personal calendar")
-       );
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_tasks\" VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/taskmanag_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_tasks ? "CHECKED" : ""),
-               _("Tasks"),
-               _("A shortcut to your personal task list")
-       );
-#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_rooms\" VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/chatrooms_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_rooms ? "CHECKED" : ""),
-               _("Rooms"),
-               _("Clicking this icon displays a list of all accessible "
-               "rooms (or folders) available.")
-       );
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_users\" VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/usermanag_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_users ? "CHECKED" : ""),
-               _("Who is online?"),
-               _("Clicking this icon displays a list of all users "
-               "currently logged in.")
-       );
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_chat\" VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/citadelchat_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_chat ? "CHECKED" : ""),
-               _("Chat"),
-               _("Clicking this icon enters real-time chat mode "
-               "with other users in the same room.")
-               
-       );
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_advanced\" "
-               "VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/advanpage2_48x.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_advanced ? "CHECKED" : ""),
-               _("Advanced options"),
-               _("Access to the complete menu of Citadel functions.")
-
-       );
-
-       wprintf("<TR bgcolor=%s><TD>"
-               "<INPUT TYPE=\"checkbox\" NAME=\"ib_citadel\" "
-               "VALUE=\"yes\" %s>"
-               "</TD><TD>"
-               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
-               "src=\"static/citadel-logo.gif\" ALT=\"&nbsp;\">"
-               "</TD><TD>"
-               "<B>%s</B><br />"
-               "%s"
-               "</TD></TR>\n",
-               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
-               (ib_citadel ? "CHECKED" : ""),
-               _("Citadel logo"),
-               _("Displays the 'Powered by Citadel' icon")
-       );
-
-       wprintf("</TABLE><br />\n"
-               "<CENTER>"
-               "<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">"
-               "&nbsp;"
-               "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">"
-               "</CENTER></FORM>\n",
-               _("Save changes"),
-               _("Cancel")
-       );
-
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(2);
-}
-
-/**
- * \brief commit the changes of an edited iconbar ????
- */
-void commit_iconbar(void) {
-       char iconbar[SIZ];
-       int i;
-
-       char *boxen[] = {
-               "ib_logo",
-               "ib_summary",
-               "ib_inbox",
-               "ib_calendar",
-               "ib_contacts",
-               "ib_notes",
-               "ib_tasks",
-               "ib_rooms",
-               "ib_users",
-               "ib_chat",
-               "ib_advanced",
-               "ib_logoff",
-               "ib_citadel"
-       };
-
-       if (strlen(bstr("ok_button")) == 0) {
-               display_main_menu();
-               return;
-       }
-
-       sprintf(iconbar, "ib_displayas=%d", atoi(bstr("ib_displayas")));
-
-       for (i=0; i<(sizeof(boxen)/sizeof(char *)); ++i) {
-               sprintf(&iconbar[strlen(iconbar)], ",%s=", boxen[i]);
-               if (!strcasecmp(bstr(boxen[i]), "yes")) {
-                       sprintf(&iconbar[strlen(iconbar)], "1");
-               }
-               else {
-                       sprintf(&iconbar[strlen(iconbar)], "0");
-               }
-       }
-
-       set_preference("iconbar", iconbar, 1);
-
-       output_headers(1, 1, 0, 0, 0, 0);
-       wprintf(
-               "<center><table border=1 bgcolor=\"#ffffff\"><tr><td>"
-               "<img src=\"static/advanpage2_48x.gif\">"
-               "&nbsp;");
-       wprintf(_("Your icon bar has been updated.  Please select any of its "
-               "choices to continue."));
-       wprintf("</td></tr></table>\n");
-       wDumpContent(2);
-}
-
-
-
-/*@}*/
diff --git a/webcit/inetconf.c b/webcit/inetconf.c
deleted file mode 100644 (file)
index 85f9815..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-/* 
- * $Id$
- */
-/**
- * \defgroup InetCfg Functions which handle Internet domain configuration etc.
- * \ingroup CitadelConfig
- */
-/*@{*/
-#include "webcit.h"
-
-
-/**
- * \brief display the inet config dialog 
- */
-void display_inetconf(void)
-{
-       char buf[SIZ];
-       char ename[SIZ];
-       char etype[SIZ];
-       int i;
-       int which;
-
-       enum {
-               ic_localhost,
-               ic_directory,
-               ic_gwdom,
-               ic_smarthost,
-               ic_rbl,
-               ic_spamass,
-               ic_max
-       };
-       char *ic_spec[ic_max];
-       char *ic_misc;
-       char *ic_keyword[ic_max];
-       char *ic_boxtitle[ic_max];
-       char *ic_desc[ic_max];
-
-       ic_keyword[0] = _("localhost");
-       ic_keyword[1] = _("directory");
-       ic_keyword[2] = _("gatewaydomain");
-       ic_keyword[3] = _("smarthost");
-       ic_keyword[4] = _("rbl");
-       ic_keyword[5] = _("spamassassin");
-
-       ic_boxtitle[0] = _("Local host aliases");
-       ic_boxtitle[1] = _("Directory domains");
-       ic_boxtitle[2] = _("Gateway domains");
-       ic_boxtitle[3] = _("Smart hosts");
-       ic_boxtitle[4] = _("RBL hosts");
-       ic_boxtitle[5] = _("SpamAssassin hosts");
-
-       ic_desc[0] = _("(domains for which this host receives mail)");
-       ic_desc[1] = _("(domains mapped with the Global Address Book)");
-       ic_desc[2] = _("(domains whose subdomains match Citadel hosts)");
-       ic_desc[3] = _("(if present, forward all outbound mail to one of these hosts)");
-       ic_desc[4] = _("(hosts running a Realtime Blackhole List)");
-       ic_desc[5] = _("(hosts running the SpamAssassin service)");
-
-       for (i=0; i<ic_max; ++i) {
-               ic_spec[i] = strdup("");
-       }
-       ic_misc = strdup("");
-
-       serv_printf("CONF GETSYS|application/x-citadel-internet-config");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-
-               extract_token(ename, buf, 0, '|', sizeof ename);
-               extract_token(etype, buf, 1, '|', sizeof etype);
-               which = (-1);
-               for (i=0; i<ic_max; ++i) {
-                       if (!strcasecmp(etype, ic_keyword[i])) {
-                               which = i;
-                       }
-               }
-
-               if (which >= 0) {
-                       ic_spec[which] = realloc(ic_spec[which], strlen(ic_spec[which]) + strlen(ename) + 2);
-                       if (strlen(ic_spec[which]) > 0) strcat(ic_spec[which], "\n");
-                       strcat(ic_spec[which], ename);
-               }
-               else {
-                       ic_misc = realloc(ic_misc, strlen(ic_misc) + strlen(buf) + 2);
-                       if (strlen(ic_misc) > 0) strcat(ic_misc, "\n");
-                       strcat(ic_misc, buf);
-               }
-
-       }
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
-       wprintf("<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Internet configuration"));
-       wprintf("</SPAN>\n");
-       wprintf("</TD></TR></TABLE>\n");
-       wprintf("</div>\n<div id=\"content\">\n");
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%%><tr><td valign=top>\n");
-       for (which=0; which<ic_max; ++which) {
-               if (which == (ic_max / 2)) {
-                       wprintf("</TD><TD VALIGN=TOP>");
-               }
-               svprintf("BOXTITLE", WCS_STRING, ic_boxtitle[which]);
-               do_template("beginbox");
-               wprintf("<span class=\"menudesc\">");
-               escputs(ic_desc[which]);
-               wprintf("</span><br />");
-               wprintf("<TABLE border=0 cellspacing=0 cellpadding=0 width=100%%>\n");
-               if (strlen(ic_spec[which]) > 0) {
-                       for (i=0; i<num_tokens(ic_spec[which], '\n'); ++i) {
-                               wprintf("<TR><TD ALIGN=LEFT>");
-                               extract_token(buf, ic_spec[which], i, '\n', sizeof buf);
-                               escputs(buf);
-                               wprintf("</TD><TD ALIGN=RIGHT>"
-                                       "<a href=\"save_inetconf?oper=delete&ename=");
-                               escputs(buf);
-                               wprintf("&etype=%s\" ", ic_keyword[which]);
-                               wprintf("onClick=\"return confirm('%s');\">",
-                                       _("Delete this entry?"));
-                               wprintf("<font size=-1>");
-                               wprintf(_("(Delete)"));
-                               wprintf("</font></a></TD></TR>\n");
-                       }
-               }
-               wprintf("<FORM METHOD=\"POST\" action=\"save_inetconf\">\n"
-                       "<TR><TD>"
-                       "<INPUT TYPE=\"text\" NAME=\"ename\" MAXLENGTH=\"64\">"
-                       "<INPUT TYPE=\"hidden\" NAME=\"etype\" VALUE=\"%s\">", ic_keyword[which]);
-               wprintf("</TD><TD ALIGN=RIGHT>"
-                       "<INPUT TYPE=\"submit\" NAME=\"oper\" VALUE=\"Add\">"
-                       "</TD></TR></TABLE></FORM>\n");
-               do_template("endbox");
-       }
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-
-       for (i=0; i<ic_max; ++i) {
-               free(ic_spec[i]);
-       }
-       free(ic_misc);
-}
-
-
-/**
- * \brief save changes to the inet config
- */
-void save_inetconf(void) {
-       char *buf;
-       char *ename;
-       char *etype;
-       char *newconfig;
-
-       buf = malloc(SIZ);
-       ename = malloc(SIZ);
-       etype = malloc(SIZ);
-       newconfig = malloc(65536);
-
-       strcpy(newconfig, "");
-       serv_printf("CONF GETSYS|application/x-citadel-internet-config");
-       serv_getln(buf, SIZ);
-       if (buf[0] == '1') while (serv_getln(buf, SIZ), strcmp(buf, "000")) {
-               extract_token(ename, buf, 0, '|', SIZ);
-               extract_token(etype, buf, 1, '|', SIZ);
-               if (strlen(buf) == 0) {
-                       /** skip blank lines */
-               }
-               else if ((!strcasecmp(ename, bstr("ename")))
-                  &&   (!strcasecmp(etype, bstr("etype")))
-                  &&   (!strcasecmp(bstr("oper"), "delete"))
-               ) {
-                       sprintf(WC->ImportantMessage, _("%s has been deleted."), ename);
-               }
-               else {
-                       if (strlen(newconfig) > 0) strcat(newconfig, "\n");
-                       strcat(newconfig, buf);
-               }
-       }
-
-       serv_printf("CONF PUTSYS|application/x-citadel-internet-config");
-       serv_getln(buf, SIZ);
-       if (buf[0] == '4') {
-               serv_puts(newconfig);
-               if (!strcasecmp(bstr("oper"), "add")) {
-                       serv_printf("%s|%s", bstr("ename"), bstr("etype") );
-                       sprintf(WC->ImportantMessage, "%s added.", bstr("ename"));
-               }
-               serv_puts("000");
-       }
-       
-       display_inetconf();
-
-       free(buf);
-       free(ename);
-       free(etype);
-       free(newconfig);
-}
-
-
-
-/*@}*/
diff --git a/webcit/listsub.c b/webcit/listsub.c
deleted file mode 100644 (file)
index 3f7e9cf..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup ListSubForms Web forms for handling mailing list subscribe/unsubscribe requests.
- * \ingroup WebcitDisplayItems
- */
-
-/*@{*/
-#include "webcit.h"
-
-
-
-/**
- * \brief List subscription handling
- */
-void do_listsub(void)
-{
-       char cmd[256];
-       char room[256];
-       char token[256];
-       char email[256];
-       char subtype[256];
-       char escaped_email[256];
-       char escaped_room[256];
-
-       char buf[SIZ];
-       int self;
-       char sroom[SIZ];
-
-       strcpy(WC->wc_fullname, "");
-       strcpy(WC->wc_username, "");
-       strcpy(WC->wc_password, "");
-       strcpy(WC->wc_roomname, "");
-
-       output_headers(1, 0, 0, 1, 1, 0);
-       begin_burst();
-
-       wprintf("<HTML><HEAD>\n"
-               "<meta name=\"MSSmartTagsPreventParsing\" content=\"TRUE\" />\n"
-               "<link href=\"static/webcit.css\" rel=\"stylesheet\" type=\"text/css\">\n"
-               "<TITLE>\n"
-       );
-       wprintf(_("List subscription"));
-       wprintf("</TITLE></HEAD><BODY>\n");
-
-       strcpy(cmd, bstr("cmd"));
-       strcpy(room, bstr("room"));
-       strcpy(token, bstr("token"));
-       strcpy(email, bstr("email"));
-       strcpy(subtype, bstr("subtype"));
-
-       wprintf("<CENTER>"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("List subscribe/unsubscribe"));
-       wprintf("</SPAN></TD></TR></TABLE><br />\n");
-
-       /**
-        * Subscribe command
-        */
-       if (!strcasecmp(cmd, "subscribe")) {
-               serv_printf("SUBS subscribe|%s|%s|%s|%s://%s/listsub",
-                       room,
-                       email,
-                       subtype,
-                       (is_https ? "https" : "http"),
-                       WC->http_host
-               );
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       stresc(escaped_email, email, 0, 0);
-                       stresc(escaped_room, room, 0, 0);
-
-                       wprintf("<CENTER><H1>");
-                       wprintf(_("Confirmation request sent"));
-                       wprintf("</H1>");
-                       wprintf(_("You are subscribing <TT>%s"
-                               "</TT> to the <b>%s</b> mailing list.  "
-                               "The listserver has "
-                               "sent you an e-mail with one additional "
-                               "Web link for you to click on to confirm "
-                               "your subscription.  This extra step is for "
-                               "your protection, as it prevents others from "
-                               "being able to subscribe you to lists "
-                               "without your consent.<br /><br />"
-                               "Please click on the link which is being "
-                               "e-mailed to you and your subscription will "
-                               "be confirmed.<br />\n"),
-                               escaped_email, escaped_room);
-                       wprintf("<a href=\"listsub\">%s</A></CENTER>\n", _("Go back..."));
-               }
-               else {
-                       wprintf("<FONT SIZE=+1><B>ERROR: %s</B>"
-                               "</FONT><br /><br />\n",
-                               &buf[4]);
-                       goto FORM;
-               }
-       }
-
-       /**
-        * Unsubscribe command
-        */
-       else if (!strcasecmp(cmd, "unsubscribe")) {
-               serv_printf("SUBS unsubscribe|%s|%s|%s://%s/listsub",
-                       room,
-                       email,
-                       (is_https ? "https" : "http"),
-                       WC->http_host
-               );
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       wprintf("<CENTER><H1>Confirmation request sent</H1>"
-                               "You are unsubscribing <TT>");
-                       escputs(email);
-                       wprintf("</TT> from the &quot;");
-                       escputs(room);
-                       wprintf("&quot; mailing list.  The listserver has "
-                               "sent you an e-mail with one additional "
-                               "Web link for you to click on to confirm "
-                               "your unsubscription.  This extra step is for "
-                               "your protection, as it prevents others from "
-                               "being able to unsubscribe you from "
-                               "lists without your consent.<br /><br />"
-                               "Please click on the link which is being "
-                               "e-mailed to you and your unsubscription will "
-                               "be confirmed.<br />\n"
-                               "<a href=\"listsub\">Back...</A></CENTER>\n"
-                       );
-               }
-               else {
-                       wprintf("<FONT SIZE=+1><B>ERROR: %s</B>"
-                               "</FONT><br /><br />\n",
-                               &buf[4]);
-                       goto FORM;
-               }
-       }
-
-       /**
-        * Confirm command
-        */
-       else if (!strcasecmp(cmd, "confirm")) {
-               serv_printf("SUBS confirm|%s|%s",
-                       room,
-                       token
-               );
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       wprintf("<CENTER><H1>Confirmation successful!</H1>");
-               }
-               else {
-                       wprintf("<CENTER><H1>Confirmation failed.</H1>"
-                               "This could mean one of two things:<UL>\n"
-                               "<LI>You waited too long to confirm your "
-                               "subscribe/unsubscribe request (the "
-                               "confirmation link is only valid for three "
-                               "days)\n<LI>You have <i>already</i> "
-                               "successfully confirmed your "
-                               "subscribe/unsubscribe request and are "
-                               "attempting to do it again.</UL>\n"
-                               "The error returned by the server was: "
-                       );
-               }
-               wprintf("%s</CENTER><br />\n", &buf[4]);
-       }
-
-       /**
-        * Any other (invalid) command causes the form to be displayed
-        */
-       else {
-FORM:          wprintf("<FORM METHOD=\"POST\" action=\"listsub\">\n"
-                       "<TABLE BORDER=0>\n"
-               );
-
-               wprintf("<TR><TD>Name of list</TD><TD>"
-                       "<SELECT NAME=\"room\" SIZE=1>\n");
-
-               serv_puts("LPRM");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '1') {
-                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                               extract_token(sroom, buf, 0, '|', sizeof sroom);
-                               self = extract_int(buf, 4) & QR2_SELFLIST ;
-                               if (self) {
-                                       wprintf("<OPTION VALUE=\"");
-                                       escputs(sroom);
-                                       wprintf("\">");
-                                       escputs(sroom);
-                                       wprintf("</OPTION>\n");
-                               }
-                       }
-               }
-               wprintf("</SELECT>"
-                       "</TD></TR>\n");
-
-               wprintf("<TR><TD>Your e-mail address</TD><TD>"
-                       "<INPUT TYPE=\"text\" NAME=\"email\" "
-                       "VALUE=\""
-               );
-               escputs(email);
-               wprintf("\" MAXLENGTH=128></TD></TR>\n");
-
-               wprintf("</TABLE>"
-                       "(If subscribing) preferred format: "
-                       "<INPUT TYPE=\"radio\" NAME=\"subtype\""
-                       "VALUE=\"list\">One message at a time&nbsp; "
-                       "<INPUT TYPE=\"radio\" NAME=\"subtype\""
-                       "VALUE=\"digest\" CHECKED>Digest format&nbsp; "
-                       "<br />\n"
-                       "<INPUT TYPE=\"submit\" NAME=\"cmd\""
-                       " VALUE=\"subscribe\">\n"
-                       "<INPUT TYPE=\"submit\" NAME=\"cmd\""
-                       " VALUE=\"unsubscribe\">\n"
-                       "</FORM>\n"
-               );
-
-               wprintf("<br />When you attempt to subscribe or unsubscribe to "
-                       "a mailing list, you will receive an e-mail containing"
-                       " one additional web link to click on for final "
-                       "confirmation.  This extra step is for your "
-                       "protection, as it prevents others from being able to "
-                       "subscribe or unsubscribe you to lists.<br />\n"
-               );
-
-       }
-
-       wprintf("</BODY></HTML>\n");
-       wDumpContent(0);
-       end_webcit_session();
-}
-
-
-
-/*@}*/
diff --git a/webcit/locate_host.c b/webcit/locate_host.c
deleted file mode 100644 (file)
index 7b58699..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup Hostlookup Examine a socket and determine the name/address of the originating host.
- * \ingroup WebcitHttpServer
- */
-/*@{*/
-
-#include "webcit.h"
-
-/**
- * \brief get a hostname 
- * \todo buffersize?
- * \param tbuf the returnbuffer
- * \param client_socket the sock fd where the client is connected
- */
-void locate_host(char *tbuf, int client_socket)
-{
-       struct sockaddr_in cs;
-       struct hostent *ch;
-       socklen_t len;
-       char *i;
-       int a1, a2, a3, a4;
-
-       len = sizeof(cs);
-       if (getpeername(client_socket, (struct sockaddr *) &cs, &len) < 0) {
-               strcpy(tbuf, "<unknown>");
-               return;
-       }
-       if ((ch = gethostbyaddr((char *) &cs.sin_addr, sizeof(cs.sin_addr),
-                               AF_INET)) == NULL) {
-               i = (char *) &cs.sin_addr;
-               a1 = ((*i++) & 0xff);
-               a2 = ((*i++) & 0xff);
-               a3 = ((*i++) & 0xff);
-               a4 = ((*i++) & 0xff);
-               sprintf(tbuf, "%d.%d.%d.%d", a1, a2, a3, a4);
-               return;
-       }
-       safestrncpy(tbuf, ch->h_name, 64);
-}
-
-/*@}*/
diff --git a/webcit/mainmenu.c b/webcit/mainmenu.c
deleted file mode 100644 (file)
index 3f66e6d..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup DispAdvancedMenu Displays the "advanced" (main) menu.
- * \ingroup MenuInfrastructure
- *
- */
-/*@{*/
-#include "webcit.h"
-
-/**
- * \brief The Main Menu
- */
-void display_main_menu(void)
-{
-       output_headers(1, 1, 1, 0, 0, 0);
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<TABLE WIDTH=100%%>"
-               "<TR><TD COLSPAN=2>\n");
-
-       svprintf("BOXTITLE", WCS_STRING, _("Basic commands"));
-       do_template("beginbox");
-
-       wprintf("\n"
-               "<TABLE border=0 cellspacing=1 cellpadding=1 width=100%%>"
-               "<TR>"
-               "<TD>");        /**< start of first column */
-
-       wprintf("<a href=\"knrooms\"><span class=\"mainmenu\">");
-       wprintf(_("List known rooms"));
-       wprintf("</span></A><br /><span class=\"menudesc\">");
-       wprintf(_("Where can I go from here?"));
-       wprintf("</span><br />\n");
-
-       wprintf("<a href=\"gotonext\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Goto next room"));
-       wprintf("</span></A><br />"
-               "<span class=\"menudesc\">");
-       wprintf(_("...with <EM>unread</EM> messages"));
-       wprintf("</span><br />\n");
-
-       wprintf("<a href=\"skip\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Skip to next room"));
-       wprintf("</span></a><br />"
-               "<span class=\"menudesc\">");
-       wprintf(_("(come back here later)"));
-       wprintf("</span>\n");
-
-       if ((strlen(WC->ugname) > 0) && (strcasecmp(WC->ugname, WC->wc_roomname))) {
-               wprintf("<br />"
-                       "<a href=\"ungoto\">"
-                       "<span class=\"mainmenu\">");
-               wprintf(_("Ungoto"));
-               wprintf("</span></A><br />"
-                       "<span class=\"menudesc\">");
-               wprintf(_("(oops! Back to %s)"), WC->ugname);
-               wprintf("</span>\n");
-       }
-
-       wprintf("</TD><TD>\n"); /* start of second column */
-
-       wprintf("<a href=\"readnew\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Read new messages"));
-       wprintf("</span></A><br />"
-               "<span class=\"menudesc\">");
-       wprintf(_("...in this room"));
-       wprintf("</span><br />\n");
-
-       wprintf("<a href=\"readfwd\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Read all messages"));
-       wprintf("</span></A><br />"
-               "<span class=\"menudesc\">");
-       wprintf(_("...old <EM>and</EM> new"));
-       wprintf("</span><br />\n");
-
-       wprintf("<a href=\"display_enter\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Enter a message"));
-       wprintf("</span></A><br />"
-               "<span class=\"menudesc\">");
-       wprintf(_("(post in this room)"));
-       wprintf("</span>\n");
-
-       wprintf("</TD><TD>");   /* start of third column */
-
-       wprintf("<a href=\"summary\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Summary page"));
-       wprintf("</span></A><br />"
-               "<span class=\"menudesc\">");
-       wprintf(_("Summary of my account"));
-       wprintf("</span><br />\n");
-
-       wprintf("<a href=\"userlist\">\n"
-               "<span class=\"mainmenu\">");
-       wprintf(_("User list"));
-       wprintf("</span></A><br />"
-               "<span class=\"menudesc\">");
-       wprintf(_("(all registered users)"));
-       wprintf("</span><br />\n");
-
-       wprintf("<a href=\"termquit\" TARGET=\"_top\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Log off"));
-       wprintf("</span></A><br />"
-               "<span class=\"menudesc\">");
-       wprintf(_("Bye!"));
-       wprintf("</span>\n");
-
-       wprintf("</TD></TR></TABLE>\n");
-       do_template("endbox");
-
-       wprintf("</TD></TR>"
-               "<TR VALIGN=TOP><TD>");
-
-       svprintf("BOXTITLE", WCS_STRING, _("Your info"));
-       do_template("beginbox");
-
-       wprintf("<a href=\"display_preferences\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Change your preferences and settings"));
-       wprintf("</span><br />\n");
-
-       wprintf("<a href=\"display_reg\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Update your contact information"));
-       wprintf("</span><br />\n");
-
-       wprintf("<a href=\"display_changepw\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Change your password"));
-       wprintf("</span></A><br />\n");
-
-       wprintf("<a href=\"display_editbio\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Enter your 'bio'"));
-       wprintf("</span></a><br />\n");
-
-       wprintf("<a href=\"display_editpic\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Edit your online photo"));
-       wprintf("</span></a>\n");
-
-       do_template("endbox");
-
-       wprintf("</TD><TD>");
-
-       svprintf("BOXTITLE", WCS_STRING, _("Advanced room commands"));
-       do_template("beginbox");
-
-       if ((WC->axlevel >= 6) || (WC->is_room_aide)) {
-               wprintf("<a href=\"display_editroom\">"
-                       "<span class=\"mainmenu\">");
-               wprintf(_("Edit or delete this room"));
-               wprintf("</span></A><br />\n");
-       }
-
-       wprintf("<a href=\"display_private\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Go to a 'hidden' room"));
-       wprintf("</span></A><br />\n");
-
-       wprintf("<a href=\"display_entroom\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Create a new room"));
-       wprintf("</span></A><br />\n");
-
-       wprintf("<a href=\"display_zap\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Zap (forget) this room (%s)"), WC->wc_roomname);
-       wprintf("</span></A><br />\n");
-
-       wprintf("<a href=\"zapped_list\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("List all forgotten rooms"));
-       wprintf("</span></A>\n");
-
-       do_template("endbox");
-
-       wprintf("</td></tr></table></div>");
-       wDumpContent(2);
-}
-
-
-/**
- * \brief System administration menu
- */
-void display_aide_menu(void)
-{
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("System Administration Menu"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%%><tr valign=top><td>");
-
-       svprintf("BOXTITLE", WCS_STRING, _("Global Configuration"));
-       do_template("beginbox");
-
-       wprintf("<a href=\"display_siteconfig\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Edit site-wide configuration"));
-       wprintf("</span></A><br />\n");
-
-       wprintf("<a href=\"display_inetconf\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Domain names and Internet mail configuration"));
-       wprintf("</span></a><br />\n");
-
-       wprintf("<a href=\"display_netconf\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Configure replication with other Citadel servers"));
-       wprintf("</span></A>\n");
-
-       do_template("endbox");
-
-       wprintf("</td><td>");
-
-       svprintf("BOXTITLE", WCS_STRING, _("User account management"));
-       do_template("beginbox");
-
-       wprintf("<a href=\"select_user_to_edit\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Add, change, delete user accounts"));
-       wprintf("</span></A><br />\n");
-
-       wprintf("<a href=\"validate\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Validate new users"));
-       wprintf("</span></A><br />\n");
-
-       do_template("endbox");
-
-       svprintf("BOXTITLE", WCS_STRING, _("Rooms and Floors"));
-       do_template("beginbox");
-
-       wprintf("<a href=\"display_floorconfig\">"
-               "<span class=\"mainmenu\">");
-       wprintf(_("Add, change, or delete floors"));
-       wprintf("</span></A>\n");
-
-       do_template("endbox");
-
-       wprintf("</td></tr></table></div>");
-       wDumpContent(2);
-}
-
-
-
-
-
-/**
- * \brief Display the screen to enter a generic server command
- */
-void display_generic(void)
-{
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Enter a server command"));
-       wprintf("</SPAN></TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       wprintf("<CENTER>");
-       wprintf(_("This screen allows you to enter Citadel server commands which are "
-               "not supported by WebCit.  If you do not know what that means, "
-               "then this screen will not be of much use to you."));
-       wprintf("<br />\n");
-
-       wprintf("<FORM METHOD=\"POST\" action=\"do_generic\">\n");
-
-       wprintf(_("Enter command:"));
-       wprintf("<br /><INPUT TYPE=\"text\" NAME=\"g_cmd\" SIZE=80 MAXLENGTH=\"250\"><br />\n");
-
-       wprintf(_("Command input (if requesting SEND_LISTING transfer mode):"));
-       wprintf("<br /><TEXTAREA NAME=\"g_input\" ROWS=10 COLS=80 WIDTH=80></TEXTAREA><br />\n");
-
-       wprintf("<FONT SIZE=-2>");
-       wprintf(_("Detected host header is %s://%s"), (is_https ? "https" : "http"), WC->http_host);
-       wprintf("</FONT>\n");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"sc_button\" VALUE=\"%s\">", _("Send command"));
-       wprintf("&nbsp;");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\"><br />\n", _("Cancel"));
-
-       wprintf("</FORM></CENTER>\n");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-/**
- * \brief Interactive window to perform generic Citadel server commands.
- */
-void do_generic(void)
-{
-       char buf[SIZ];
-       char gcontent[SIZ];
-       char *junk;
-       size_t len;
-
-       if (strlen(bstr("sc_button")) == 0) {
-               display_main_menu();
-               return;
-       }
-
-       output_headers(1, 1, 0, 0, 0, 0);
-
-       serv_printf("%s", bstr("g_cmd"));
-       serv_getln(buf, sizeof buf);
-
-       svprintf("BOXTITLE", WCS_STRING, _("Server command results"));
-       do_template("beginbox");
-
-       wprintf("<TABLE border=0><TR><TD>Command:</TD><TD><TT>");
-       escputs(bstr("g_cmd"));
-       wprintf("</TT></TD></TR><TR><TD>Result:</TD><TD><TT>");
-       escputs(buf);
-       wprintf("</TT></TD></TR></TABLE><br />\n");
-
-       if (buf[0] == '8') {
-               serv_printf("\n\n000");
-       }
-       if ((buf[0] == '1') || (buf[0] == '8')) {
-               while (serv_getln(gcontent, sizeof gcontent), strcmp(gcontent, "000")) {
-                       escputs(gcontent);
-                       wprintf("<br />\n");
-               }
-               wprintf("000");
-       }
-       if (buf[0] == '4') {
-               text_to_server(bstr("g_input"));
-               serv_puts("000");
-       }
-       if (buf[0] == '6') {
-               len = atol(&buf[4]);
-               junk = malloc(len);
-               serv_read(junk, len);
-               free(junk);
-       }
-       if (buf[0] == '7') {
-               len = atol(&buf[4]);
-               junk = malloc(len);
-               memset(junk, 0, len);
-               serv_write(junk, len);
-               free(junk);
-       }
-       wprintf("<hr />");
-       wprintf("<a href=\"display_generic\">Enter another command</A><br />\n");
-       wprintf("<a href=\"display_advanced\">Return to menu</A>\n");
-       do_template("endbox");
-       wDumpContent(1);
-}
-
-
-/**
- * \brief Display the menubar.  
- * \param as_single_page Set to display HTML headers and footers -- otherwise it's assumed
- * that the menubar is being embedded in another page.
- */
-void display_menubar(int as_single_page) {
-
-       if (as_single_page) {
-               output_headers(0, 0, 0, 0, 0, 0);
-               wprintf("<HTML>\n"
-                       "<HEAD>\n"
-                       "<TITLE>MenuBar</TITLE>\n"
-                       "<STYLE TYPE=\"text/css\">\n"
-                       "BODY   { text-decoration: none; }\n"
-                       "</STYLE>\n"
-                       "</HEAD>\n");
-               do_template("background");
-       }
-
-       do_template("menubar");
-
-       if (as_single_page) {
-               wDumpContent(2);
-       }
-
-
-}
-
-
-/*@}*/
diff --git a/webcit/messages.c b/webcit/messages.c
deleted file mode 100644 (file)
index 1be8639..0000000
+++ /dev/null
@@ -1,3183 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup MsgDisp Functions which deal with the fetching and displaying of messages.
- * \ingroup WebcitDisplayItems
- *
- */
-/*@{*/
-#include "webcit.h"
-#include "vcard.h"
-#include "webserver.h"
-#include "groupdav.h"
-
-#define SUBJ_COL_WIDTH_PCT             50      /**< Mailbox view column width */
-#define SENDER_COL_WIDTH_PCT           30      /**< Mailbox view column width */
-#define DATE_PLUS_BUTTONS_WIDTH_PCT    20      /**< Mailbox view column width */
-
-/**
- * Address book entry (keep it short and sweet, it's just a quickie lookup
- * which we can use to get to the real meat and bones later)
- */
-struct addrbookent {
-       char ab_name[64]; /**< name string */
-       long ab_msgnum;   /**< message number of address book entry */
-};
-
-
-
-#ifdef HAVE_ICONV
-
-/**
- * \brief      Wrapper around iconv_open()
- *             Our version adds aliases for non-standard Microsoft charsets
- *              such as 'MS950', aliasing them to names like 'CP950'
- *
- * \param      tocode          Target encoding
- * \param      fromcode        Source encoding
- */
-iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode)
-{
-       iconv_t ic = (iconv_t)(-1) ;
-       ic = iconv_open(tocode, fromcode);
-       if (ic == (iconv_t)(-1) ) {
-               char alias_fromcode[64];
-               if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
-                       safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
-                       alias_fromcode[0] = 'C';
-                       alias_fromcode[1] = 'P';
-                       ic = iconv_open(tocode, alias_fromcode);
-               }
-       }
-       return(ic);
-}
-
-
-/**
- * \brief  Handle subjects with RFC2047 encoding
- *  such as:
- * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
- * \param buf the stringbuffer to process
- */
-void utf8ify_rfc822_string(char *buf) {
-       char *start, *end;
-       char newbuf[1024];
-       char charset[128];
-       char encoding[16];
-       char istr[1024];
-       iconv_t ic = (iconv_t)(-1) ;
-       char *ibuf;                     /**< Buffer of characters to be converted */
-       char *obuf;                     /**< Buffer for converted characters */
-       size_t ibuflen;                 /**< Length of input buffer */
-       size_t obuflen;                 /**< Length of output buffer */
-       char *isav;                     /**< Saved pointer to input buffer */
-       char *osav;                     /**< Saved pointer to output buffer */
-       int passes = 0;
-       int i;
-       int illegal_non_rfc2047_encoding = 0;
-
-       /** Sometimes, badly formed messages contain strings which were simply
-        *  written out directly in some foreign character set instead of
-        *  using RFC2047 encoding.  This is illegal but we will attempt to
-        *  handle it anyway by converting from a user-specified default
-        *  charset to UTF-8 if we see any nonprintable characters.
-        */
-       for (i=0; i<strlen(buf); ++i) {
-               if ((buf[i] < 32) || (buf[i] > 126)) {
-                       illegal_non_rfc2047_encoding = 1;
-               }
-       }
-       if (illegal_non_rfc2047_encoding) {
-               char default_header_charset[128];
-               get_preference("default_header_charset", default_header_charset, sizeof default_header_charset);
-               if ( (strcasecmp(default_header_charset, "UTF-8")) && (strcasecmp(default_header_charset, "us-ascii")) ) {
-                       ic = ctdl_iconv_open("UTF-8", default_header_charset);
-                       if (ic != (iconv_t)(-1) ) {
-                               ibuf = malloc(1024);
-                               isav = ibuf;
-                               safestrncpy(ibuf, buf, 1024);
-                               ibuflen = strlen(ibuf);
-                               obuflen = 1024;
-                               obuf = (char *) malloc(obuflen);
-                               osav = obuf;
-                               iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
-                               osav[1024-obuflen] = 0;
-                               strcpy(buf, osav);
-                               free(osav);
-                               iconv_close(ic);
-                               free(isav);
-                       }
-               }
-       }
-
-       /** Now we handle foreign character sets properly encoded
-        *  in RFC2047 format.
-        */
-       while (start=strstr(buf, "=?"), end=strstr(buf, "?="),
-               ((start != NULL) && (end != NULL) && (end > start)) )
-       {
-               extract_token(charset, start, 1, '?', sizeof charset);
-               extract_token(encoding, start, 2, '?', sizeof encoding);
-               extract_token(istr, start, 3, '?', sizeof istr);
-
-               ibuf = malloc(1024);
-               isav = ibuf;
-               if (!strcasecmp(encoding, "B")) {       /**< base64 */
-                       ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr));
-               }
-               else if (!strcasecmp(encoding, "Q")) {  /**< quoted-printable */
-                       ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, strlen(istr));
-               }
-               else {
-                       strcpy(ibuf, istr);             /**< unknown encoding */
-                       ibuflen = strlen(istr);
-               }
-
-               ic = ctdl_iconv_open("UTF-8", charset);
-               if (ic != (iconv_t)(-1) ) {
-                       obuflen = 1024;
-                       obuf = (char *) malloc(obuflen);
-                       osav = obuf;
-                       iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
-                       osav[1024-obuflen] = 0;
-
-                       end = start;
-                       end++;
-                       strcpy(start, "");
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       strcpy(end, &end[1]);
-
-                       snprintf(newbuf, sizeof newbuf, "%s%s%s", buf, osav, end);
-                       strcpy(buf, newbuf);
-                       free(osav);
-                       iconv_close(ic);
-               }
-               else {
-                       end = start;
-                       end++;
-                       strcpy(start, "");
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       remove_token(end, 0, '?');
-                       strcpy(end, &end[1]);
-
-                       snprintf(newbuf, sizeof newbuf, "%s(unreadable)%s", buf, end);
-                       strcpy(buf, newbuf);
-               }
-
-               free(isav);
-
-               /**
-                * Since spammers will go to all sorts of absurd lengths to get their
-                * messages through, there are LOTS of corrupt headers out there.
-                * So, prevent a really badly formed RFC2047 header from throwing
-                * this function into an infinite loop.
-                */
-               ++passes;
-               if (passes > 20) return;
-       }
-
-}
-#endif
-
-
-
-
-/**
- * \brief      RFC2047-encode a header field if necessary.
- *             If no non-ASCII characters are found, the string
- *             will be copied verbatim without encoding.
- *
- * \param      target          Target buffer.
- * \param      maxlen          Maximum size of target buffer.
- * \param      source          Source string to be encoded.
- */
-void rfc2047encode(char *target, int maxlen, char *source)
-{
-       int need_to_encode = 0;
-       int i;
-       unsigned char ch;
-
-       if (target == NULL) return;
-
-       for (i=0; i<strlen(source); ++i) {
-               if ((source[i] < 32) || (source[i] > 126)) {
-                       need_to_encode = 1;
-               }
-       }
-
-       if (!need_to_encode) {
-               safestrncpy(target, source, maxlen);
-               return;
-       }
-
-       strcpy(target, "=?UTF-8?Q?");
-       for (i=0; i<strlen(source); ++i) {
-               ch = (unsigned char) source[i];
-               if ((ch < 32) || (ch > 126) || (ch == 61)) {
-                       sprintf(&target[strlen(target)], "=%02X", ch);
-               }
-               else {
-                       sprintf(&target[strlen(target)], "%c", ch);
-               }
-       }
-       
-       strcat(target, "?=");
-}
-
-
-
-
-/**
- * \brief Look for URL's embedded in a buffer and make them linkable.  We use a
- * target window in order to keep the BBS session in its own window.
- * \param buf the message buffer
- */
-void url(char *buf)
-{
-
-       int pos;
-       int start, end;
-       char ench;
-       char urlbuf[SIZ];
-       char outbuf[1024];
-
-       start = (-1);
-       end = strlen(buf);
-       ench = 0;
-
-       for (pos = 0; pos < strlen(buf); ++pos) {
-               if (!strncasecmp(&buf[pos], "http://", 7))
-                       start = pos;
-               if (!strncasecmp(&buf[pos], "ftp://", 6))
-                       start = pos;
-       }
-
-       if (start < 0)
-               return;
-
-       if ((start > 0) && (buf[start - 1] == '<'))
-               ench = '>';
-       if ((start > 0) && (buf[start - 1] == '['))
-               ench = ']';
-       if ((start > 0) && (buf[start - 1] == '('))
-               ench = ')';
-       if ((start > 0) && (buf[start - 1] == '{'))
-               ench = '}';
-
-       for (pos = strlen(buf); pos > start; --pos) {
-               if ((buf[pos] == ' ') || (buf[pos] == ench))
-                       end = pos;
-       }
-
-       strncpy(urlbuf, &buf[start], end - start);
-       urlbuf[end - start] = 0;
-
-       strncpy(outbuf, buf, start);
-       sprintf(&outbuf[start], "%ca href=%c%s%c TARGET=%c%s%c%c%s%c/A%c",
-               LB, QU, urlbuf, QU, QU, TARGET, QU, RB, urlbuf, LB, RB);
-       strcat(outbuf, &buf[end]);
-       if ( strlen(outbuf) < 250 )
-               strcpy(buf, outbuf);
-}
-
-
-/**
- * \brief Turn a vCard "n" (name) field into something displayable.
- * \param name the name field to convert
- */
-void vcard_n_prettyize(char *name)
-{
-       char *original_name;
-       int i;
-
-       original_name = strdup(name);
-       for (i=0; i<5; ++i) {
-               if (strlen(original_name) > 0) {
-                       if (original_name[strlen(original_name)-1] == ' ') {
-                               original_name[strlen(original_name)-1] = 0;
-                       }
-                       if (original_name[strlen(original_name)-1] == ';') {
-                               original_name[strlen(original_name)-1] = 0;
-                       }
-               }
-       }
-       strcpy(name, "");
-       for (i=0; i<strlen(original_name); ++i) {
-               if (original_name[i] == ';') {
-                       strcat(name, ", ");
-               }
-               else {
-                       name[strlen(name)+1] = 0;
-                       name[strlen(name)] = original_name[i];
-               }
-       }
-       free(original_name);
-}
-
-
-
-
-/**
- * \brief preparse a vcard name
- * display_vcard() calls this after parsing the textual vCard into
- * our 'struct vCard' data object.
- * This gets called instead of display_parsed_vcard() if we are only looking
- * to extract the person's name instead of displaying the card.
- * \param v the vcard to retrieve the name from
- * \param storename where to put the name at
- */
-void fetchname_parsed_vcard(struct vCard *v, char *storename) {
-       char *name;
-
-       strcpy(storename, "");
-
-       name = vcard_get_prop(v, "n", 1, 0, 0);
-       if (name != NULL) {
-               strcpy(storename, name);
-               /* vcard_n_prettyize(storename); */
-       }
-
-}
-
-
-
-/**
- * \brief html print a vcard
- * display_vcard() calls this after parsing the textual vCard into
- * our 'struct vCard' data object.
- *
- * Set 'full' to nonzero to display the full card, otherwise it will only
- * show a summary line.
- *
- * This code is a bit ugly, so perhaps an explanation is due: we do this
- * in two passes through the vCard fields.  On the first pass, we process
- * fields we understand, and then render them in a pretty fashion at the
- * end.  Then we make a second pass, outputting all the fields we don't
- * understand in a simple two-column name/value format.
- * \param v the vCard to display
- * \param full display all items of the vcard?
- */
-void display_parsed_vcard(struct vCard *v, int full) {
-       int i, j;
-       char buf[SIZ];
-       char *name;
-       int is_qp = 0;
-       int is_b64 = 0;
-       char *thisname, *thisvalue;
-       char firsttoken[SIZ];
-       int pass;
-
-       char fullname[SIZ];
-       char title[SIZ];
-       char org[SIZ];
-       char phone[SIZ];
-       char mailto[SIZ];
-
-       strcpy(fullname, "");
-       strcpy(phone, "");
-       strcpy(mailto, "");
-       strcpy(title, "");
-       strcpy(org, "");
-
-       if (!full) {
-               wprintf("<TD>");
-               name = vcard_get_prop(v, "fn", 1, 0, 0);
-               if (name != NULL) {
-                       escputs(name);
-               }
-               else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) {
-                       strcpy(fullname, name);
-                       vcard_n_prettyize(fullname);
-                       escputs(fullname);
-               }
-               else {
-                       wprintf("&nbsp;");
-               }
-               wprintf("</TD>");
-               return;
-       }
-
-       wprintf("<div align=center><table bgcolor=#aaaaaa width=50%%>");
-       for (pass=1; pass<=2; ++pass) {
-
-               if (v->numprops) for (i=0; i<(v->numprops); ++i) {
-
-                       thisname = strdup(v->prop[i].name);
-                       extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken);
-       
-                       for (j=0; j<num_tokens(thisname, ';'); ++j) {
-                               extract_token(buf, thisname, j, ';', sizeof buf);
-                               if (!strcasecmp(buf, "encoding=quoted-printable")) {
-                                       is_qp = 1;
-                                       remove_token(thisname, j, ';');
-                               }
-                               if (!strcasecmp(buf, "encoding=base64")) {
-                                       is_b64 = 1;
-                                       remove_token(thisname, j, ';');
-                               }
-                       }
-       
-                       if (is_qp) {
-                               thisvalue = malloc(strlen(v->prop[i].value) + 50);
-                               j = CtdlDecodeQuotedPrintable(
-                                       thisvalue, v->prop[i].value,
-                                       strlen(v->prop[i].value) );
-                               thisvalue[j] = 0;
-                       }
-                       else if (is_b64) {
-                               thisvalue = malloc(strlen(v->prop[i].value) + 50);
-                               CtdlDecodeBase64(
-                                       thisvalue, v->prop[i].value,
-                                       strlen(v->prop[i].value) );
-                       }
-                       else {
-                               thisvalue = strdup(v->prop[i].value);
-                       }
-       
-                       /** Various fields we may encounter ***/
-       
-                       /** N is name, but only if there's no FN already there */
-                       if (!strcasecmp(firsttoken, "n")) {
-                               if (strlen(fullname) == 0) {
-                                       strcpy(fullname, thisvalue);
-                                       vcard_n_prettyize(fullname);
-                               }
-                       }
-       
-                       /** FN (full name) is a true 'display name' field */
-                       else if (!strcasecmp(firsttoken, "fn")) {
-                               strcpy(fullname, thisvalue);
-                       }
-
-                       /** title */
-                       else if (!strcasecmp(firsttoken, "title")) {
-                               strcpy(title, thisvalue);
-                       }
-       
-                       /** organization */
-                       else if (!strcasecmp(firsttoken, "org")) {
-                               strcpy(org, thisvalue);
-                       }
-       
-                       else if (!strcasecmp(firsttoken, "email")) {
-                               if (strlen(mailto) > 0) strcat(mailto, "<br />");
-                               strcat(mailto,
-                                       "<a href=\"display_enter"
-                                       "?force_room=_MAIL_?recp=");
-
-                               urlesc(&mailto[strlen(mailto)], fullname);
-                               urlesc(&mailto[strlen(mailto)], " <");
-                               urlesc(&mailto[strlen(mailto)], thisvalue);
-                               urlesc(&mailto[strlen(mailto)], ">");
-
-                               strcat(mailto, "\">");
-                               stresc(&mailto[strlen(mailto)], thisvalue, 1, 1);
-                               strcat(mailto, "</A>");
-                       }
-                       else if (!strcasecmp(firsttoken, "tel")) {
-                               if (strlen(phone) > 0) strcat(phone, "<br />");
-                               strcat(phone, thisvalue);
-                               for (j=0; j<num_tokens(thisname, ';'); ++j) {
-                                       extract_token(buf, thisname, j, ';', sizeof buf);
-                                       if (!strcasecmp(buf, "tel"))
-                                               strcat(phone, "");
-                                       else if (!strcasecmp(buf, "work"))
-                                               strcat(phone, _(" (work)"));
-                                       else if (!strcasecmp(buf, "home"))
-                                               strcat(phone, _(" (home)"));
-                                       else if (!strcasecmp(buf, "cell"))
-                                               strcat(phone, _(" (cell)"));
-                                       else {
-                                               strcat(phone, " (");
-                                               strcat(phone, buf);
-                                               strcat(phone, ")");
-                                       }
-                               }
-                       }
-                       else if (!strcasecmp(firsttoken, "adr")) {
-                               if (pass == 2) {
-                                       wprintf("<TR><TD>");
-                                       wprintf(_("Address:"));
-                                       wprintf("</TD><TD>");
-                                       for (j=0; j<num_tokens(thisvalue, ';'); ++j) {
-                                               extract_token(buf, thisvalue, j, ';', sizeof buf);
-                                               if (strlen(buf) > 0) {
-                                                       escputs(buf);
-                                                       if (j<3) wprintf("<br />");
-                                                       else wprintf(" ");
-                                               }
-                                       }
-                                       wprintf("</TD></TR>\n");
-                               }
-                       }
-                       else if (!strcasecmp(firsttoken, "version")) {
-                               /* ignore */
-                       }
-                       else if (!strcasecmp(firsttoken, "rev")) {
-                               /* ignore */
-                       }
-                       else if (!strcasecmp(firsttoken, "label")) {
-                               /* ignore */
-                       }
-                       else {
-
-                               /*** Don't show extra fields.  They're ugly.
-                               if (pass == 2) {
-                                       wprintf("<TR><TD>");
-                                       escputs(thisname);
-                                       wprintf("</TD><TD>");
-                                       escputs(thisvalue);
-                                       wprintf("</TD></TR>\n");
-                               }
-                               ***/
-                       }
-       
-                       free(thisname);
-                       free(thisvalue);
-               }
-       
-               if (pass == 1) {
-                       wprintf("<TR BGCOLOR=\"#AAAAAA\">"
-                       "<TD COLSPAN=2 BGCOLOR=\"#FFFFFF\">"
-                       "<IMG ALIGN=CENTER src=\"static/viewcontacts_48x.gif\">"
-                       "<FONT SIZE=+1><B>");
-                       escputs(fullname);
-                       wprintf("</B></FONT>");
-                       if (strlen(title) > 0) {
-                               wprintf("<div align=right>");
-                               escputs(title);
-                               wprintf("</div>");
-                       }
-                       if (strlen(org) > 0) {
-                               wprintf("<div align=right>");
-                               escputs(org);
-                               wprintf("</div>");
-                       }
-                       wprintf("</TD></TR>\n");
-               
-                       if (strlen(phone) > 0) {
-                               wprintf("<tr><td>");
-                               wprintf(_("Telephone:"));
-                               wprintf("</td><td>%s</td></tr>\n", phone);
-                       }
-                       if (strlen(mailto) > 0) {
-                               wprintf("<tr><td>");
-                               wprintf(_("E-mail:"));
-                               wprintf("</td><td>%s</td></tr>\n", mailto);
-                       }
-               }
-
-       }
-
-       wprintf("</table></div>\n");
-}
-
-
-
-/**
- * \brief  Display a textual vCard
- * (Converts to a vCard object and then calls the actual display function)
- * Set 'full' to nonzero to display the whole card instead of a one-liner.
- * Or, if "storename" is non-NULL, just store the person's name in that
- * buffer instead of displaying the card at all.
- * \param vcard_source the buffer containing the vcard text
- * \param alpha what???
- * \param full should we usse all lines?
- * \param storename where to store???
- */
-void display_vcard(char *vcard_source, char alpha, int full, char *storename) {
-       struct vCard *v;
-       char *name;
-       char buf[SIZ];
-       char this_alpha = 0;
-
-       v = vcard_load(vcard_source);
-       if (v == NULL) return;
-
-       name = vcard_get_prop(v, "n", 1, 0, 0);
-       if (name != NULL) {
-               strcpy(buf, name);
-               this_alpha = buf[0];
-       }
-
-       if (storename != NULL) {
-               fetchname_parsed_vcard(v, storename);
-       }
-       else if (       (alpha == 0)
-                       || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) )
-                       || ((!isalpha(alpha)) && (!isalpha(this_alpha)))
-               ) {
-               display_parsed_vcard(v, full);
-       }
-
-       vcard_free(v);
-}
-
-
-/**
- * \brief I wanna SEE that message!  
- * \param msgnum the citadel number of the message to display
- * \param printable_view are we doing a print view?
- * \param section Optional for encapsulated message/rfc822 submessage)
- */
-void read_message(long msgnum, int printable_view, char *section) {
-       char buf[SIZ];
-       char mime_partnum[256];
-       char mime_filename[256];
-       char mime_content_type[256];
-       char mime_charset[256];
-       char mime_disposition[256];
-       int mime_length;
-       char mime_http[SIZ];
-       char mime_submessages[256];
-       char m_subject[256];
-       char m_cc[1024];
-       char from[256];
-       char node[256];
-       char rfca[256];
-       char reply_to[512];
-       char reply_all[4096];
-       char now[64];
-       int format_type = 0;
-       int nhdr = 0;
-       int bq = 0;
-       int i = 0;
-       char vcard_partnum[256];
-       char cal_partnum[256];
-       char *part_source = NULL;
-#ifdef HAVE_ICONV
-       iconv_t ic = (iconv_t)(-1) ;
-       char *ibuf;                /**< Buffer of characters to be converted */
-       char *obuf;                /**< Buffer for converted characters      */
-       size_t ibuflen;    /**< Length of input buffer         */
-       size_t obuflen;    /**< Length of output buffer       */
-       char *osav;                /**< Saved pointer to output buffer       */
-#endif
-
-       strcpy(from, "");
-       strcpy(node, "");
-       strcpy(rfca, "");
-       strcpy(reply_to, "");
-       strcpy(reply_all, "");
-       strcpy(vcard_partnum, "");
-       strcpy(cal_partnum, "");
-       strcpy(mime_http, "");
-       strcpy(mime_content_type, "text/plain");
-       strcpy(mime_charset, "us-ascii");
-       strcpy(mime_submessages, "");
-
-       serv_printf("MSG4 %ld|%s", msgnum, section);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               wprintf("<STRONG>");
-               wprintf(_("ERROR:"));
-               wprintf("</STRONG> %s<br />\n", &buf[4]);
-               return;
-       }
-
-       /** begin everythingamundo table */
-       if (!printable_view) {
-               wprintf("<div class=\"fix_scrollbar_bug\">\n");
-               wprintf("<table width=100%% border=1 cellspacing=0 "
-                       "cellpadding=0><TR><TD>\n");
-       }
-
-       /** begin message header table */
-       wprintf("<table width=100%% border=0 cellspacing=0 "
-               "cellpadding=1 bgcolor=\"#CCCCCC\"><tr><td>\n");
-
-       wprintf("<span class=\"message_header\">");
-       strcpy(m_subject, "");
-       strcpy(m_cc, "");
-
-       while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
-               if (!strcmp(buf, "000")) {
-                       wprintf("<i>");
-                       wprintf(_("unexpected end of message"));
-                       wprintf("</i><br /><br />\n");
-                       wprintf("</span>\n");
-                       return;
-               }
-               if (!strncasecmp(buf, "nhdr=yes", 8))
-                       nhdr = 1;
-               if (nhdr == 1)
-                       buf[0] = '_';
-               if (!strncasecmp(buf, "type=", 5))
-                       format_type = atoi(&buf[5]);
-               if (!strncasecmp(buf, "from=", 5)) {
-                       strcpy(from, &buf[5]);
-                       wprintf(_("from "));
-                       wprintf("<a href=\"showuser?who=");
-#ifdef HAVE_ICONV
-                       utf8ify_rfc822_string(from);
-#endif
-                       urlescputs(from);
-                       wprintf("\">");
-                       escputs(from);
-                       wprintf("</a> ");
-               }
-               if (!strncasecmp(buf, "subj=", 5)) {
-                       safestrncpy(m_subject, &buf[5], sizeof m_subject);
-               }
-               if (!strncasecmp(buf, "cccc=", 5)) {
-                       safestrncpy(m_cc, &buf[5], sizeof m_cc);
-                       if (strlen(reply_all) > 0) {
-                               strcat(reply_all, ", ");
-                       }
-                       safestrncpy(&reply_all[strlen(reply_all)], &buf[5],
-                               (sizeof reply_all - strlen(reply_all)) );
-               }
-               if ((!strncasecmp(buf, "hnod=", 5))
-                   && (strcasecmp(&buf[5], serv_info.serv_humannode))) {
-                       wprintf("(%s) ", &buf[5]);
-               }
-               if ((!strncasecmp(buf, "room=", 5))
-                   && (strcasecmp(&buf[5], WC->wc_roomname))
-                   && (strlen(&buf[5])>0) ) {
-                       wprintf(_("in "));
-                       wprintf("%s&gt; ", &buf[5]);
-               }
-               if (!strncasecmp(buf, "rfca=", 5)) {
-                       strcpy(rfca, &buf[5]);
-                       wprintf("&lt;");
-                       escputs(rfca);
-                       wprintf("&gt; ");
-               }
-
-               if (!strncasecmp(buf, "node=", 5)) {
-                       strcpy(node, &buf[5]);
-                       if ( ((WC->room_flags & QR_NETWORK)
-                       || ((strcasecmp(&buf[5], serv_info.serv_nodename)
-                       && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
-                       && (strlen(rfca)==0)
-                       ) {
-                               wprintf("@%s ", &buf[5]);
-                       }
-               }
-               if (!strncasecmp(buf, "rcpt=", 5)) {
-                       wprintf(_("to "));
-                       if (strlen(reply_all) > 0) {
-                               strcat(reply_all, ", ");
-                       }
-                       safestrncpy(&reply_all[strlen(reply_all)], &buf[5],
-                               (sizeof reply_all - strlen(reply_all)) );
-#ifdef HAVE_ICONV
-                       utf8ify_rfc822_string(&buf[5]);
-#endif
-                       escputs(&buf[5]);
-                       wprintf(" ");
-               }
-               if (!strncasecmp(buf, "time=", 5)) {
-                       fmt_date(now, atol(&buf[5]), 0);
-                       wprintf("%s ", now);
-               }
-
-               if (!strncasecmp(buf, "part=", 5)) {
-                       extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
-                       extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
-                       extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
-                       extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
-                       mime_length = extract_int(&buf[5], 5);
-
-                       if (!strcasecmp(mime_content_type, "message/rfc822")) {
-                               if (strlen(mime_submessages) > 0) {
-                                       strcat(mime_submessages, "|");
-                               }
-                               strcat(mime_submessages, mime_partnum);
-                       }
-                       else if ((!strcasecmp(mime_disposition, "inline"))
-                          && (!strncasecmp(mime_content_type, "image/", 6)) ){
-                               snprintf(&mime_http[strlen(mime_http)],
-                                       (sizeof(mime_http) - strlen(mime_http) - 1),
-                                       "<img src=\"mimepart/%ld/%s/%s\">",
-                                       msgnum, mime_partnum, mime_filename);
-                       }
-                       else if ( (!strcasecmp(mime_disposition, "attachment")) 
-                            || (!strcasecmp(mime_disposition, "inline")) ) {
-                               snprintf(&mime_http[strlen(mime_http)],
-                                       (sizeof(mime_http) - strlen(mime_http) - 1),
-                                       "<img src=\"static/diskette_24x.gif\" "
-                                       "border=0 align=middle>\n"
-                                       "%s (%s, %d bytes) [ "
-                                       "<a href=\"mimepart/%ld/%s/%s\""
-                                       "target=\"wc.%ld.%s\">%s</a>"
-                                       " | "
-                                       "<a href=\"mimepart_download/%ld/%s/%s\">%s</a>"
-                                       " ]<br />\n",
-                                       mime_filename,
-                                       mime_content_type, mime_length,
-                                       msgnum, mime_partnum, mime_filename,
-                                       msgnum, mime_partnum,
-                                       _("View"),
-                                       msgnum, mime_partnum, mime_filename,
-                                       _("Download")
-                               );
-                       }
-
-                       /** begin handler prep ***/
-                       if (!strcasecmp(mime_content_type, "text/x-vcard")) {
-                               strcpy(vcard_partnum, mime_partnum);
-                       }
-
-                       if (!strcasecmp(mime_content_type, "text/calendar")) {
-                               strcpy(cal_partnum, mime_partnum);
-                       }
-
-                       /** end handler prep ***/
-
-               }
-
-       }
-
-       /** Generate a reply-to address */
-       if (strlen(rfca) > 0) {
-               strcpy(reply_to, rfca);
-       }
-       else {
-               if ( (strlen(node) > 0)
-                  && (strcasecmp(node, serv_info.serv_nodename))
-                  && (strcasecmp(node, serv_info.serv_humannode)) ) {
-                       snprintf(reply_to, sizeof(reply_to), "%s @ %s",
-                               from, node);
-               }
-               else {
-                       snprintf(reply_to, sizeof(reply_to), "%s", from);
-               }
-       }
-
-       if (nhdr == 1) {
-               wprintf("****");
-       }
-
-       wprintf("</span>");
-#ifdef HAVE_ICONV
-       utf8ify_rfc822_string(m_cc);
-       utf8ify_rfc822_string(m_subject);
-#endif
-       if (strlen(m_cc) > 0) {
-               wprintf("<br />"
-                       "<span class=\"message_subject\">");
-               wprintf(_("CC:"));
-               wprintf(" ");
-               escputs(m_cc);
-               wprintf("</span>");
-       }
-       if (strlen(m_subject) > 0) {
-               wprintf("<br />"
-                       "<span class=\"message_subject\">");
-               wprintf(_("Subject:"));
-               wprintf(" ");
-               escputs(m_subject);
-               wprintf("</span>");
-       }
-       wprintf("</td>\n");
-
-       /** start msg buttons */
-       if (!printable_view) {
-               wprintf("<td align=right><span class=\"msgbuttons\">\n");
-
-               /** Reply */
-               if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) {
-                       wprintf("<a href=\"display_enter");
-                       if (WC->is_mailbox) {
-                               wprintf("?replyquote=%ld", msgnum);
-                       }
-                       wprintf("?recp=");
-                       urlescputs(reply_to);
-                       if (strlen(m_subject) > 0) {
-                               wprintf("?subject=");
-                               if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
-                               urlescputs(m_subject);
-                       }
-                       wprintf("\">[%s]</a> ", _("Reply"));
-               }
-
-               /** ReplyQuoted */
-               if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) {
-                       if (!WC->is_mailbox) {
-                               wprintf("<a href=\"display_enter");
-                               wprintf("?replyquote=%ld", msgnum);
-                               wprintf("?recp=");
-                               urlescputs(reply_to);
-                               if (strlen(m_subject) > 0) {
-                                       wprintf("?subject=");
-                                       if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
-                                       urlescputs(m_subject);
-                               }
-                               wprintf("\">[%s]</a> ", _("ReplyQuoted"));
-                       }
-               }
-
-               /** ReplyAll */
-               if (WC->wc_view == VIEW_MAILBOX) {
-                       wprintf("<a href=\"display_enter");
-                       wprintf("?replyquote=%ld", msgnum);
-                       wprintf("?recp=");
-                       urlescputs(reply_to);
-                       wprintf("?cc=");
-                       urlescputs(reply_all);
-                       if (strlen(m_subject) > 0) {
-                               wprintf("?subject=");
-                               if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
-                               urlescputs(m_subject);
-                       }
-                       wprintf("\">[%s]</a> ", _("ReplyAll"));
-               }
-
-               /** Forward */
-               if (WC->wc_view == VIEW_MAILBOX) {
-                       wprintf("<a href=\"display_enter?fwdquote=%ld?subject=", msgnum);
-                       if (strncasecmp(m_subject, "Fwd:", 4)) wprintf("Fwd:%20");
-                       urlescputs(m_subject);
-                       wprintf("\">[%s]</a> ", _("Forward"));
-               }
-
-               /** If this is one of my own rooms, or if I'm an Aide or Room Aide, I can move/delete */
-               if ( (WC->is_room_aide) || (WC->is_mailbox) ) {
-                       /** Move */
-                       wprintf("<a href=\"confirm_move_msg?msgid=%ld\">[%s]</a> ",
-                               msgnum, _("Move"));
-       
-                       /** Delete */
-                       wprintf("<a href=\"delete_msg?msgid=%ld\" "
-                               "onClick=\"return confirm('%s');\">"
-                               "[%s]</a> ", msgnum, _("Delete this message?"), _("Delete")
-                       );
-               }
-
-               /** Headers */
-               wprintf("<a href=\"#\" onClick=\"window.open('msgheaders/%ld', 'headers%ld', 'toolbar=no,location=no,directories=no,copyhistory=no,status=yes,scrollbars=yes,resizable=yes,width=600,height=400'); \" >"
-                       "[%s]</a>", msgnum, msgnum, _("Headers"));
-
-
-               /** Print */
-               wprintf("<a href=\"#\" onClick=\"window.open('printmsg/%ld', 'print%ld', 'toolbar=no,location=no,directories=no,copyhistory=no,status=yes,scrollbars=yes,resizable=yes,width=600,height=400'); \" >"
-                       "[%s]</a>", msgnum, msgnum, _("Print"));
-
-               wprintf("</span></td>");
-       }
-
-       wprintf("</tr></table>\n");
-
-       /** Begin body */
-       wprintf("<table border=0 width=100%% bgcolor=\"#FFFFFF\" "
-               "cellpadding=1 cellspacing=0><tr><td>");
-
-       /**
-        * Learn the content type
-        */
-       strcpy(mime_content_type, "text/plain");
-       while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) {
-               if (!strcmp(buf, "000")) {
-                       wprintf("<i>");
-                       wprintf(_("unexpected end of message"));
-                       wprintf("</i><br /><br />\n");
-                       goto ENDBODY;
-               }
-               if (!strncasecmp(buf, "Content-type: ", 14)) {
-                       safestrncpy(mime_content_type, &buf[14],
-                               sizeof(mime_content_type));
-                       for (i=0; i<strlen(mime_content_type); ++i) {
-                               if (!strncasecmp(&mime_content_type[i], "charset=", 8)) {
-                                       safestrncpy(mime_charset, &mime_content_type[i+8],
-                                               sizeof mime_charset);
-                               }
-                       }
-                       for (i=0; i<strlen(mime_content_type); ++i) {
-                               if (mime_content_type[i] == ';') {
-                                       mime_content_type[i] = 0;
-                               }
-                       }
-                       for (i=0; i<strlen(mime_charset); ++i) {
-                               if (mime_charset[i] == ';') {
-                                       mime_charset[i] = 0;
-                               }
-                       }
-               }
-       }
-
-       /** Set up a character set conversion if we need to (and if we can) */
-#ifdef HAVE_ICONV
-       if (strchr(mime_charset, ';')) strcpy(strchr(mime_charset, ';'), "");
-       if ( (strcasecmp(mime_charset, "us-ascii"))
-          && (strcasecmp(mime_charset, "UTF-8"))
-          && (strcasecmp(mime_charset, ""))
-       ) {
-               ic = ctdl_iconv_open("UTF-8", mime_charset);
-               if (ic == (iconv_t)(-1) ) {
-                       lprintf(5, "%s:%d iconv_open(UTF-8, %s) failed: %s\n",
-                               __FILE__, __LINE__, mime_charset, strerror(errno));
-               }
-       }
-#endif
-
-       /** Messages in legacy Citadel variformat get handled thusly... */
-       if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
-               fmout("JUSTIFY");
-       }
-
-       /** Boring old 80-column fixed format text gets handled this way... */
-       else if ( (!strcasecmp(mime_content_type, "text/plain"))
-               || (!strcasecmp(mime_content_type, "text")) ) {
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
-                       if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
-
-#ifdef HAVE_ICONV
-                       if (ic != (iconv_t)(-1) ) {
-                               ibuf = buf;
-                               ibuflen = strlen(ibuf);
-                               obuflen = SIZ;
-                               obuf = (char *) malloc(obuflen);
-                               osav = obuf;
-                               iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
-                               osav[SIZ-obuflen] = 0;
-                               safestrncpy(buf, osav, sizeof buf);
-                               free(osav);
-                       }
-#endif
-
-                       while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
-                               buf[strlen(buf) - 1] = 0;
-                       if ((bq == 0) &&
-                       ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) {
-                               wprintf("<blockquote>");
-                               bq = 1;
-                       } else if ((bq == 1) &&
-                               (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) {
-                               wprintf("</blockquote>");
-                               bq = 0;
-                       }
-                       wprintf("<tt>");
-                       url(buf);
-                       escputs(buf);
-                       wprintf("</tt><br />\n");
-               }
-               wprintf("</i><br />");
-       }
-
-       else /** HTML is fun, but we've got to strip it first */
-       if (!strcasecmp(mime_content_type, "text/html")) {
-               output_html(mime_charset, (WC->wc_view == VIEW_WIKI ? 1 : 0));
-       }
-
-       /** Unknown weirdness */
-       else {
-               wprintf(_("I don't know how to display %s"), mime_content_type);
-               wprintf("<br />\n", mime_content_type);
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { }
-       }
-
-       /** If there are attached submessages, display them now... */
-       if ( (strlen(mime_submessages) > 0) && (!section[0]) ) {
-               for (i=0; i<num_tokens(mime_submessages, '|'); ++i) {
-                       extract_token(buf, mime_submessages, i, '|', sizeof buf);
-                       /** use printable_view to suppress buttons */
-                       wprintf("<blockquote>");
-                       read_message(msgnum, 1, buf);
-                       wprintf("</blockquote>");
-               }
-       }
-
-
-       /** Afterwards, offer links to download attachments 'n' such */
-       if ( (strlen(mime_http) > 0) && (!section[0]) ) {
-               wprintf("%s", mime_http);
-       }
-
-       /** Handler for vCard parts */
-       if (strlen(vcard_partnum) > 0) {
-               part_source = load_mimepart(msgnum, vcard_partnum);
-               if (part_source != NULL) {
-
-                       /** If it's my vCard I can edit it */
-                       if (    (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
-                               || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
-                               || (WC->wc_view == VIEW_ADDRESSBOOK)
-                       ) {
-                               wprintf("<a href=\"edit_vcard?"
-                                       "msgnum=%ld?partnum=%s\">",
-                                       msgnum, vcard_partnum);
-                               wprintf("[%s]</a>", _("edit"));
-                       }
-
-                       /** In all cases, display the full card */
-                       display_vcard(part_source, 0, 1, NULL);
-               }
-       }
-
-       /** Handler for calendar parts */
-       if (strlen(cal_partnum) > 0) {
-               part_source = load_mimepart(msgnum, cal_partnum);
-               if (part_source != NULL) {
-                       cal_process_attachment(part_source,
-                                               msgnum, cal_partnum);
-               }
-       }
-
-       if (part_source) {
-               free(part_source);
-               part_source = NULL;
-       }
-
-ENDBODY:
-       wprintf("</td></tr></table>\n");
-
-       /** end everythingamundo table */
-       if (!printable_view) {
-               wprintf("</td></tr></table>\n");
-               wprintf("</div><br />\n");
-       }
-
-#ifdef HAVE_ICONV
-       if (ic != (iconv_t)(-1) ) {
-               iconv_close(ic);
-       }
-#endif
-}
-
-
-
-/**
- * \brief Unadorned HTML output of an individual message, suitable
- * for placing in a hidden iframe, for printing, or whatever
- *
- * \param msgnum_as_string Message number, as a string instead of as a long int
- */
-void embed_message(char *msgnum_as_string) {
-       long msgnum = 0L;
-
-       msgnum = atol(msgnum_as_string);
-       begin_ajax_response();
-       read_message(msgnum, 0, "");
-       end_ajax_response();
-}
-
-
-/**
- * \brief Printable view of a message
- *
- * \param msgnum_as_string Message number, as a string instead of as a long int
- */
-void print_message(char *msgnum_as_string) {
-       long msgnum = 0L;
-
-       msgnum = atol(msgnum_as_string);
-       output_headers(0, 0, 0, 0, 0, 0);
-
-       wprintf("Content-type: text/html\r\n"
-               "Server: %s\r\n"
-               "Connection: close\r\n",
-               SERVER);
-       begin_burst();
-
-       wprintf("\r\n\r\n<html>\n"
-               "<head><title>Printable view</title></head>\n"
-               "<body onLoad=\" window.print(); window.close(); \">\n"
-       );
-       
-       read_message(msgnum, 1, "");
-
-       wprintf("\n</body></html>\n\n");
-       wDumpContent(0);
-}
-
-
-
-/**
- * \brief Display a message's headers
- *
- * \param msgnum_as_string Message number, as a string instead of as a long int
- */
-void display_headers(char *msgnum_as_string) {
-       long msgnum = 0L;
-       char buf[1024];
-
-       msgnum = atol(msgnum_as_string);
-       output_headers(0, 0, 0, 0, 0, 0);
-
-       wprintf("Content-type: text/plain\r\n"
-               "Server: %s\r\n"
-               "Connection: close\r\n",
-               SERVER);
-       begin_burst();
-
-       serv_printf("MSG2 %ld|3", msgnum);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       wprintf("%s\n", buf);
-               }
-       }
-
-       wDumpContent(0);
-}
-
-
-
-/**
- * \brief Read message in simple, JavaScript-embeddable form for 'forward'
- *        or 'reply quoted' operations.
- *
- * NOTE: it is VITALLY IMPORTANT that we output no single-quotes or linebreaks
- *       in this function.  Doing so would throw a JavaScript error in the
- *       'supplied text' argument to the editor.
- *
- * \param msgnum Message number of the message we want to quote
- * \param forward_attachments Nonzero if we want attachments to be forwarded
- */
-void pullquote_message(long msgnum, int forward_attachments, int include_headers) {
-       char buf[SIZ];
-       char mime_partnum[256];
-       char mime_filename[256];
-       char mime_content_type[256];
-       char mime_charset[256];
-       char mime_disposition[256];
-       int mime_length;
-       char *attachments = NULL;
-       char *ptr = NULL;
-       int num_attachments = 0;
-       struct wc_attachment *att, *aptr;
-       char m_subject[256];
-       char from[256];
-       char node[256];
-       char rfca[256];
-       char reply_to[512];
-       char now[256];
-       int format_type = 0;
-       int nhdr = 0;
-       int bq = 0;
-       int i = 0;
-#ifdef HAVE_ICONV
-       iconv_t ic = (iconv_t)(-1) ;
-       char *ibuf;                /**< Buffer of characters to be converted */
-       char *obuf;                /**< Buffer for converted characters      */
-       size_t ibuflen;    /**< Length of input buffer         */
-       size_t obuflen;    /**< Length of output buffer       */
-       char *osav;                /**< Saved pointer to output buffer       */
-#endif
-
-       strcpy(from, "");
-       strcpy(node, "");
-       strcpy(rfca, "");
-       strcpy(reply_to, "");
-       strcpy(mime_content_type, "text/plain");
-       strcpy(mime_charset, "us-ascii");
-
-       serv_printf("MSG4 %ld", msgnum);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               wprintf(_("ERROR:"));
-               wprintf("%s<br />", &buf[4]);
-               return;
-       }
-
-       strcpy(m_subject, "");
-
-       while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
-               if (!strcmp(buf, "000")) {
-                       wprintf(_("unexpected end of message"));
-                       return;
-               }
-               if (include_headers) {
-                       if (!strncasecmp(buf, "nhdr=yes", 8))
-                               nhdr = 1;
-                       if (nhdr == 1)
-                               buf[0] = '_';
-                       if (!strncasecmp(buf, "type=", 5))
-                               format_type = atoi(&buf[5]);
-                       if (!strncasecmp(buf, "from=", 5)) {
-                               strcpy(from, &buf[5]);
-                               wprintf(_("from "));
-#ifdef HAVE_ICONV
-                               utf8ify_rfc822_string(from);
-#endif
-                               msgescputs(from);
-                       }
-                       if (!strncasecmp(buf, "subj=", 5)) {
-                               strcpy(m_subject, &buf[5]);
-                       }
-                       if ((!strncasecmp(buf, "hnod=", 5))
-                           && (strcasecmp(&buf[5], serv_info.serv_humannode))) {
-                               wprintf("(%s) ", &buf[5]);
-                       }
-                       if ((!strncasecmp(buf, "room=", 5))
-                           && (strcasecmp(&buf[5], WC->wc_roomname))
-                           && (strlen(&buf[5])>0) ) {
-                               wprintf(_("in "));
-                               wprintf("%s&gt; ", &buf[5]);
-                       }
-                       if (!strncasecmp(buf, "rfca=", 5)) {
-                               strcpy(rfca, &buf[5]);
-                               wprintf("&lt;");
-                               msgescputs(rfca);
-                               wprintf("&gt; ");
-                       }
-       
-                       if (!strncasecmp(buf, "node=", 5)) {
-                               strcpy(node, &buf[5]);
-                               if ( ((WC->room_flags & QR_NETWORK)
-                               || ((strcasecmp(&buf[5], serv_info.serv_nodename)
-                               && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
-                               && (strlen(rfca)==0)
-                               ) {
-                                       wprintf("@%s ", &buf[5]);
-                               }
-                       }
-                       if (!strncasecmp(buf, "rcpt=", 5)) {
-                               wprintf(_("to "));
-                               wprintf("%s ", &buf[5]);
-                       }
-                       if (!strncasecmp(buf, "time=", 5)) {
-                               fmt_date(now, atol(&buf[5]), 0);
-                               wprintf("%s ", now);
-                       }
-               }
-
-               /**
-                * Save attachment info for later.  We can't start downloading them
-                * yet because we're in the middle of a server transaction.
-                */
-               if (!strncasecmp(buf, "part=", 5)) {
-                       ptr = malloc( (strlen(buf) + ((attachments != NULL) ? strlen(attachments) : 0)) ) ;
-                       if (ptr != NULL) {
-                               ++num_attachments;
-                               sprintf(ptr, "%s%s\n",
-                                       ((attachments != NULL) ? attachments : ""),
-                                       &buf[5]
-                               );
-                               free(attachments);
-                               attachments = ptr;
-                               lprintf(9, "attachments=<%s>\n", attachments);
-                       }
-               }
-
-       }
-
-       if (include_headers) {
-               wprintf("<br>");
-
-#ifdef HAVE_ICONV
-               utf8ify_rfc822_string(m_subject);
-#endif
-               if (strlen(m_subject) > 0) {
-                       wprintf(_("Subject:"));
-                       wprintf(" ");
-                       msgescputs(m_subject);
-                       wprintf("<br />");
-               }
-
-               /**
-                * Begin body
-                */
-               wprintf("<br />");
-       }
-
-       /**
-        * Learn the content type
-        */
-       strcpy(mime_content_type, "text/plain");
-       while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) {
-               if (!strcmp(buf, "000")) {
-                       wprintf(_("unexpected end of message"));
-                       goto ENDBODY;
-               }
-               if (!strncasecmp(buf, "Content-type: ", 14)) {
-                       safestrncpy(mime_content_type, &buf[14],
-                               sizeof(mime_content_type));
-                       for (i=0; i<strlen(mime_content_type); ++i) {
-                               if (!strncasecmp(&mime_content_type[i], "charset=", 8)) {
-                                       safestrncpy(mime_charset, &mime_content_type[i+8],
-                                               sizeof mime_charset);
-                               }
-                       }
-                       for (i=0; i<strlen(mime_content_type); ++i) {
-                               if (mime_content_type[i] == ';') {
-                                       mime_content_type[i] = 0;
-                               }
-                       }
-                       for (i=0; i<strlen(mime_charset); ++i) {
-                               if (mime_charset[i] == ';') {
-                                       mime_charset[i] = 0;
-                               }
-                       }
-               }
-       }
-
-       /** Set up a character set conversion if we need to (and if we can) */
-#ifdef HAVE_ICONV
-       if ( (strcasecmp(mime_charset, "us-ascii"))
-          && (strcasecmp(mime_charset, "UTF-8"))
-          && (strcasecmp(mime_charset, ""))
-       ) {
-               ic = ctdl_iconv_open("UTF-8", mime_charset);
-               if (ic == (iconv_t)(-1) ) {
-                       lprintf(5, "%s:%d iconv_open(%s, %s) failed: %s\n",
-                               __FILE__, __LINE__, "UTF-8", mime_charset, strerror(errno));
-               }
-       }
-#endif
-
-       /** Messages in legacy Citadel variformat get handled thusly... */
-       if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
-               pullquote_fmout();
-       }
-
-       /* Boring old 80-column fixed format text gets handled this way... */
-       else if (!strcasecmp(mime_content_type, "text/plain")) {
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
-                       if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
-
-#ifdef HAVE_ICONV
-                       if (ic != (iconv_t)(-1) ) {
-                               ibuf = buf;
-                               ibuflen = strlen(ibuf);
-                               obuflen = SIZ;
-                               obuf = (char *) malloc(obuflen);
-                               osav = obuf;
-                               iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
-                               osav[SIZ-obuflen] = 0;
-                               safestrncpy(buf, osav, sizeof buf);
-                               free(osav);
-                       }
-#endif
-
-                       while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
-                               buf[strlen(buf) - 1] = 0;
-                       if ((bq == 0) &&
-                       ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) {
-                               wprintf("<blockquote>");
-                               bq = 1;
-                       } else if ((bq == 1) &&
-                               (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) {
-                               wprintf("</blockquote>");
-                               bq = 0;
-                       }
-                       wprintf("<tt>");
-                       url(buf);
-                       msgescputs(buf);
-                       wprintf("</tt><br />");
-               }
-               wprintf("</i><br />");
-       }
-
-       /** HTML just gets escaped and stuffed back into the editor */
-       else if (!strcasecmp(mime_content_type, "text/html")) {
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       strcat(buf, "\n");
-                       msgescputs(buf);
-               }
-       }
-
-       /** Unknown weirdness ... don't know how to handle this content type */
-       else {
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { }
-       }
-
-ENDBODY:
-       /** end of body handler */
-
-       /*
-        * If there were attachments, we have to download them and insert them
-        * into the attachment chain for the forwarded message we are composing.
-        */
-       if ( (forward_attachments) && (num_attachments) ) {
-               for (i=0; i<num_attachments; ++i) {
-                       extract_token(buf, attachments, i, '\n', sizeof buf);
-                       extract_token(mime_filename, buf, 1, '|', sizeof mime_filename);
-                       extract_token(mime_partnum, buf, 2, '|', sizeof mime_partnum);
-                       extract_token(mime_disposition, buf, 3, '|', sizeof mime_disposition);
-                       extract_token(mime_content_type, buf, 4, '|', sizeof mime_content_type);
-                       mime_length = extract_int(buf, 5);
-
-                       /*
-                        * tracing  ... uncomment if necessary
-                        *
-                        */
-                       lprintf(9, "fwd filename: %s\n", mime_filename);
-                       lprintf(9, "fwd partnum : %s\n", mime_partnum);
-                       lprintf(9, "fwd conttype: %s\n", mime_content_type);
-                       lprintf(9, "fwd dispose : %s\n", mime_disposition);
-                       lprintf(9, "fwd length  : %d\n", mime_length);
-
-                       if ( (!strcasecmp(mime_disposition, "inline"))
-                          || (!strcasecmp(mime_disposition, "attachment")) ) {
-               
-                               /* Create an attachment struct from this mime part... */
-                               att = malloc(sizeof(struct wc_attachment));
-                               memset(att, 0, sizeof(struct wc_attachment));
-                               att->length = mime_length;
-                               strcpy(att->content_type, mime_content_type);
-                               strcpy(att->filename, mime_filename);
-                               att->next = NULL;
-                               att->data = load_mimepart(msgnum, mime_partnum);
-               
-                               /* And add it to the list. */
-                               if (WC->first_attachment == NULL) {
-                                       WC->first_attachment = att;
-                               }
-                               else {
-                                       aptr = WC->first_attachment;
-                                       while (aptr->next != NULL) aptr = aptr->next;
-                                       aptr->next = att;
-                               }
-                       }
-
-               }
-       }
-
-#ifdef HAVE_ICONV
-       if (ic != (iconv_t)(-1) ) {
-               iconv_close(ic);
-       }
-#endif
-
-       if (attachments != NULL) {
-               free(attachments);
-       }
-}
-
-/**
- * \brief Display one row in the mailbox summary view
- *
- * \param num The row number to be displayed
- */
-void display_summarized(int num) {
-       char datebuf[64];
-
-       wprintf("<tr id=\"m%ld\" style=\"width:100%%;font-weight:%s;background-color:#ffffff\" "
-               "onMouseDown=\"CtdlMoveMsgMouseDown(event,%ld)\">",
-               WC->summ[num].msgnum,
-               (WC->summ[num].is_new ? "bold" : "normal"),
-               WC->summ[num].msgnum
-       );
-
-       wprintf("<td width=%d%%>", SUBJ_COL_WIDTH_PCT);
-       escputs(WC->summ[num].subj);
-       wprintf("</td>");
-
-       wprintf("<td width=%d%%>", SENDER_COL_WIDTH_PCT);
-       escputs(WC->summ[num].from);
-       wprintf("</td>");
-
-       wprintf("<td width=%d%%>", DATE_PLUS_BUTTONS_WIDTH_PCT);
-       fmt_date(datebuf, WC->summ[num].date, 1);       /* brief */
-       escputs(datebuf);
-       wprintf("</td>");
-
-       wprintf("</tr>\n");
-}
-
-
-
-/**
- * \brief display the adressbook overview
- * \param msgnum the citadel message number
- * \param alpha what????
- */
-void display_addressbook(long msgnum, char alpha) {
-       char buf[SIZ];
-       char mime_partnum[SIZ];
-       char mime_filename[SIZ];
-       char mime_content_type[SIZ];
-       char mime_disposition[SIZ];
-       int mime_length;
-       char vcard_partnum[SIZ];
-       char *vcard_source = NULL;
-       struct message_summary summ;
-
-       memset(&summ, 0, sizeof(summ));
-       safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj);
-
-       sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
-       serv_puts(buf);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') return;
-
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               if (!strncasecmp(buf, "part=", 5)) {
-                       extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
-                       extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
-                       extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
-                       extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
-                       mime_length = extract_int(&buf[5], 5);
-
-                       if (!strcasecmp(mime_content_type, "text/x-vcard")) {
-                               strcpy(vcard_partnum, mime_partnum);
-                       }
-
-               }
-       }
-
-       if (strlen(vcard_partnum) > 0) {
-               vcard_source = load_mimepart(msgnum, vcard_partnum);
-               if (vcard_source != NULL) {
-
-                       /** Display the summary line */
-                       display_vcard(vcard_source, alpha, 0, NULL);
-
-                       /** If it's my vCard I can edit it */
-                       if (    (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
-                               || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
-                               || (WC->wc_view == VIEW_ADDRESSBOOK)
-                       ) {
-                               wprintf("<a href=\"edit_vcard?"
-                                       "msgnum=%ld?partnum=%s\">",
-                                       msgnum, vcard_partnum);
-                               wprintf("[%s]</a>", _("edit"));
-                       }
-
-                       free(vcard_source);
-               }
-       }
-
-}
-
-
-
-/**
- * \brief  If it's an old "Firstname Lastname" style record, try to convert it.
- * \param namebuf name to analyze, reverse if nescessary
- */
-void lastfirst_firstlast(char *namebuf) {
-       char firstname[SIZ];
-       char lastname[SIZ];
-       int i;
-
-       if (namebuf == NULL) return;
-       if (strchr(namebuf, ';') != NULL) return;
-
-       i = num_tokens(namebuf, ' ');
-       if (i < 2) return;
-
-       extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
-       remove_token(namebuf, i-1, ' ');
-       strcpy(firstname, namebuf);
-       sprintf(namebuf, "%s; %s", lastname, firstname);
-}
-
-/**
- * \brief fetch what??? name
- * \param msgnum the citadel message number
- * \param namebuf where to put the name in???
- */
-void fetch_ab_name(long msgnum, char *namebuf) {
-       char buf[SIZ];
-       char mime_partnum[SIZ];
-       char mime_filename[SIZ];
-       char mime_content_type[SIZ];
-       char mime_disposition[SIZ];
-       int mime_length;
-       char vcard_partnum[SIZ];
-       char *vcard_source = NULL;
-       int i;
-       struct message_summary summ;
-
-       if (namebuf == NULL) return;
-       strcpy(namebuf, "");
-
-       memset(&summ, 0, sizeof(summ));
-       safestrncpy(summ.subj, "(no subject)", sizeof summ.subj);
-
-       sprintf(buf, "MSG0 %ld|1", msgnum);     /** ask for headers only */
-       serv_puts(buf);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') return;
-
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               if (!strncasecmp(buf, "part=", 5)) {
-                       extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
-                       extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
-                       extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
-                       extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
-                       mime_length = extract_int(&buf[5], 5);
-
-                       if (!strcasecmp(mime_content_type, "text/x-vcard")) {
-                               strcpy(vcard_partnum, mime_partnum);
-                       }
-
-               }
-       }
-
-       if (strlen(vcard_partnum) > 0) {
-               vcard_source = load_mimepart(msgnum, vcard_partnum);
-               if (vcard_source != NULL) {
-
-                       /* Grab the name off the card */
-                       display_vcard(vcard_source, 0, 0, namebuf);
-
-                       free(vcard_source);
-               }
-       }
-
-       lastfirst_firstlast(namebuf);
-       striplt(namebuf);
-       for (i=0; i<strlen(namebuf); ++i) {
-               if (namebuf[i] != ';') return;
-       }
-       strcpy(namebuf, _("(no name)"));
-}
-
-
-
-/**
- * \brief Record compare function for sorting address book indices
- * \param ab1 adressbook one
- * \param ab2 adressbook two
- */
-int abcmp(const void *ab1, const void *ab2) {
-       return(strcasecmp(
-               (((const struct addrbookent *)ab1)->ab_name),
-               (((const struct addrbookent *)ab2)->ab_name)
-       ));
-}
-
-
-/**
- * \brief Helper function for do_addrbook_view()
- * Converts a name into a three-letter tab label
- * \param tabbuf the tabbuffer to add name to
- * \param name the name to add to the tabbuffer
- */
-void nametab(char *tabbuf, char *name) {
-       stresc(tabbuf, name, 0, 0);
-       tabbuf[0] = toupper(tabbuf[0]);
-       tabbuf[1] = tolower(tabbuf[1]);
-       tabbuf[2] = tolower(tabbuf[2]);
-       tabbuf[3] = 0;
-}
-
-
-/**
- * \brief Render the address book using info we gathered during the scan
- * \param addrbook the addressbook to render
- * \param num_ab the number of the addressbook
- */
-void do_addrbook_view(struct addrbookent *addrbook, int num_ab) {
-       int i = 0;
-       int displayed = 0;
-       int bg = 0;
-       static int NAMESPERPAGE = 60;
-       int num_pages = 0;
-       int page = 0;
-       int tabfirst = 0;
-       char tabfirst_label[SIZ];
-       int tablast = 0;
-       char tablast_label[SIZ];
-
-       if (num_ab == 0) {
-               wprintf("<br /><br /><br /><div align=\"center\"><i>");
-               wprintf(_("This address book is empty."));
-               wprintf("</i></div>\n");
-               return;
-       }
-
-       if (num_ab > 1) {
-               qsort(addrbook, num_ab, sizeof(struct addrbookent), abcmp);
-       }
-
-       num_pages = num_ab / NAMESPERPAGE;
-
-       page = atoi(bstr("page"));
-
-       wprintf("Page: ");
-       for (i=0; i<=num_pages; ++i) {
-               if (i != page) {
-                       wprintf("<a href=\"readfwd?page=%d\">", i);
-               }
-               else {
-                       wprintf("<B>");
-               }
-               tabfirst = i * NAMESPERPAGE;
-               tablast = tabfirst + NAMESPERPAGE - 1;
-               if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
-               nametab(tabfirst_label, addrbook[tabfirst].ab_name);
-               nametab(tablast_label, addrbook[tablast].ab_name);
-               wprintf("[%s&nbsp;-&nbsp;%s]",
-                       tabfirst_label, tablast_label
-               );
-               if (i != page) {
-                       wprintf("</A>\n");
-               }
-               else {
-                       wprintf("</B>\n");
-               }
-       }
-       wprintf("<br />\n");
-
-       wprintf("<table border=0 cellspacing=0 "
-               "cellpadding=3 width=100%%>\n"
-       );
-
-       for (i=0; i<num_ab; ++i) {
-
-               if ((i / NAMESPERPAGE) == page) {
-
-                       if ((displayed % 4) == 0) {
-                               if (displayed > 0) {
-                                       wprintf("</tr>\n");
-                               }
-                               bg = 1 - bg;
-                               wprintf("<tr bgcolor=\"#%s\">",
-                                       (bg ? "DDDDDD" : "FFFFFF")
-                               );
-                       }
-       
-                       wprintf("<td>");
-       
-                       wprintf("<a href=\"readfwd?startmsg=%ld&is_singlecard=1",
-                               addrbook[i].ab_msgnum);
-                       wprintf("?maxmsgs=1?summary=0?alpha=%s\">", bstr("alpha"));
-                       vcard_n_prettyize(addrbook[i].ab_name);
-                       escputs(addrbook[i].ab_name);
-                       wprintf("</a></td>\n");
-                       ++displayed;
-               }
-       }
-
-       wprintf("</tr></table>\n");
-}
-
-
-
-/**
- * \brief load message pointers from the server
- * \param servcmd the citadel command to send to the citserver
- * \param with_headers what headers???
- */
-int load_msg_ptrs(char *servcmd, int with_headers)
-{
-       char buf[1024];
-       time_t datestamp;
-       char fullname[128];
-       char nodename[128];
-       char inetaddr[128];
-       char subject[256];
-       int nummsgs;
-       int maxload = 0;
-
-       int num_summ_alloc = 0;
-
-       if (WC->summ != NULL) {
-               free(WC->summ);
-               WC->num_summ = 0;
-               WC->summ = NULL;
-       }
-       num_summ_alloc = 100;
-       WC->num_summ = 0;
-       WC->summ = malloc(num_summ_alloc * sizeof(struct message_summary));
-
-       nummsgs = 0;
-       maxload = sizeof(WC->msgarr) / sizeof(long) ;
-       serv_puts(servcmd);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               return (nummsgs);
-       }
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               if (nummsgs < maxload) {
-                       WC->msgarr[nummsgs] = extract_long(buf, 0);
-                       datestamp = extract_long(buf, 1);
-                       extract_token(fullname, buf, 2, '|', sizeof fullname);
-                       extract_token(nodename, buf, 3, '|', sizeof nodename);
-                       extract_token(inetaddr, buf, 4, '|', sizeof inetaddr);
-                       extract_token(subject, buf, 5, '|', sizeof subject);
-                       ++nummsgs;
-
-                       if (with_headers) {
-                               if (nummsgs > num_summ_alloc) {
-                                       num_summ_alloc *= 2;
-                                       WC->summ = realloc(WC->summ,
-                                               num_summ_alloc * sizeof(struct message_summary));
-                               }
-                               ++WC->num_summ;
-
-                               memset(&WC->summ[nummsgs-1], 0, sizeof(struct message_summary));
-                               WC->summ[nummsgs-1].msgnum = WC->msgarr[nummsgs-1];
-                               safestrncpy(WC->summ[nummsgs-1].subj,
-                                       _("(no subject)"), sizeof WC->summ[nummsgs-1].subj);
-                               if (strlen(fullname) > 0) {
-                                       safestrncpy(WC->summ[nummsgs-1].from,
-                                               fullname, sizeof WC->summ[nummsgs-1].from);
-                               }
-                               if (strlen(subject) > 0) {
-                               safestrncpy(WC->summ[nummsgs-1].subj, subject,
-                                       sizeof WC->summ[nummsgs-1].subj);
-                               }
-#ifdef HAVE_ICONV
-                               /** Handle subjects with RFC2047 encoding */
-                               utf8ify_rfc822_string(WC->summ[nummsgs-1].subj);
-#endif
-                               if (strlen(WC->summ[nummsgs-1].subj) > 75) {
-                                       strcpy(&WC->summ[nummsgs-1].subj[72], "...");
-                               }
-
-                               if (strlen(nodename) > 0) {
-                                       if ( ((WC->room_flags & QR_NETWORK)
-                                          || ((strcasecmp(nodename, serv_info.serv_nodename)
-                                          && (strcasecmp(nodename, serv_info.serv_fqdn)))))
-                                       ) {
-                                               strcat(WC->summ[nummsgs-1].from, " @ ");
-                                               strcat(WC->summ[nummsgs-1].from, nodename);
-                                       }
-                               }
-
-                               WC->summ[nummsgs-1].date = datestamp;
-       
-#ifdef HAVE_ICONV
-                               /** Handle senders with RFC2047 encoding */
-                               utf8ify_rfc822_string(WC->summ[nummsgs-1].from);
-#endif
-                               if (strlen(WC->summ[nummsgs-1].from) > 25) {
-                                       strcpy(&WC->summ[nummsgs-1].from[22], "...");
-                               }
-                       }
-               }
-       }
-       return (nummsgs);
-}
-
-/**
- * \brief qsort() compatible function to compare two longs in descending order.
- *
- * \param s1 first number to compare 
- * \param s2 second number to compare
- */
-int longcmp_r(const void *s1, const void *s2) {
-       long l1;
-       long l2;
-
-       l1 = *(long *)s1;
-       l2 = *(long *)s2;
-
-       if (l1 > l2) return(-1);
-       if (l1 < l2) return(+1);
-       return(0);
-}
-
-/**
- * \brief qsort() compatible function to compare two message summary structs by ascending subject.
- *
- * \param s1 first item to compare 
- * \param s2 second item to compare
- */
-int summcmp_subj(const void *s1, const void *s2) {
-       struct message_summary *summ1;
-       struct message_summary *summ2;
-       
-       summ1 = (struct message_summary *)s1;
-       summ2 = (struct message_summary *)s2;
-       return strcasecmp(summ1->subj, summ2->subj);
-}
-
-/**
- * \brief qsort() compatible function to compare two message summary structs by descending subject.
- *
- * \param s1 first item to compare 
- * \param s2 second item to compare
- */
-int summcmp_rsubj(const void *s1, const void *s2) {
-       struct message_summary *summ1;
-       struct message_summary *summ2;
-       
-       summ1 = (struct message_summary *)s1;
-       summ2 = (struct message_summary *)s2;
-       return strcasecmp(summ2->subj, summ1->subj);
-}
-
-/**
- * \brief qsort() compatible function to compare two message summary structs by ascending sender.
- *
- * \param s1 first item to compare 
- * \param s2 second item to compare
- */
-int summcmp_sender(const void *s1, const void *s2) {
-       struct message_summary *summ1;
-       struct message_summary *summ2;
-       
-       summ1 = (struct message_summary *)s1;
-       summ2 = (struct message_summary *)s2;
-       return strcasecmp(summ1->from, summ2->from);
-}
-
-/**
- * \brief qsort() compatible function to compare two message summary structs by descending sender.
- *
- * \param s1 first item to compare 
- * \param s2 second item to compare
- */
-int summcmp_rsender(const void *s1, const void *s2) {
-       struct message_summary *summ1;
-       struct message_summary *summ2;
-       
-       summ1 = (struct message_summary *)s1;
-       summ2 = (struct message_summary *)s2;
-       return strcasecmp(summ2->from, summ1->from);
-}
-
-/**
- * \brief qsort() compatible function to compare two message summary structs by ascending date.
- *
- * \param s1 first item to compare 
- * \param s2 second item to compare
- */
-int summcmp_date(const void *s1, const void *s2) {
-       struct message_summary *summ1;
-       struct message_summary *summ2;
-       
-       summ1 = (struct message_summary *)s1;
-       summ2 = (struct message_summary *)s2;
-
-       if (summ1->date < summ2->date) return -1;
-       else if (summ1->date > summ2->date) return +1;
-       else return 0;
-}
-
-/**
- * \brief qsort() compatible function to compare two message summary structs by descending date.
- *
- * \param s1 first item to compare 
- * \param s2 second item to compare
- */
-int summcmp_rdate(const void *s1, const void *s2) {
-       struct message_summary *summ1;
-       struct message_summary *summ2;
-       
-       summ1 = (struct message_summary *)s1;
-       summ2 = (struct message_summary *)s2;
-
-       if (summ1->date < summ2->date) return +1;
-       else if (summ1->date > summ2->date) return -1;
-       else return 0;
-}
-
-
-
-/**
- * \brief command loop for reading messages
- *
- * \param oper Set to "readnew" or "readold" or "readfwd" or "headers"
- */
-void readloop(char *oper)
-{
-       char cmd[SIZ];
-       char buf[SIZ];
-       char old_msgs[SIZ];
-       int a, b;
-       int nummsgs;
-       long startmsg;
-       int maxmsgs;
-       long *displayed_msgs = NULL;
-       int num_displayed = 0;
-       int is_summary = 0;
-       int is_addressbook = 0;
-       int is_singlecard = 0;
-       int is_calendar = 0;
-       int is_tasks = 0;
-       int is_notes = 0;
-       int is_bbview = 0;
-       int lo, hi;
-       int lowest_displayed = (-1);
-       int highest_displayed = 0;
-       struct addrbookent *addrbook = NULL;
-       int num_ab = 0;
-       char *sortby = NULL;
-       char sortpref_name[128];
-       char sortpref_value[128];
-       char *subjsort_button;
-       char *sendsort_button;
-       char *datesort_button;
-       int bbs_reverse = 0;
-
-       if (WC->wc_view == VIEW_WIKI) {
-               sprintf(buf, "wiki?room=%s?page=home", WC->wc_roomname);
-               http_redirect(buf);
-               return;
-       }
-
-       startmsg = atol(bstr("startmsg"));
-       maxmsgs = atoi(bstr("maxmsgs"));
-       is_summary = atoi(bstr("summary"));
-       if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS;
-
-       snprintf(sortpref_name, sizeof sortpref_name, "sort %s", WC->wc_roomname);
-       get_preference(sortpref_name, sortpref_value, sizeof sortpref_value);
-
-       sortby = bstr("sortby");
-       if ( (strlen(sortby) > 0) && (strcasecmp(sortby, sortpref_value)) ) {
-               set_preference(sortpref_name, sortby, 1);
-       }
-       if (strlen(sortby) == 0) sortby = sortpref_value;
-
-       /** mailbox sort */
-       if (strlen(sortby) == 0) sortby = "rdate";
-
-       /** message board sort */
-       if (!strcasecmp(sortby, "reverse")) {
-               bbs_reverse = 1;
-       }
-       else {
-               bbs_reverse = 0;
-       }
-
-       output_headers(1, 1, 1, 0, 0, 0);
-
-       /**
-        * When in summary mode, always show ALL messages instead of just
-        * new or old.  Otherwise, show what the user asked for.
-        */
-       if (!strcmp(oper, "readnew")) {
-               strcpy(cmd, "MSGS NEW");
-       }
-       else if (!strcmp(oper, "readold")) {
-               strcpy(cmd, "MSGS OLD");
-       }
-       else {
-               strcpy(cmd, "MSGS ALL");
-       }
-
-       if ((WC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1)) {
-               is_summary = 1;
-               strcpy(cmd, "MSGS ALL");
-       }
-
-       if ((WC->wc_view == VIEW_ADDRESSBOOK) && (maxmsgs > 1)) {
-               is_addressbook = 1;
-               strcpy(cmd, "MSGS ALL");
-               maxmsgs = 9999999;
-       }
-
-       if (is_summary) {
-               strcpy(cmd, "MSGS ALL|||1");    /**< fetch header summary */
-               startmsg = 1;
-               maxmsgs = 9999999;
-       }
-
-       /**
-        * Are we doing a summary view?  If so, we need to know old messages
-        * and new messages, so we can do that pretty boldface thing for the
-        * new messages.
-        */
-       strcpy(old_msgs, "");
-       if (is_summary) {
-               serv_puts("GTSN");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       strcpy(old_msgs, &buf[4]);
-               }
-       }
-
-       is_singlecard = atoi(bstr("is_singlecard"));
-
-       if (WC->wc_default_view == VIEW_CALENDAR) {             /**< calendar */
-               is_calendar = 1;
-               strcpy(cmd, "MSGS ALL");
-               maxmsgs = 32767;
-       }
-       if (WC->wc_default_view == VIEW_TASKS) {                /**< tasks */
-               is_tasks = 1;
-               strcpy(cmd, "MSGS ALL");
-               maxmsgs = 32767;
-       }
-       if (WC->wc_default_view == VIEW_NOTES) {                /**< notes */
-               is_notes = 1;
-               strcpy(cmd, "MSGS ALL");
-               maxmsgs = 32767;
-       }
-
-       nummsgs = load_msg_ptrs(cmd, is_summary);
-       if (nummsgs == 0) {
-
-               if ((!is_tasks) && (!is_calendar) && (!is_notes) && (!is_addressbook)) {
-                       wprintf("<em>");
-                       if (!strcmp(oper, "readnew")) {
-                               wprintf(_("No new messages."));
-                       } else if (!strcmp(oper, "readold")) {
-                               wprintf(_("No old messages."));
-                       } else {
-                               wprintf(_("No messages here."));
-                       }
-                       wprintf("</em>\n");
-               }
-
-               goto DONE;
-       }
-
-       if (is_summary) {
-               for (a = 0; a < nummsgs; ++a) {
-                       /** Are you a new message, or an old message? */
-                       if (is_summary) {
-                               if (is_msg_in_mset(old_msgs, WC->msgarr[a])) {
-                                       WC->summ[a].is_new = 0;
-                               }
-                               else {
-                                       WC->summ[a].is_new = 1;
-                               }
-                       }
-               }
-       }
-
-       if (startmsg == 0L) {
-               if (bbs_reverse) {
-                       startmsg = WC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0];
-               }
-               else {
-                       startmsg = WC->msgarr[0];
-               }
-       }
-
-       if (is_summary) {
-               if (!strcasecmp(sortby, "subject")) {
-                       qsort(WC->summ, WC->num_summ,
-                               sizeof(struct message_summary), summcmp_subj);
-               }
-               else if (!strcasecmp(sortby, "rsubject")) {
-                       qsort(WC->summ, WC->num_summ,
-                               sizeof(struct message_summary), summcmp_rsubj);
-               }
-               else if (!strcasecmp(sortby, "sender")) {
-                       qsort(WC->summ, WC->num_summ,
-                               sizeof(struct message_summary), summcmp_sender);
-               }
-               else if (!strcasecmp(sortby, "rsender")) {
-                       qsort(WC->summ, WC->num_summ,
-                               sizeof(struct message_summary), summcmp_rsender);
-               }
-               else if (!strcasecmp(sortby, "date")) {
-                       qsort(WC->summ, WC->num_summ,
-                               sizeof(struct message_summary), summcmp_date);
-               }
-               else if (!strcasecmp(sortby, "rdate")) {
-                       qsort(WC->summ, WC->num_summ,
-                               sizeof(struct message_summary), summcmp_rdate);
-               }
-       }
-
-       if (!strcasecmp(sortby, "subject")) {
-               subjsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rsubject\"><img border=\"0\" src=\"static/down_pointer.gif\" /></a>" ;
-       }
-       else if (!strcasecmp(sortby, "rsubject")) {
-               subjsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=subject\"><img border=\"0\" src=\"static/up_pointer.gif\" /></a>" ;
-       }
-       else {
-               subjsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=subject\"><img border=\"0\" src=\"static/sort_none.gif\" /></a>" ;
-       }
-
-       if (!strcasecmp(sortby, "sender")) {
-               sendsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rsender\"><img border=\"0\" src=\"static/down_pointer.gif\" /></a>" ;
-       }
-       else if (!strcasecmp(sortby, "rsender")) {
-               sendsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=sender\"><img border=\"0\" src=\"static/up_pointer.gif\" /></a>" ;
-       }
-       else {
-               sendsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=sender\"><img border=\"0\" src=\"static/sort_none.gif\" /></a>" ;
-       }
-
-       if (!strcasecmp(sortby, "date")) {
-               datesort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rdate\"><img border=\"0\" src=\"static/down_pointer.gif\" /></a>" ;
-       }
-       else if (!strcasecmp(sortby, "rdate")) {
-               datesort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=date\"><img border=\"0\" src=\"static/up_pointer.gif\" /></a>" ;
-       }
-       else {
-               datesort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rdate\"><img border=\"0\" src=\"static/sort_none.gif\" /></a>" ;
-       }
-
-       if (is_summary) {
-               wprintf("</div>\n");            /** end of 'content' div */
-
-               wprintf("<script language=\"javascript\" type=\"text/javascript\">"
-                       " document.onkeydown = CtdlMsgListKeyPress;     "
-                       " if (document.layers) {                        "
-                       "       document.captureEvents(Event.KEYPRESS); "
-                       " }                                             "
-                       "</script>\n"
-               );
-
-               /** note that Date and Delete are now in the same column */
-               wprintf("<div id=\"message_list_hdr\">"
-                       "<div class=\"fix_scrollbar_bug\">"
-                       "<table cellspacing=0 style=\"width:100%%\">"
-                       "<tr>"
-               );
-               wprintf("<td width=%d%%><b><i>%s</i></b> %s</td>"
-                       "<td width=%d%%><b><i>%s</i></b> %s</td>"
-                       "<td width=%d%%><b><i>%s</i></b> %s"
-                       "&nbsp;"
-                       "<input type=\"submit\" name=\"delete_button\" style=\"font-size:6pt\" "
-                       " onClick=\"CtdlDeleteSelectedMessages(event)\" "
-                       " value=\"%s\">"
-                       "</td>"
-                       "</tr>\n"
-                       ,
-                       SUBJ_COL_WIDTH_PCT,
-                       _("Subject"),   subjsort_button,
-                       SENDER_COL_WIDTH_PCT,
-                       _("Sender"),    sendsort_button,
-                       DATE_PLUS_BUTTONS_WIDTH_PCT,
-                       _("Date"),      datesort_button,
-                       _("Delete")
-               );
-               wprintf("</table></div></div>\n");
-
-               wprintf("<div id=\"message_list\">"
-
-                       "<div class=\"fix_scrollbar_bug\">\n"
-
-                       "<table class=\"mailbox_summary\" id=\"summary_headers\" rules=rows "
-                       "cellspacing=0 style=\"width:100%%;-moz-user-select:none;\">"
-               );
-       }
-
-       if (is_notes) {
-               wprintf("<div align=center>%s</div>\n", _("Click on any note to edit it."));
-               wprintf("<div id=\"new_notes_here\"></div>\n");
-       }
-
-       for (a = 0; a < nummsgs; ++a) {
-               if ((WC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) {
-
-                       /** Display the message */
-                       if (is_summary) {
-                               display_summarized(a);
-                       }
-                       else if (is_addressbook) {
-                               fetch_ab_name(WC->msgarr[a], buf);
-                               ++num_ab;
-                               addrbook = realloc(addrbook,
-                                       (sizeof(struct addrbookent) * num_ab) );
-                               safestrncpy(addrbook[num_ab-1].ab_name, buf,
-                                       sizeof(addrbook[num_ab-1].ab_name));
-                               addrbook[num_ab-1].ab_msgnum = WC->msgarr[a];
-                       }
-                       else if (is_calendar) {
-                               display_calendar(WC->msgarr[a]);
-                       }
-                       else if (is_tasks) {
-                               display_task(WC->msgarr[a]);
-                       }
-                       else if (is_notes) {
-                               display_note(WC->msgarr[a]);
-                       }
-                       else {
-                               if (displayed_msgs == NULL) {
-                                       displayed_msgs = malloc(sizeof(long) *
-                                                               (maxmsgs<nummsgs ? maxmsgs : nummsgs));
-                               }
-                               displayed_msgs[num_displayed] = WC->msgarr[a];
-                       }
-
-                       if (lowest_displayed < 0) lowest_displayed = a;
-                       highest_displayed = a;
-
-                       ++num_displayed;
-               }
-       }
-
-       /**
-        * Set the "is_bbview" variable if it appears that we are looking at
-        * a classic bulletin board view.
-        */
-       if ((!is_tasks) && (!is_calendar) && (!is_addressbook)
-             && (!is_notes) && (!is_singlecard) && (!is_summary)) {
-               is_bbview = 1;
-       }
-
-       /** Output loop */
-       if (displayed_msgs != NULL) {
-               if (bbs_reverse) {
-                       qsort(displayed_msgs, num_displayed, sizeof(long), longcmp_r);
-               }
-
-               /** if we do a split bbview in the future, begin messages div here */
-
-               for (a=0; a<num_displayed; ++a) {
-                       read_message(displayed_msgs[a], 0, "");
-               }
-
-               /** if we do a split bbview in the future, end messages div here */
-
-               free(displayed_msgs);
-               displayed_msgs = NULL;
-       }
-
-       if (is_summary) {
-               wprintf("</table>"
-                       "</div>\n");                    /**< end of 'fix_scrollbar_bug' div */
-               wprintf("</div>");                      /**< end of 'message_list' div */
-
-               /** Here's the grab-it-to-resize-the-message-list widget */
-               wprintf("<div id=\"resize_msglist\" "
-                       "onMouseDown=\"CtdlResizeMsgListMouseDown(event)\">"
-                       "<div class=\"fix_scrollbar_bug\">"
-                       "<table width=100%% border=3 cellspacing=0 "
-                       "bgcolor=\"#cccccc\" "
-                       "cellpadding=0><TR><TD> </td></tr></table>"
-                       "</div></div>\n"
-               );
-
-               wprintf("<div id=\"preview_pane\">");   /**< The preview pane will initially be empty */
-       }
-
-       /**
-        * Bump these because although we're thinking in zero base, the user
-        * is a drooling idiot and is thinking in one base.
-        */
-       ++lowest_displayed;
-       ++highest_displayed;
-
-       /**
-        * If we're not currently looking at ALL requested
-        * messages, then display the selector bar
-        */
-       if (is_bbview) {
-               /** begin bbview scroller */
-               wprintf("<form name=\"msgomatic\">");
-               wprintf(_("Reading #"), lowest_displayed, highest_displayed);
-
-               wprintf("<select name=\"whichones\" size=\"1\" "
-                       "OnChange=\"location.href=msgomatic.whichones.options"
-                       "[selectedIndex].value\">\n");
-
-               if (bbs_reverse) {
-                       for (b=nummsgs-1; b>=0; b = b - maxmsgs) {
-                               hi = b + 1;
-                               lo = b - maxmsgs + 2;
-                               if (lo < 1) lo = 1;
-                               wprintf("<option %s value="
-                                       "\"%s"
-                                       "?startmsg=%ld"
-                                       "?maxmsgs=%d"
-                                       "?summary=%d\">"
-                                       "%d-%d</option> \n",
-                                       ((WC->msgarr[lo-1] == startmsg) ? "selected" : ""),
-                                       oper,
-                                       WC->msgarr[lo-1],
-                                       maxmsgs,
-                                       is_summary,
-                                       hi, lo);
-                       }
-               }
-               else {
-                       for (b=0; b<nummsgs; b = b + maxmsgs) {
-                               lo = b + 1;
-                               hi = b + maxmsgs + 1;
-                               if (hi > nummsgs) hi = nummsgs;
-                               wprintf("<option %s value="
-                                       "\"%s"
-                                       "?startmsg=%ld"
-                                       "?maxmsgs=%d"
-                                       "?summary=%d\">"
-                                       "%d-%d</option> \n",
-                                       ((WC->msgarr[b] == startmsg) ? "selected" : ""),
-                                       oper,
-                                       WC->msgarr[lo-1],
-                                       maxmsgs,
-                                       is_summary,
-                                       lo, hi);
-                       }
-               }
-
-               wprintf("<option value=\"%s?startmsg=%ld"
-                       "?maxmsgs=9999999?summary=%d\">"
-                       "ALL"
-                       "</option> ",
-                       oper,
-                       WC->msgarr[0], is_summary);
-
-               wprintf("</select> ");
-               wprintf(_("of %d messages."), nummsgs);
-
-               /** forward/reverse */
-               wprintf("&nbsp;<select name=\"direction\" size=\"1\" "
-                       "OnChange=\"location.href=msgomatic.direction.options"
-                       "[selectedIndex].value\">\n"
-               );
-
-               wprintf("<option %s value=\"%s?sortby=forward\">oldest to newest</option>\n",
-                       (bbs_reverse ? "" : "selected"),
-                       oper
-               );
-       
-               wprintf("<option %s value=\"%s?sortby=reverse\">newest to oldest</option>\n",
-                       (bbs_reverse ? "selected" : ""),
-                       oper
-               );
-       
-               wprintf("</select></form>\n");
-               /** end bbview scroller */
-       }
-
-DONE:
-       if (is_tasks) {
-               do_tasks_view();        /** Render the task list */
-       }
-
-       if (is_calendar) {
-               do_calendar_view();     /** Render the calendar */
-       }
-
-       if (is_addressbook) {
-               do_addrbook_view(addrbook, num_ab);     /** Render the address book */
-       }
-
-       /** Note: wDumpContent() will output one additional </div> tag. */
-       wDumpContent(1);
-       if (addrbook != NULL) free(addrbook);
-
-       /** free the summary */
-       if (WC->summ != NULL) {
-               free(WC->summ);
-               WC->num_summ = 0;
-               WC->summ = NULL;
-       }
-}
-
-
-/**
- * \brief Back end for post_message()
- * ... this is where the actual message gets transmitted to the server.
- */
-void post_mime_to_server(void) {
-       char boundary[SIZ];
-       int is_multipart = 0;
-       static int seq = 0;
-       struct wc_attachment *att;
-       char *encoded;
-       size_t encoded_length;
-
-       /** RFC2045 requires this, and some clients look for it... */
-       serv_puts("MIME-Version: 1.0");
-
-       /** If there are attachments, we have to do multipart/mixed */
-       if (WC->first_attachment != NULL) {
-               is_multipart = 1;
-       }
-
-       if (is_multipart) {
-               sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x",
-                       serv_info.serv_fqdn,
-                       getpid(),
-                       ++seq
-               );
-
-               /** Remember, serv_printf() appends an extra newline */
-               serv_printf("Content-type: multipart/mixed; "
-                       "boundary=\"%s\"\n", boundary);
-               serv_printf("This is a multipart message in MIME format.\n");
-               serv_printf("--%s", boundary);
-       }
-
-       serv_puts("Content-type: text/html; charset=utf-8");
-       serv_puts("Content-Transfer-Encoding: quoted-printable");
-       serv_puts("");
-       serv_puts("<html><body>\r\n");
-       text_to_server_qp(bstr("msgtext"));     /** Transmit message in quoted-printable encoding */
-       serv_puts("</body></html>\r\n");
-       
-       if (is_multipart) {
-
-               /** Add in the attachments */
-               for (att = WC->first_attachment; att!=NULL; att=att->next) {
-
-                       encoded_length = ((att->length * 150) / 100);
-                       encoded = malloc(encoded_length);
-                       if (encoded == NULL) break;
-                       CtdlEncodeBase64(encoded, att->data, att->length);
-
-                       serv_printf("--%s", boundary);
-                       serv_printf("Content-type: %s", att->content_type);
-                       serv_printf("Content-disposition: attachment; "
-                               "filename=\"%s\"", att->filename);
-                       serv_puts("Content-transfer-encoding: base64");
-                       serv_puts("");
-                       serv_write(encoded, strlen(encoded));
-                       serv_puts("");
-                       serv_puts("");
-                       free(encoded);
-               }
-               serv_printf("--%s--", boundary);
-       }
-
-       serv_puts("000");
-}
-
-
-/**
- * \brief Post message (or don't post message)
- *
- * Note regarding the "dont_post" variable:
- * A random value (actually, it's just a timestamp) is inserted as a hidden
- * field called "postseq" when the display_enter page is generated.  This
- * value is checked when posting, using the static variable dont_post.  If a
- * user attempts to post twice using the same dont_post value, the message is
- * discarded.  This prevents the accidental double-saving of the same message
- * if the user happens to click the browser "back" button.
- */
-void post_message(void)
-{
-       char buf[1024];
-       char encoded_subject[1024];
-       static long dont_post = (-1L);
-       struct wc_attachment *att, *aptr;
-       int is_anonymous = 0;
-
-       if (!strcasecmp(bstr("is_anonymous"), "yes")) {
-               is_anonymous = 1;
-       }
-
-       if (WC->upload_length > 0) {
-
-               /** There's an attachment.  Save it to this struct... */
-               att = malloc(sizeof(struct wc_attachment));
-               memset(att, 0, sizeof(struct wc_attachment));
-               att->length = WC->upload_length;
-               strcpy(att->content_type, WC->upload_content_type);
-               strcpy(att->filename, WC->upload_filename);
-               att->next = NULL;
-
-               /** And add it to the list. */
-               if (WC->first_attachment == NULL) {
-                       WC->first_attachment = att;
-               }
-               else {
-                       aptr = WC->first_attachment;
-                       while (aptr->next != NULL) aptr = aptr->next;
-                       aptr->next = att;
-               }
-
-               /**
-                * Mozilla sends a simple filename, which is what we want,
-                * but Satan's Browser sends an entire pathname.  Reduce
-                * the path to just a filename if we need to.
-                */
-               while (num_tokens(att->filename, '/') > 1) {
-                       remove_token(att->filename, 0, '/');
-               }
-               while (num_tokens(att->filename, '\\') > 1) {
-                       remove_token(att->filename, 0, '\\');
-               }
-
-               /**
-                * Transfer control of this memory from the upload struct
-                * to the attachment struct.
-                */
-               att->data = WC->upload;
-               WC->upload_length = 0;
-               WC->upload = NULL;
-               display_enter();
-               return;
-       }
-
-       if (strlen(bstr("cancel_button")) > 0) {
-               sprintf(WC->ImportantMessage, 
-                       _("Cancelled.  Message was not posted."));
-       } else if (strlen(bstr("attach_button")) > 0) {
-               display_enter();
-               return;
-       } else if (atol(bstr("postseq")) == dont_post) {
-               sprintf(WC->ImportantMessage, 
-                       _("Automatically cancelled because you have already "
-                       "saved this message."));
-       } else {
-               rfc2047encode(encoded_subject, sizeof encoded_subject, bstr("subject"));
-               sprintf(buf, "ENT0 1|%s|%d|4|%s|||%s|%s|%s",
-                       bstr("recp"),
-                       is_anonymous,
-                       encoded_subject,
-                       bstr("cc"),
-                       bstr("bcc"),
-                       bstr("wikipage")
-               );
-               serv_puts(buf);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '4') {
-                       post_mime_to_server();
-                       if ( (strlen(bstr("recp")) > 0)
-                          || (strlen(bstr("cc")) > 0)
-                          || (strlen(bstr("bcc")) > 0)
-                       ) {
-                               sprintf(WC->ImportantMessage, _("Message has been sent.\n"));
-                       }
-                       else {
-                               sprintf(WC->ImportantMessage, _("Message has been posted.\n"));
-                       }
-                       dont_post = atol(bstr("postseq"));
-               } else {
-                       sprintf(WC->ImportantMessage, "%s", &buf[4]);
-                       display_enter();
-                       return;
-               }
-       }
-
-       free_attachments(WC);
-
-       /**
-        *  We may have been supplied with instructions regarding the location
-        *  to which we must return after posting.  If found, go there.
-        */
-       if (strlen(bstr("return_to")) > 0) {
-               http_redirect(bstr("return_to"));
-       }
-       /**
-        *  If we were editing a page in a wiki room, go to that page now.
-        */
-       else if (strlen(bstr("wikipage")) > 0) {
-               snprintf(buf, sizeof buf, "wiki?page=%s", bstr("wikipage"));
-               http_redirect(buf);
-       }
-       /**
-        *  Otherwise, just go to the "read messages" loop.
-        */
-       else {
-               readloop("readnew");
-       }
-}
-
-
-
-
-/**
- * \brief display the message entry screen
- */
-void display_enter(void)
-{
-       char buf[SIZ];
-       char ebuf[SIZ];
-       long now;
-       struct wc_attachment *att;
-       int recipient_required = 0;
-       int recipient_bad = 0;
-       int i;
-       int is_anonymous = 0;
-       long existing_page = (-1L);
-
-       if (strlen(bstr("force_room")) > 0) {
-               gotoroom(bstr("force_room"));
-       }
-
-       if (!strcasecmp(bstr("is_anonymous"), "yes")) {
-               is_anonymous = 1;
-       }
-
-       /**
-        * Are we perhaps in an address book view?  If so, then an "enter
-        * message" command really means "add new entry."
-        */
-       if (WC->wc_default_view == VIEW_ADDRESSBOOK) {
-               do_edit_vcard(-1, "", "");
-               return;
-       }
-
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-       /**
-        * Are we perhaps in a calendar room?  If so, then an "enter
-        * message" command really means "add new calendar item."
-        */
-       if (WC->wc_default_view == VIEW_CALENDAR) {
-               display_edit_event();
-               return;
-       }
-
-       /**
-        * Are we perhaps in a tasks view?  If so, then an "enter
-        * message" command really means "add new task."
-        */
-       if (WC->wc_default_view == VIEW_TASKS) {
-               display_edit_task();
-               return;
-       }
-#endif
-
-       /**
-        * Otherwise proceed normally.
-        * Do a custom room banner with no navbar...
-        */
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       embed_room_banner(NULL, navbar_none);
-       wprintf("</div>\n");
-       wprintf("<div id=\"content\">\n"
-               "<div class=\"fix_scrollbar_bug\">"
-               "<table width=100%% border=0 bgcolor=\"#ffffff\"><tr><td>");
-
-       /** First test to see whether this is a room that requires recipients to be entered */
-       serv_puts("ENT0 0");
-       serv_getln(buf, sizeof buf);
-       if (!strncmp(buf, "570", 3)) {          /** 570 means that we need a recipient here */
-               recipient_required = 1;
-       }
-       else if (buf[0] != '2') {               /** Any other error means that we cannot continue */
-               wprintf("<em>%s</em><br />\n", &buf[4]);
-               goto DONE;
-       }
-
-       /** Now check our actual recipients if there are any */
-       if (recipient_required) {
-               sprintf(buf, "ENT0 0|%s|%d|0||||%s|%s|%s", bstr("recp"), is_anonymous,
-                       bstr("cc"), bstr("bcc"), bstr("wikipage"));
-               serv_puts(buf);
-               serv_getln(buf, sizeof buf);
-
-               if (!strncmp(buf, "570", 3)) {  /** 570 means we have an invalid recipient listed */
-                       if (strlen(bstr("recp")) + strlen(bstr("cc")) + strlen(bstr("bcc")) > 0) {
-                               recipient_bad = 1;
-                       }
-               }
-               else if (buf[0] != '2') {       /** Any other error means that we cannot continue */
-                       wprintf("<em>%s</em><br />\n", &buf[4]);
-                       goto DONE;
-               }
-       }
-
-       /** If we got this far, we can display the message entry screen. */
-
-       now = time(NULL);
-       fmt_date(buf, now, 0);
-       strcat(&buf[strlen(buf)], _(" <I>from</I> "));
-       stresc(&buf[strlen(buf)], WC->wc_fullname, 1, 1);
-
-       /* Don't need this anymore, it's in the input box below
-       if (strlen(bstr("recp")) > 0) {
-               strcat(&buf[strlen(buf)], _(" <I>to</I> "));
-               stresc(&buf[strlen(buf)], bstr("recp"), 1, 1);
-       }
-       */
-
-       strcat(&buf[strlen(buf)], _(" <I>in</I> "));
-       stresc(&buf[strlen(buf)], WC->wc_roomname, 1, 1);
-
-       /** begin message entry screen */
-       wprintf("<form "
-               "enctype=\"multipart/form-data\" "
-               "method=\"POST\" "
-               "accept-charset=\"UTF-8\" "
-               "action=\"post\" "
-               "name=\"enterform\""
-               ">\n");
-       wprintf("<input type=\"hidden\" name=\"postseq\" value=\"%ld\">\n", now);
-       if (WC->wc_view == VIEW_WIKI) {
-               wprintf("<input type=\"hidden\" name=\"wikipage\" value=\"%s\">\n", bstr("wikipage"));
-       }
-       wprintf("<input type=\"hidden\" name=\"return_to\" value=\"%s\">\n", bstr("return_to"));
-
-       wprintf("<img src=\"static/newmess3_24x.gif\" align=middle alt=\" \">");
-       wprintf("%s\n", buf);   /** header bar */
-       if (WC->room_flags & QR_ANONOPT) {
-               wprintf("&nbsp;"
-                       "<input type=\"checkbox\" name=\"is_anonymous\" value=\"yes\" %s>",
-                               (is_anonymous ? "checked" : "")
-               );
-               wprintf("Anonymous");
-       }
-       wprintf("<br>\n");      /** header bar */
-
-       wprintf("<table border=\"0\" width=\"100%%\">\n");
-       if (recipient_required) {
-
-               wprintf("<tr><td>");
-               wprintf("<font size=-1>");
-               wprintf(_("To:"));
-               wprintf("</font>");
-               wprintf("</td><td>"
-                       "<input autocomplete=\"off\" type=\"text\" name=\"recp\" id=\"recp_id\" value=\"");
-               escputs(bstr("recp"));
-               wprintf("\" size=50 maxlength=1000 />");
-               wprintf("<div class=\"auto_complete\" id=\"recp_name_choices\"></div>");
-               wprintf("</td><td></td></tr>\n");
-
-               wprintf("<tr><td>");
-               wprintf("<font size=-1>");
-               wprintf(_("CC:"));
-               wprintf("</font>");
-               wprintf("</td><td>"
-                       "<input autocomplete=\"off\" type=\"text\" name=\"cc\" id=\"cc_id\" value=\"");
-               escputs(bstr("cc"));
-               wprintf("\" size=50 maxlength=1000 />");
-               wprintf("<div class=\"auto_complete\" id=\"cc_name_choices\"></div>");
-               wprintf("</td><td></td></tr>\n");
-
-               wprintf("<tr><td>");
-               wprintf("<font size=-1>");
-               wprintf(_("BCC:"));
-               wprintf("</font>");
-               wprintf("</td><td>"
-                       "<input autocomplete=\"off\" type=\"text\" name=\"bcc\" id=\"bcc_id\" value=\"");
-               escputs(bstr("bcc"));
-               wprintf("\" size=50 maxlength=1000 />");
-               wprintf("<div class=\"auto_complete\" id=\"bcc_name_choices\"></div>");
-               wprintf("</td><td></td></tr>\n");
-
-               /** Initialize the autocomplete ajax helpers (found in wclib.js) */
-               wprintf("<script type=\"text/javascript\">      \n"
-                       " activate_entmsg_autocompleters();     \n"
-                       "</script>                              \n"
-               );
-       }
-
-       wprintf("<tr><td>");
-       wprintf("<font size=-1>");
-       wprintf(_("Subject (optional):"));
-       wprintf("</font>");
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"subject\" value=\"");
-       escputs(bstr("subject"));
-       wprintf("\" size=50 maxlength=70></td><td>\n");
-
-       wprintf("<input type=\"submit\" name=\"send_button\" value=\"");
-       if (recipient_required) {
-               wprintf(_("Send message"));
-       } else {
-               wprintf(_("Post message"));
-       }
-       wprintf("\">&nbsp;"
-               "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
-       wprintf("</td></tr></table>\n");
-
-       wprintf("<center>");
-
-       wprintf("<textarea name=\"msgtext\" cols=\"80\" rows=\"15\">");
-
-       /** If we're continuing from a previous edit, put our partially-composed message back... */
-       msgescputs(bstr("msgtext"));
-
-       /* If we're forwarding a message, insert it here... */
-       if (atol(bstr("fwdquote")) > 0L) {
-               wprintf("<br><div align=center><i>");
-               wprintf(_("--- forwarded message ---"));
-               wprintf("</i></div><br>");
-               pullquote_message(atol(bstr("fwdquote")), 1, 1);
-       }
-
-       /** If we're replying quoted, insert the quote here... */
-       else if (atol(bstr("replyquote")) > 0L) {
-               wprintf("<br>"
-                       "<blockquote>");
-               pullquote_message(atol(bstr("replyquote")), 0, 1);
-               wprintf("</blockquote><br>");
-       }
-
-       /** If we're editing a wiki page, insert the existing page here... */
-       else if (WC->wc_view == VIEW_WIKI) {
-               safestrncpy(buf, bstr("wikipage"), sizeof buf);
-               str_wiki_index(buf);
-               existing_page = locate_message_by_uid(buf);
-               if (existing_page >= 0L) {
-                       pullquote_message(existing_page, 1, 0);
-               }
-       }
-
-       /** Insert our signature if appropriate... */
-       if ( (WC->is_mailbox) && (strcmp(bstr("sig_inserted"), "yes")) ) {
-               get_preference("use_sig", buf, sizeof buf);
-               if (!strcasecmp(buf, "yes")) {
-                       get_preference("signature", ebuf, sizeof ebuf);
-                       euid_unescapize(buf, ebuf);
-                       wprintf("<br>--<br>");
-                       for (i=0; i<strlen(buf); ++i) {
-                               if (buf[i] == '\n') {
-                                       wprintf("<br>");
-                               }
-                               else if (buf[i] == '<') {
-                                       wprintf("&lt;");
-                               }
-                               else if (buf[i] == '>') {
-                                       wprintf("&gt;");
-                               }
-                               else if (buf[i] == '&') {
-                                       wprintf("&amp;");
-                               }
-                               else if (buf[i] == '\"') {
-                                       wprintf("&quot;");
-                               }
-                               else if (buf[i] == '\'') {
-                                       wprintf("&#39;");
-                               }
-                               else if (isprint(buf[i])) {
-                                       wprintf("%c", buf[i]);
-                               }
-                       }
-               }
-       }
-
-       wprintf("</textarea>");
-       wprintf("</center><br />\n");
-
-       /**
-        * The following script embeds the TinyMCE richedit control, and automatically
-        * transforms the textarea into a richedit textarea.
-        */
-       wprintf(
-               "<script language=\"javascript\" type=\"text/javascript\" src=\"tiny_mce/tiny_mce.js\"></script>\n"
-               "<script language=\"javascript\" type=\"text/javascript\">"
-               "tinyMCE.init({"
-               "       mode : \"textareas\", width : \"100%%\", browsers : \"msie,gecko\", "
-               "       theme : \"advanced\", plugins : \"iespell\", "
-               "       theme_advanced_buttons1 : \"bold, italic, underline, strikethrough, justifyleft, justifycenter, justifyright, justifyfull, bullist, numlist, cut, copy, paste, link, image, help, forecolor, iespell, code\", "
-               "       theme_advanced_buttons2 : \"\", "
-               "       theme_advanced_buttons3 : \"\" "
-               "});"
-               "</script>\n"
-       );
-
-
-       /** Enumerate any attachments which are already in place... */
-       wprintf("<img src=\"static/diskette_24x.gif\" border=0 "
-               "align=middle height=16 width=16> ");
-       wprintf(_("Attachments:"));
-       wprintf(" ");
-       wprintf("<select name=\"which_attachment\" size=1>");
-       for (att = WC->first_attachment; att != NULL; att = att->next) {
-               wprintf("<option value=\"");
-               urlescputs(att->filename);
-               wprintf("\">");
-               escputs(att->filename);
-               /* wprintf(" (%s, %d bytes)",att->content_type,att->length); */
-               wprintf("</option>\n");
-       }
-       wprintf("</select>");
-
-       /** Now offer the ability to attach additional files... */
-       wprintf("&nbsp;&nbsp;&nbsp;");
-       wprintf(_("Attach file:"));
-       wprintf(" <input NAME=\"attachfile\" "
-               "SIZE=16 TYPE=\"file\">\n&nbsp;&nbsp;"
-               "<input type=\"submit\" name=\"attach_button\" value=\"%s\">\n", _("Add"));
-
-       /** Seth asked for these to be at the top *and* bottom... */
-       wprintf("<input type=\"submit\" name=\"send_button\" value=\"");
-       if (recipient_required) {
-               wprintf(_("Send message"));
-       } else {
-               wprintf(_("Post message"));
-       }
-       wprintf("\">&nbsp;"
-               "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
-
-       /** Make sure we only insert our signature once */
-       if (strcmp(bstr("sig_inserted"), "yes")) {
-               wprintf("<INPUT TYPE=\"hidden\" NAME=\"sig_inserted\" VALUE=\"yes\">\n");
-       }
-
-       wprintf("</form>\n");
-
-       wprintf("</td></tr></table></div>\n");
-DONE:  wDumpContent(1);
-}
-
-
-
-/**
- * \brief delete a message
- */
-void delete_msg(void)
-{
-       long msgid;
-       char buf[SIZ];
-
-       msgid = atol(bstr("msgid"));
-
-       output_headers(1, 1, 1, 0, 0, 0);
-
-       if (WC->wc_is_trash) {  /** Delete from Trash is a real delete */
-               serv_printf("DELE %ld", msgid); 
-       }
-       else {                  /** Otherwise move it to Trash */
-               serv_printf("MOVE %ld|_TRASH_|0", msgid);
-       }
-
-       serv_getln(buf, sizeof buf);
-       wprintf("<em>%s</em><br />\n", &buf[4]);
-
-       wDumpContent(1);
-}
-
-
-
-
-/**
- * \brief Confirm move of a message
- */
-void confirm_move_msg(void)
-{
-       long msgid;
-       char buf[SIZ];
-       char targ[SIZ];
-
-       msgid = atol(bstr("msgid"));
-
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0><TR><TD>");
-       wprintf("<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Confirm move of message"));
-       wprintf("</SPAN>\n");
-       wprintf("</TD></TR></TABLE>\n");
-       wprintf("</div>\n<div id=\"content\">\n");
-
-       wprintf("<CENTER>");
-
-       wprintf(_("Move this message to:"));
-       wprintf("<br />\n");
-
-       wprintf("<form METHOD=\"POST\" action=\"move_msg\">\n");
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n", bstr("msgid"));
-
-       wprintf("<SELECT NAME=\"target_room\" SIZE=5>\n");
-       serv_puts("LKRA");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       extract_token(targ, buf, 0, '|', sizeof targ);
-                       wprintf("<OPTION>");
-                       escputs(targ);
-                       wprintf("\n");
-               }
-       }
-       wprintf("</SELECT>\n");
-       wprintf("<br />\n");
-
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"move_button\" VALUE=\"%s\">", _("Move"));
-       wprintf("&nbsp;");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
-       wprintf("</form></CENTER>\n");
-
-       wprintf("</CENTER>\n");
-       wDumpContent(1);
-}
-
-
-/**
- * \brief move a message to another folder
- */
-void move_msg(void)
-{
-       long msgid;
-       char buf[SIZ];
-
-       msgid = atol(bstr("msgid"));
-
-       if (strlen(bstr("move_button")) > 0) {
-               sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room"));
-               serv_puts(buf);
-               serv_getln(buf, sizeof buf);
-               sprintf(WC->ImportantMessage, "%s", &buf[4]);
-       } else {
-               sprintf(WC->ImportantMessage, (_("The message was not moved.")));
-       }
-
-       readloop("readnew");
-
-}
-
-
-/*@}*/
diff --git a/webcit/mime_parser.c b/webcit/mime_parser.c
deleted file mode 100644 (file)
index 02edd1c..0000000
+++ /dev/null
@@ -1,662 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup MIME This is the MIME parser for Citadel.
- *
- * Copyright (c) 1998-2005 by Art Cancro
- * This code is distributed under the terms of the GNU General Public License.
- * \ingroup WebcitHttpServer
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-#include "mime_parser.h"
-
-/**
- * \brief get mime key
- * \param target where to put the mime buffer at???
- * \param source where to extract the mimetype from
- * \param key what???
- */
-void extract_key(char *target, char *source, char *key)
-{
-       int a, b;
-
-       strcpy(target, source);
-       for (a = 0; a < strlen(target); ++a) {
-               if ((!strncasecmp(&target[a], key, strlen(key)))
-                   && (target[a + strlen(key)] == '=')) {
-                       strcpy(target, &target[a + strlen(key) + 1]);
-                       if (target[0] == 34)
-                               strcpy(target, &target[1]);
-                       for (b = 0; b < strlen(target); ++b)
-                               if (target[b] == 34)
-                                       target[b] = 0;
-                       return;
-               }
-       }
-       strcpy(target, "");
-}
-
-
-/**
- * \brief For non-multipart messages, we need to generate a quickie partnum of "1"
- * to return to callback functions.  Some callbacks demand it.
- * \param supplied_partnum partnum to convert
- * \return the converted num
- */
-char *fixed_partnum(char *supplied_partnum) {
-       if (supplied_partnum == NULL) return "1";
-       if (strlen(supplied_partnum)==0) return "1";
-       return supplied_partnum;
-}
-
-
-
-/**
- * \brief Convert "quoted-printable" to binary.  Returns number of bytes decoded.
- * \param decoded the buffer with the decoded output
- * \param encoded the encoded string to decode
- * \param sourcelen length of the decoded buffer
- */
-int CtdlDecodeQuotedPrintable(char *decoded, char *encoded, int sourcelen) {
-       char buf[SIZ];
-       int buf_length = 0;
-       int soft_line_break = 0;
-       unsigned int ch;
-       int decoded_length = 0;
-       int i;
-
-       decoded[0] = 0;
-       decoded_length = 0;
-       buf[0] = 0;
-       buf_length = 0;
-
-       for (i = 0; i < sourcelen; ++i) {
-
-               buf[buf_length++] = encoded[i];
-
-               if ( (encoded[i] == '\n')
-                  || (encoded[i] == 0)
-                  || (i == (sourcelen-1)) ) {
-                       buf[buf_length++] = 0;
-
-                       /*** begin -- process one line ***/
-
-                       if (buf[strlen(buf)-1] == '\n') {
-                               buf[strlen(buf)-1] = 0;
-                       }
-                       if (buf[strlen(buf)-1] == '\r') {
-                               buf[strlen(buf)-1] = 0;
-                       }
-                       while (isspace(buf[strlen(buf)-1])) {
-                               buf[strlen(buf)-1] = 0;
-                       }
-                       soft_line_break = 0;
-
-                       while (strlen(buf) > 0) {
-                               if (!strcmp(buf, "=")) {
-                                       soft_line_break = 1;
-                                       strcpy(buf, "");
-                               } else if ((strlen(buf)>=3) && (buf[0]=='=')) {
-                                       sscanf(&buf[1], "%02x", &ch);
-                                       decoded[decoded_length++] = ch;
-                                       strcpy(buf, &buf[3]);
-                               } else {
-                                       decoded[decoded_length++] = buf[0];
-                                       strcpy(buf, &buf[1]);
-                               }
-                       }
-                       if (soft_line_break == 0) {
-                               decoded[decoded_length++] = '\r';
-                               decoded[decoded_length++] = '\n';
-                       }
-                       buf_length = 0;
-                       /*** end -- process one line ***/
-               }
-       }
-
-       decoded[decoded_length++] = 0;
-       return(decoded_length);
-}
-
-/**
- * \brief fully decode a message
- * Given a message or message-part body and a length, handle any necessary
- * decoding and pass the request up the stack.
- * \param partnum todo ?????
- * \param part_start todo
- * \param length todo
- * \param content_type todo
- * \param charset todo
- * \param encoding todo
- * \param disposition todo
- * \param name todo
- * \param filename todo
- * \param CallBack todo
- * \param PreMultiPartCallBack todo
- * \param PostMultiPartCallBack todo
- * \param userdata todo
- * \param dont_decode todo
- */
-void mime_decode(char *partnum,
-                char *part_start, size_t length,
-                char *content_type, char *charset, char *encoding,
-                char *disposition,
-                char *name, char *filename,
-                void (*CallBack)
-                 (char *cbname,
-                  char *cbfilename,
-                  char *cbpartnum,
-                  char *cbdisp,
-                  void *cbcontent,
-                  char *cbtype,
-                  char *cbcharset,
-                  size_t cblength,
-                  char *cbencoding,
-                  void *cbuserdata),
-                void (*PreMultiPartCallBack)
-                 (char *cbname,
-                  char *cbfilename,
-                  char *cbpartnum,
-                  char *cbdisp,
-                  void *cbcontent,
-                  char *cbtype,
-                  char *cbcharset,
-                  size_t cblength,
-                  char *cbencoding,
-                  void *cbuserdata),
-                void (*PostMultiPartCallBack)
-                 (char *cbname,
-                  char *cbfilename,
-                  char *cbpartnum,
-                  char *cbdisp,
-                  void *cbcontent,
-                  char *cbtype,
-                  char *cbcharset,
-                  size_t cblength,
-                  char *cbencoding,
-                  void *cbuserdata),
-                 void *userdata,
-                 int dont_decode
-)
-{
-
-       char *decoded;
-       size_t bytes_decoded = 0;
-
-       /* Some encodings aren't really encodings */
-       if (!strcasecmp(encoding, "7bit"))
-               strcpy(encoding, "");
-       if (!strcasecmp(encoding, "8bit"))
-               strcpy(encoding, "");
-       if (!strcasecmp(encoding, "binary"))
-               strcpy(encoding, "");
-
-       /* If this part is not encoded, send as-is */
-       if ( (strlen(encoding) == 0) || (dont_decode)) {
-               if (CallBack != NULL) {
-                       CallBack(name, filename, fixed_partnum(partnum),
-                               disposition, part_start,
-                               content_type, charset, length, encoding, userdata);
-                       }
-               return;
-       }
-       
-       if ((strcasecmp(encoding, "base64"))
-           && (strcasecmp(encoding, "quoted-printable"))) {
-               return;
-       }
-       /**
-        * Allocate a buffer for the decoded data.  The output buffer is the
-        * same size as the input buffer; this assumes that the decoded data
-        * will never be larger than the encoded data.  This is a safe
-        * assumption with base64, uuencode, and quoted-printable.
-        */
-       decoded = malloc(length+2048);
-       if (decoded == NULL) {
-               return;
-       }
-
-       if (!strcasecmp(encoding, "base64")) {
-               bytes_decoded = CtdlDecodeBase64(decoded, part_start, length);
-       }
-       else if (!strcasecmp(encoding, "quoted-printable")) {
-               bytes_decoded = CtdlDecodeQuotedPrintable(decoded,
-                                                       part_start, length);
-       }
-
-       if (bytes_decoded > 0) if (CallBack != NULL) {
-               CallBack(name, filename, fixed_partnum(partnum),
-                       disposition, decoded,
-                       content_type, charset, bytes_decoded, "binary", userdata);
-       }
-
-       free(decoded);
-}
-
-/**
- * \brief Break out the components of a multipart message
- * (This function expects to be fed HEADERS + CONTENT)
- * Note: NULL can be supplied as content_end; in this case, the message is
- * considered to have ended when the parser encounters a 0x00 byte.
- * \param partnum todo
- * \param content_start todo ?????
- * \param content_end todo
- * \param CallBack todo
- * \param PreMultiPartCallBack
- * \param PostMultiPartCallBack
- * \param userdata todo
- * \param dont_decode todo
- */
-void the_mime_parser(char *partnum,
-                    char *content_start, char *content_end,
-                    void (*CallBack)
-                     (char *cbname,
-                      char *cbfilename,
-                      char *cbpartnum,
-                      char *cbdisp,
-                      void *cbcontent,
-                      char *cbtype,
-                      char *cbcharset,
-                      size_t cblength,
-                      char *cbencoding,
-                      void *cbuserdata),
-                    void (*PreMultiPartCallBack)
-                     (char *cbname,
-                      char *cbfilename,
-                      char *cbpartnum,
-                      char *cbdisp,
-                      void *cbcontent,
-                      char *cbtype,
-                      char *cbcharset,
-                      size_t cblength,
-                      char *cbencoding,
-                      void *cbuserdata),
-                    void (*PostMultiPartCallBack)
-                     (char *cbname,
-                      char *cbfilename,
-                      char *cbpartnum,
-                      char *cbdisp,
-                      void *cbcontent,
-                      char *cbtype,
-                      char *cbcharset,
-                      size_t cblength,
-                      char *cbencoding,
-                      void *cbuserdata),
-                     void *userdata,
-                     int dont_decode
-)
-{
-
-       char *ptr;
-       char *srch = NULL;
-       char *part_start, *part_end = NULL;
-       char buf[SIZ];
-       char *header;
-       char *boundary;
-       char *startary;
-       size_t startary_len = 0;
-       char *endary;
-       char *next_boundary;
-       char *content_type;
-       char *charset;
-       size_t content_length;
-       char *encoding;
-       char *disposition;
-       char *name = NULL;
-       char *content_type_name;
-       char *content_disposition_name;
-       char *filename;
-       int is_multipart;
-       int part_seq = 0;
-       int i;
-       size_t length;
-       char nested_partnum[SIZ];
-
-       ptr = content_start;
-       content_length = 0;
-
-       boundary = malloc(SIZ);
-       memset(boundary, 0, SIZ);
-
-       startary = malloc(SIZ);
-       memset(startary, 0, SIZ);
-
-       endary = malloc(SIZ);
-       memset(endary, 0, SIZ);
-
-       header = malloc(SIZ);
-       memset(header, 0, SIZ);
-
-       content_type = malloc(SIZ);
-       memset(content_type, 0, SIZ);
-
-       charset = malloc(SIZ);
-       memset(charset, 0, SIZ);
-
-       encoding = malloc(SIZ);
-       memset(encoding, 0, SIZ);
-
-       content_type_name = malloc(SIZ);
-       memset(content_type_name, 0, SIZ);
-
-       content_disposition_name = malloc(SIZ);
-       memset(content_disposition_name, 0, SIZ);
-
-       filename = malloc(SIZ);
-       memset(filename, 0, SIZ);
-
-       disposition = malloc(SIZ);
-       memset(disposition, 0, SIZ);
-
-       /** If the caller didn't supply an endpointer, generate one by measure */
-       if (content_end == NULL) {
-               content_end = &content_start[strlen(content_start)];
-       }
-
-       /** Learn interesting things from the headers */
-       strcpy(header, "");
-       do {
-               ptr = memreadline(ptr, buf, SIZ);
-               if (ptr >= content_end) {
-                       goto end_parser;
-               }
-
-               for (i = 0; i < strlen(buf); ++i) {
-                       if (isspace(buf[i])) {
-                               buf[i] = ' ';
-                       }
-               }
-
-               if (!isspace(buf[0])) {
-                       if (!strncasecmp(header, "Content-type: ", 14)) {
-                               strcpy(content_type, &header[14]);
-                               extract_key(content_type_name, content_type, "name");
-                               extract_key(charset, content_type, "charset");
-                               /** Deal with weird headers */
-                               if (strchr(content_type, ' '))
-                                       *(strchr(content_type, ' ')) = '\0';
-                               if (strchr(content_type, ';'))
-                                       *(strchr(content_type, ';')) = '\0';
-                       }
-                       if (!strncasecmp(header, "Content-Disposition: ", 21)) {
-                               strcpy(disposition, &header[21]);
-                               extract_key(content_disposition_name, disposition, "name");
-                               extract_key(filename, disposition, "filename");
-                       }
-                       if (!strncasecmp(header, "Content-length: ", 16)) {
-                               content_length = (size_t) atol(&header[16]);
-                       }
-                       if (!strncasecmp(header,
-                                     "Content-transfer-encoding: ", 27))
-                               strcpy(encoding, &header[27]);
-                       if (strlen(boundary) == 0)
-                               extract_key(boundary, header, "boundary");
-                       strcpy(header, "");
-               }
-               if ((strlen(header) + strlen(buf) + 2) < SIZ)
-                       strcat(header, buf);
-       } while ((strlen(buf) > 0) && (*ptr != 0));
-
-       if (strchr(disposition, ';'))
-               *(strchr(disposition, ';')) = '\0';
-       striplt(disposition);
-       if (strchr(content_type, ';'))
-               *(strchr(content_type, ';')) = '\0';
-       striplt(content_type);
-
-       if (strlen(boundary) > 0) {
-               is_multipart = 1;
-       } else {
-               is_multipart = 0;
-       }
-
-       /** If this is a multipart message, then recursively process it */
-       part_start = NULL;
-       if (is_multipart) {
-
-               /** Tell the client about this message's multipartedness */
-               if (PreMultiPartCallBack != NULL) {
-                       PreMultiPartCallBack("", "", partnum, "",
-                               NULL, content_type, charset,
-                               0, encoding, userdata);
-               }
-
-               /** Figure out where the boundaries are */
-               snprintf(startary, SIZ, "--%s", boundary);
-               snprintf(endary, SIZ, "--%s--", boundary);
-               startary_len = strlen(startary);
-
-               part_start = NULL;
-               do {
-                       next_boundary = NULL;
-                       for (srch=ptr; srch<content_end; ++srch) {
-                               if (!memcmp(srch, startary, startary_len)) {
-                                       next_boundary = srch;
-                                       srch = content_end;
-                               }
-                       }
-
-                       if ( (part_start != NULL) && (next_boundary != NULL) ) {
-                               part_end = next_boundary;
-                               --part_end;
-
-                               if (strlen(partnum) > 0) {
-                                       snprintf(nested_partnum,
-                                                sizeof nested_partnum,
-                                                "%s.%d", partnum,
-                                                ++part_seq);
-                               }
-                               else {
-                                       snprintf(nested_partnum,
-                                                sizeof nested_partnum,
-                                                "%d", ++part_seq);
-                               }
-                               the_mime_parser(nested_partnum,
-                                           part_start, part_end,
-                                               CallBack,
-                                               PreMultiPartCallBack,
-                                               PostMultiPartCallBack,
-                                               userdata,
-                                               dont_decode);
-                       }
-
-                       if (next_boundary != NULL) {
-                               /**
-                                * If we pass out of scope, don't attempt to
-                                * read past the end boundary. */
-                               if (!strcmp(next_boundary, endary)) {
-                                       ptr = content_end;
-                               }
-                               else {
-                                       /** Set up for the next part. */
-                                       part_start = strstr(next_boundary, "\n");
-                                       ++part_start;
-                                       ptr = part_start;
-                               }
-                       }
-                       else {
-                               /** Invalid end of multipart.  Bail out! */
-                               ptr = content_end;
-                       }
-               } while ( (ptr < content_end) && (next_boundary != NULL) );
-
-               if (PostMultiPartCallBack != NULL) {
-                       PostMultiPartCallBack("", "", partnum, "", NULL,
-                               content_type, charset, 0, encoding, userdata);
-               }
-               goto end_parser;
-       }
-
-       /** If it's not a multipart message, then do something with it */
-       if (!is_multipart) {
-               part_start = ptr;
-               length = 0;
-               while (ptr < content_end) {
-                       ++ptr;
-                       ++length;
-               }
-               part_end = content_end;
-               /** fix an off-by-one error */
-               --part_end;
-               --length;
-               
-               /** Truncate if the header told us to */
-               if ( (content_length > 0) && (length > content_length) ) {
-                       length = content_length;
-               }
-
-               /**
-                * Sometimes the "name" field is tacked on to Content-type,
-                * and sometimes it's tacked on to Content-disposition.  Use
-                * whichever one we have.
-                */
-               if (strlen(content_disposition_name) > strlen(content_type_name)) {
-                       name = content_disposition_name;
-               }
-               else {
-                       name = content_type_name;
-               }
-       
-               /*
-               lprintf(9, "mime_decode part=%s, len=%d, type=%s, charset=%s, encoding=%s\n",
-                       partnum, length, content_type, charset, encoding);
-               */
-
-               /**
-                * Ok, we've got a non-multipart part here, so do something with it.
-                */
-               mime_decode(partnum,
-                       part_start, length,
-                       content_type, charset, encoding, disposition,
-                       name, filename,
-                       CallBack, NULL, NULL,
-                       userdata, dont_decode
-               );
-
-               /**
-                * Now if it's an encapsulated message/rfc822 then we have to recurse into it
-                */
-               if (!strcasecmp(content_type, "message/rfc822")) {
-
-                       if (PreMultiPartCallBack != NULL) {
-                               PreMultiPartCallBack("", "", partnum, "",
-                                       NULL, content_type, charset,
-                                       0, encoding, userdata);
-                       }
-                       if (CallBack != NULL) {
-                               if (strlen(partnum) > 0) {
-                                       snprintf(nested_partnum,
-                                                sizeof nested_partnum,
-                                                "%s.%d", partnum,
-                                                ++part_seq);
-                               }
-                               else {
-                                       snprintf(nested_partnum,
-                                                sizeof nested_partnum,
-                                                "%d", ++part_seq);
-                               }
-                               the_mime_parser(nested_partnum,
-                                       part_start, part_end,
-                                       CallBack,
-                                       PreMultiPartCallBack,
-                                       PostMultiPartCallBack,
-                                       userdata,
-                                       dont_decode
-                               );
-                       }
-                       if (PostMultiPartCallBack != NULL) {
-                               PostMultiPartCallBack("", "", partnum, "", NULL,
-                                       content_type, charset, 0, encoding, userdata);
-                       }
-
-
-               }
-
-       }
-
-end_parser:    /** free the buffers!  end the oppression!! */
-       free(boundary);
-       free(startary);
-       free(endary);   
-       free(header);
-       free(content_type);
-       free(charset);
-       free(encoding);
-       free(content_type_name);
-       free(content_disposition_name);
-       free(filename);
-       free(disposition);
-}
-
-
-
-/**
- * \brief Entry point for the MIME parser.
- * (This function expects to be fed HEADERS + CONTENT)
- * Note: NULL can be supplied as content_end; in this case, the message is
- * considered to have ended when the parser encounters a 0x00 byte.
- * \param content_start todo ?????????
- * \param content_end todo 
- * \param CallBack todo
- * \param PreMultiPartCallBack todo
- * \param PostMultiPartCallBack todo
- * \param userdata todo
- * \param dont_decode todo
- */
-void mime_parser(char *content_start,
-               char *content_end,
-
-                void (*CallBack)
-                 (char *cbname,
-                  char *cbfilename,
-                  char *cbpartnum,
-                  char *cbdisp,
-                  void *cbcontent,
-                  char *cbtype,
-                  char *cbcharset,
-                  size_t cblength,
-                  char *cbencoding,
-                  void *cbuserdata),
-
-                void (*PreMultiPartCallBack)
-                 (char *cbname,
-                  char *cbfilename,
-                  char *cbpartnum,
-                  char *cbdisp,
-                  void *cbcontent,
-                  char *cbtype,
-                  char *cbcharset,
-                  size_t cblength,
-                  char *cbencoding,
-                  void *cbuserdata),
-
-                void (*PostMultiPartCallBack)
-                 (char *cbname,
-                  char *cbfilename,
-                  char *cbpartnum,
-                  char *cbdisp,
-                  void *cbcontent,
-                  char *cbtype,
-                  char *cbcharset,
-                  size_t cblength,
-                  char *cbencoding,
-                  void *cbuserdata),
-
-                 void *userdata,
-                 int dont_decode
-)
-{
-
-       the_mime_parser("", content_start, content_end,
-                       CallBack,
-                       PreMultiPartCallBack,
-                       PostMultiPartCallBack,
-                       userdata, dont_decode);
-}
-
-
-
-/*@}*/
diff --git a/webcit/mime_parser.h b/webcit/mime_parser.h
deleted file mode 100644 (file)
index b82cd68..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * $Id$
- *
- */
-
-/*
- * Here's a bunch of stupid magic to make the MIME parser portable between
- * Citadel and WebCit.
- */
-#ifndef SIZ
-#define SIZ    4096
-#endif
-
-
-/* 
- * Declarations for functions in the parser
- */
-
-void extract_key(char *target, char *source, char *key);
-
-void mime_parser(char *content_start, char *content_end,
-               void (*CallBack)
-                       (char *cbname,
-                       char *cbfilename,
-                       char *cbpartnum,
-                       char *cbdisp,
-                       void *cbcontent,
-                       char *cbtype,
-                       char *cbcharset,
-                       size_t cblength,
-                       char *cbencoding,
-                       void *cbuserdata),
-               void (*PreMultiPartCallBack)
-                       (char *cbname,
-                       char *cbfilename,
-                       char *cbpartnum,
-                       char *cbdisp,
-                       void *cbcontent,
-                       char *cbtype,
-                       char *cbcharset,
-                       size_t cblength,
-                       char *cbencoding,
-                       void *cbuserdata),
-               void (*PostMultiPartCallBack)
-                       (char *cbname,
-                       char *cbfilename,
-                       char *cbpartnum,
-                       char *cbdisp,
-                       void *cbcontent,
-                       char *cbtype,
-                       char *cbcharset,
-                       size_t cblength,
-                       char *cbencoding,
-                       void *cbuserdata),
-               void *userdata,
-               int dont_decode
-               );
diff --git a/webcit/netconf.c b/webcit/netconf.c
deleted file mode 100644 (file)
index f380296..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-/* 
- * $Id$
- */
-/**
- * \defgroup NetShareConf Functions which handle network and sharing configuration.
- *
- * \ingroup CitadelConfig
- */
-/*@{*/
-#include "webcit.h"
-
-/**
- * \brief edit a network node
- */
-void edit_node(void) {
-       char buf[SIZ];
-       char node[SIZ];
-       char cnode[SIZ];
-       FILE *fp;
-
-       if (strlen(bstr("ok_button")) > 0) {
-               strcpy(node, bstr("node") );
-               fp = tmpfile();
-               if (fp != NULL) {
-                       serv_puts("CONF getsys|application/x-citadel-ignet-config");
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] == '1') {
-                               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                                       extract_token(cnode, buf, 0, '|', sizeof cnode);
-                                       if (strcasecmp(node, cnode)) {
-                                               fprintf(fp, "%s\n", buf);
-                                       }
-                               }
-                       fprintf(fp, "%s|%s|%s|%s\n", 
-                               bstr("node"),
-                               bstr("secret"),
-                               bstr("host"),
-                               bstr("port") );
-                       }
-                       rewind(fp);
-
-                       serv_puts("CONF putsys|application/x-citadel-ignet-config");
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] == '4') {
-                               while (fgets(buf, sizeof buf, fp) != NULL) {
-                                       buf[strlen(buf)-1] = 0;
-                                       serv_puts(buf);
-                               }
-                               serv_puts("000");
-                       }
-                       fclose(fp);
-               }
-       }
-
-       display_netconf();
-}
-
-
-/**
- * \brief add a node
- */
-void display_add_node(void)
-{
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
-       wprintf("<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Add a new node"));
-       wprintf("</SPAN>");
-       wprintf("</TD></TR></TABLE>\n");
-       wprintf("</div>\n<div id=\"content\">\n");
-
-       wprintf("<FORM METHOD=\"POST\" action=\"edit_node\">\n");
-       wprintf("<CENTER><TABLE border=0>\n");
-       wprintf("<TR><TD>%s</TD>", _("Node name"));
-       wprintf("<TD><INPUT TYPE=\"text\" NAME=\"node\" MAXLENGTH=\"16\"></TD></TR>\n");
-       wprintf("<TR><TD>%s</TD>", _("Shared secret"));
-       wprintf("<TD><INPUT TYPE=\"password\" NAME=\"secret\" MAXLENGTH=\"16\"></TD></TR>\n");
-       wprintf("<TR><TD>%s</TD>", _("Host or IP address"));
-       wprintf("<TD><INPUT TYPE=\"text\" NAME=\"host\" MAXLENGTH=\"64\"></TD></TR>\n");
-       wprintf("<TR><TD>%s</TD>", _("Port number"));
-       wprintf("<TD><INPUT TYPE=\"text\" NAME=\"port\" MAXLENGTH=\"8\"></TD></TR>\n");
-       wprintf("</TABLE><br />");
-               wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Add node"));
-       wprintf("&nbsp;");
-               wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
-       wprintf("</CENTER></FORM>\n");
-
-       wDumpContent(1);
-}
-
-/**
- * \brief modify an existing node
- */
-void display_edit_node(void)
-{
-       char buf[512];
-       char node[256];
-       char cnode[256];
-       char csecret[256];
-       char chost[256];
-       char cport[256];
-
-       strcpy(node, bstr("node"));
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
-       wprintf("<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Edit node configuration for "));
-       escputs(node);
-       wprintf("</SPAN>\n");
-       wprintf("</TD></TR></TABLE>\n");
-       wprintf("</div>\n<div id=\"content\">\n");
-
-       serv_puts("CONF getsys|application/x-citadel-ignet-config");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       extract_token(cnode, buf, 0, '|', sizeof cnode);
-                       extract_token(csecret, buf, 1, '|', sizeof csecret);
-                       extract_token(chost, buf, 2, '|', sizeof chost);
-                       extract_token(cport, buf, 3, '|', sizeof cport);
-
-                       if (!strcasecmp(node, cnode)) {
-                               wprintf("<FORM METHOD=\"POST\" action=\"edit_node\">\n");
-                               wprintf("<CENTER><TABLE border=0>\n");
-                               wprintf("<TR><TD>");
-                               wprintf(_("Node name"));
-                               wprintf("</TD>");
-                               wprintf("<TD><INPUT TYPE=\"text\" NAME=\"node\" MAXLENGTH=\"16\" VALUE=\"%s\"></TD></TR>\n", cnode);
-                               wprintf("<TR><TD>");
-                               wprintf(_("Shared secret"));
-                               wprintf("</TD>");
-                               wprintf("<TD><INPUT TYPE=\"password\" NAME=\"secret\" MAXLENGTH=\"16\" VALUE=\"%s\"></TD></TR>\n", csecret);
-                               wprintf("<TR><TD>");
-                               wprintf(_("Host or IP address"));
-                               wprintf("</TD>");
-                               wprintf("<TD><INPUT TYPE=\"text\" NAME=\"host\" MAXLENGTH=\"64\" VALUE=\"%s\"></TD></TR>\n", chost);
-                               wprintf("<TR><TD>");
-                               wprintf(_("Port number"));
-                               wprintf("</TD>");
-                               wprintf("<TD><INPUT TYPE=\"text\" NAME=\"port\" MAXLENGTH=\"8\" VALUE=\"%s\"></TD></TR>\n", cport);
-                               wprintf("</TABLE><br />");
-                               wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">",
-                                       _("Save changes"));
-                               wprintf("&nbsp;");
-                               wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">",
-                                       _("Cancel"));
-                               wprintf("</CENTER></FORM>\n");
-                       }
-
-               }
-       }
-
-       else {          /** command error getting configuration */
-               wprintf("%s<br />\n", &buf[4]);
-       }
-
-       wDumpContent(1);
-}
-
-
-/**
- * \brief display all configured nodes
- */
-void display_netconf(void)
-{
-       char buf[SIZ];
-       char node[SIZ];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
-       wprintf("<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Network configuration"));
-       wprintf("</SPAN>\n");
-       wprintf("</TD></TR></TABLE>\n");
-       wprintf("</div>\n<div id=\"content\">\n");
-
-       wprintf("<CENTER>");
-       wprintf("<a href=\"display_add_node\">");
-       wprintf(_("Add a new node"));
-       wprintf("</A><br />\n");
-       wprintf("</CENTER>");
-
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
-       wprintf("<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Currently configured nodes"));
-       wprintf("</SPAN>\n");
-       wprintf("</TD></TR></TABLE>\n");
-       serv_puts("CONF getsys|application/x-citadel-ignet-config");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               wprintf("<CENTER><TABLE border=0>\n");
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       extract_token(node, buf, 0, '|', sizeof node);
-                       wprintf("<TR><TD><FONT SIZE=+1>");
-                       escputs(node);
-                       wprintf("</FONT></TD>");
-                       wprintf("<TD><a href=\"display_edit_node&node=");
-                       urlescputs(node);
-                       wprintf("\">");
-                       wprintf(_("(Edit)"));
-                       wprintf("</A></TD>");
-                       wprintf("<TD><a href=\"display_confirm_delete_node&node=");
-                       urlescputs(node);
-                       wprintf("\">");
-                       wprintf(_("(Delete)"));
-                       wprintf("</A></TD>");
-                       wprintf("</TR>\n");
-               }
-               wprintf("</TABLE></CENTER>\n");
-       }
-       wDumpContent(1);
-}
-
-/**
- * \brief display the dialog to verify the deletion
- */
-void display_confirm_delete_node(void)
-{
-       char node[SIZ];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
-       wprintf("<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Confirm delete"));
-       wprintf("</SPAN>\n");
-       wprintf("</TD></TR></TABLE>\n");
-       wprintf("</div>\n<div id=\"content\">\n");
-
-       strcpy(node, bstr("node"));
-       wprintf("<CENTER>");
-       wprintf(_("Are you sure you want to delete "));
-       wprintf("<FONT SIZE=+1>");
-       escputs(node);
-       wprintf("</FONT>?<br />\n");
-       wprintf("<a href=\"delete_node&node=");
-       urlescputs(node);
-       wprintf("\">");
-       wprintf(_("Yes"));
-       wprintf("</A>&nbsp;&nbsp;&nbsp;");
-       wprintf("<a href=\"display_netconf\">");
-       wprintf(_("No"));
-       wprintf("</A><br />\n");
-       wDumpContent(1);
-}
-
-/**
- * \brief actually delete the node
- */
-void delete_node(void)
-{
-       char buf[SIZ];
-       char node[SIZ];
-       char cnode[SIZ];
-       FILE *fp;
-
-       strcpy(node, bstr("node") );
-       fp = tmpfile();
-       if (fp != NULL) {
-               serv_puts("CONF getsys|application/x-citadel-ignet-config");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '1') {
-                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                               extract_token(cnode, buf, 0, '|', sizeof cnode);
-                               if (strcasecmp(node, cnode)) {
-                                       fprintf(fp, "%s\n", buf);
-                               }
-                       }
-               }
-               rewind(fp);
-
-               serv_puts("CONF putsys|application/x-citadel-ignet-config");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '4') {
-                       while (fgets(buf, sizeof buf, fp) != NULL) {
-                               buf[strlen(buf)-1] = 0;
-                               serv_puts(buf);
-                       }
-                       serv_puts("000");
-               }
-               fclose(fp);
-       }
-
-       display_netconf();
-}
-
-/**
- * \brief add a new node
- */
-void add_node(void)
-{
-       char node[SIZ];
-       char buf[SIZ];
-
-       strcpy(node, bstr("node"));
-
-       if (strlen(bstr("add_button")) > 0)  {
-               sprintf(buf, "NSET addnode|%s", node);
-               serv_puts(buf);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '1') {
-                       output_headers(1, 1, 0, 0, 0, 0);
-                       server_to_text();
-                       wprintf("<a href=\"display_netconf\">");
-                       wprintf(_("Back to menu"));
-                       wprintf("</A>\n");
-                       wDumpContent(1);
-               } else {
-                       strcpy(WC->ImportantMessage, &buf[4]);
-                       display_netconf();
-               }
-       }
-}
-
-
-/*@}*/
diff --git a/webcit/notes.c b/webcit/notes.c
deleted file mode 100644 (file)
index 137480a..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup StickyNotes Functions which handle "sticky notes"
- * \ingroup WebcitDisplayItems
- */
-/*@{*/
-#include "webcit.h"
-#include "groupdav.h"
-#include "webserver.h"
-
-/**
- * \brief display sticky notes
- * \param msgnum the citadel mesage number
- */
-void display_note(long msgnum)
-{
-       char buf[SIZ];
-       char notetext[SIZ];
-       char display_notetext[SIZ];
-       char eid[128];
-       int in_text = 0;
-       int i;
-
-       wprintf("<IMG ALIGN=MIDDLE src=\"static/storenotes_48x.gif\">\n");
-
-       serv_printf("MSG0 %ld", msgnum);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               wprintf("%s<br />\n", &buf[4]);
-               return;
-       }
-
-       strcpy(notetext, "");
-       strcpy(eid, "");
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-
-               /** Fill the buffer */
-               if ( (in_text) && (strlen(notetext) < SIZ-256) ) {
-                       strcat(notetext, buf);
-               }
-
-               if ( (!in_text) && (!strncasecmp(buf, "exti=", 5)) ) {
-                       safestrncpy(eid, &buf[5], sizeof eid);
-               }
-
-               if ( (!in_text) && (!strcasecmp(buf, "text")) ) {
-                       in_text = 1;
-               }
-       }
-
-       /** Now sanitize the buffer */
-       for (i=0; i<strlen(notetext); ++i) {
-               if (isspace(notetext[i])) notetext[i] = ' ';
-       }
-
-       /** Make it HTML-happy and print it. */
-       stresc(display_notetext, notetext, 0, 0);
-       if (strlen(eid) > 0) {
-               wprintf("<span id=\"note%s\">%s</span><br />\n", eid, display_notetext);
-       }
-       else {
-               wprintf("<span id=\"note%ld\">%s</span><br />\n", msgnum, display_notetext);
-       }
-
-       /** Offer in-place editing. */
-       if (strlen(eid) > 0) {
-               wprintf("<script type=\"text/javascript\">"
-                       " new Ajax.InPlaceEditor('note%s', 'updatenote?eid=%s', {rows:5,cols:72}); "
-                       "</script>\n",
-                       eid,
-                       eid
-               );
-       }
-}
-
-
-/**
- * \brief  This gets called by the Ajax.InPlaceEditor when we save a note.
- */
-void updatenote(void)
-{
-       char buf[SIZ];
-       char notetext[SIZ];
-       char display_notetext[SIZ];
-       long msgnum;
-       int in_text = 0;
-       int i;
-
-       serv_printf("ENT0 1||0|0||||||%s", bstr("eid"));
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '4') {
-               text_to_server(bstr("value"));
-               serv_puts("000");
-       }
-
-       begin_ajax_response();
-       msgnum = locate_message_by_uid(bstr("eid"));
-       if (msgnum >= 0L) {
-               serv_printf("MSG0 %ld", msgnum);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '1') {
-                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               
-                               /** Fill the buffer */
-                               if ( (in_text) && (strlen(notetext) < SIZ-256) ) {
-                                       strcat(notetext, buf);
-                               }
-               
-                               if ( (!in_text) && (!strcasecmp(buf, "text")) ) {
-                                       in_text = 1;
-                               }
-                       }
-                       /** Now sanitize the buffer */
-                       for (i=0; i<strlen(notetext); ++i) {
-                               if (isspace(notetext[i])) notetext[i] = ' ';
-                       }
-               
-                       /** Make it HTML-happy and print it. */
-                       stresc(display_notetext, notetext, 0, 0);
-                       wprintf("%s\n", display_notetext);
-               }
-       }
-       else {
-               wprintf("%s", _("An error has occurred."));
-       }
-
-       end_ajax_response();
-}
-
-
-
-/*@}*/
diff --git a/webcit/paging.c b/webcit/paging.c
deleted file mode 100644 (file)
index 9aee17f..0000000
+++ /dev/null
@@ -1,517 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup PageFunc Functions which implement the chat and paging facilities.
- * \ingroup ClientPower
- */
-/*@{*/
-#include "webcit.h"
-
-/**
- * \brief display the form for paging (x-messaging) another user
- */
-void display_page(void)
-{
-       char recp[SIZ];
-
-       strcpy(recp, bstr("recp"));
-
-        output_headers(1, 1, 2, 0, 0, 0);
-        wprintf("<div id=\"banner\">\n"
-                "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-                "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Send instant message"));
-       wprintf("</SPAN>"
-                "</TD></TR></TABLE>\n"
-                "</div>\n<div id=\"content\">\n"
-        );
-                                                                                                                             
-        wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       wprintf(_("Send an instant message to: "));
-       escputs(recp);
-       wprintf("<br>\n");
-
-       wprintf("<FORM METHOD=\"POST\" action=\"page_user\">\n");
-
-       wprintf("<TABLE border=0 width=100%%><TR><TD>\n");
-
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"recp\" VALUE=\"");
-       escputs(recp);
-       wprintf("\">\n");
-
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"closewin\" VALUE=\"");
-       escputs(bstr("closewin"));
-       wprintf("\">\n");
-
-       wprintf(_("Enter message text:"));
-       wprintf("<br />");
-
-       wprintf("<TEXTAREA NAME=\"msgtext\" wrap=soft ROWS=5 COLS=40 "
-               "WIDTH=40></TEXTAREA>\n");
-
-       wprintf("</TD></TR></TABLE><br />\n");
-
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"send_button\" VALUE=\"%s\">", _("Send message"));
-       wprintf("<br /><a href=\"javascript:window.close();\"%s</A>\n", _("Cancel"));
-
-       wprintf("</FORM></CENTER>\n");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-/**
- * \brief page another user
- */
-void page_user(void)
-{
-       char recp[SIZ];
-       char buf[SIZ];
-       char closewin[SIZ];
-
-        output_headers(1, 1, 2, 0, 0, 0);
-        wprintf("<div id=\"banner\">\n"
-                "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-                "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Add or edit an event"));
-       wprintf("</SPAN>"
-                "</TD></TR></TABLE>\n"
-                "</div>\n<div id=\"content\">\n"
-        );
-                                                                                                                             
-       strcpy(recp, bstr("recp"));
-       strcpy(closewin, bstr("closewin"));
-
-       if (strlen(bstr("send_button")) == 0) {
-               wprintf("<EM>");
-               wprintf(_("Message was not sent."));
-               wprintf("</EM><br />\n");
-       } else {
-               serv_printf("SEXP %s|-", recp);
-               serv_getln(buf, sizeof buf);
-
-               if (buf[0] == '4') {
-                       text_to_server(bstr("msgtext"));
-                       serv_puts("000");
-                       wprintf("<EM>");
-                       wprintf(_("Message has been sent to "));
-                       escputs(recp);
-                       wprintf(".</EM><br />\n");
-               }
-               else {
-                       wprintf("<em>%s</em><br />\n", &buf[4]);
-               }
-       }
-       
-       if (!strcasecmp(closewin, "yes")) {
-               wprintf("<CENTER><a href=\"javascript:window.close();\">");
-               wprintf(_("[ close window ]"));
-               wprintf("</A></CENTER>\n");
-       }
-
-       wDumpContent(1);
-}
-
-
-
-/**
- * \brief multiuser chat
- */
-void do_chat(void)
-{
-       char buf[SIZ];
-
-       /** First, check to make sure we're still allowed in this room. */
-       serv_printf("GOTO %s", WC->wc_roomname);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               smart_goto("_BASEROOM_");
-               return;
-       }
-
-       /**
-        * If the chat socket is still open from a previous chat,
-        * close it -- because it might be stale or in the wrong room.
-        */
-       if (WC->chat_sock < 0) {
-               close(WC->chat_sock);
-               WC->chat_sock = (-1);
-       }
-
-       /**
-        * WebCit Chat works by having transmit, receive, and refresh
-        * frames.  Load the frameset.  (This isn't AJAX but the headers
-        * output by begin_ajax_response() happen to be the ones we need.)
-        */
-       begin_ajax_response();
-       do_template("chatframeset");
-       end_ajax_response();
-       return;
-}
-
-
-/**
- * \brief display page popup
- * If there are instant messages waiting, and we notice that we haven't checked them in
- * a while, it probably means that we need to open the instant messenger window.
- */
-void page_popup(void)
-{
-       char buf[SIZ];
-
-       /** JavaScript function to alert the user that popups are probably blocked */
-       wprintf("<script type=\"text/javascript\">      "
-               "function PopUpFailed() {       "
-               " alert(\"%s\");        "
-               "}      "
-               "</script>\n",
-               _("You have one or more instant messages waiting, but the Citadel Instant Messenger "
-                 "window failed to open.  This is probably because you have a popup blocker "
-                 "installed.  Please configure your popup blocker to allow popups from this site "
-                 "if you wish to receive instant messages.")
-       );
-
-       /** First, do the check as part of our page load. */
-       serv_puts("NOOP");
-       serv_getln(buf, sizeof buf);
-       if (buf[3] == '*') {
-               if ((time(NULL) - WC->last_pager_check) > 60) {
-                       wprintf("<script type=\"text/javascript\">"
-                               " var oWin = window.open('static/instant_messenger.html', "
-                               " 'CTDL_MESSENGER', 'width=700,height=400');    "
-                               " if (oWin==null || typeof(oWin)==\"undefined\") {      "
-                               "  PopUpFailed();       "
-                               " }     "
-                               "</script>"
-                       );      
-               }
-       }
-
-       /** Then schedule it to happen again a minute from now if the user is idle. */
-       wprintf("<script type=\"text/javascript\">      "
-               " function HandleSslp(sslg_xmlresponse) {       "
-               "  sslg_response = sslg_xmlresponse.responseText.substr(0, 1);  "
-               "  if (sslg_response == 'Y') {  "
-               "   var oWin = window.open('static/instant_messenger.html', 'CTDL_MESSENGER',   "
-               "    'width=700,height=400');   "
-               "   if (oWin==null || typeof(oWin)==\"undefined\") {    "
-               "    PopUpFailed();     "
-               "   }   "
-               "  }    "
-               " }     "
-               " function CheckPager() {       "
-               "  new Ajax.Request('sslg', { method: 'get', parameters: Math.random(), "
-               "   onSuccess: HandleSslp } );  "
-               " }     "
-               " new PeriodicalExecuter(CheckPager, 30);       "
-               "</script>      "
-       );
-}
-
-
-
-/**
- * \brief Support function for chat
- * make sure the chat socket is connected
- * and in chat mode.
- */
-int setup_chat_socket(void) {
-       char buf[SIZ];
-       int i;
-       int good_chatmode = 0;
-
-       if (WC->chat_sock < 0) {
-
-               if (!strcasecmp(ctdlhost, "uds")) {
-                       /** unix domain socket */
-                       sprintf(buf, "%s/citadel.socket", ctdlport);
-                       WC->chat_sock = uds_connectsock(buf);
-               }
-               else {
-                       /** tcp socket */
-                       WC->chat_sock = tcp_connectsock(ctdlhost, ctdlport);
-               }
-
-               if (WC->chat_sock < 0) {
-                       return(errno);
-               }
-
-               /** Temporarily swap the serv and chat sockets during chat talk */
-               i = WC->serv_sock;
-               WC->serv_sock = WC->chat_sock;
-               WC->chat_sock = i;
-
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       serv_printf("USER %s", WC->wc_username);
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] == '3') {
-                               serv_printf("PASS %s", WC->wc_password);
-                               serv_getln(buf, sizeof buf);
-                               if (buf[0] == '2') {
-                                       serv_printf("GOTO %s", WC->wc_roomname);
-                                       serv_getln(buf, sizeof buf);
-                                       if (buf[0] == '2') {
-                                               serv_puts("CHAT");
-                                               serv_getln(buf, sizeof buf);
-                                               if (buf[0] == '8') {
-                                                       good_chatmode = 1;
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               /** Unswap the sockets. */
-               i = WC->serv_sock;
-               WC->serv_sock = WC->chat_sock;
-               WC->chat_sock = i;
-
-               if (!good_chatmode) close(WC->serv_sock);
-
-       }
-       return(0);
-}
-
-
-
-/**
- * \brief Receiving side of the chat window.  
- * This is implemented in a
- * tiny hidden IFRAME that just does JavaScript writes to
- * other frames whenever it refreshes and finds new data.
- */
-void chat_recv(void) {
-       int i;
-       struct pollfd pf;
-       int got_data = 0;
-       int end_chat_now = 0;
-       char buf[SIZ];
-       char cl_user[SIZ];
-       char cl_text[SIZ];
-       char *output_data = NULL;
-
-       output_headers(0, 0, 0, 0, 0, 0);
-
-       wprintf("Content-type: text/html; charset=utf-8\n");
-       wprintf("\n");
-       wprintf("<html>\n"
-               "<head>\n"
-               "<meta http-equiv=\"refresh\" content=\"3\" />\n"
-               "</head>\n"
-
-               "<body bgcolor=\"#FFFFFF\">\n"
-       );
-
-       if (setup_chat_socket() != 0) {
-               wprintf(_("An error occurred while setting up the chat socket."));
-               wprintf("</BODY></HTML>\n");
-               wDumpContent(0);
-               return;
-       }
-
-       /**
-        * See if there is any chat data waiting.
-        */
-       output_data = strdup("");
-       do {
-               got_data = 0;
-               pf.fd = WC->chat_sock;
-               pf.events = POLLIN;
-               pf.revents = 0;
-               if (poll(&pf, 1, 1) > 0) if (pf.revents & POLLIN) {
-                       ++got_data;
-
-                       /** Temporarily swap the serv and chat sockets during chat talk */
-                       i = WC->serv_sock;
-                       WC->serv_sock = WC->chat_sock;
-                       WC->chat_sock = i;
-       
-                       serv_getln(buf, sizeof buf);
-
-                       if (!strcmp(buf, "000")) {
-                               strcpy(buf, ":|");
-                               strcat(buf, _("Now exiting chat mode."));
-                               end_chat_now = 1;
-                       }
-                       
-                       /** Unswap the sockets. */
-                       i = WC->serv_sock;
-                       WC->serv_sock = WC->chat_sock;
-                       WC->chat_sock = i;
-
-                       /** Append our output data */
-                       output_data = realloc(output_data, strlen(output_data) + strlen(buf) + 4);
-                       strcat(output_data, buf);
-                       strcat(output_data, "\n");
-               }
-
-       } while ( (got_data) && (!end_chat_now) );
-
-       if (end_chat_now) {
-               close(WC->chat_sock);
-               WC->chat_sock = (-1);
-               wprintf("<img src=\"static/blank.gif\" onLoad=\"parent.window.close();\">\n");
-       }
-
-       if (strlen(output_data) > 0) {
-
-               if (output_data[strlen(output_data)-1] == '\n') {
-                       output_data[strlen(output_data)-1] = 0;
-               }
-
-               /** Output our fun to the other frame. */
-               wprintf("<img src=\"static/blank.gif\" WIDTH=1 HEIGHT=1\n"
-                       "onLoad=\" \n"
-               );
-
-               for (i=0; i<num_tokens(output_data, '\n'); ++i) {
-                       extract_token(buf, output_data, i, '\n', sizeof buf);
-                       extract_token(cl_user, buf, 0, '|', sizeof cl_user);
-                       extract_token(cl_text, buf, 1, '|', sizeof cl_text);
-
-                       if (strcasecmp(cl_text, "NOOP")) {
-
-                               wprintf("parent.chat_transcript.document.write('");
-       
-                               if (strcasecmp(cl_user, WC->last_chat_user)) {
-                                       wprintf("<TABLE border=0 WIDTH=100%% "
-                                               "CELLSPACING=1 CELLPADDING=0 "
-                                               "BGCOLOR=&quot;#FFFFFF&quot;>"
-                                               "<TR><TD></TR></TD></TABLE>"
-                                       );
-       
-                               }
-
-                               wprintf("<TABLE border=0 WIDTH=100%% "
-                                       "CELLSPACING=0 CELLPADDING=0 "
-                                       "BGCOLOR=&quot;#EEEEEE&quot;>");
-       
-                               wprintf("<TR><TD>");
-       
-                               if (!strcasecmp(cl_user, ":")) {
-                                       wprintf("<I>");
-                               }
-
-                               if (strcasecmp(cl_user, WC->last_chat_user)) {
-                                       wprintf("<B>");
-       
-                                       if (!strcasecmp(cl_user, WC->wc_fullname)) {
-                                               wprintf("<FONT COLOR=&quot;#FF0000&quot;>");
-                                       }
-                                       else {
-                                               wprintf("<FONT COLOR=&quot;#0000FF&quot;>");
-                                       }
-                                       jsescputs(cl_user);
-       
-                                       wprintf("</FONT>: </B>");
-                               }
-                               else {
-                                       wprintf("&nbsp;&nbsp;&nbsp;");
-                               }
-                               jsescputs(cl_text);
-                               if (!strcasecmp(cl_user, ":")) {
-                                       wprintf("</I>");
-                               }
-
-                               wprintf("</TD></TR></TABLE>");
-                               wprintf("'); \n");
-
-                               strcpy(WC->last_chat_user, cl_user);
-                       }
-               }
-
-               wprintf("parent.chat_transcript.scrollTo(999999,999999);\">\n");
-       }
-
-       free(output_data);
-
-       wprintf("</BODY></HTML>\n");
-       wDumpContent(0);
-}
-
-
-/**
- * \brief sending side of the chat window
- */
-void chat_send(void) {
-       int i;
-       char send_this[SIZ];
-       char buf[SIZ];
-
-       output_headers(0, 0, 0, 0, 0, 0);
-       wprintf("Content-type: text/html; charset=utf-8\n");
-       wprintf("\n");
-       wprintf("<HTML>"
-               "<BODY onLoad=\"document.chatsendform.send_this.focus();\" >"
-       );
-
-       if (bstr("send_this") != NULL) {
-               strcpy(send_this, bstr("send_this"));
-       }
-       else {
-               strcpy(send_this, "");
-       }
-
-       if (strlen(bstr("help_button")) > 0) {
-               strcpy(send_this, "/help");
-       }
-
-       if (strlen(bstr("list_button")) > 0) {
-               strcpy(send_this, "/who");
-       }
-
-       if (strlen(bstr("exit_button")) > 0) {
-               strcpy(send_this, "/quit");
-       }
-
-       if (setup_chat_socket() != 0) {
-               wprintf(_("An error occurred while setting up the chat socket."));
-               wprintf("</BODY></HTML>\n");
-               wDumpContent(0);
-               return;
-       }
-
-       /** Temporarily swap the serv and chat sockets during chat talk */
-       i = WC->serv_sock;
-       WC->serv_sock = WC->chat_sock;
-       WC->chat_sock = i;
-
-       while (strlen(send_this) > 0) {
-               if (strlen(send_this) < 67) {
-                       serv_puts(send_this);
-                       strcpy(send_this, "");
-               }
-               else {
-                       for (i=55; i<67; ++i) {
-                               if (send_this[i] == ' ') break;
-                       }
-                       strncpy(buf, send_this, i);
-                       buf[i] = 0;
-                       strcpy(send_this, &send_this[i]);
-                       serv_puts(buf);
-               }
-       }
-
-       /** Unswap the sockets. */
-       i = WC->serv_sock;
-       WC->serv_sock = WC->chat_sock;
-       WC->chat_sock = i;
-
-       wprintf("<FORM METHOD=\"POST\" action=\"chat_send\" NAME=\"chatsendform\">\n");
-       wprintf("<INPUT TYPE=\"text\" SIZE=\"80\" MAXLENGTH=\"%d\" "
-               "NAME=\"send_this\">\n", SIZ-10);
-       wprintf("<br />");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"send_button\" VALUE=\"%s\">\n", _("Send"));
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"help_button\" VALUE=\"%s\">\n", _("Help"));
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"list_button\" VALUE=\"%s\">\n", _("List users"));
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"exit_button\" VALUE=\"%s\">\n", _("Exit"));
-       wprintf("</FORM>\n");
-
-       wprintf("</BODY></HTML>\n");
-       wDumpContent(0);
-}
-
-/*@}*/
diff --git a/webcit/preferences.c b/webcit/preferences.c
deleted file mode 100644 (file)
index a21468c..0000000
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup ManagePrefs Manage user preferences with a little help from the Citadel server.
- * \ingroup CitadelConfig
- *
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-#include "groupdav.h"
-
-
-/**
- * \brief display preferences dialog
- */
-void load_preferences(void) {
-       char buf[SIZ];
-       long msgnum = 0L;
-
-       serv_printf("GOTO %s", USERCONFIGROOM);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') return;
-       
-       serv_puts("MSGS ALL|0|1");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '8') {
-               serv_puts("subj|__ WebCit Preferences __");
-               serv_puts("000");
-       }
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               msgnum = atol(buf);
-       }
-
-       if (msgnum > 0L) {
-               serv_printf("MSG0 %ld", msgnum);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '1') {
-                       while (serv_getln(buf, sizeof buf),
-                               (strcmp(buf, "text") && strcmp(buf, "000"))) {
-                       }
-                       if (!strcmp(buf, "text")) {
-                               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                                       if (WC->preferences == NULL) {
-                                               WC->preferences = malloc(SIZ);
-                                               strcpy(WC->preferences, "");
-                                       }
-                                       else {
-                                               WC->preferences = realloc(
-                                                       WC->preferences,
-                                                       strlen(WC->preferences)
-                                                       +SIZ
-                                               );
-                                       }
-                                       strcat(WC->preferences, buf);
-                                       strcat(WC->preferences, "\n");
-                               }
-                       }
-               }
-       }
-
-       /** Go back to the room we're supposed to be in */
-       serv_printf("GOTO %s", WC->wc_roomname);
-       serv_getln(buf, sizeof buf);
-}
-
-/**
- * \brief Goto the user's configuration room, creating it if necessary.
- * \return 0 on success or nonzero upon failure.
- */
-int goto_config_room(void) {
-       char buf[SIZ];
-
-       serv_printf("GOTO %s", USERCONFIGROOM);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') { /* try to create the config room if not there */
-               serv_printf("CRE8 1|%s|4|0", USERCONFIGROOM);
-               serv_getln(buf, sizeof buf);
-               serv_printf("GOTO %s", USERCONFIGROOM);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '2') return(1);
-       }
-       return(0);
-}
-
-/**
- * \brief save the modifications
- */
-void save_preferences(void) {
-       char buf[SIZ];
-       long msgnum = 0L;
-
-       if (goto_config_room() != 0) return;    /* oh well. */
-       serv_puts("MSGS ALL|0|1");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '8') {
-               serv_puts("subj|__ WebCit Preferences __");
-               serv_puts("000");
-       }
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               msgnum = atol(buf);
-       }
-
-       if (msgnum > 0L) {
-               serv_printf("DELE %ld", msgnum);
-               serv_getln(buf, sizeof buf);
-       }
-
-       serv_printf("ENT0 1||0|1|__ WebCit Preferences __|");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '4') {
-               serv_puts(WC->preferences);
-               serv_puts("");
-               serv_puts("000");
-       }
-
-       /** Go back to the room we're supposed to be in */
-       serv_printf("GOTO %s", WC->wc_roomname);
-       serv_getln(buf, sizeof buf);
-}
-
-/**
- * \brief query the actual setting of key in the citadel database
- * \param key config key to query
- * \param value value to the key to get
- * \param value_len length of the value string
- */
-void get_preference(char *key, char *value, size_t value_len) {
-       int num_prefs;
-       int i;
-       char buf[SIZ];
-       char thiskey[SIZ];
-
-       strcpy(value, "");
-
-       num_prefs = num_tokens(WC->preferences, '\n');
-       for (i=0; i<num_prefs; ++i) {
-               extract_token(buf, WC->preferences, i, '\n', sizeof buf);
-               extract_token(thiskey, buf, 0, '|', sizeof thiskey);
-               if (!strcasecmp(thiskey, key)) {
-                       extract_token(value, buf, 1, '|', value_len);
-               }
-       }
-}
-
-/**
- * \brief      Write a key into the webcit preferences database for this user
- *
- * \params     key             key whichs value is to be modified
- * \param      value           value to set
- * \param      save_to_server  1 = flush all data to the server, 0 = cache it for now
- */
-void set_preference(char *key, char *value, int save_to_server) {
-       int num_prefs;
-       int i;
-       char buf[SIZ];
-       char thiskey[SIZ];
-       char *newprefs = NULL;
-
-       num_prefs = num_tokens(WC->preferences, '\n');
-       for (i=0; i<num_prefs; ++i) {
-               extract_token(buf, WC->preferences, i, '\n', sizeof buf);
-               if (num_tokens(buf, '|') == 2) {
-                       extract_token(thiskey, buf, 0, '|', sizeof thiskey);
-                       if (strcasecmp(thiskey, key)) {
-                               if (newprefs == NULL) newprefs = strdup("");
-                               newprefs = realloc(newprefs,
-                                       strlen(newprefs) + SIZ );
-                               strcat(newprefs, buf);
-                               strcat(newprefs, "\n");
-                       }
-               }
-       }
-
-
-       if (newprefs == NULL) newprefs = strdup("");
-       newprefs = realloc(newprefs, strlen(newprefs) + SIZ);
-       sprintf(&newprefs[strlen(newprefs)], "%s|%s\n", key, value);
-
-       free(WC->preferences);
-       WC->preferences = newprefs;
-
-       if (save_to_server) save_preferences();
-}
-
-
-
-
-/** 
- * \brief display form for changing your preferences and settings
- */
-void display_preferences(void)
-{
-       output_headers(1, 1, 2, 0, 0, 0);
-       char ebuf[300];
-       char buf[256];
-       char calhourformat[16];
-       int i;
-
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
-       wprintf("<img src=\"static/advanpage2_48x.gif\" ALT=\" \" ALIGN=MIDDLE>");
-       wprintf("<SPAN CLASS=\"titlebar\">&nbsp;");
-       wprintf(_("Preferences and settings"));
-       wprintf("</SPAN></TD><TD ALIGN=RIGHT>");
-       offer_start_page();
-       wprintf("</TD></TR></TABLE>\n");
-       wprintf("</div>\n"
-               "<div id=\"content\">\n");
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       /** begin form */
-       wprintf("<center>\n"
-               "<form name=\"prefform\" action=\"set_preferences\" "
-               "method=\"post\">\n"
-               "<table border=0 cellspacing=5 cellpadding=5>\n");
-
-       /**
-        * Room list view
-        */
-       get_preference("roomlistview", buf, sizeof buf);
-       wprintf("<tr><td>");
-       wprintf(_("Room list view"));
-       wprintf("</td><td>");
-
-       wprintf("<input type=\"radio\" name=\"roomlistview\" VALUE=\"folders\"");
-       if (!strcasecmp(buf, "folders")) wprintf(" checked");
-       wprintf(">");
-       wprintf(_("Tree (folders) view"));
-       wprintf("<br></input>\n");
-
-       wprintf("<input type=\"radio\" name=\"roomlistview\" VALUE=\"rooms\"");
-       if (!strcasecmp(buf, "rooms")) wprintf(" checked");
-       wprintf(">");
-       wprintf(_("Table (rooms) view"));
-       wprintf("<br></input>\n");
-
-       wprintf("</td></tr>\n");
-
-       /**
-        * Calendar hour format
-        */
-       get_preference("calhourformat", calhourformat, sizeof calhourformat);
-       if (calhourformat[0] == 0) strcpy(calhourformat, "12");
-       wprintf("<tr><td>");
-       wprintf(_("Calendar hour format"));
-       wprintf("</td><td>");
-
-       wprintf("<input type=\"radio\" name=\"calhourformat\" VALUE=\"12\"");
-       if (!strcasecmp(calhourformat, "12")) wprintf(" checked");
-       wprintf(">");
-       wprintf(_("12 hour (am/pm)"));
-       wprintf("<br></input>\n");
-
-       wprintf("<input type=\"radio\" name=\"calhourformat\" VALUE=\"24\"");
-       if (!strcasecmp(calhourformat, "24")) wprintf(" checked");
-       wprintf(">");
-       wprintf(_("24 hour"));
-       wprintf("<br></input>\n");
-
-       wprintf("</td></tr>\n");
-
-       /**
-        * Calendar day view -- day start time
-        */
-       get_preference("daystart", buf, sizeof buf);
-       if (buf[0] == 0) strcpy(buf, "8");
-       wprintf("<tr><td>");
-       wprintf(_("Calendar day view begins at:"));
-       wprintf("</td><td>");
-
-       wprintf("<SELECT NAME=\"daystart\" SIZE=\"1\">\n");
-       for (i=0; i<=23; ++i) {
-
-               if (!strcasecmp(calhourformat, "24")) {
-                       wprintf("<OPTION %s VALUE=\"%d\">%d:00</OPTION>\n",
-                               ((atoi(buf) == i) ? "SELECTED" : ""),
-                               i, i
-                       );
-               }
-               else {
-                       wprintf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
-                               ((atoi(buf) == i) ? "SELECTED" : ""),
-                               i, hourname[i]
-                       );
-               }
-
-       }
-       wprintf("</SELECT>\n");
-       wprintf("</td></tr>\n");
-
-       /**
-        * Calendar day view -- day end time
-        */
-       get_preference("dayend", buf, sizeof buf);
-       if (buf[0] == 0) strcpy(buf, "17");
-       wprintf("<tr><td>");
-       wprintf(_("Calendar day view ends at:"));
-       wprintf("</td><td>");
-
-       wprintf("<SELECT NAME=\"dayend\" SIZE=\"1\">\n");
-       for (i=0; i<=23; ++i) {
-
-               if (!strcasecmp(calhourformat, "24")) {
-                       wprintf("<OPTION %s VALUE=\"%d\">%d:00</OPTION>\n",
-                               ((atoi(buf) == i) ? "SELECTED" : ""),
-                               i, i
-                       );
-               }
-               else {
-                       wprintf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
-                               ((atoi(buf) == i) ? "SELECTED" : ""),
-                               i, hourname[i]
-                       );
-               }
-
-       }
-       wprintf("</SELECT>\n");
-       wprintf("</td></tr>\n");
-
-       /**
-        * Signature
-        */
-       get_preference("use_sig", buf, sizeof buf);
-       if (buf[0] == 0) strcpy(buf, "no");
-       wprintf("<tr><td>");
-       wprintf(_("Attach signature to email messages?"));
-       wprintf("</td><td>");
-
-       wprintf("       <script type=\"text/javascript\">                                       "
-               "       function show_or_hide_sigbox() {                                        "
-               "               if ( $F('yes_sig') ) {                                          "
-               "                       $('signature_box').style.display = 'inline';            "
-               "               }                                                               "
-               "               else {                                                          "
-               "                       $('signature_box').style.display = 'none';              "
-               "               }                                                               "
-               "       }                                                                       "
-               "       </script>                                                               "
-       );
-
-       wprintf("<input type=\"radio\" id=\"no_sig\" name=\"use_sig\" VALUE=\"no\"");
-       if (!strcasecmp(buf, "no")) wprintf(" checked");
-       wprintf(" onChange=\"show_or_hide_sigbox();\" >");
-       wprintf(_("No signature"));
-       wprintf("<br></input>\n");
-
-       wprintf("<input type=\"radio\" id=\"yes_sig\" name=\"use_sig\" VALUE=\"yes\"");
-       if (!strcasecmp(buf, "yes")) wprintf(" checked");
-       wprintf(" onChange=\"show_or_hide_sigbox();\" >");
-       wprintf(_("Use this signature:"));
-       wprintf("<div id=\"signature_box\">"
-               "<br><textarea name=\"signature\" cols=\"40\" rows=\"5\">"
-       );
-       get_preference("signature", ebuf, sizeof ebuf);
-       euid_unescapize(buf, ebuf);
-       escputs(buf);
-       wprintf("</textarea>"
-               "</div>"
-       );
-
-       wprintf("<br></input>\n");
-
-       wprintf("</td></tr>\n");
-
-       wprintf("       <script type=\"text/javascript\">       "
-               "       show_or_hide_sigbox();                  "
-               "       </script>                               "
-       );
-
-       /** Character set to assume is in use for improperly encoded headers */
-       get_preference("default_header_charset", buf, sizeof buf);
-       if (buf[0] == 0) strcpy(buf, "UTF-8");
-       wprintf("<tr><td>");
-       wprintf(_("Default character set for email headers:"));
-       wprintf("</td><td>");
-       wprintf("<input type=\"text\" NAME=\"default_header_charset\" MAXLENGTH=\"32\" VALUE=\"%s\">", buf);
-       wprintf("</td></tr>");
-
-       /** submit buttons */
-       wprintf("</table>\n"
-               "<input type=\"submit\" name=\"change_button\" value=\"%s\">"
-               "&nbsp;"
-               "<INPUT type=\"submit\" name=\"cancel_button\" value=\"%s\">\n",
-               _("Change"),
-               _("Cancel")
-       );
-
-       /** end form */
-       wprintf("</form></center>\n");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-/**
- * \brief Commit new preferences and settings
- */
-void set_preferences(void)
-{
-       char ebuf[300];
-
-       if (strlen(bstr("change_button")) == 0) {
-               safestrncpy(WC->ImportantMessage, 
-                       _("Cancelled.  No settings were changed."),
-                       sizeof WC->ImportantMessage);
-               display_main_menu();
-               return;
-       }
-
-       /**
-        * Set the last argument to 1 only for the final setting, so
-        * we don't send the prefs file to the server repeatedly
-        */
-       set_preference("roomlistview", bstr("roomlistview"), 0);
-       set_preference("calhourformat", bstr("calhourformat"), 0);
-       set_preference("use_sig", bstr("use_sig"), 0);
-       set_preference("daystart", bstr("daystart"), 0);
-       set_preference("dayend", bstr("dayend"), 0);
-       set_preference("default_header_charset", bstr("default_header_charset"), 0);
-
-       euid_escapize(ebuf, bstr("signature"));
-       set_preference("signature", ebuf, 1);
-
-       display_main_menu();
-}
-
-
-/*@}*/
diff --git a/webcit/roomops.c b/webcit/roomops.c
deleted file mode 100644 (file)
index a7993a0..0000000
+++ /dev/null
@@ -1,3052 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup RoomOps Lots of different room-related operations.
- * \ingroup CitadelCommunitacion
- */
-/*@{*/
-#include "webcit.h"
-
-char floorlist[128][SIZ]; /**< list of our floor names */
-
-char *viewdefs[8]; /**< the different kinds of available views */
-
-/**
- * \brief initialize the viewdefs with localized strings
- */
-void initialize_viewdefs(void) {
-       viewdefs[0] = _("Bulletin Board");
-       viewdefs[1] = _("Mail Folder");
-       viewdefs[2] = _("Address Book");
-       viewdefs[3] = _("Calendar");
-       viewdefs[4] = _("Task List");
-       viewdefs[5] = _("Notes List");
-       viewdefs[6] = _("Wiki");
-       viewdefs[7] = _("Calendar List");
-}
-
-/**
- * \brief      Determine which views are allowed as the default for creating a new room.
- *
- * \param      which_view      The view ID being queried.
- */
-int is_view_allowed_as_default(int which_view)
-{
-       switch(which_view) {
-               case VIEW_BBS:          return(1);
-               case VIEW_MAILBOX:      return(1);
-               case VIEW_ADDRESSBOOK:  return(1);
-               case VIEW_CALENDAR:     return(1);
-               case VIEW_TASKS:        return(1);
-               case VIEW_NOTES:        return(1);
-               case VIEW_WIKI:         return(0);      /**< because it isn't finished yet */
-               case VIEW_CALBRIEF:     return(0);
-               default:                return(0);      /**< should never get here */
-       }
-}
-
-
-/**
- * \brief load the list of floors
- */
-void load_floorlist(void)
-{
-       int a;
-       char buf[SIZ];
-
-       for (a = 0; a < 128; ++a)
-               floorlist[a][0] = 0;
-
-       serv_puts("LFLR");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               strcpy(floorlist[0], "Main Floor");
-               return;
-       }
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               extract_token(floorlist[extract_int(buf, 0)], buf, 1, '|', sizeof floorlist[0]);
-       }
-}
-
-
-/**
- * \brief      Free a session's march list
- *
- * \param      wcf             Pointer to session being cleared
- */
-void free_march_list(struct wcsession *wcf)
-{
-       struct march *mptr;
-
-       while (wcf->march != NULL) {
-               mptr = wcf->march->next;
-               free(wcf->march);
-               wcf->march = mptr;
-       }
-
-}
-
-
-
-/**
- * \brief remove a room from the march list
- */
-void remove_march(char *aaa)
-{
-       struct march *mptr, *mptr2;
-
-       if (WC->march == NULL)
-               return;
-
-       if (!strcasecmp(WC->march->march_name, aaa)) {
-               mptr = WC->march->next;
-               free(WC->march);
-               WC->march = mptr;
-               return;
-       }
-       mptr2 = WC->march;
-       for (mptr = WC->march; mptr != NULL; mptr = mptr->next) {
-               if (!strcasecmp(mptr->march_name, aaa)) {
-                       mptr2->next = mptr->next;
-                       free(mptr);
-                       mptr = mptr2;
-               } else {
-                       mptr2 = mptr;
-               }
-       }
-}
-
-
-
-
-/**
- * \brief display rooms in tree structure???
- * \param rp the roomlist to build a tree from
- */
-void room_tree_list(struct roomlisting *rp)
-{
-       char rmname[64];
-       int f;
-
-       if (rp == NULL) {
-               return;
-       }
-
-       room_tree_list(rp->lnext);
-
-       strcpy(rmname, rp->rlname);
-       f = rp->rlflags;
-
-       wprintf("<a href=\"dotgoto&room=");
-       urlescputs(rmname);
-       wprintf("\"");
-       wprintf(">");
-       escputs1(rmname, 1, 1);
-       if ((f & QR_DIRECTORY) && (f & QR_NETWORK))
-               wprintf("}");
-       else if (f & QR_DIRECTORY)
-               wprintf("]");
-       else if (f & QR_NETWORK)
-               wprintf(")");
-       else
-               wprintf("&gt;");
-       wprintf("</A><TT> </TT>\n");
-
-       room_tree_list(rp->rnext);
-       free(rp);
-}
-
-
-/** 
- * \brief Room ordering stuff (compare first by floor, then by order)
- * \param r1 first roomlist to compare
- * \param r2 second roomlist co compare
- * \return are they the same???
- */
-int rordercmp(struct roomlisting *r1, struct roomlisting *r2)
-{
-       if ((r1 == NULL) && (r2 == NULL))
-               return (0);
-       if (r1 == NULL)
-               return (-1);
-       if (r2 == NULL)
-               return (1);
-       if (r1->rlfloor < r2->rlfloor)
-               return (-1);
-       if (r1->rlfloor > r2->rlfloor)
-               return (1);
-       if (r1->rlorder < r2->rlorder)
-               return (-1);
-       if (r1->rlorder > r2->rlorder)
-               return (1);
-       return (0);
-}
-
-
-/**
- * \brief Common code for all room listings
- * \param variety what???
- */
-void listrms(char *variety)
-{
-       char buf[SIZ];
-       int num_rooms = 0;
-
-       struct roomlisting *rl = NULL;
-       struct roomlisting *rp;
-       struct roomlisting *rs;
-
-
-       /** Ask the server for a room list */
-       serv_puts(variety);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               wprintf("&nbsp;");
-               return;
-       }
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               ++num_rooms;
-               rp = malloc(sizeof(struct roomlisting));
-               extract_token(rp->rlname, buf, 0, '|', sizeof rp->rlname);
-               rp->rlflags = extract_int(buf, 1);
-               rp->rlfloor = extract_int(buf, 2);
-               rp->rlorder = extract_int(buf, 3);
-               rp->lnext = NULL;
-               rp->rnext = NULL;
-
-               rs = rl;
-               if (rl == NULL) {
-                       rl = rp;
-               } else
-                       while (rp != NULL) {
-                               if (rordercmp(rp, rs) < 0) {
-                                       if (rs->lnext == NULL) {
-                                               rs->lnext = rp;
-                                               rp = NULL;
-                                       } else {
-                                               rs = rs->lnext;
-                                       }
-                               } else {
-                                       if (rs->rnext == NULL) {
-                                               rs->rnext = rp;
-                                               rp = NULL;
-                                       } else {
-                                               rs = rs->rnext;
-                                       }
-                               }
-                       }
-       }
-
-       room_tree_list(rl);
-
-       /**
-        * If no rooms were listed, print an nbsp to make the cell
-        * borders show up anyway.
-        */
-       if (num_rooms == 0) wprintf("&nbsp;");
-}
-
-
-/**
- * \brief list all forgotten rooms
- */
-void zapped_list(void)
-{
-       output_headers(1, 1, 0, 0, 0, 0);
-
-       svprintf("BOXTITLE", WCS_STRING, _("Zapped (forgotten) rooms"));
-       do_template("beginbox");
-
-       listrms("LZRM -1");
-
-       wprintf("<br /><br />\n");
-       wprintf(_("Click on any room to un-zap it and goto that room.\n"));
-       do_template("endbox");
-       wDumpContent(1);
-}
-
-
-/**
- * \brief read this room's info file (set v to 1 for verbose mode)
- */
-void readinfo(void)
-{
-       char buf[SIZ];
-
-       serv_puts("RINF");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               fmout("CENTER");
-       }
-       else {
-               wprintf("&nbsp;");
-       }
-}
-
-
-
-
-/**
- * \brief Display room banner icon.  
- * The server doesn't actually
- * need the room name, but we supply it in order to
- * keep the browser from using a cached icon from 
- * another room.
- */
-void embed_room_graphic(void) {
-       char buf[SIZ];
-
-       serv_puts("OIMG _roompic_");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               wprintf("<IMG HEIGHT=64 src=\"image&name=_roompic_&room=");
-               urlescputs(WC->wc_roomname);
-               wprintf("\">");
-               serv_puts("CLOS");
-               serv_getln(buf, sizeof buf);
-       }
-       else if (WC->wc_view == VIEW_ADDRESSBOOK) {
-               wprintf("<img height=48 width=48 src=\""
-                       "static/viewcontacts_48x.gif"
-                       "\">"
-               );
-       }
-       else if ( (WC->wc_view == VIEW_CALENDAR) || (WC->wc_view == VIEW_CALBRIEF) ) {
-               wprintf("<img height=48 width=48 src=\""
-                       "static/calarea_48x.gif"
-                       "\">"
-               );
-       }
-       else if (WC->wc_view == VIEW_TASKS) {
-               wprintf("<img height=48 width=48 src=\""
-                       "static/taskmanag_48x.gif"
-                       "\">"
-               );
-       }
-       else if (WC->wc_view == VIEW_NOTES) {
-               wprintf("<img height=48 width=48 src=\""
-                       "static/storenotes_48x.gif"
-                       "\">"
-               );
-       }
-       else if (WC->wc_view == VIEW_MAILBOX) {
-               wprintf("<img height=48 width=48 src=\""
-                       "static/privatemess_48x.gif"
-                       "\">"
-               );
-       }
-       else {
-               wprintf("<img height=48 width=48 src=\""
-                       "static/chatrooms_48x.gif"
-                       "\">"
-               );
-       }
-
-}
-
-
-
-/**
- * \brief Display the current view and offer an option to change it
- */
-void embed_view_o_matic(void) {
-       int i;
-
-       wprintf("<form name=\"viewomatic\" action=\"changeview\">\n"
-               "<span class=\"room_banner_new_messages\">");
-       wprintf(_("View as:"));
-       wprintf(" "
-               "<SELECT NAME=\"newview\" SIZE=\"1\" "
-               "STYLE=\"font-size: 7pt; background: #444455; color: #ddddcc;\" "
-               "OnChange=\"location.href=viewomatic.newview.options"
-               "[selectedIndex].value\">\n");
-
-       for (i=0; i<(sizeof viewdefs / sizeof (char *)); ++i) {
-               /**
-                * Only offer the views that make sense, given the default
-                * view for the room.  For example, don't offer a Calendar
-                * view in a non-Calendar room.
-                */
-               if (
-                       (i == WC->wc_view)
-                       ||      (i == WC->wc_default_view)                      /**< default */
-                       ||      ( (i == 0) && (WC->wc_default_view == 1) )      /**< mail or bulletin */
-                       ||      ( (i == 1) && (WC->wc_default_view == 0) )      /**< mail or bulletin */
-                       /** ||  ( (i == 7) && (WC->wc_default_view == 3) )      (calendar list temporarily disabled) */
-               ) {
-
-                       wprintf("<OPTION %s VALUE=\"changeview?view=%d\">",
-                               ((i == WC->wc_view) ? "SELECTED" : ""),
-                               i );
-                       escputs(viewdefs[i]);
-                       wprintf("</OPTION>\n");
-               }
-       }
-       wprintf("</select></span></form>\n");
-}
-
-
-/**
- * \brief view room banner
- * \param got what???
- * \param navbar_style
- */
-void embed_room_banner(char *got, int navbar_style) {
-       char buf[256];
-
-       /**
-        * We need to have the information returned by a GOTO server command.
-        * If it isn't supplied, we fake it by issuing our own GOTO.
-        */
-       if (got == NULL) {
-               serv_printf("GOTO %s", WC->wc_roomname);
-               serv_getln(buf, sizeof buf);
-               got = buf;
-       }
-
-       /** The browser needs some information for its own use */
-       wprintf("<script type=\"text/javascript\">      \n"
-               "       room_is_trash = %d;             \n"
-               "</script>\n",
-               WC->wc_is_trash
-       );
-
-       /**
-        * If the user happens to select the "make this my start page" link,
-        * we want it to remember the URL as a "/dotskip" one instead of
-        * a "skip" or "gotonext" or something like that.
-        */
-       snprintf(WC->this_page, sizeof(WC->this_page), "dotskip&room=%s",
-               WC->wc_roomname);
-
-       /** Check for new mail. */
-       WC->new_mail = extract_int(&got[4], 9);
-       WC->wc_view = extract_int(&got[4], 11);
-
-       svprintf("ROOMNAME", WCS_STRING, "%s", WC->wc_roomname);
-       svprintf("NUMMSGS", WCS_STRING,
-               _("%d new of %d messages"),
-               extract_int(&got[4], 1),
-               extract_int(&got[4], 2)
-       );
-       svcallback("ROOMPIC", embed_room_graphic);
-       svcallback("ROOMINFO", readinfo);
-       svcallback("VIEWOMATIC", embed_view_o_matic);
-       svcallback("START", offer_start_page);
-
-       do_template("roombanner");
-       if (navbar_style != navbar_none) {
-
-               wprintf("<div style=\"position:absolute; bottom:0px; left:0px\">\n"
-                       "<table width=\"100%%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\"><tr>\n");
-
-
-               if (navbar_style == navbar_default) wprintf(
-                       "<td>"
-                       "<a href=\"ungoto\">"
-                       "<img align=\"middle\" src=\"static/ungoto2_24x.gif\" border=\"0\">"
-                       "<span class=\"navbar_link\">%s</span></A>"
-                       "</td>\n", _("Ungoto")
-               );
-
-               if ( (navbar_style == navbar_default) && (WC->wc_view == VIEW_BBS) ) {
-                       wprintf(
-                               "<td>"
-                               "<a href=\"readnew\">"
-                               "<img align=\"middle\" src=\"static/newmess2_24x.gif\" border=\"0\">"
-                               "<span class=\"navbar_link\">%s</span></A>"
-                               "</td>\n", _("Read new messages")
-                       );
-               }
-
-               if (navbar_style == navbar_default) {
-                       switch(WC->wc_view) {
-                               case VIEW_ADDRESSBOOK:
-                                       wprintf(
-                                               "<td>"
-                                               "<a href=\"readfwd\">"
-                                               "<img align=\"middle\" src=\"static/viewcontacts_24x.gif\" "
-                                               "border=\"0\">"
-                                               "<span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("View contacts")
-                                       );
-                                       break;
-                               case VIEW_CALENDAR:
-                                       wprintf(
-                                               "<td>"
-                                               "<a href=\"readfwd?calview=day\">"
-                                               "<img align=\"middle\" src=\"static/taskday2_24x.gif\" "
-                                               "border=\"0\">"
-                                               "<span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Day view")
-                                       );
-                                       wprintf(
-                                               "<td>"
-                                               "<a href=\"readfwd?calview=month\">"
-                                               "<img align=\"middle\" src=\"static/monthview2_24x.gif\" "
-                                               "border=\"0\">"
-                                               "<span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Month view")
-                                       );
-                                       break;
-                               case VIEW_CALBRIEF:
-                                       wprintf(
-                                               "<td>"
-                                               "<a href=\"readfwd?calview=month\">"
-                                               "<img align=\"middle\" src=\"static/monthview2_24x.gif\" "
-                                               "border=\"0\">"
-                                               "<span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Calendar list")
-                                       );
-                                       break;
-                               case VIEW_TASKS:
-                                       wprintf(
-                                               "<td>"
-                                               "<a href=\"readfwd\">"
-                                               "<img align=\"middle\" src=\"static/taskmanag_24x.gif\" "
-                                               "border=\"0\">"
-                                               "<span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("View tasks")
-                                       );
-                                       break;
-                               case VIEW_NOTES:
-                                       wprintf(
-                                               "<td>"
-                                               "<a href=\"readfwd\">"
-                                               "<img align=\"middle\" src=\"static/viewnotes_24x.gif\" "
-                                               "border=\"0\">"
-                                               "<span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("View notes")
-                                       );
-                                       break;
-                               case VIEW_MAILBOX:
-                                       wprintf(
-                                               "<td>"
-                                               "<a href=\"readfwd\">"
-                                               "<img align=\"middle\" src=\"static/readallmess3_24x.gif\" "
-                                               "border=\"0\">"
-                                               "<span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("View message list")
-                                       );
-                                       break;
-                               case VIEW_WIKI:
-                                       wprintf(
-                                               "<td>"
-                                               "<a href=\"readfwd\">"
-                                               "<img align=\"middle\" src=\"static/readallmess3_24x.gif\" "
-                                               "border=\"0\">"
-                                               "<span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Wiki home")
-                                       );
-                                       break;
-                               default:
-                                       wprintf(
-                                               "<td>"
-                                               "<a href=\"readfwd\">"
-                                               "<img align=\"middle\" src=\"static/readallmess3_24x.gif\" "
-                                               "border=\"0\">"
-                                               "<span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Read all messages")
-                                       );
-                                       break;
-                       }
-               }
-
-               if (navbar_style == navbar_default) {
-                       switch(WC->wc_view) {
-                               case VIEW_ADDRESSBOOK:
-                                       wprintf(
-                                               "<td><a href=\"display_enter\">"
-                                               "<img align=\"middle\" src=\"static/addnewcontact_24x.gif\" "
-                                               "border=\"0\"><span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Add new contact")
-                                       );
-                                       break;
-                               case VIEW_CALENDAR:
-                               case VIEW_CALBRIEF:
-                                       wprintf(
-                                               "<td><a href=\"display_enter\">"
-                                               "<img align=\"middle\" src=\"static/addevent_24x.gif\" "
-                                               "border=\"0\"><span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Add new event")
-                                       );
-                                       break;
-                               case VIEW_TASKS:
-                                       wprintf(
-                                               "<td><a href=\"display_enter\">"
-                                               "<img align=\"middle\" src=\"static/newmess3_24x.gif\" "
-                                               "border=\"0\"><span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Add new task")
-                                       );
-                                       break;
-                               case VIEW_NOTES:
-                                       wprintf(
-                                               "<td><a href=\"javascript:add_new_note();\">"
-                                               "<img align=\"middle\" src=\"static/enternewnote_24x.gif\" "
-                                               "border=\"0\"><span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Add new note")
-                                       );
-                                       break;
-                               case VIEW_WIKI:
-                                       safestrncpy(buf, bstr("page"), sizeof buf);
-                                       str_wiki_index(buf);
-                                       wprintf(
-                                               "<td><a href=\"display_enter?wikipage=%s\">"
-                                               "<img align=\"middle\" src=\"static/newmess3_24x.gif\" "
-                                               "border=\"0\"><span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", buf, _("Edit this page")
-                                       );
-                                       break;
-                               default:
-                                       wprintf(
-                                               "<td><a href=\"display_enter\">"
-                                               "<img align=\"middle\" src=\"static/newmess3_24x.gif\" "
-                                               "border=\"0\"><span class=\"navbar_link\">"
-                                               "%s"
-                                               "</span></a></td>\n", _("Enter a message")
-                                       );
-                                       break;
-                       }
-               }
-
-               if (navbar_style == navbar_default) wprintf(
-                       "<td>"
-                       "<a href=\"skip\" "
-                       "TITLE=\"%s\">"
-                       "<img align=\"middle\" src=\"static/skipthisroom_24x.gif\" border=\"0\">"
-                       "<span class=\"navbar_link\">%s</span></a>"
-                       "</td>\n",
-                       _("Leave all messages marked as unread, go to next room with unread messages"),
-                       _("Skip this room")
-               );
-
-               if (navbar_style == navbar_default) wprintf(
-                       "<td>"
-                       "<a href=\"gotonext\" "
-                       "TITLE=\"%s\">"
-                       "<img align=\"middle\" src=\"static/markngo_24x.gif\" border=\"0\">"
-                       "<span class=\"navbar_link\">%s</span></a>"
-                       "</td>\n",
-                       _("Mark all messages as read, go to next room with unread messages"),
-                       _("Goto next room")
-               );
-
-               wprintf("</tr></table></div>\n");
-       }
-
-}
-
-
-
-
-
-/**
- * \brief back end routine to take the session to a new room
- * \param gname room to go to
- *
- */
-int gotoroom(char *gname)
-{
-       char buf[SIZ];
-       static long ls = (-1L);
-       int err = 0;
-
-       /** store ungoto information */
-       strcpy(WC->ugname, WC->wc_roomname);
-       WC->uglsn = ls;
-
-       /** move to the new room */
-       serv_printf("GOTO %s", gname);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               buf[3] = 0;
-               err = atoi(buf);
-               serv_puts("GOTO _BASEROOM_");
-               serv_getln(buf, sizeof buf);
-       }
-       if (buf[0] != '2') {
-               buf[3] = 0;
-               err = atoi(buf);
-               return err;
-       }
-       extract_token(WC->wc_roomname, &buf[4], 0, '|', sizeof WC->wc_roomname);
-       WC->room_flags = extract_int(&buf[4], 4);
-       /* highest_msg_read = extract_int(&buf[4],6);
-          maxmsgnum = extract_int(&buf[4],5);
-        */
-       WC->is_mailbox = extract_int(&buf[4],7);
-       ls = extract_long(&buf[4], 6);
-       WC->wc_floor = extract_int(&buf[4], 10);
-       WC->wc_view = extract_int(&buf[4], 11);
-       WC->wc_default_view = extract_int(&buf[4], 12);
-       WC->wc_is_trash = extract_int(&buf[4], 13);
-
-       if (WC->is_aide)
-               WC->is_room_aide = WC->is_aide;
-       else
-               WC->is_room_aide = (char) extract_int(&buf[4], 8);
-
-       remove_march(WC->wc_roomname);
-       if (!strcasecmp(gname, "_BASEROOM_"))
-               remove_march(gname);
-
-       return err;
-}
-
-
-/**
- * \brief Locate the room on the march list which we most want to go to.  
- * Each room
- * is measured given a "weight" of preference based on various factors.
- * \param desired_floor the room number on the citadel server
- * \return the roomname
- */
-char *pop_march(int desired_floor)
-{
-       static char TheRoom[128];
-       int TheFloor = 0;
-       int TheOrder = 32767;
-       int TheWeight = 0;
-       int weight;
-       struct march *mptr = NULL;
-
-       strcpy(TheRoom, "_BASEROOM_");
-       if (WC->march == NULL)
-               return (TheRoom);
-
-       for (mptr = WC->march; mptr != NULL; mptr = mptr->next) {
-               weight = 0;
-               if ((strcasecmp(mptr->march_name, "_BASEROOM_")))
-                       weight = weight + 10000;
-               if (mptr->march_floor == desired_floor)
-                       weight = weight + 5000;
-
-               weight = weight + ((128 - (mptr->march_floor)) * 128);
-               weight = weight + (128 - (mptr->march_order));
-
-               if (weight > TheWeight) {
-                       TheWeight = weight;
-                       strcpy(TheRoom, mptr->march_name);
-                       TheFloor = mptr->march_floor;
-                       TheOrder = mptr->march_order;
-               }
-       }
-       return (TheRoom);
-}
-
-
-
-/**
- *\brief Goto next room having unread messages.
- * We want to skip over rooms that the user has already been to, and take the
- * user back to the lobby when done.  The room we end up in is placed in
- * newroom - which is set to 0 (the lobby) initially.
- * We start the search in the current room rather than the beginning to prevent
- * two or more concurrent users from dragging each other back to the same room.
- */
-void gotonext(void)
-{
-       char buf[256];
-       struct march *mptr, *mptr2;
-       char next_room[128];
-
-       /**
-        * First check to see if the march-mode list is already allocated.
-        * If it is, pop the first room off the list and go there.
-        */
-
-       if (WC->march == NULL) {
-               serv_puts("LKRN");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '1')
-                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                               mptr = (struct march *) malloc(sizeof(struct march));
-                               mptr->next = NULL;
-                               extract_token(mptr->march_name, buf, 0, '|', sizeof mptr->march_name);
-                               mptr->march_floor = extract_int(buf, 2);
-                               mptr->march_order = extract_int(buf, 3);
-                               if (WC->march == NULL) {
-                                       WC->march = mptr;
-                               } else {
-                                       mptr2 = WC->march;
-                                       while (mptr2->next != NULL)
-                                               mptr2 = mptr2->next;
-                                       mptr2->next = mptr;
-                               }
-                       }
-               /**
-                * add _BASEROOM_ to the end of the march list, so the user will end up
-                * in the system base room (usually the Lobby>) at the end of the loop
-                */
-               mptr = (struct march *) malloc(sizeof(struct march));
-               mptr->next = NULL;
-               strcpy(mptr->march_name, "_BASEROOM_");
-               if (WC->march == NULL) {
-                       WC->march = mptr;
-               } else {
-                       mptr2 = WC->march;
-                       while (mptr2->next != NULL)
-                               mptr2 = mptr2->next;
-                       mptr2->next = mptr;
-               }
-               /**
-                * ...and remove the room we're currently in, so a <G>oto doesn't make us
-                * walk around in circles
-                */
-               remove_march(WC->wc_roomname);
-       }
-       if (WC->march != NULL) {
-               strcpy(next_room, pop_march(-1));
-       } else {
-               strcpy(next_room, "_BASEROOM_");
-       }
-
-
-       smart_goto(next_room);
-}
-
-
-/**
- * \brief goto next room
- * \param next_room next room to go to
- */
-void smart_goto(char *next_room) {
-       gotoroom(next_room);
-       readloop("readnew");
-}
-
-
-
-/**
- * \brief mark all messages in current room as having been read
- */
-void slrp_highest(void)
-{
-       char buf[256];
-
-       serv_puts("SLRP HIGHEST");
-       serv_getln(buf, sizeof buf);
-}
-
-
-/**
- * \brief un-goto the previous room
- */
-void ungoto(void)
-{
-       char buf[SIZ];
-
-       if (!strcmp(WC->ugname, "")) {
-               smart_goto(WC->wc_roomname);
-               return;
-       }
-       serv_printf("GOTO %s", WC->ugname);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               smart_goto(WC->wc_roomname);
-               return;
-       }
-       if (WC->uglsn >= 0L) {
-               serv_printf("SLRP %ld", WC->uglsn);
-               serv_getln(buf, sizeof buf);
-       }
-       strcpy(buf, WC->ugname);
-       strcpy(WC->ugname, "");
-       smart_goto(buf);
-}
-
-
-
-
-
-/**
- * \brief Set/clear/read the "self-service list subscribe" flag for a room
- * 
- * \param newval set to 0 to clear, 1 to set, any other value to leave unchanged.
- * \return return the new value.
- */
-
-int self_service(int newval) {
-       int current_value = 0;
-       char buf[SIZ];
-       
-       char name[SIZ];
-       char password[SIZ];
-       char dirname[SIZ];
-       int flags, floor, order, view, flags2;
-
-       serv_puts("GETR");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') return(0);
-
-       extract_token(name, &buf[4], 0, '|', sizeof name);
-       extract_token(password, &buf[4], 1, '|', sizeof password);
-       extract_token(dirname, &buf[4], 2, '|', sizeof dirname);
-       flags = extract_int(&buf[4], 3);
-       floor = extract_int(&buf[4], 4);
-       order = extract_int(&buf[4], 5);
-       view = extract_int(&buf[4], 6);
-       flags2 = extract_int(&buf[4], 7);
-
-       if (flags2 & QR2_SELFLIST) {
-               current_value = 1;
-       }
-       else {
-               current_value = 0;
-       }
-
-       if (newval == 1) {
-               flags2 = flags2 | QR2_SELFLIST;
-       }
-       else if (newval == 0) {
-               flags2 = flags2 & ~QR2_SELFLIST;
-       }
-       else {
-               return(current_value);
-       }
-
-       if (newval != current_value) {
-               serv_printf("SETR %s|%s|%s|%d|0|%d|%d|%d|%d",
-                       name, password, dirname, flags,
-                       floor, order, view, flags2);
-               serv_getln(buf, sizeof buf);
-       }
-
-       return(newval);
-
-}
-
-
-
-
-
-
-/**
- * \brief display the form for editing a room
- */
-void display_editroom(void)
-{
-       char buf[SIZ];
-       char cmd[SIZ];
-       char node[SIZ];
-       char remote_room[SIZ];
-       char recp[SIZ];
-       char er_name[128];
-       char er_password[10];
-       char er_dirname[15];
-       char er_roomaide[26];
-       unsigned er_flags;
-       int er_floor;
-       int i, j;
-       char *tab;
-       char *shared_with;
-       char *not_shared_with;
-       int roompolicy = 0;
-       int roomvalue = 0;
-       int floorpolicy = 0;
-       int floorvalue = 0;
-
-       tab = bstr("tab");
-       if (strlen(tab) == 0) tab = "admin";
-
-       load_floorlist();
-       serv_puts("GETR");
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] != '2') {
-               strcpy(WC->ImportantMessage, &buf[4]);
-               display_main_menu();
-               return;
-       }
-       extract_token(er_name, &buf[4], 0, '|', sizeof er_name);
-       extract_token(er_password, &buf[4], 1, '|', sizeof er_password);
-       extract_token(er_dirname, &buf[4], 2, '|', sizeof er_dirname);
-       er_flags = extract_int(&buf[4], 3);
-       er_floor = extract_int(&buf[4], 4);
-
-       output_headers(1, 1, 1, 0, 0, 0);
-
-       /** print the tabbed dialog */
-       wprintf("<br />"
-               "<div class=\"fix_scrollbar_bug\">"
-               "<TABLE border=0 cellspacing=0 cellpadding=0 width=100%%>"
-               "<TR ALIGN=CENTER>"
-               "<TD>&nbsp;</TD>\n");
-
-       if (!strcmp(tab, "admin")) {
-               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
-       }
-       else {
-               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=admin\">");
-       }
-       wprintf(_("Administration"));
-       if (!strcmp(tab, "admin")) {
-               wprintf("</SPAN></TD>\n");
-       }
-       else {
-               wprintf("</A></TD>\n");
-       }
-
-       wprintf("<TD>&nbsp;</TD>\n");
-
-       if (!strcmp(tab, "config")) {
-               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
-       }
-       else {
-               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=config\">");
-       }
-       wprintf(_("Configuration"));
-       if (!strcmp(tab, "config")) {
-               wprintf("</SPAN></TD>\n");
-       }
-       else {
-               wprintf("</A></TD>\n");
-       }
-
-       wprintf("<TD>&nbsp;</TD>\n");
-
-       if (!strcmp(tab, "expire")) {
-               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
-       }
-       else {
-               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=expire\">");
-       }
-       wprintf(_("Message expire policy"));
-       if (!strcmp(tab, "expire")) {
-               wprintf("</SPAN></TD>\n");
-       }
-       else {
-               wprintf("</A></TD>\n");
-       }
-
-       wprintf("<TD>&nbsp;</TD>\n");
-
-       if (!strcmp(tab, "access")) {
-               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
-       }
-       else {
-               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=access\">");
-       }
-       wprintf(_("Access controls"));
-       if (!strcmp(tab, "access")) {
-               wprintf("</SPAN></TD>\n");
-       }
-       else {
-               wprintf("</A></TD>\n");
-       }
-
-       wprintf("<TD>&nbsp;</TD>\n");
-
-       if (!strcmp(tab, "sharing")) {
-               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
-       }
-       else {
-               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=sharing\">");
-       }
-       wprintf(_("Sharing"));
-       if (!strcmp(tab, "sharing")) {
-               wprintf("</SPAN></TD>\n");
-       }
-       else {
-               wprintf("</A></TD>\n");
-       }
-
-       wprintf("<TD>&nbsp;</TD>\n");
-
-       if (!strcmp(tab, "listserv")) {
-               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
-       }
-       else {
-               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=listserv\">");
-       }
-       wprintf(_("Mailing list service"));
-       if (!strcmp(tab, "listserv")) {
-               wprintf("</SPAN></TD>\n");
-       }
-       else {
-               wprintf("</A></TD>\n");
-       }
-
-       wprintf("<TD>&nbsp;</TD>\n");
-
-       wprintf("</TR></TABLE></div>\n");
-       /** end tabbed dialog */        
-
-       /** begin content of whatever tab is open now */
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<TABLE border=0 width=100%% bgcolor=\"#FFFFFF\">\n"
-               "<TR><TD>\n");
-
-       if (!strcmp(tab, "admin")) {
-               wprintf("<UL>"
-                       "<LI><a href=\"delete_room\" "
-                       "onClick=\"return confirm('");
-               wprintf(_("Are you sure you want to delete this room?"));
-               wprintf("');\">\n");
-               wprintf(_("Delete this room"));
-               wprintf("</A>\n"
-                       "<LI><a href=\"display_editroompic\">\n");
-               wprintf(_("Set or change the icon for this room's banner"));
-               wprintf("</A>\n"
-                       "<LI><a href=\"display_editinfo\">\n");
-               wprintf(_("Edit this room's Info file"));
-               wprintf("</A>\n"
-                       "</UL>");
-       }
-
-       if (!strcmp(tab, "config")) {
-               wprintf("<FORM METHOD=\"POST\" action=\"editroom\">\n");
-       
-               wprintf("<UL><LI>");
-               wprintf(_("Name of room: "));
-               wprintf("<INPUT TYPE=\"text\" NAME=\"er_name\" VALUE=\"%s\" MAXLENGTH=\"%d\">\n",
-                       er_name,
-                       (sizeof(er_name)-1)
-               );
-       
-               wprintf("<LI>");
-               wprintf(_("Resides on floor: "));
-               wprintf("<SELECT NAME=\"er_floor\" SIZE=\"1\">\n");
-               for (i = 0; i < 128; ++i)
-                       if (strlen(floorlist[i]) > 0) {
-                               wprintf("<OPTION ");
-                               if (i == er_floor)
-                                       wprintf("SELECTED ");
-                               wprintf("VALUE=\"%d\">", i);
-                               escputs(floorlist[i]);
-                               wprintf("</OPTION>\n");
-                       }
-               wprintf("</SELECT>\n");
-       
-               wprintf("<LI>");
-               wprintf(_("Type of room:"));
-               wprintf("<UL>\n");
-
-               wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"public\" ");
-               if ((er_flags & QR_PRIVATE) == 0)
-               wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Public room"));
-               wprintf("\n");
-
-               wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"hidden\" ");
-               if ((er_flags & QR_PRIVATE) &&
-                   (er_flags & QR_GUESSNAME))
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Private - guess name"));
-       
-               wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"passworded\" ");
-               if ((er_flags & QR_PRIVATE) &&
-                   (er_flags & QR_PASSWORDED))
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Private - require password:"));
-               wprintf("\n<INPUT TYPE=\"text\" NAME=\"er_password\" VALUE=\"%s\" MAXLENGTH=\"9\">\n",
-                       er_password);
-       
-               wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"invonly\" ");
-               if ((er_flags & QR_PRIVATE)
-                   && ((er_flags & QR_GUESSNAME) == 0)
-                   && ((er_flags & QR_PASSWORDED) == 0))
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Private - invitation only"));
-       
-               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"bump\" VALUE=\"yes\" ");
-               wprintf("> ");
-               wprintf(_("If private, cause current users to forget room"));
-       
-               wprintf("\n</UL>\n");
-       
-               wprintf("<LI><INPUT TYPE=\"checkbox\" NAME=\"prefonly\" VALUE=\"yes\" ");
-               if (er_flags & QR_PREFONLY)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Preferred users only"));
-       
-               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"readonly\" VALUE=\"yes\" ");
-               if (er_flags & QR_READONLY)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Read-only room"));
-       
-               /** directory stuff */
-               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"directory\" VALUE=\"yes\" ");
-               if (er_flags & QR_DIRECTORY)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("File directory room"));
-
-               wprintf("\n<UL><LI>");
-               wprintf(_("Directory name: "));
-               wprintf("<INPUT TYPE=\"text\" NAME=\"er_dirname\" VALUE=\"%s\" MAXLENGTH=\"14\">\n",
-                       er_dirname);
-
-               wprintf("<LI><INPUT TYPE=\"checkbox\" NAME=\"ulallowed\" VALUE=\"yes\" ");
-               if (er_flags & QR_UPLOAD)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Uploading allowed"));
-       
-               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"dlallowed\" VALUE=\"yes\" ");
-               if (er_flags & QR_DOWNLOAD)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Downloading allowed"));
-       
-               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"visdir\" VALUE=\"yes\" ");
-               if (er_flags & QR_VISDIR)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Visible directory"));
-               wprintf("</UL>\n");
-       
-               /** end of directory stuff */
-       
-               wprintf("<LI><INPUT TYPE=\"checkbox\" NAME=\"network\" VALUE=\"yes\" ");
-               if (er_flags & QR_NETWORK)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Network shared room"));
-
-               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"permanent\" VALUE=\"yes\" ");
-               if (er_flags & QR_PERMANENT)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Permanent (does not auto-purge)"));
-
-               /** start of anon options */
-       
-               wprintf("\n<LI>");
-               wprintf(_("Anonymous messages"));
-               wprintf("<UL>\n");
-       
-               wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"anon\" VALUE=\"no\" ");
-               if (((er_flags & QR_ANONONLY) == 0)
-                   && ((er_flags & QR_ANONOPT) == 0))
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("No anonymous messages"));
-       
-               wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"anon\" VALUE=\"anononly\" ");
-               if (er_flags & QR_ANONONLY)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("All messages are anonymous"));
-       
-               wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"anon\" VALUE=\"anon2\" ");
-               if (er_flags & QR_ANONOPT)
-                       wprintf("CHECKED ");
-               wprintf("> ");
-               wprintf(_("Prompt user when entering messages"));
-               wprintf("</UL>\n");
-       
-       /* end of anon options */
-       
-               wprintf("<LI>");
-               wprintf(_("Room aide: "));
-               serv_puts("GETA");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '2') {
-                       wprintf("<em>%s</em>\n", &buf[4]);
-               } else {
-                       extract_token(er_roomaide, &buf[4], 0, '|', sizeof er_roomaide);
-                       wprintf("<INPUT TYPE=\"text\" NAME=\"er_roomaide\" VALUE=\"%s\" MAXLENGTH=\"25\">\n", er_roomaide);
-               }
-       
-               wprintf("</UL><CENTER>\n");
-               wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"config\">\n"
-                       "<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">"
-                       "&nbsp;"
-                       "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">"
-                       "</CENTER>\n",
-                       _("Save changes"),
-                       _("Cancel")
-               );
-       }
-
-
-       /** Sharing the room with other Citadel nodes... */
-       if (!strcmp(tab, "sharing")) {
-
-               shared_with = strdup("");
-               not_shared_with = strdup("");
-
-               /** Learn the current configuration */
-               serv_puts("CONF getsys|application/x-citadel-ignet-config");
-               serv_getln(buf, sizeof buf);
-               if (buf[0]=='1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       extract_token(node, buf, 0, '|', sizeof node);
-                       not_shared_with = realloc(not_shared_with,
-                                       strlen(not_shared_with) + 32);
-                       strcat(not_shared_with, node);
-                       strcat(not_shared_with, "\n");
-               }
-
-               serv_puts("GNET");
-               serv_getln(buf, sizeof buf);
-               if (buf[0]=='1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       extract_token(cmd, buf, 0, '|', sizeof cmd);
-                       extract_token(node, buf, 1, '|', sizeof node);
-                       extract_token(remote_room, buf, 2, '|', sizeof remote_room);
-                       if (!strcasecmp(cmd, "ignet_push_share")) {
-                               shared_with = realloc(shared_with,
-                                               strlen(shared_with) + 32);
-                               strcat(shared_with, node);
-                               if (strlen(remote_room) > 0) {
-                                       strcat(shared_with, "|");
-                                       strcat(shared_with, remote_room);
-                               }
-                               strcat(shared_with, "\n");
-                       }
-               }
-
-               for (i=0; i<num_tokens(shared_with, '\n'); ++i) {
-                       extract_token(buf, shared_with, i, '\n', sizeof buf);
-                       extract_token(node, buf, 0, '|', sizeof node);
-                       for (j=0; j<num_tokens(not_shared_with, '\n'); ++j) {
-                               extract_token(cmd, not_shared_with, j, '\n', sizeof cmd);
-                               if (!strcasecmp(node, cmd)) {
-                                       remove_token(not_shared_with, j, '\n');
-                               }
-                       }
-               }
-
-               /** Display the stuff */
-               wprintf("<CENTER><br />"
-                       "<TABLE border=1 cellpadding=5><TR>"
-                       "<TD><B><I>");
-               wprintf(_("Shared with"));
-               wprintf("</I></B></TD>"
-                       "<TD><B><I>");
-               wprintf(_("Not shared with"));
-               wprintf("</I></B></TD></TR>\n"
-                       "<TR><TD VALIGN=TOP>\n");
-
-               wprintf("<TABLE border=0 cellpadding=5><TR BGCOLOR=\"#CCCCCC\"><TD>");
-               wprintf(_("Remote node name"));
-               wprintf("</TD><TD>");
-               wprintf(_("Remote room name"));
-               wprintf("</TD><TD>");
-               wprintf(_("Actions"));
-               wprintf("</TD></TR>\n");
-
-               for (i=0; i<num_tokens(shared_with, '\n'); ++i) {
-                       extract_token(buf, shared_with, i, '\n', sizeof buf);
-                       extract_token(node, buf, 0, '|', sizeof node);
-                       extract_token(remote_room, buf, 1, '|', sizeof remote_room);
-                       if (strlen(node) > 0) {
-                               wprintf("<FORM METHOD=\"POST\" "
-                                       "action=\"netedit\">"
-                                       "<TR><TD>%s</TD>\n", node);
-
-                               wprintf("<TD>");
-                               if (strlen(remote_room) > 0) {
-                                       escputs(remote_room);
-                               }
-                               wprintf("</TD>");
-
-                               wprintf("<TD>");
-               
-                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"line\" "
-                                       "VALUE=\"ignet_push_share|");
-                               urlescputs(node);
-                               if (strlen(remote_room) > 0) {
-                                       wprintf("|");
-                                       urlescputs(remote_room);
-                               }
-                               wprintf("\">");
-                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" "
-                                       "VALUE=\"sharing\">\n");
-                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"cmd\" "
-                                       "VALUE=\"remove\">\n");
-                               wprintf("<INPUT TYPE=\"submit\" "
-                                       "NAME=\"unshare_button\" VALUE=\"%s\">", _("Unshare"));
-                               wprintf("</TD></TR></FORM>\n");
-                       }
-               }
-
-               wprintf("</TABLE>\n");
-               wprintf("</TD><TD VALIGN=TOP>\n");
-               wprintf("<TABLE border=0 cellpadding=5><TR BGCOLOR=\"#CCCCCC\"><TD>");
-               wprintf(_("Remote node name"));
-               wprintf("</TD><TD>");
-               wprintf(_("Remote room name"));
-               wprintf("</TD><TD>");
-               wprintf(_("Actions"));
-               wprintf("</TD></TR>\n");
-
-               for (i=0; i<num_tokens(not_shared_with, '\n'); ++i) {
-                       extract_token(node, not_shared_with, i, '\n', sizeof node);
-                       if (strlen(node) > 0) {
-                               wprintf("<FORM METHOD=\"POST\" "
-                                       "action=\"netedit\">"
-                                       "<TR><TD>");
-                               escputs(node);
-                               wprintf("</TD><TD>"
-                                       "<INPUT TYPE=\"INPUT\" "
-                                       "NAME=\"suffix\" "
-                                       "MAXLENGTH=128>"
-                                       "</TD><TD>");
-                               wprintf("<INPUT TYPE=\"hidden\" "
-                                       "NAME=\"line\" "
-                                       "VALUE=\"ignet_push_share|");
-                               urlescputs(node);
-                               wprintf("|\">");
-                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" "
-                                       "VALUE=\"sharing\">\n");
-                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"cmd\" "
-                                       "VALUE=\"add\">\n");
-                               wprintf("<INPUT TYPE=\"submit\" "
-                                       "NAME=\"add_button\" VALUE=\"%s\">", _("Share"));
-                               wprintf("</TD></TR></FORM>\n");
-                       }
-               }
-
-               wprintf("</TABLE>\n");
-               wprintf("</TD></TR>"
-                       "</TABLE></CENTER><br />\n"
-                       "<I><B>%s</B><UL><LI>", _("Notes:"));
-               wprintf(_("When sharing a room, "
-                       "it must be shared from both ends.  Adding a node to "
-                       "the 'shared' list sends messages out, but in order to"
-                       " receive messages, the other nodes must be configured"
-                       " to send messages out to your system as well. "
-                       "<LI>If the remote room name is blank, it is assumed "
-                       "that the room name is identical on the remote node."
-                       "<LI>If the remote room name is different, the remote "
-                       "node must also configure the name of the room here."
-                       "</UL></I><br />\n"
-               ));
-
-       }
-
-       /** Mailing list management */
-       if (!strcmp(tab, "listserv")) {
-
-               wprintf("<br /><center>"
-                       "<TABLE BORDER=0 WIDTH=100%% CELLPADDING=5>"
-                       "<TR><TD VALIGN=TOP>");
-
-               wprintf(_("<i>The contents of this room are being "
-                       "mailed <b>as individual messages</b> "
-                       "to the following list recipients:"
-                       "</i><br /><br />\n"));
-
-               serv_puts("GNET");
-               serv_getln(buf, sizeof buf);
-               if (buf[0]=='1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       extract_token(cmd, buf, 0, '|', sizeof cmd);
-                       if (!strcasecmp(cmd, "listrecp")) {
-                               extract_token(recp, buf, 1, '|', sizeof recp);
-                       
-                               escputs(recp);
-                               wprintf(" <a href=\"netedit&cmd=remove&line="
-                                       "listrecp|");
-                               urlescputs(recp);
-                               wprintf("&tab=listserv\">");
-                               wprintf(_("(remove)"));
-                               wprintf("</A><br />");
-                       }
-               }
-               wprintf("<br /><FORM METHOD=\"POST\" action=\"netedit\">\n"
-                       "<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"listserv\">\n"
-                       "<INPUT TYPE=\"hidden\" NAME=\"prefix\" VALUE=\"listrecp|\">\n");
-               wprintf("<INPUT TYPE=\"text\" NAME=\"line\">\n");
-               wprintf("<INPUT TYPE=\"submit\" NAME=\"add_button\" VALUE=\"%s\">", _("Add"));
-               wprintf("</FORM>\n");
-
-               wprintf("</TD><TD VALIGN=TOP>\n");
-               
-               wprintf(_("<i>The contents of this room are being "
-                       "mailed <b>in digest form</b> "
-                       "to the following list recipients:"
-                       "</i><br /><br />\n"));
-
-               serv_puts("GNET");
-               serv_getln(buf, sizeof buf);
-               if (buf[0]=='1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       extract_token(cmd, buf, 0, '|', sizeof cmd);
-                       if (!strcasecmp(cmd, "digestrecp")) {
-                               extract_token(recp, buf, 1, '|', sizeof recp);
-                       
-                               escputs(recp);
-                               wprintf(" <a href=\"netedit&cmd=remove&line="
-                                       "digestrecp|");
-                               urlescputs(recp);
-                               wprintf("&tab=listserv\">");
-                               wprintf(_("(remove)"));
-                               wprintf("</A><br />");
-                       }
-               }
-               wprintf("<br /><FORM METHOD=\"POST\" action=\"netedit\">\n"
-                       "<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"listserv\">\n"
-                       "<INPUT TYPE=\"hidden\" NAME=\"prefix\" VALUE=\"digestrecp|\">\n");
-               wprintf("<INPUT TYPE=\"text\" NAME=\"line\">\n");
-               wprintf("<INPUT TYPE=\"submit\" NAME=\"add_button\" VALUE=\"%s\">", _("Add"));
-               wprintf("</FORM>\n");
-               
-               wprintf("</TD></TR></TABLE><hr />\n");
-
-               if (self_service(999) == 1) {
-                       wprintf(_("This room is configured to allow "
-                               "self-service subscribe/unsubscribe requests."));
-                       wprintf("<a href=\"toggle_self_service?newval=0&tab=listserv\">");
-                       wprintf(_("Click to disable."));
-                       wprintf("</A><br />\n");
-                       wprintf(_("The URL for subscribe/unsubscribe is: "));
-                       wprintf("<TT>%s://%s/listsub</TT><br />\n",
-                               (is_https ? "https" : "http"),
-                               WC->http_host);
-               }
-               else {
-                       wprintf(_("This room is <i>not</i> configured to allow "
-                               "self-service subscribe/unsubscribe requests."));
-                       wprintf(" <a href=\"toggle_self_service?newval=1&"
-                               "tab=listserv\">");
-                       wprintf(_("Click to enable."));
-                       wprintf("</A><br />\n");
-               }
-
-
-               wprintf("</CENTER>\n");
-       }
-
-
-       /** Mailing list management */
-       if (!strcmp(tab, "expire")) {
-
-               serv_puts("GPEX room");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       roompolicy = extract_int(&buf[4], 0);
-                       roomvalue = extract_int(&buf[4], 1);
-               }
-               
-               serv_puts("GPEX floor");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       floorpolicy = extract_int(&buf[4], 0);
-                       floorvalue = extract_int(&buf[4], 1);
-               }
-               
-               wprintf("<br /><FORM METHOD=\"POST\" action=\"set_room_policy\">\n");
-               wprintf("<TABLE border=0 cellspacing=5>\n");
-               wprintf("<TR><TD>");
-               wprintf(_("Message expire policy for this room"));
-               wprintf("<br />(");
-               escputs(WC->wc_roomname);
-               wprintf(")</TD><TD>");
-               wprintf("<INPUT TYPE=\"radio\" NAME=\"roompolicy\" VALUE=\"0\" %s>",
-                       ((roompolicy == 0) ? "CHECKED" : "") );
-               wprintf(_("Use the default policy for this floor"));
-               wprintf("<br />\n");
-               wprintf("<INPUT TYPE=\"radio\" NAME=\"roompolicy\" VALUE=\"1\" %s>",
-                       ((roompolicy == 1) ? "CHECKED" : "") );
-               wprintf(_("Never automatically expire messages"));
-               wprintf("<br />\n");
-               wprintf("<INPUT TYPE=\"radio\" NAME=\"roompolicy\" VALUE=\"2\" %s>",
-                       ((roompolicy == 2) ? "CHECKED" : "") );
-               wprintf(_("Expire by message count"));
-               wprintf("<br />\n");
-               wprintf("<INPUT TYPE=\"radio\" NAME=\"roompolicy\" VALUE=\"3\" %s>",
-                       ((roompolicy == 3) ? "CHECKED" : "") );
-               wprintf(_("Expire by message age"));
-               wprintf("<br />");
-               wprintf(_("Number of messages or days: "));
-               wprintf("<INPUT TYPE=\"text\" NAME=\"roomvalue\" MAXLENGTH=\"5\" VALUE=\"%d\">", roomvalue);
-               wprintf("</TD></TR>\n");
-
-               if (WC->axlevel >= 6) {
-                       wprintf("<TR><TD COLSPAN=2><hr /></TD></TR>\n");
-                       wprintf("<TR><TD>");
-                       wprintf(_("Message expire policy for this floor"));
-                       wprintf("<br />(");
-                       escputs(floorlist[WC->wc_floor]);
-                       wprintf(")</TD><TD>");
-                       wprintf("<INPUT TYPE=\"radio\" NAME=\"floorpolicy\" VALUE=\"0\" %s>",
-                               ((floorpolicy == 0) ? "CHECKED" : "") );
-                       wprintf(_("Use the system default"));
-                       wprintf("<br />\n");
-                       wprintf("<INPUT TYPE=\"radio\" NAME=\"floorpolicy\" VALUE=\"1\" %s>",
-                               ((floorpolicy == 1) ? "CHECKED" : "") );
-                       wprintf(_("Never automatically expire messages"));
-                       wprintf("<br />\n");
-                       wprintf("<INPUT TYPE=\"radio\" NAME=\"floorpolicy\" VALUE=\"2\" %s>",
-                               ((floorpolicy == 2) ? "CHECKED" : "") );
-                       wprintf(_("Expire by message count"));
-                       wprintf("<br />\n");
-                       wprintf("<INPUT TYPE=\"radio\" NAME=\"floorpolicy\" VALUE=\"3\" %s>",
-                               ((floorpolicy == 3) ? "CHECKED" : "") );
-                       wprintf(_("Expire by message age"));
-                       wprintf("<br />");
-                       wprintf(_("Number of messages or days: "));
-                       wprintf("<INPUT TYPE=\"text\" NAME=\"floorvalue\" MAXLENGTH=\"5\" VALUE=\"%d\">",
-                               floorvalue);
-               }
-
-               wprintf("<CENTER>\n");
-               wprintf("<TR><TD COLSPAN=2><hr /><CENTER>\n");
-               wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Save changes"));
-               wprintf("&nbsp;");
-               wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
-               wprintf("</CENTER></TD><TR>\n");
-
-               wprintf("</TABLE>\n"
-                       "<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"expire\">\n"
-                       "</FORM>\n"
-               );
-
-       }
-
-       /** Mailing list management */
-       if (!strcmp(tab, "access")) {
-               display_whok();
-       }
-
-       /** end content of whatever tab is open now */
-       wprintf("</TD></TR></TABLE></div>\n");
-
-       wDumpContent(1);
-}
-
-
-/** 
- * \brief Toggle self-service list subscription
- */
-void toggle_self_service(void) {
-       int newval = 0;
-
-       newval = atoi(bstr("newval"));
-       self_service(newval);
-       display_editroom();
-}
-
-
-
-/**
- * \brief save new parameters for a room
- */
-void editroom(void)
-{
-       char buf[SIZ];
-       char er_name[128];
-       char er_password[10];
-       char er_dirname[15];
-       char er_roomaide[26];
-       int er_floor;
-       unsigned er_flags;
-       int bump;
-
-
-       if (strlen(bstr("ok_button")) == 0) {
-               strcpy(WC->ImportantMessage,
-                       _("Cancelled.  Changes were not saved."));
-               display_editroom();
-               return;
-       }
-       serv_puts("GETR");
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] != '2') {
-               strcpy(WC->ImportantMessage, &buf[4]);
-               display_editroom();
-               return;
-       }
-       extract_token(er_name, &buf[4], 0, '|', sizeof er_name);
-       extract_token(er_password, &buf[4], 1, '|', sizeof er_password);
-       extract_token(er_dirname, &buf[4], 2, '|', sizeof er_dirname);
-       er_flags = extract_int(&buf[4], 3);
-
-       strcpy(er_roomaide, bstr("er_roomaide"));
-       if (strlen(er_roomaide) == 0) {
-               serv_puts("GETA");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '2') {
-                       strcpy(er_roomaide, "");
-               } else {
-                       extract_token(er_roomaide, &buf[4], 0, '|', sizeof er_roomaide);
-               }
-       }
-       strcpy(buf, bstr("er_name"));
-       buf[128] = 0;
-       if (strlen(buf) > 0) {
-               strcpy(er_name, buf);
-       }
-
-       strcpy(buf, bstr("er_password"));
-       buf[10] = 0;
-       if (strlen(buf) > 0)
-               strcpy(er_password, buf);
-
-       strcpy(buf, bstr("er_dirname"));
-       buf[15] = 0;
-       if (strlen(buf) > 0)
-               strcpy(er_dirname, buf);
-
-       strcpy(buf, bstr("type"));
-       er_flags &= !(QR_PRIVATE | QR_PASSWORDED | QR_GUESSNAME);
-
-       if (!strcmp(buf, "invonly")) {
-               er_flags |= (QR_PRIVATE);
-       }
-       if (!strcmp(buf, "hidden")) {
-               er_flags |= (QR_PRIVATE | QR_GUESSNAME);
-       }
-       if (!strcmp(buf, "passworded")) {
-               er_flags |= (QR_PRIVATE | QR_PASSWORDED);
-       }
-       if (!strcmp(bstr("prefonly"), "yes")) {
-               er_flags |= QR_PREFONLY;
-       } else {
-               er_flags &= ~QR_PREFONLY;
-       }
-
-       if (!strcmp(bstr("readonly"), "yes")) {
-               er_flags |= QR_READONLY;
-       } else {
-               er_flags &= ~QR_READONLY;
-       }
-
-       if (!strcmp(bstr("permanent"), "yes")) {
-               er_flags |= QR_PERMANENT;
-       } else {
-               er_flags &= ~QR_PERMANENT;
-       }
-
-       if (!strcmp(bstr("network"), "yes")) {
-               er_flags |= QR_NETWORK;
-       } else {
-               er_flags &= ~QR_NETWORK;
-       }
-
-       if (!strcmp(bstr("directory"), "yes")) {
-               er_flags |= QR_DIRECTORY;
-       } else {
-               er_flags &= ~QR_DIRECTORY;
-       }
-
-       if (!strcmp(bstr("ulallowed"), "yes")) {
-               er_flags |= QR_UPLOAD;
-       } else {
-               er_flags &= ~QR_UPLOAD;
-       }
-
-       if (!strcmp(bstr("dlallowed"), "yes")) {
-               er_flags |= QR_DOWNLOAD;
-       } else {
-               er_flags &= ~QR_DOWNLOAD;
-       }
-
-       if (!strcmp(bstr("visdir"), "yes")) {
-               er_flags |= QR_VISDIR;
-       } else {
-               er_flags &= ~QR_VISDIR;
-       }
-
-       strcpy(buf, bstr("anon"));
-
-       er_flags &= ~(QR_ANONONLY | QR_ANONOPT);
-       if (!strcmp(buf, "anononly"))
-               er_flags |= QR_ANONONLY;
-       if (!strcmp(buf, "anon2"))
-               er_flags |= QR_ANONOPT;
-
-       bump = 0;
-       if (!strcmp(bstr("bump"), "yes"))
-               bump = 1;
-
-       er_floor = atoi(bstr("er_floor"));
-
-       sprintf(buf, "SETR %s|%s|%s|%u|%d|%d",
-            er_name, er_password, er_dirname, er_flags, bump, er_floor);
-       serv_puts(buf);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               strcpy(WC->ImportantMessage, &buf[4]);
-               display_editroom();
-               return;
-       }
-       gotoroom(er_name);
-
-       if (strlen(er_roomaide) > 0) {
-               sprintf(buf, "SETA %s", er_roomaide);
-               serv_puts(buf);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '2') {
-                       strcpy(WC->ImportantMessage, &buf[4]);
-                       display_main_menu();
-                       return;
-               }
-       }
-       gotoroom(er_name);
-       strcpy(WC->ImportantMessage, _("Your changes have been saved."));
-       display_editroom();
-       return;
-}
-
-
-/**
- * \brief Display form for Invite, Kick, and show Who Knows a room
- */
-void do_invt_kick(void) {
-        char buf[SIZ], room[SIZ], username[SIZ];
-
-        serv_puts("GETR");
-        serv_getln(buf, sizeof buf);
-
-        if (buf[0] != '2') {
-               escputs(&buf[4]);
-               return;
-        }
-        extract_token(room, &buf[4], 0, '|', sizeof room);
-
-        strcpy(username, bstr("username"));
-
-        if (strlen(bstr("kick_button")) > 0) {
-                sprintf(buf, "KICK %s", username);
-                serv_puts(buf);
-                serv_getln(buf, sizeof buf);
-
-                if (buf[0] != '2') {
-                        strcpy(WC->ImportantMessage, &buf[4]);
-                } else {
-                        sprintf(WC->ImportantMessage,
-                               _("<B><I>User %s kicked out of room %s.</I></B>\n"), 
-                                username, room);
-                }
-        }
-
-       if (strlen(bstr("invite_button")) > 0) {
-                sprintf(buf, "INVT %s", username);
-                serv_puts(buf);
-                serv_getln(buf, sizeof buf);
-
-                if (buf[0] != '2') {
-                        strcpy(WC->ImportantMessage, &buf[4]);
-                } else {
-                        sprintf(WC->ImportantMessage,
-                               _("<B><I>User %s invited to room %s.</I></B>\n"), 
-                                username, room);
-                }
-        }
-
-       display_editroom();
-}
-
-
-
-/**
- * \brief Display form for Invite, Kick, and show Who Knows a room
- */
-void display_whok(void)
-{
-        char buf[SIZ], room[SIZ], username[SIZ];
-
-        serv_puts("GETR");
-        serv_getln(buf, sizeof buf);
-
-        if (buf[0] != '2') {
-               escputs(&buf[4]);
-               return;
-        }
-        extract_token(room, &buf[4], 0, '|', sizeof room);
-
-        
-       wprintf("<TABLE border=0 CELLSPACING=10><TR VALIGN=TOP><TD>");
-       wprintf(_("The users listed below have access to this room.  "
-               "To remove a user from the access list, select the user "
-               "name from the list and click 'Kick'."));
-       wprintf("<br /><br />");
-       
-        wprintf("<CENTER><FORM METHOD=\"POST\" action=\"do_invt_kick\">\n");
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"access\">\n");
-        wprintf("<SELECT NAME=\"username\" SIZE=\"10\" style=\"width:100%%\">\n");
-        serv_puts("WHOK");
-        serv_getln(buf, sizeof buf);
-        if (buf[0] == '1') {
-                while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                        extract_token(username, buf, 0, '|', sizeof username);
-                        wprintf("<OPTION>");
-                        escputs(username);
-                        wprintf("\n");
-                }
-        }
-        wprintf("</SELECT><br />\n");
-
-        wprintf("<input type=\"submit\" name=\"kick_button\" value=\"%s\">", _("Kick"));
-        wprintf("</FORM></CENTER>\n");
-
-       wprintf("</TD><TD>");
-       wprintf(_("To grant another user access to this room, enter the "
-               "user name in the box below and click 'Invite'."));
-       wprintf("<br /><br />");
-
-        wprintf("<CENTER><FORM METHOD=\"POST\" action=\"do_invt_kick\">\n");
-       wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"access\">\n");
-        wprintf(_("Invite:"));
-       wprintf(" ");
-        wprintf("<input type=\"text\" name=\"username\" style=\"width:100%%\"><br />\n"
-               "<input type=\"hidden\" name=\"invite_button\" value=\"Invite\">"
-               "<input type=\"submit\" value=\"%s\">"
-               "</FORM></CENTER>\n", _("Invite"));
-
-       wprintf("</TD></TR></TABLE>\n");
-        wDumpContent(1);
-}
-
-
-
-/**
- * \brief display the form for entering a new room
- */
-void display_entroom(void)
-{
-       int i;
-       char buf[SIZ];
-
-       serv_puts("CRE8 0");
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] != '2') {
-               strcpy(WC->ImportantMessage, &buf[4]);
-               display_main_menu();
-               return;
-       }
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Create a new room"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       wprintf("<form name=\"create_room_form\" method=\"POST\" action=\"entroom\">\n");
-
-       wprintf("<UL><LI>");
-       wprintf(_("Name of room: "));
-       wprintf("<INPUT TYPE=\"text\" NAME=\"er_name\" MAXLENGTH=\"127\">\n");
-
-        wprintf("<LI>");
-       wprintf(_("Resides on floor: "));
-        load_floorlist(); 
-        wprintf("<SELECT NAME=\"er_floor\" SIZE=\"1\">\n");
-        for (i = 0; i < 128; ++i)
-                if (strlen(floorlist[i]) > 0) {
-                        wprintf("<OPTION ");
-                        wprintf("VALUE=\"%d\">", i);
-                        escputs(floorlist[i]);
-                        wprintf("</OPTION>\n");
-                }
-        wprintf("</SELECT>\n");
-
-               /**
-                * Our clever little snippet of JavaScript automatically selects
-                * a public room if the view is set to Bulletin Board or wiki, and
-                * it selects a mailbox room otherwise.  The user can override this,
-                * of course.  We also disable the floor selector for mailboxes.
-                */
-               wprintf("<LI>");
-               wprintf(_("Default view for room: "));
-        wprintf("<SELECT NAME=\"er_view\" SIZE=\"1\" OnChange=\""
-               "       if ( (this.form.er_view.value == 0)             "
-               "          || (this.form.er_view.value == 6) ) {        "
-               "               this.form.type[0].checked=true;         "
-               "               this.form.er_floor.disabled = false;    "
-               "       }                                               "
-               "       else {                                          "
-               "               this.form.type[4].checked=true;         "
-               "               this.form.er_floor.disabled = true;     "
-               "       }                                               "
-               "\">\n");
-       for (i=0; i<(sizeof viewdefs / sizeof (char *)); ++i) {
-               if (is_view_allowed_as_default(i)) {
-                       wprintf("<OPTION %s VALUE=\"%d\">",
-                               ((i == 0) ? "SELECTED" : ""), i );
-                       escputs(viewdefs[i]);
-                       wprintf("</OPTION>\n");
-               }
-       }
-       wprintf("</SELECT>\n");
-
-       wprintf("<LI>");
-       wprintf(_("Type of room:"));
-       wprintf("<UL>\n");
-
-       wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"public\" ");
-       wprintf("CHECKED OnChange=\""
-               "       if (this.form.type[0].checked == true) {        "
-               "               this.form.er_floor.disabled = false;    "
-               "       }                                               "
-               "\"> ");
-       wprintf(_("Public (automatically appears to everyone)"));
-
-       wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"hidden\" OnChange=\""
-               "       if (this.form.type[1].checked == true) {        "
-               "               this.form.er_floor.disabled = false;    "
-               "       }                                               "
-               "\"> ");
-       wprintf(_("Private - hidden (accessible to anyone who knows its name)"));
-
-       wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"passworded\" OnChange=\""
-               "       if (this.form.type[2].checked == true) {        "
-               "               this.form.er_floor.disabled = false;    "
-               "       }                                               "
-               "\"> ");
-       wprintf(_("Private - require password: "));
-       wprintf("<INPUT TYPE=\"text\" NAME=\"er_password\" MAXLENGTH=\"9\">\n");
-
-       wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"invonly\" OnChange=\""
-               "       if (this.form.type[3].checked == true) {        "
-               "               this.form.er_floor.disabled = false;    "
-               "       }                                               "
-               "\"> ");
-       wprintf(_("Private - invitation only"));
-
-       wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"personal\" "
-               "OnChange=\""
-               "       if (this.form.type[4].checked == true) {        "
-               "               this.form.er_floor.disabled = true;     "
-               "       }                                               "
-               "\"> ");
-       wprintf(_("Personal (mailbox for you only)"));
-
-       wprintf("\n</UL>\n");
-
-       wprintf("<CENTER>\n");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Create new room"));
-       wprintf("&nbsp;");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
-       wprintf("</CENTER>\n");
-       wprintf("</FORM>\n<hr />");
-       serv_printf("MESG roomaccess");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               fmout("CENTER");
-       }
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-
-
-
-/**
- * \brief support function for entroom() -- sets the default view 
- */
-void er_set_default_view(int newview) {
-
-       char buf[SIZ];
-
-       char rm_name[SIZ];
-       char rm_pass[SIZ];
-       char rm_dir[SIZ];
-       int rm_bits1;
-       int rm_floor;
-       int rm_listorder;
-       int rm_bits2;
-
-       serv_puts("GETR");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') return;
-
-       extract_token(rm_name, &buf[4], 0, '|', sizeof rm_name);
-       extract_token(rm_pass, &buf[4], 1, '|', sizeof rm_pass);
-       extract_token(rm_dir, &buf[4], 2, '|', sizeof rm_dir);
-       rm_bits1 = extract_int(&buf[4], 3);
-       rm_floor = extract_int(&buf[4], 4);
-       rm_listorder = extract_int(&buf[4], 5);
-       rm_bits2 = extract_int(&buf[4], 7);
-
-       serv_printf("SETR %s|%s|%s|%d|0|%d|%d|%d|%d",
-               rm_name, rm_pass, rm_dir, rm_bits1, rm_floor,
-               rm_listorder, newview, rm_bits2
-       );
-       serv_getln(buf, sizeof buf);
-}
-
-
-
-/**
- * \brief enter a new room
- */
-void entroom(void)
-{
-       char buf[SIZ];
-       char er_name[SIZ];
-       char er_type[SIZ];
-       char er_password[SIZ];
-       int er_floor;
-       int er_num_type;
-       int er_view;
-
-       if (strlen(bstr("ok_button")) == 0) {
-               strcpy(WC->ImportantMessage,
-                       _("Cancelled.  No new room was created."));
-               display_main_menu();
-               return;
-       }
-       strcpy(er_name, bstr("er_name"));
-       strcpy(er_type, bstr("type"));
-       strcpy(er_password, bstr("er_password"));
-       er_floor = atoi(bstr("er_floor"));
-       er_view = atoi(bstr("er_view"));
-
-       er_num_type = 0;
-       if (!strcmp(er_type, "hidden"))
-               er_num_type = 1;
-       if (!strcmp(er_type, "passworded"))
-               er_num_type = 2;
-       if (!strcmp(er_type, "invonly"))
-               er_num_type = 3;
-       if (!strcmp(er_type, "personal"))
-               er_num_type = 4;
-
-       sprintf(buf, "CRE8 1|%s|%d|%s|%d|%d|%d", 
-               er_name, er_num_type, er_password, er_floor, 0, er_view);
-       serv_puts(buf);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               strcpy(WC->ImportantMessage, &buf[4]);
-               display_main_menu();
-               return;
-       }
-       gotoroom(er_name);
-       do_change_view(er_view);                /* Now go there */
-}
-
-
-/**
- * \brief display the screen to enter a private room
- */
-void display_private(char *rname, int req_pass)
-{
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Go to a hidden room"));
-       wprintf("</SPAN>"
-               "</TD></TR></TABLE>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       wprintf("<CENTER>\n");
-       wprintf("<br />");
-       wprintf(_("If you know the name of a hidden (guess-name) or "
-               "passworded room, you can enter that room by typing "
-               "its name below.  Once you gain access to a private "
-               "room, it will appear in your regular room listings "
-               "so you don't have to keep returning here."));
-       wprintf("\n<br /><br />");
-
-       wprintf("<FORM METHOD=\"POST\" action=\"goto_private\">\n");
-
-       wprintf("<table border=\"0\" cellspacing=\"5\" "
-               "cellpadding=\"5\" BGCOLOR=\"#EEEEEE\">\n"
-               "<TR><TD>");
-       wprintf(_("Enter room name:"));
-       wprintf("</TD><TD>"
-               "<INPUT TYPE=\"text\" NAME=\"gr_name\" "
-               "VALUE=\"%s\" MAXLENGTH=\"128\">\n", rname);
-
-       if (req_pass) {
-               wprintf("</TD></TR><TR><TD>");
-               wprintf(_("Enter room password:"));
-               wprintf("</TD><TD>");
-               wprintf("<INPUT TYPE=\"password\" NAME=\"gr_pass\" MAXLENGTH=\"9\">\n");
-       }
-       wprintf("</TD></TR></TABLE><br />\n");
-
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">"
-               "&nbsp;"
-               "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">",
-               _("Go there"),
-               _("Cancel")
-       );
-       wprintf("</FORM>\n");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-/**
- * \brief goto a private room
- */
-void goto_private(void)
-{
-       char hold_rm[SIZ];
-       char buf[SIZ];
-
-       if (strlen(bstr("ok_button")) == 0) {
-               display_main_menu();
-               return;
-       }
-       strcpy(hold_rm, WC->wc_roomname);
-       strcpy(buf, "GOTO ");
-       strcat(buf, bstr("gr_name"));
-       strcat(buf, "|");
-       strcat(buf, bstr("gr_pass"));
-       serv_puts(buf);
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] == '2') {
-               smart_goto(bstr("gr_name"));
-               return;
-       }
-       if (!strncmp(buf, "540", 3)) {
-               display_private(bstr("gr_name"), 1);
-               return;
-       }
-       output_headers(1, 1, 1, 0, 0, 0);
-       wprintf("%s\n", &buf[4]);
-       wDumpContent(1);
-       return;
-}
-
-
-/**
- * \brief display the screen to zap a room
- */
-void display_zap(void)
-{
-       output_headers(1, 1, 2, 0, 0, 0);
-
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#770000\"><TR><TD>");
-       wprintf("<SPAN CLASS=\"titlebar\">");
-       wprintf(_("Zap (forget/unsubscribe) the current room"));
-       wprintf("</SPAN>\n");
-       wprintf("</TD></TR></TABLE>\n");
-       wprintf("</div>\n<div id=\"content\">\n");
-
-       wprintf(_("If you select this option, <em>%s</em> will "
-               "disappear from your room list.  Is this what you wish "
-               "to do?<br />\n"), WC->wc_roomname);
-
-       wprintf("<FORM METHOD=\"POST\" action=\"zap\">\n");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Zap this room"));
-       wprintf("&nbsp;");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
-       wprintf("</FORM>\n");
-       wDumpContent(1);
-}
-
-
-/**
- * \brief zap a room
- */
-void zap(void)
-{
-       char buf[SIZ];
-       char final_destination[SIZ];
-
-       /**
-        * If the forget-room routine fails for any reason, we fall back
-        * to the current room; otherwise, we go to the Lobby
-        */
-       strcpy(final_destination, WC->wc_roomname);
-
-       if (strlen(bstr("ok_button")) > 0) {
-               serv_printf("GOTO %s", WC->wc_roomname);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       serv_puts("FORG");
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] == '2') {
-                               strcpy(final_destination, "_BASEROOM_");
-                       }
-               }
-       }
-       smart_goto(final_destination);
-}
-
-
-
-/**
- * \brief Delete the current room
- */
-void delete_room(void)
-{
-       char buf[SIZ];
-
-       serv_puts("KILL 1");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               strcpy(WC->ImportantMessage, &buf[4]);
-               display_main_menu();
-               return;
-       } else {
-               smart_goto("_BASEROOM_");
-       }
-}
-
-
-
-/**
- * \brief Perform changes to a room's network configuration
- */
-void netedit(void) {
-       FILE *fp;
-       char buf[SIZ];
-       char line[SIZ];
-       char cmpa0[SIZ];
-       char cmpa1[SIZ];
-       char cmpb0[SIZ];
-       char cmpb1[SIZ];
-
-       if (strlen(bstr("line"))==0) {
-               display_editroom();
-               return;
-       }
-
-       strcpy(line, bstr("prefix"));
-       strcat(line, bstr("line"));
-       strcat(line, bstr("suffix"));
-
-       fp = tmpfile();
-       if (fp == NULL) {
-               display_editroom();
-               return;
-       }
-
-       serv_puts("GNET");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               fclose(fp);
-               display_editroom();
-               return;
-       }
-
-       /** This loop works for add *or* remove.  Spiffy, eh? */
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               extract_token(cmpa0, buf, 0, '|', sizeof cmpa0);
-               extract_token(cmpa1, buf, 1, '|', sizeof cmpa1);
-               extract_token(cmpb0, line, 0, '|', sizeof cmpb0);
-               extract_token(cmpb1, line, 1, '|', sizeof cmpb1);
-               if ( (strcasecmp(cmpa0, cmpb0)) 
-                  || (strcasecmp(cmpa1, cmpb1)) ) {
-                       fprintf(fp, "%s\n", buf);
-               }
-       }
-
-       rewind(fp);
-       serv_puts("SNET");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '4') {
-               fclose(fp);
-               display_editroom();
-               return;
-       }
-
-       while (fgets(buf, sizeof buf, fp) != NULL) {
-               buf[strlen(buf)-1] = 0;
-               serv_puts(buf);
-       }
-
-       if (strlen(bstr("add_button")) > 0) {
-               serv_puts(line);
-       }
-
-       serv_puts("000");
-       fclose(fp);
-       display_editroom();
-}
-
-
-
-/**
- * \brief Convert a room name to a folder-ish-looking name.
- * \param folder the folderish name
- * \param room the room name
- * \param floor the floor name
- * \param is_mailbox is it a mailbox?
- */
-void room_to_folder(char *folder, char *room, int floor, int is_mailbox)
-{
-       int i;
-
-       /**
-        * For mailboxes, just do it straight...
-        */
-       if (is_mailbox) {
-               sprintf(folder, "My folders|%s", room);
-       }
-
-       /**
-        * Otherwise, prefix the floor name as a "public folders" moniker
-        */
-       else {
-               sprintf(folder, "%s|%s", floorlist[floor], room);
-       }
-
-       /**
-        * Replace "\" characters with "|" for pseudo-folder-delimiting
-        */
-       for (i=0; i<strlen(folder); ++i) {
-               if (folder[i] == '\\') folder[i] = '|';
-       }
-}
-
-
-
-
-/**
- * \brief Back end for change_view()
- * \param newview set newview???
- */
-void do_change_view(int newview) {
-       char buf[SIZ];
-
-       serv_printf("VIEW %d", newview);
-       serv_getln(buf, sizeof buf);
-       WC->wc_view = newview;
-       smart_goto(WC->wc_roomname);
-}
-
-
-
-/**
- * \brief Change the view for this room
- */
-void change_view(void) {
-       int view;
-
-       view = atol(bstr("view"));
-       do_change_view(view);
-}
-
-
-/**
- * \brief One big expanded tree list view --- like a folder list
- * \param fold the folder to view
- * \param max_folders how many folders???
- * \param num_floors hom many floors???
- */
-void do_folder_view(struct folder *fold, int max_folders, int num_floors) {
-       char buf[SIZ];
-       int levels;
-       int i;
-       int has_subfolders = 0;
-       int *parents;
-
-       parents = malloc(max_folders * sizeof(int));
-
-       /** BEGIN TREE MENU */
-       wprintf("<div id=\"roomlist_div\">Loading folder list...</div>\n");
-
-       /** include NanoTree */
-       wprintf("<script type=\"text/javascript\" src=\"static/nanotree.js\"></script>\n");
-
-       /** initialize NanoTree */
-       wprintf("<script type=\"text/javascript\">                      \n"
-               "       showRootNode = false;                           \n"
-               "       sortNodes = false;                              \n"
-               "       dragable = false;                               \n"
-               "                                                       \n"
-               "       function standardClick(treeNode) {              \n"
-               "       }                                               \n"
-               "                                                       \n"
-               "       var closedGif = 'static/folder_closed.gif';     \n"
-               "       var openGif = 'static/folder_open.gif';         \n"
-               "                                                       \n"
-               "       rootNode = new TreeNode(1, 'root node - hide'); \n"
-       );
-
-       levels = 0;
-       for (i=0; i<max_folders; ++i) {
-
-               has_subfolders = 0;
-               if ((i+1) < max_folders) {
-                       if ( (!strncasecmp(fold[i].name, fold[i+1].name, strlen(fold[i].name)))
-                          && (fold[i+1].name[strlen(fold[i].name)] == '|') ) {
-                               has_subfolders = 1;
-                       }
-               }
-
-               levels = num_tokens(fold[i].name, '|');
-               parents[levels] = i;
-
-               wprintf("var node%d = new TreeNode(%d, '", i, i);
-
-               if (fold[i].selectable) {
-                       wprintf("<a href=\"dotgoto?room=");
-                       urlescputs(fold[i].room);
-                       wprintf("\">");
-               }
-
-               if (levels == 1) {
-                       wprintf("<SPAN CLASS=\"roomlist_floor\">");
-               }
-               else if (fold[i].hasnewmsgs) {
-                       wprintf("<SPAN CLASS=\"roomlist_new\">");
-               }
-               else {
-                       wprintf("<SPAN CLASS=\"roomlist_old\">");
-               }
-               extract_token(buf, fold[i].name, levels-1, '|', sizeof buf);
-               escputs(buf);
-               wprintf("</SPAN>");
-
-               wprintf("</a>', ");
-               if (has_subfolders) {
-                       wprintf("new Array(closedGif, openGif)");
-               }
-               else if (fold[i].view == VIEW_ADDRESSBOOK) {
-                       wprintf("'static/viewcontacts_16x.gif'");
-               }
-               else if (fold[i].view == VIEW_CALENDAR) {
-                       wprintf("'static/calarea_16x.gif'");
-               }
-               else if (fold[i].view == VIEW_CALBRIEF) {
-                       wprintf("'static/calarea_16x.gif'");
-               }
-               else if (fold[i].view == VIEW_TASKS) {
-                       wprintf("'static/taskmanag_16x.gif'");
-               }
-               else if (fold[i].view == VIEW_NOTES) {
-                       wprintf("'static/storenotes_16x.gif'");
-               }
-               else if (fold[i].view == VIEW_MAILBOX) {
-                       wprintf("'static/privatemess_16x.gif'");
-               }
-               else {
-                       wprintf("'static/chatrooms_16x.gif'");
-               }
-               wprintf(", '");
-               urlescputs(fold[i].name);
-               wprintf("');\n");
-
-               if (levels < 2) {
-                       wprintf("rootNode.addChild(node%d);\n", i);
-               }
-               else {
-                       wprintf("node%d.addChild(node%d);\n", parents[levels-1], i);
-               }
-       }
-
-       wprintf("container = document.getElementById('roomlist_div');   \n"
-               "showTree('');  \n"
-               "</script>\n"
-       );
-
-       free(parents);
-       /** END TREE MENU */
-}
-
-/**
- * \brief Boxes and rooms and lists ... oh my!
- * \param fold the folder to view
- * \param max_folders how many folders???
- * \param num_floors hom many floors???
- */
-void do_rooms_view(struct folder *fold, int max_folders, int num_floors) {
-       char buf[256];
-       char floor_name[256];
-       char old_floor_name[256];
-       char boxtitle[256];
-       int levels, oldlevels;
-       int i, t;
-       int num_boxes = 0;
-       static int columns = 3;
-       int boxes_per_column = 0;
-       int current_column = 0;
-       int nf;
-
-       strcpy(floor_name, "");
-       strcpy(old_floor_name, "");
-
-       nf = num_floors;
-       while (nf % columns != 0) ++nf;
-       boxes_per_column = (nf / columns);
-       if (boxes_per_column < 1) boxes_per_column = 1;
-
-       /** Outer table (for columnization) */
-       wprintf("<TABLE BORDER=0 WIDTH=96%% CELLPADDING=5>"
-               "<tr><td valign=top>");
-
-       levels = 0;
-       oldlevels = 0;
-       for (i=0; i<max_folders; ++i) {
-
-               levels = num_tokens(fold[i].name, '|');
-               extract_token(floor_name, fold[i].name, 0,
-                       '|', sizeof floor_name);
-
-               if ( (strcasecmp(floor_name, old_floor_name))
-                  && (strlen(old_floor_name) > 0) ) {
-                       /* End inner box */
-                       do_template("endbox");
-
-                       ++num_boxes;
-                       if ((num_boxes % boxes_per_column) == 0) {
-                               ++current_column;
-                               if (current_column < columns) {
-                                       wprintf("</td><td valign=top>\n");
-                               }
-                       }
-               }
-               strcpy(old_floor_name, floor_name);
-
-               if (levels == 1) {
-                       /** Begin inner box */
-                       stresc(boxtitle, floor_name, 1, 0);
-                       svprintf("BOXTITLE", WCS_STRING, boxtitle);
-                       do_template("beginbox");
-               }
-
-               oldlevels = levels;
-
-               if (levels > 1) {
-                       wprintf("&nbsp;");
-                       if (levels>2) for (t=0; t<(levels-2); ++t) wprintf("&nbsp;&nbsp;&nbsp;");
-                       if (fold[i].selectable) {
-                               wprintf("<a href=\"dotgoto?room=");
-                               urlescputs(fold[i].room);
-                               wprintf("\">");
-                       }
-                       else {
-                               wprintf("<i>");
-                       }
-                       if (fold[i].hasnewmsgs) {
-                               wprintf("<SPAN CLASS=\"roomlist_new\">");
-                       }
-                       else {
-                               wprintf("<SPAN CLASS=\"roomlist_old\">");
-                       }
-                       extract_token(buf, fold[i].name, levels-1, '|', sizeof buf);
-                       escputs(buf);
-                       wprintf("</SPAN>");
-                       if (fold[i].selectable) {
-                               wprintf("</A>");
-                       }
-                       else {
-                               wprintf("</i>");
-                       }
-                       if (!strcasecmp(fold[i].name, "My Folders|Mail")) {
-                               wprintf(" (INBOX)");
-                       }
-                       wprintf("<br />\n");
-               }
-       }
-       /** End the final inner box */
-       do_template("endbox");
-
-       wprintf("</TD></TR></TABLE>\n");
-}
-
-/**
- * \brief print a floor div???
- * \param which_floordiv name of the floordiv???
- */
-void set_floordiv_expanded(char *which_floordiv) {
-       begin_ajax_response();
-       safestrncpy(WC->floordiv_expanded, which_floordiv, sizeof WC->floordiv_expanded);
-       end_ajax_response();
-}
-
-/**
- * \brief view the iconbar
- * \param fold the folder to view
- * \param max_folders how many folders???
- * \param num_floors hom many floors???
- */
-void do_iconbar_view(struct folder *fold, int max_folders, int num_floors) {
-       char buf[256];
-       char floor_name[256];
-       char old_floor_name[256];
-       char floordivtitle[256];
-       char floordiv_id[32];
-       int levels, oldlevels;
-       int i, t;
-       int num_drop_targets = 0;
-       char *icon = NULL;
-
-       strcpy(floor_name, "");
-       strcpy(old_floor_name, "");
-
-       levels = 0;
-       oldlevels = 0;
-       for (i=0; i<max_folders; ++i) {
-
-               levels = num_tokens(fold[i].name, '|');
-               extract_token(floor_name, fold[i].name, 0,
-                       '|', sizeof floor_name);
-
-               if ( (strcasecmp(floor_name, old_floor_name))
-                  && (strlen(old_floor_name) > 0) ) {
-                       /** End inner box */
-                       wprintf("<br>\n");
-                       wprintf("</div>\n");    /** floordiv */
-               }
-               strcpy(old_floor_name, floor_name);
-
-               if (levels == 1) {
-                       /** Begin floor */
-                       stresc(floordivtitle, floor_name, 0, 0);
-                       sprintf(floordiv_id, "floordiv%d", i);
-                       wprintf("<span class=\"ib_roomlist_floor\" "
-                               "onClick=\"expand_floor('%s')\">"
-                               "%s</span><br>\n", floordiv_id, floordivtitle);
-                       wprintf("<div id=\"%s\" style=\"display:%s\">",
-                               floordiv_id,
-                               (!strcasecmp(floordiv_id, WC->floordiv_expanded) ? "block" : "none")
-                       );
-               }
-
-               oldlevels = levels;
-
-               if (levels > 1) {
-                       wprintf("<div id=\"roomdiv%d\">", i);
-                       wprintf("&nbsp;");
-                       if (levels>2) for (t=0; t<(levels-2); ++t) wprintf("&nbsp;");
-
-                       /** choose the icon */
-                       if (fold[i].view == VIEW_ADDRESSBOOK) {
-                               icon = "viewcontacts_16x.gif" ;
-                       }
-                       else if (fold[i].view == VIEW_CALENDAR) {
-                               icon = "calarea_16x.gif" ;
-                       }
-                       else if (fold[i].view == VIEW_CALBRIEF) {
-                               icon = "calarea_16x.gif" ;
-                       }
-                       else if (fold[i].view == VIEW_TASKS) {
-                               icon = "taskmanag_16x.gif" ;
-                       }
-                       else if (fold[i].view == VIEW_NOTES) {
-                               icon = "storenotes_16x.gif" ;
-                       }
-                       else if (fold[i].view == VIEW_MAILBOX) {
-                               icon = "privatemess_16x.gif" ;
-                       }
-                       else {
-                               icon = "chatrooms_16x.gif" ;
-                       }
-
-                       if (fold[i].selectable) {
-                               wprintf("<a href=\"dotgoto?room=");
-                               urlescputs(fold[i].room);
-                               wprintf("\">");
-                               wprintf("<img align=\"middle\" border=0 src=\"static/%s\" alt=\"\"> ", icon);
-                       }
-                       else {
-                               wprintf("<i>");
-                       }
-                       if (fold[i].hasnewmsgs) {
-                               wprintf("<SPAN CLASS=\"ib_roomlist_new\">");
-                       }
-                       else {
-                               wprintf("<SPAN CLASS=\"ib_roomlist_old\">");
-                       }
-                       extract_token(buf, fold[i].name, levels-1, '|', sizeof buf);
-                       escputs(buf);
-                       if (!strcasecmp(fold[i].name, "My Folders|Mail")) {
-                               wprintf(" (INBOX)");
-                       }
-                       wprintf("</SPAN>");
-                       if (fold[i].selectable) {
-                               wprintf("</A>");
-                       }
-                       else {
-                               wprintf("</i>");
-                       }
-                       wprintf("<br />");
-                       wprintf("</div>\n");    /** roomdiv */
-               }
-       }
-       wprintf("</div>\n");    /** floordiv */
-
-
-       /** BEGIN: The old invisible pixel trick, to get our JavaScript to initialize */
-       wprintf("<img src=\"static/blank.gif\" onLoad=\"\n");
-
-       num_drop_targets = 0;
-
-       for (i=0; i<max_folders; ++i) {
-               levels = num_tokens(fold[i].name, '|');
-               if (levels > 1) {
-                       wprintf("drop_targets_elements[%d]=$('roomdiv%d');\n", num_drop_targets, i);
-                       wprintf("drop_targets_roomnames[%d]='", num_drop_targets);
-                       jsescputs(fold[i].room);
-                       wprintf("';\n");
-                       ++num_drop_targets;
-               }
-       }
-
-       wprintf("num_drop_targets = %d;\n", num_drop_targets);
-       if (strlen(WC->floordiv_expanded) > 1) {
-               wprintf("which_div_expanded = '%s';\n", WC->floordiv_expanded);
-       }
-
-       wprintf("\">\n");
-       /** END: The old invisible pixel trick, to get our JavaScript to initialize */
-}
-
-
-
-/**
- * \brief Show the room list.  
- * (only should get called by
- * knrooms() because that's where output_headers() is called from)
- * \param viewpref the view preferences???
- */
-
-void list_all_rooms_by_floor(char *viewpref) {
-       char buf[SIZ];
-       int swap = 0;
-       struct folder *fold = NULL;
-       struct folder ftmp;
-       int max_folders = 0;
-       int alloc_folders = 0;
-       int i, j;
-       int ra_flags = 0;
-       int flags = 0;
-       int num_floors = 1;     /** add an extra one for private folders */
-
-       /** If our cached folder list is very old, burn it. */
-       if (WC->cache_fold != NULL) {
-               if ((time(NULL) - WC->cache_timestamp) > 300) {
-                       free(WC->cache_fold);
-                       WC->cache_fold = NULL;
-               }
-       }
-
-       /** Can we do the iconbar roomlist from cache? */
-       if ((WC->cache_fold != NULL) && (!strcasecmp(viewpref, "iconbar"))) {
-               do_iconbar_view(WC->cache_fold, WC->cache_max_folders, WC->cache_num_floors);
-               return;
-       }
-
-       /** Grab the floor table so we know how to build the list... */
-       load_floorlist();
-
-       /** Start with the mailboxes */
-       max_folders = 1;
-       alloc_folders = 1;
-       fold = malloc(sizeof(struct folder));
-       memset(fold, 0, sizeof(struct folder));
-       strcpy(fold[0].name, "My folders");
-       fold[0].is_mailbox = 1;
-
-       /** Then add floors */
-       serv_puts("LFLR");
-       serv_getln(buf, sizeof buf);
-       if (buf[0]=='1') while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               if (max_folders >= alloc_folders) {
-                       alloc_folders = max_folders + 100;
-                       fold = realloc(fold,
-                               alloc_folders * sizeof(struct folder));
-               }
-               memset(&fold[max_folders], 0, sizeof(struct folder));
-               extract_token(fold[max_folders].name, buf, 1, '|', sizeof fold[max_folders].name);
-               ++max_folders;
-               ++num_floors;
-       }
-
-       /** Now add rooms */
-       serv_puts("LKRA");
-       serv_getln(buf, sizeof buf);
-       if (buf[0]=='1') while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               if (max_folders >= alloc_folders) {
-                       alloc_folders = max_folders + 100;
-                       fold = realloc(fold,
-                               alloc_folders * sizeof(struct folder));
-               }
-               memset(&fold[max_folders], 0, sizeof(struct folder));
-               extract_token(fold[max_folders].room, buf, 0, '|', sizeof fold[max_folders].room);
-               ra_flags = extract_int(buf, 5);
-               flags = extract_int(buf, 1);
-               fold[max_folders].floor = extract_int(buf, 2);
-               fold[max_folders].hasnewmsgs =
-                       ((ra_flags & UA_HASNEWMSGS) ? 1 : 0 );
-               if (flags & QR_MAILBOX) {
-                       fold[max_folders].is_mailbox = 1;
-               }
-               fold[max_folders].view = extract_int(buf, 6);
-               room_to_folder(fold[max_folders].name,
-                               fold[max_folders].room,
-                               fold[max_folders].floor,
-                               fold[max_folders].is_mailbox);
-               fold[max_folders].selectable = 1;
-               ++max_folders;
-       }
-
-       /** Bubble-sort the folder list */
-       for (i=0; i<max_folders; ++i) {
-               for (j=0; j<(max_folders-1)-i; ++j) {
-                       if (fold[j].is_mailbox == fold[j+1].is_mailbox) {
-                               swap = strcasecmp(fold[j].name, fold[j+1].name);
-                       }
-                       else {
-                               if ( (fold[j+1].is_mailbox)
-                                  && (!fold[j].is_mailbox)) {
-                                       swap = 1;
-                               }
-                               else {
-                                       swap = 0;
-                               }
-                       }
-                       if (swap > 0) {
-                               memcpy(&ftmp, &fold[j], sizeof(struct folder));
-                               memcpy(&fold[j], &fold[j+1],
-                                                       sizeof(struct folder));
-                               memcpy(&fold[j+1], &ftmp,
-                                                       sizeof(struct folder));
-                       }
-               }
-       }
-
-
-       if (!strcasecmp(viewpref, "folders")) {
-               do_folder_view(fold, max_folders, num_floors);
-       }
-       else if (!strcasecmp(viewpref, "hackish_view")) {
-               for (i=0; i<max_folders; ++i) {
-                       escputs(fold[i].name);
-                       wprintf("<br />\n");
-               }
-       }
-       else if (!strcasecmp(viewpref, "iconbar")) {
-               do_iconbar_view(fold, max_folders, num_floors);
-       }
-       else {
-               do_rooms_view(fold, max_folders, num_floors);
-       }
-
-       /* Don't free the folder list ... cache it for future use! */
-       if (WC->cache_fold != NULL) {
-               free(WC->cache_fold);
-       }
-       WC->cache_fold = fold;
-       WC->cache_max_folders = max_folders;
-       WC->cache_num_floors = num_floors;
-       WC->cache_timestamp = time(NULL);
-}
-
-
-/**
- * \brief Do either a known rooms list or a folders list, depending on the
- * user's preference
- */
-void knrooms(void)
-{
-       char listviewpref[SIZ];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-
-       /** Determine whether the user is trying to change views */
-       if (bstr("view") != NULL) {
-               if (strlen(bstr("view")) > 0) {
-                       set_preference("roomlistview", bstr("view"), 1);
-               }
-       }
-
-       get_preference("roomlistview", listviewpref, sizeof listviewpref);
-
-       if ( (strcasecmp(listviewpref, "folders"))
-          && (strcasecmp(listviewpref, "table")) ) {
-               strcpy(listviewpref, "rooms");
-       }
-
-       /** title bar */
-       wprintf("<div id=\"banner\">\n"
-               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
-               "<SPAN CLASS=\"titlebar\">"
-       );
-       if (!strcasecmp(listviewpref, "rooms")) {
-               wprintf(_("Room list"));
-       }
-       if (!strcasecmp(listviewpref, "folders")) {
-               wprintf(_("Folder list"));
-       }
-       if (!strcasecmp(listviewpref, "table")) {
-               wprintf(_("Room list"));
-       }
-       wprintf("</SPAN></TD>\n");
-
-       /** offer the ability to switch views */
-       wprintf("<TD ALIGN=RIGHT><FORM NAME=\"roomlistomatic\">\n"
-               "<SELECT NAME=\"newview\" SIZE=\"1\" "
-               "OnChange=\"location.href=roomlistomatic.newview.options"
-               "[selectedIndex].value\">\n");
-
-       wprintf("<OPTION %s VALUE=\"knrooms&view=rooms\">"
-               "View as room list"
-               "</OPTION>\n",
-               ( !strcasecmp(listviewpref, "rooms") ? "SELECTED" : "" )
-       );
-
-       wprintf("<OPTION %s VALUE=\"knrooms&view=folders\">"
-               "View as folder list"
-               "</OPTION>\n",
-               ( !strcasecmp(listviewpref, "folders") ? "SELECTED" : "" )
-       );
-
-       wprintf("</SELECT><br />");
-       offer_start_page();
-       wprintf("</FORM></TD></TR></TABLE>\n");
-       wprintf("</div>\n"
-               "</div>\n"
-               "<div id=\"content\">\n");
-
-       /** Display the room list in the user's preferred format */
-       list_all_rooms_by_floor(listviewpref);
-       wDumpContent(1);
-}
-
-
-
-/**
- * \brief Set the message expire policy for this room and/or floor
- */
-void set_room_policy(void) {
-       char buf[SIZ];
-
-       if (strlen(bstr("ok_button")) == 0) {
-               strcpy(WC->ImportantMessage,
-                       _("Cancelled.  Changes were not saved."));
-               display_editroom();
-               return;
-       }
-
-       serv_printf("SPEX room|%d|%d", atoi(bstr("roompolicy")), atoi(bstr("roomvalue")));
-       serv_getln(buf, sizeof buf);
-       strcpy(WC->ImportantMessage, &buf[4]);
-
-       if (WC->axlevel >= 6) {
-               strcat(WC->ImportantMessage, "<br />\n");
-               serv_printf("SPEX floor|%d|%d", atoi(bstr("floorpolicy")), atoi(bstr("floorvalue")));
-               serv_getln(buf, sizeof buf);
-               strcat(WC->ImportantMessage, &buf[4]);
-       }
-
-       display_editroom();
-}
-
-
-/*@}*/
diff --git a/webcit/rss.c b/webcit/rss.c
deleted file mode 100644 (file)
index 6a96255..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup RssRooms Generate some RSS for our rooms.
- * \ingroup WebcitHttpServerRSS
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-
-
-time_t if_modified_since;    /**< the last modified stamp */
-
-/**
- * \brief view rss Config menu
- * \param reply_to the original author
- * \param subject the subject of the feed
- */
-void display_rss_control(char *reply_to, char *subject)
-{
-       wprintf("<div style=\"align: right;\"><p>\n");
-       wprintf("<a href=\"display_enter?recp=");
-       urlescputs(reply_to);
-       wprintf("&subject=");
-       if (strncasecmp(subject, "Re: ", 3)) wprintf("Re:%20");
-       urlescputs(subject);
-       wprintf("\">[%s]</a> \n", _("Reply"));
-       wprintf("<a href=\"display_enter?recp=");
-       urlescputs(reply_to);
-       wprintf("&force_room=_MAIL_&subject=");
-       if (strncasecmp(subject, "Re: ", 3)) wprintf("Re:%20");
-       urlescputs(subject);
-       wprintf("\">[%s]</a>\n", _("Email"));
-       wprintf("</p></div>\n");
-}
-
-
-/**
- * \brief print the feed to the subscriber
- * \param roomname the room we sould print out as rss 
- * \param request_method the way the rss is requested????
- */
-void display_rss(char *roomname, char *request_method)
-{
-       int nummsgs;
-       int a, b;
-       int bq = 0;
-       time_t now = 0L;
-       struct tm now_tm;
-#ifdef HAVE_ICONV
-       iconv_t ic = (iconv_t)(-1) ;
-       char *ibuf;                   /**< Buffer of characters to be converted */
-       char *obuf;                   /**< Buffer for converted characters      */
-       size_t ibuflen;               /**< Length of input buffer               */
-       size_t obuflen;               /**< Length of output buffer              */
-       char *osav;                   /**< Saved pointer to output buffer       */
-#endif
-       char buf[SIZ];
-       char date[30];
-       char from[256];
-       char subj[256];
-       char node[256];
-       char hnod[256];
-       char room[256];
-       char rfca[256];
-       char rcpt[256];
-       char msgn[256];
-       char content_type[256];
-       char charset[256];
-
-       if (!WC->logged_in) {
-               authorization_required(_("Not logged in"));
-               return;
-       }
-
-       if (gotoroom((char *)roomname)) {
-               lprintf(3, "RSS: Can't goto requested room\n");
-               wprintf("HTTP/1.1 404 Not Found\r\n");
-               wprintf("Content-Type: text/html\r\n");
-               wprintf("\r\n");
-               wprintf("Error retrieving RSS feed: couldn't find room\n");
-               return;
-       }
-
-       nummsgs = load_msg_ptrs("MSGS LAST|15", 0);
-       if (nummsgs == 0) {
-               lprintf(3, "RSS: No messages found\n");
-               wprintf("HTTP/1.1 404 Not Found\r\n");
-               wprintf("Content-Type: text/html\r\n");
-               wprintf("\r\n");
-               wprintf(_("Error retrieving RSS feed: couldn't find messages\n"));
-               return;
-       }
-
-       /** Read time of last message immediately */
-       serv_printf("MSG4 %ld", WC->msgarr[nummsgs - 1]);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               while (serv_getln(buf, sizeof buf), strcasecmp(buf, "000")) {
-                       if (!strncasecmp(buf, "msgn=", 5)) {
-                               strcpy(msgn, &buf[5]);
-                       }
-                       if (!strncasecmp(buf, "time=", 5)) {
-                               now = atol(&buf[5]);
-                               gmtime_r(&now, &now_tm);
-                               strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
-                       }
-               }
-       }
-
-       if (if_modified_since > 0 && if_modified_since > now) {
-               lprintf(3, "RSS: Feed not updated since the last time you looked\n");
-               wprintf("HTTP/1.1 304 Not Modified\r\n");
-               wprintf("Last-Modified: %s\r\n", date);
-               now = time(NULL);
-               gmtime_r(&now, &now_tm);
-               strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
-               wprintf("Date: %s\r\n", date);
-/*             if (*msgn) wprintf("ETag: %s\r\n\r\n", msgn); */
-               wDumpContent(0);
-               return;
-       }
-
-       /* Do RSS header */
-       lprintf(3, "RSS: Yum yum! This feed is tasty!\n");
-       wprintf("HTTP/1.1 200 OK\r\n");
-       wprintf("Last-Modified: %s\r\n", date);
-/*     if (*msgn) wprintf("ETag: %s\r\n\r\n", msgn); */
-       wprintf("Content-Type: application/rss+xml\r\n");
-       wprintf("$erver: %s\r\n", SERVER);
-       wprintf("Connection: close\r\n");
-       wprintf("\r\n");
-       if (!strcasecmp(request_method, "HEAD"))
-               return;
-
-       wprintf("<?xml version=\"1.0\"?>\n");
-       wprintf("<rss version=\"2.0\">\n");
-       wprintf("   <channel>\n");
-       wprintf("   <title>%s - %s</title>\n", WC->wc_roomname, serv_info.serv_humannode);
-       wprintf("   <link>%s://%s:%d/dotgoto?room=", (is_https ? "https" : "http"), WC->http_host, PORT_NUM);
-       escputs(roomname);
-       wprintf("</link>\n");
-       wprintf("   <description>");
-       /** Get room info for description */
-       serv_puts("RINF");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               while (1) {
-                       serv_getln(buf, sizeof buf);
-                       if (!strcmp(buf, "000"))
-                               break;
-                       wprintf("%s\n", buf);
-               }
-       }
-       wprintf("</description>\n");
-       if (now) {
-               wprintf("   <pubDate>%s</pubDate>\n", date);
-       }
-       wprintf("   <generator>%s</generator>\n", SERVER);
-       wprintf("   <docs>http://blogs.law.harvard.edu/tech/rss</docs>\n");
-       wprintf("   <ttl>30</ttl>\n");
-
-       /** Read all messages and output as RSS items */
-       for (a = 0; a < nummsgs; ++a) {
-               /** Read message and output each as RSS item */
-               serv_printf("MSG4 %ld", WC->msgarr[a]);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '1') continue;
-
-               now = 0L;
-               strcpy(subj, "");
-               strcpy(hnod, "");
-               strcpy(node, "");
-               strcpy(room, "");
-               strcpy(rfca, "");
-               strcpy(rcpt, "");
-               strcpy(msgn, "");
-
-               while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
-                       if (!strcmp(buf, "000")) {
-                               goto ENDITEM;   /** screw it */
-                       } else if (!strncasecmp(buf, "from=", 5)) {
-                               strcpy(from, &buf[5]);
-#ifdef HAVE_ICONV
-                               utf8ify_rfc822_string(from);
-#endif
-                       } else if (!strncasecmp(buf, "subj=", 5)) {
-                               strcpy(subj, &buf[5]);
-#ifdef HAVE_ICONV
-                               utf8ify_rfc822_string(subj);
-#endif
-                       } else if (!strncasecmp(buf, "hnod=", 5)) {
-                               strcpy(node, &buf[5]);
-                       } else if (!strncasecmp(buf, "room=", 5)) {
-                               strcpy(room, &buf[5]);
-                       } else if (!strncasecmp(buf, "rfca=", 5)) {
-                               strcpy(rfca, &buf[5]);
-                       } else if (!strncasecmp(buf, "rcpt=", 5)) {
-                               strcpy(rcpt, &buf[5]);
-                       } else if (!strncasecmp(buf, "msgn=", 5)) {
-                               strcpy(msgn, &buf[5]);
-                       } else if (!strncasecmp(buf, "time=", 5)) {
-                               now = atol(&buf[5]);
-                               gmtime_r(&now, &now_tm);
-                               strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
-                       }
-               }
-               wprintf("   <item>\n");
-               if (subj[0]) {
-                       wprintf("      <title>%s from", subj);
-               } else {
-                       wprintf("      <title>From");
-               }
-               wprintf(" %s", from);
-               wprintf(" in %s", room);
-               if (strcmp(hnod, serv_info.serv_humannode) && strlen(hnod) > 0) {
-                       wprintf(" on %s", hnod);
-               }
-               wprintf("</title>\n");
-               if (now) {
-                       wprintf("      <pubDate>%s</pubDate>\n", date);
-               }
-               wprintf("      <guid isPermaLink=\"false\">%s</guid>\n", msgn);
-               /** Now the hard part, the message itself */
-               strcpy(content_type, "text/plain");
-               while (serv_getln(buf, sizeof buf), strlen(buf) > 0) {
-                       if (!strcmp(buf, "000")) {
-                               goto ENDBODY;
-                       }
-                       if (!strncasecmp(buf, "Content-type: ", 14)) {
-                               safestrncpy(content_type, &buf[14], sizeof content_type);
-                               for (b = 0; b < strlen(content_type); ++b) {
-                                       if (!strncasecmp(&content_type[b], "charset=", 8)) {
-                                               safestrncpy(charset, &content_type[b + 8], sizeof charset);
-                                       }
-                               }
-                               for (b = 0; b < strlen(content_type); ++b) {
-                                       if (content_type[b] == ';') {
-                                               content_type[b] = 0;
-                                       }
-                               }
-                       }
-               }
-
-               /** Set up a character set conversion if we need to */
-#ifdef HAVE_ICONV
-               if (strcasecmp(charset, "us-ascii") && strcasecmp(charset, "utf-8") && strcasecmp(charset, "") ) {
-                       ic = ctdl_iconv_open("UTF-8", charset);
-                       if (ic == (iconv_t)(-1)) {
-                               lprintf(5, "%s:%d iconv_open() failed: %s\n",
-                                       __FILE__, __LINE__, strerror(errno));
-                               goto ENDBODY;
-                       }
-               }
-#endif
-
-               /** Messages in legacy Citadel variformat get handled thusly... */
-               if (!strcasecmp(content_type, "text/x-citadel-variformat")) {
-                       int intext = 0;
-
-                       wprintf("      <description><![CDATA[");
-                       while (1) {
-                               serv_getln(buf, sizeof buf);
-                               if (!strcmp(buf, "000")) {
-                                       if (bq == 1)
-                                               wprintf("</blockquote>");
-                                       wprintf("\n");
-                                       break;
-                               }
-                               if (intext == 1 && isspace(buf[0])) {
-                                       wprintf("<br/>");
-                               }
-                               intext = 1;
-                               if (bq == 0 && !strncmp(buf, " >", 2)) {
-                                       wprintf("<blockquote>");
-                                       bq = 1;
-                               } else if (bq == 1 && strncmp(buf, " >", 2)) {
-                                       wprintf("</blockquote>");
-                                       bq = 0;
-                               }
-                               url(buf);
-                               escputs(buf);
-                               wprintf("\n");
-                       }
-                       display_rss_control(from, subj);
-                       wprintf("]]></description>\n");
-               }
-               /** Boring old 80-column fixed format text gets handled this way... */
-               else if (!strcasecmp(content_type, "text/plain")) {
-                       wprintf("      <description><![CDATA[");
-                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                               if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
-                               if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
-       
-#ifdef HAVE_ICONV
-                               if (ic != (iconv_t)(-1) ) {
-                                       ibuf = buf;
-                                       ibuflen = strlen(ibuf);
-                                       obuflen = SIZ;
-                                       obuf = (char *) malloc(obuflen);
-                                       osav = obuf;
-                                       iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
-                                       osav[SIZ-obuflen] = 0;
-                                       safestrncpy(buf, osav, sizeof buf);
-                                       free(osav);
-                               }
-#endif
-
-                               while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
-                                       buf[strlen(buf) - 1] = 0;
-                               if ((bq == 0) &&
-                               ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) {
-                                       wprintf("<blockquote>");
-                                       bq = 1;
-                               } else if ((bq == 1) &&
-                                       (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) {
-                                       wprintf("</blockquote>");
-                                       bq = 0;
-                               }
-                               wprintf("<tt>");
-                               url(buf);
-                               escputs(buf);
-                               wprintf("</tt><br />\n");
-                       }
-                       display_rss_control(from, subj);
-                       wprintf("]]></description>\n");
-               }
-               /** HTML is fun, but we've got to strip it first */
-               else if (!strcasecmp(content_type, "text/html")) {
-                       wprintf("      <description><![CDATA[");
-                       output_html(charset, 0);
-                       wprintf("]]></description>\n");
-               }
-
-ENDBODY:
-               wprintf("   </item>\n");
-ENDITEM:
-               now = 0L;
-       }
-
-       /** Do RSS footer */
-       wprintf("   </channel>\n");
-       wprintf("</rss>\n");
-       wDumpContent(0);
-}
-
-
-/*@}*/
diff --git a/webcit/serv_func.c b/webcit/serv_func.c
deleted file mode 100644 (file)
index 05574c2..0000000
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup ServFuncs Handles various types of data transfer operations with the Citadel service.
- * \ingroup CitadelCommunitacion
- */
-
-/*@{*/ 
-#include "webcit.h"
-#include "webserver.h"
-
-struct serv_info serv_info; /**< our connection data to the server */
-
-/**
- * \brief get info about the server we've connected to
- * \param browser_host the citadell we want to connect to
- * \param user_agent which browser uses our client?
- */
-void get_serv_info(char *browser_host, char *user_agent)
-{
-       char buf[SIZ];
-       int a;
-
-       /** Tell the server what kind of client is connecting */
-       serv_printf("IDEN %d|%d|%d|%s|%s",
-               DEVELOPER_ID,
-               CLIENT_ID,
-               CLIENT_VERSION,
-               user_agent,
-               browser_host
-       );
-       serv_getln(buf, sizeof buf);
-
-       /** Tell the server what kind of richtext we prefer */
-       serv_puts("MSGP text/html|text/plain");
-       serv_getln(buf, sizeof buf);
-
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-       /**
-        * Tell the server that when we save a calendar event, we
-        * want invitations to be generated by the Citadel server
-        * instead of by the client.
-        */
-       serv_puts("ICAL sgi|1");
-       serv_getln(buf, sizeof buf);
-#endif
-
-       /** Now ask the server to tell us a little bit about itself... */
-       serv_puts("INFO");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1')
-               return;
-
-       a = 0;
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               switch (a) {
-               case 0:
-                       serv_info.serv_pid = atoi(buf);
-                       WC->ctdl_pid = serv_info.serv_pid;
-                       break;
-               case 1:
-                       safestrncpy(serv_info.serv_nodename, buf, sizeof serv_info.serv_nodename);
-                       break;
-               case 2:
-                       safestrncpy(serv_info.serv_humannode, buf, sizeof serv_info.serv_humannode);
-                       break;
-               case 3:
-                       safestrncpy(serv_info.serv_fqdn, buf, sizeof serv_info.serv_fqdn);
-                       break;
-               case 4:
-                       safestrncpy(serv_info.serv_software, buf, sizeof serv_info.serv_software);
-                       break;
-               case 5:
-                       serv_info.serv_rev_level = atoi(buf);
-                       break;
-               case 6:
-                       safestrncpy(serv_info.serv_bbs_city, buf, sizeof serv_info.serv_bbs_city);
-                       break;
-               case 7:
-                       safestrncpy(serv_info.serv_sysadm, buf, sizeof serv_info.serv_sysadm);
-                       break;
-               case 9:
-                       safestrncpy(serv_info.serv_moreprompt, buf, sizeof serv_info.serv_moreprompt);
-                       break;
-               case 14:
-                       serv_info.serv_supports_ldap = atoi(buf);
-                       break;
-               case 15:
-                       serv_info.serv_newuser_disabled = atoi(buf);
-                       break;
-               }
-               ++a;
-       }
-}
-
-
-
-/**
- * \brief Read Citadel variformat text and spit it out as HTML.
- * \param align html align string
- */
-void fmout(char *align)
-{
-       int intext = 0;
-       int bq = 0;
-       char buf[SIZ];
-
-       wprintf("<div align=%s>\n", align);
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-
-               if ((intext == 1) && (isspace(buf[0]))) {
-                       wprintf("<br />");
-               }
-               intext = 1;
-
-               /**
-                * Quoted text should be displayed in italics and in a
-                * different colour.  This code understands Citadel-style
-                * " >" quotes and will convert to <BLOCKQUOTE> tags.
-                */
-               if ((bq == 0) && (!strncmp(buf, " >", 2))) {
-                       wprintf("<BLOCKQUOTE>");
-                       bq = 1;
-               } else if ((bq == 1) && (strncmp(buf, " >", 2))) {
-                       wprintf("</BLOCKQUOTE>");
-                       bq = 0;
-               }
-               if ((bq == 1) && (!strncmp(buf, " >", 2))) {
-                       strcpy(buf, &buf[2]);
-               }
-               /** Activate embedded URL's */
-               url(buf);
-
-               escputs(buf);
-               wprintf("\n");
-       }
-       if (bq == 1) {
-               wprintf("</I>");
-       }
-       wprintf("</div><br />\n");
-}
-
-
-
-
-/**
- * \brief Read Citadel variformat text and spit it out as HTML in a form
- * suitable for embedding in another message (forward/quote).
- * (NO LINEBREAKS ALLOWED HERE!)
- */
-void pullquote_fmout(void) {
-       int intext = 0;
-       int bq = 0;
-       char buf[SIZ];
-
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-
-               if ((intext == 1) && (isspace(buf[0]))) {
-                       wprintf("<br />");
-               }
-               intext = 1;
-
-               /**
-                * Quoted text should be displayed in italics and in a
-                * different colour.  This code understands Citadel-style
-                * " >" quotes and will convert to <BLOCKQUOTE> tags.
-                */
-               if ((bq == 0) && (!strncmp(buf, " >", 2))) {
-                       wprintf("<BLOCKQUOTE>");
-                       bq = 1;
-               } else if ((bq == 1) && (strncmp(buf, " >", 2))) {
-                       wprintf("</BLOCKQUOTE>");
-                       bq = 0;
-               }
-               if ((bq == 1) && (!strncmp(buf, " >", 2))) {
-                       strcpy(buf, &buf[2]);
-               }
-
-               msgescputs(buf);
-       }
-       if (bq == 1) {
-               wprintf("</I>");
-       }
-}
-
-
-
-
-/**
- * \brief Transmit message text (in memory) to the server.
- *
- * \param ptr Pointer to the message being transmitted
- */
-void text_to_server(char *ptr)
-{
-       char buf[256];
-       int ch, a, pos;
-
-       pos = 0;
-       buf[0] = 0;
-
-       while (ptr[pos] != 0) {
-               ch = ptr[pos++];
-               if (ch == 10) {
-                       while ( (isspace(buf[strlen(buf) - 1]))
-                         && (strlen(buf) > 1) )
-                               buf[strlen(buf) - 1] = 0;
-                       serv_puts(buf);
-                       buf[0] = 0;
-                       if (ptr[pos] != 0) strcat(buf, " ");
-               } else {
-                       a = strlen(buf);
-                       buf[a + 1] = 0;
-                       buf[a] = ch;
-                       if ((ch == 32) && (strlen(buf) > 200)) {
-                               buf[a] = 0;
-                               serv_puts(buf);
-                               buf[0] = 0;
-                       }
-                       if (strlen(buf) > 250) {
-                               serv_puts(buf);
-                               buf[0] = 0;
-                       }
-               }
-       }
-       serv_puts(buf);
-}
-
-
-/**
- * \brief Transmit message text (in memory) to the server,
- *        converting to Quoted-Printable encoding as we go.
- *
- * \param ptr Pointer to the message being transmitted
- */
-void text_to_server_qp(char *ptr)
-{
-       char buf[256];
-       int ch, pos;
-       int output_len = 0;
-
-       pos = 0;
-       buf[0] = 0;
-       output_len = 0;
-
-       while (ptr[pos] != 0) {
-               ch = ptr[pos++];
-
-               if (ch == 13) {
-                       /* ignore carriage returns */
-               }
-               else if (ch == 10) {
-                       /* hard line break */
-                       if (output_len > 0) {
-                               if (isspace(buf[output_len-1])) {
-                                       sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]);
-                                       output_len += 2;
-                               }
-                       }
-                       buf[output_len++] = 0;
-                       serv_puts(buf);
-                       output_len = 0;
-               }
-               else if (ch == 9) {
-                       buf[output_len++] = ch;
-               }
-               else if ( (ch >= 32) && (ch <= 60) ) {
-                       buf[output_len++] = ch;
-               }
-               else if ( (ch >= 62) && (ch <= 126) ) {
-                       buf[output_len++] = ch;
-               }
-               else {
-                       sprintf(&buf[output_len], "=%02X", ch);
-                       output_len += 3;
-               }
-               
-               if (output_len > 72) {
-                       /* soft line break */
-                       if (isspace(buf[output_len-1])) {
-                               sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]);
-                               output_len += 2;
-                       }
-                       buf[output_len++] = '=';
-                       buf[output_len++] = 0;
-                       serv_puts(buf);
-                       output_len = 0;
-               }
-       }
-
-       /* end of data - transmit anything that's left */
-       if (output_len > 0) {
-               if (isspace(buf[output_len-1])) {
-                       sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]);
-                       output_len += 2;
-               }
-               buf[output_len++] = 0;
-               serv_puts(buf);
-               output_len = 0;
-       }
-}
-
-
-
-
-/**
- * \brief translate server message output to text
- * (used for editing room info files and such)
- */
-void server_to_text()
-{
-       char buf[SIZ];
-
-       int count = 0;
-
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               if ((buf[0] == 32) && (count > 0)) {
-                       wprintf("\n");
-               }
-               wprintf("%s", buf);
-               ++count;
-       }
-}
-
-
-
-/**
- * Read binary data from server into memory using a series of
- * server READ commands.
- * \param buffer the output buffer
- * \param total_len the maximal length of buffer
- */
-void read_server_binary(char *buffer, size_t total_len) {
-       char buf[SIZ];
-       size_t bytes = 0;
-       size_t thisblock = 0;
-
-       memset(buffer, 0, total_len);
-       while (bytes < total_len) {
-               thisblock = 4095;
-               if ((total_len - bytes) < thisblock) {
-                       thisblock = total_len - bytes;
-                       if (thisblock == 0) return;
-               }
-               serv_printf("READ %d|%d", (int)bytes, (int)thisblock);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '6') {
-                       thisblock = (size_t)atoi(&buf[4]);
-                       if (!WC->connected) return;
-                       serv_read(&buffer[bytes], thisblock);
-                       bytes += thisblock;
-               }
-               else {
-                       lprintf(3, "Error: %s\n", &buf[4]);
-                       return;
-               }
-       }
-}
-
-
-/**
- * \brief Read text from server, appending to a string buffer until the
- * usual 000 terminator is found.  Caller is responsible for freeing
- * the returned pointer.
- */
-char *read_server_text(void) {
-       char *text = NULL;
-       size_t bytes_allocated = 0;
-       size_t bytes_read = 0;
-       int linelen;
-       char buf[SIZ];
-
-       text = malloc(SIZ);
-       if (text == NULL) {
-               return(NULL);
-       }
-       text[0] = 0;
-       bytes_allocated = SIZ;
-
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               linelen = strlen(buf);
-               buf[linelen] = '\n';
-               buf[linelen+1] = 0;
-               ++linelen;
-
-               if ((bytes_read + linelen) >= (bytes_allocated - 2)) {
-                       bytes_allocated = 2 * bytes_allocated;
-                       text = realloc(text, bytes_allocated);
-               }
-
-               strcpy(&text[bytes_read], buf);
-               bytes_read += linelen;
-       }
-
-       return(text);
-}
-
-
-
-/*@}*/
diff --git a/webcit/setup.c b/webcit/setup.c
deleted file mode 100644 (file)
index f17e7ac..0000000
+++ /dev/null
@@ -1,683 +0,0 @@
-/*
- * $Id$
- *
- * WebCit setup utility
- * 
- * (This is basically just an install wizard.  It's not required.)
- *
- */
-
-
-#include "webcit.h"
-#include "webserver.h"
-
-
-#ifdef HAVE_NEWT
-#include <newt.h>
-#endif
-
-
-#define UI_TEXT                0       /* Default setup type -- text only */
-#define UI_DIALOG      2       /* Use the 'dialog' program */
-#define UI_SILENT      3       /* Silent running, for use in scripts */
-#define UI_NEWT                4       /* Use the "newt" window library */
-
-int setup_type;
-char setup_directory[SIZ];
-char init_entry[SIZ];
-int using_web_installer = 0;
-char suggested_url[SIZ];
-
-/*
- * Set an entry in inittab to the desired state
- */
-void set_init_entry(char *which_entry, char *new_state) {
-       char *inittab = NULL;
-       FILE *fp;
-       char buf[SIZ];
-       char entry[SIZ];
-       char levels[SIZ];
-       char state[SIZ];
-       char prog[SIZ];
-
-       inittab = strdup("");
-       if (inittab == NULL) return;
-
-       fp = fopen("/etc/inittab", "r");
-       if (fp == NULL) return;
-
-       while(fgets(buf, sizeof buf, fp) != NULL) {
-
-               if (num_tokens(buf, ':') == 4) {
-                       extract_token(entry, buf, 0, ':', sizeof entry);
-                       extract_token(levels, buf, 1, ':', sizeof levels);
-                       extract_token(state, buf, 2, ':', sizeof state);
-                       extract_token(prog, buf, 3, ':', sizeof prog); /* includes 0x0a LF */
-
-                       if (!strcmp(entry, which_entry)) {
-                               strcpy(state, new_state);
-                               sprintf(buf, "%s:%s:%s:%s",
-                                       entry, levels, state, prog);
-                       }
-               }
-
-               inittab = realloc(inittab, strlen(inittab) + strlen(buf) + 2);
-               if (inittab == NULL) {
-                       fclose(fp);
-                       return;
-               }
-               
-               strcat(inittab, buf);
-       }
-       fclose(fp);
-       fp = fopen("/etc/inittab", "w");
-       if (fp != NULL) {
-               fwrite(inittab, strlen(inittab), 1, fp);
-               fclose(fp);
-               kill(1, SIGHUP);        /* Tell init to re-read /etc/inittab */
-       }
-       free(inittab);
-}
-
-
-
-
-/* 
- * Shut down the Citadel service if necessary, during setup.
- */
-void shutdown_service(void) {
-       FILE *infp;
-       char buf[SIZ];
-       char looking_for[SIZ];
-       int have_entry = 0;
-       char entry[SIZ];
-       char prog[SIZ];
-
-       strcpy(init_entry, "");
-
-       /* Determine the fully qualified path name of webserver */
-       snprintf(looking_for, sizeof looking_for, "%s/webserver ", setup_directory);
-
-       /* Pound through /etc/inittab line by line.  Set have_entry to 1 if
-        * an entry is found which we believe starts webserver.
-        */
-       infp = fopen("/etc/inittab", "r");
-       if (infp == NULL) {
-               return;
-       } else {
-               while (fgets(buf, sizeof buf, infp) != NULL) {
-                       buf[strlen(buf) - 1] = 0;
-                       extract_token(entry, buf, 0, ':', sizeof entry);
-                       extract_token(prog, buf, 3, ':', sizeof prog);
-                       if (!strncasecmp(prog, looking_for,
-                          strlen(looking_for))) {
-                               ++have_entry;
-                               strcpy(init_entry, entry);
-                       }
-               }
-               fclose(infp);
-       }
-
-       /* Bail out if there's nothing to do. */
-       if (!have_entry) return;
-
-       set_init_entry(init_entry, "off");
-}
-
-
-/*
- * Start the Citadel service.
- */
-void start_the_service(void) {
-       if (strlen(init_entry) > 0) {
-               set_init_entry(init_entry, "respawn");
-       }
-}
-
-
-
-void cleanup(int exitcode)
-{
-#ifdef HAVE_NEWT
-       newtCls();
-       newtRefresh();
-       newtFinished();
-#endif
-       exit(exitcode);
-}
-
-
-
-void title(char *text)
-{
-       if (setup_type == UI_TEXT) {
-               printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
-       }
-}
-
-
-
-int yesno(char *question)
-{
-#ifdef HAVE_NEWT
-       newtComponent form = NULL;
-       newtComponent yesbutton = NULL;
-       newtComponent nobutton = NULL;
-#endif
-       int i = 0;
-       int answer = 0;
-       char buf[SIZ];
-
-       switch (setup_type) {
-
-       case UI_TEXT:
-               do {
-                       printf("%s\nYes/No --> ", question);
-                       fgets(buf, sizeof buf, stdin);
-                       answer = tolower(buf[0]);
-                       if (answer == 'y')
-                               answer = 1;
-                       else if (answer == 'n')
-                               answer = 0;
-               } while ((answer < 0) || (answer > 1));
-               break;
-
-       case UI_DIALOG:
-               sprintf(buf, "exec %s --yesno '%s' 10 72",
-                       getenv("CTDL_DIALOG"),
-                       question);
-               i = system(buf);
-               if (i == 0) {
-                       answer = 1;
-               }
-               else {
-                       answer = 0;
-               }
-               break;
-
-#ifdef HAVE_NEWT
-       case UI_NEWT:
-               newtCenteredWindow(76, 10, "Question");
-               form = newtForm(NULL, NULL, 0);
-               for (i=0; i<num_tokens(question, '\n'); ++i) {
-                       extract_token(buf, question, i, '\n', sizeof buf);
-                       newtFormAddComponent(form, newtLabel(1, 1+i, buf));
-               }
-               yesbutton = newtButton(10, 5, "Yes");
-               nobutton = newtButton(60, 5, "No");
-               newtFormAddComponent(form, yesbutton);
-               newtFormAddComponent(form, nobutton);
-               if (newtRunForm(form) == yesbutton) {
-                       answer = 1;
-               }
-               else {
-                       answer = 0;
-               }
-               newtPopWindow();
-               newtFormDestroy(form);  
-
-               break;
-#endif
-
-       }
-       return (answer);
-}
-
-void set_value(char *prompt, char str[])
-{
-#ifdef HAVE_NEWT
-       newtComponent form;
-       char *result;
-       int i;
-#endif
-       char buf[SIZ];
-       char dialog_result[PATH_MAX];
-       char setupmsg[SIZ];
-       FILE *fp;
-
-       strcpy(setupmsg, "");
-
-       switch (setup_type) {
-       case UI_TEXT:
-               title("WebCit setup");
-               printf("\n%s\n", prompt);
-               printf("This is currently set to:\n%s\n", str);
-               printf("Enter new value or press return to leave unchanged:\n");
-               fgets(buf, sizeof buf, stdin);
-               buf[strlen(buf) - 1] = 0;
-               if (strlen(buf) != 0)
-                       strcpy(str, buf);
-               break;
-
-       case UI_DIALOG:
-               CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
-               sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
-                       getenv("CTDL_DIALOG"),
-                       prompt,
-                       str,
-                       dialog_result);
-               system(buf);
-               fp = fopen(dialog_result, "r");
-               if (fp != NULL) {
-                       fgets(str, sizeof buf, fp);
-                       if (str[strlen(str)-1] == 10) {
-                               str[strlen(str)-1] = 0;
-                       }
-                       fclose(fp);
-                       unlink(dialog_result);
-               }
-               break;
-
-#ifdef HAVE_NEWT
-       case UI_NEWT:
-
-               newtCenteredWindow(76, 10, "WebCit setup");
-               form = newtForm(NULL, NULL, 0);
-               for (i=0; i<num_tokens(prompt, '\n'); ++i) {
-                       extract_token(buf, prompt, i, '\n', sizeof buf);
-                       newtFormAddComponent(form, newtLabel(1, 1+i, buf));
-               }
-               newtFormAddComponent(form, newtEntry(1, 8, str, 74, (const char **) &result,
-                                       NEWT_FLAG_RETURNEXIT));
-               newtRunForm(form);
-               strcpy(str, result);
-
-               newtPopWindow();
-               newtFormDestroy(form);  
-
-#endif
-       }
-}
-
-
-void important_message(char *title, char *msgtext)
-{
-#ifdef HAVE_NEWT
-       newtComponent form = NULL;
-       int i = 0;
-#endif
-       char buf[SIZ];
-
-       switch (setup_type) {
-
-       case UI_TEXT:
-               printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
-               printf("       %s \n\n%s\n\n", title, msgtext);
-               printf("Press return to continue...");
-               fgets(buf, sizeof buf, stdin);
-               break;
-
-       case UI_DIALOG:
-               sprintf(buf, "exec %s --msgbox '%s' 19 72",
-                       getenv("CTDL_DIALOG"),
-                       msgtext);
-               system(buf);
-               break;
-
-#ifdef HAVE_NEWT
-       case UI_NEWT:
-               newtCenteredWindow(76, 10, title);
-               form = newtForm(NULL, NULL, 0);
-               for (i=0; i<num_tokens(msgtext, '\n'); ++i) {
-                       extract_token(buf, msgtext, i, '\n', sizeof buf);
-                       newtFormAddComponent(form, newtLabel(1, 1+i, buf));
-               }
-               newtFormAddComponent(form, newtButton(35, 5, "OK"));
-               newtRunForm(form);
-               newtPopWindow();
-               newtFormDestroy(form);  
-               break;
-#endif
-
-       }
-}
-
-
-void display_error(char *error_message)
-{
-       important_message("Error", error_message);
-}
-
-void progress(char *text, long int curr, long int cmax)
-{
-#ifdef HAVE_NEWT
-
-       /* These variables are static because progress() gets called
-        * multiple times during the course of whatever operation is
-        * being performed.  This makes setup non-threadsafe, but who
-        * cares?
-        */
-       static newtComponent form = NULL;
-       static newtComponent scale = NULL;
-#endif
-       static long dots_printed = 0L;
-       long a = 0;
-       char buf[SIZ];
-       static FILE *fp = NULL;
-
-       switch (setup_type) {
-
-       case UI_TEXT:
-               if (curr == 0) {
-                       printf("%s\n", text);
-                       printf("..........................");
-                       printf("..........................");
-                       printf("..........................\r");
-                       fflush(stdout);
-                       dots_printed = 0;
-               } else if (curr == cmax) {
-                       printf("\r%79s\n", "");
-               } else {
-                       a = (curr * 100) / cmax;
-                       a = a * 78;
-                       a = a / 100;
-                       while (dots_printed < a) {
-                               printf("*");
-                               ++dots_printed;
-                               fflush(stdout);
-                       }
-               }
-               break;
-
-       case UI_DIALOG:
-               if (curr == 0) {
-                       sprintf(buf, "exec %s --gauge '%s' 7 72 0",
-                               getenv("CTDL_DIALOG"),
-                               text);
-                       fp = popen(buf, "w");
-                       if (fp != NULL) {
-                               fprintf(fp, "0\n");
-                               fflush(fp);
-                       }
-               } 
-               else if (curr == cmax) {
-                       if (fp != NULL) {
-                               fprintf(fp, "100\n");
-                               pclose(fp);
-                               fp = NULL;
-                       }
-               }
-               else {
-                       a = (curr * 100) / cmax;
-                       if (fp != NULL) {
-                               fprintf(fp, "%ld\n", a);
-                               fflush(fp);
-                       }
-               }
-               break;
-
-#ifdef HAVE_NEWT
-       case UI_NEWT:
-               if (curr == 0) {
-                       newtCenteredWindow(76, 8, text);
-                       form = newtForm(NULL, NULL, 0);
-                       scale = newtScale(1, 3, 74, cmax);
-                       newtFormAddComponent(form, scale);
-                       newtDrawForm(form);
-                       newtRefresh();
-               }
-               if ((curr > 0) && (curr <= cmax)) {
-                       newtScaleSet(scale, curr);
-                       newtRefresh();
-               }
-               if (curr == cmax) {
-                       newtFormDestroy(form);  
-                       newtPopWindow();
-                       newtRefresh();
-               }
-               break;
-#endif
-
-       }
-}
-
-
-
-
-/*
- * check_inittab_entry()  -- Make sure "webserver" is in /etc/inittab
- *
- */
-void check_inittab_entry(void)
-{
-       FILE *infp;
-       char buf[SIZ];
-       char looking_for[SIZ];
-       char question[SIZ];
-       char entryname[5];
-       char http_port[128];
-#ifdef HAVE_OPENSSL
-       char https_port[128];
-#endif
-       char hostname[128];
-       char portname[128];
-       struct utsname my_utsname;
-
-       /* Determine the fully qualified path name of webserver */
-       snprintf(looking_for, sizeof looking_for, "%s/webserver", setup_directory);
-
-       /* If there's already an entry, then we have nothing left to do. */
-       if (strlen(init_entry) > 0) {
-               return;
-       }
-
-       /* Otherwise, prompt the user to create an entry. */
-       snprintf(question, sizeof question,
-               "There is no '%s' entry in /etc/inittab.\n"
-               "Would you like to add one?",
-               looking_for);
-       if (yesno(question) == 0)
-               return;
-
-       snprintf(question, sizeof question,
-               "On which port do you want WebCit to listen for HTTP "
-               "requests?\n\nYou can use the standard port (80) if you are "
-               "not running another\nweb server (such as Apache), otherwise "
-               "select another port.");
-       sprintf(http_port, "2000");
-       set_value(question, http_port);
-       uname(&my_utsname);
-       sprintf(suggested_url, "http://%s:%s/", my_utsname.nodename, http_port);
-
-#ifdef HAVE_OPENSSL
-       snprintf(question, sizeof question,
-               "On which port do you want WebCit to listen for HTTPS "
-               "requests?\n\nYou can use the standard port (443) if you are "
-               "not running another\nweb server (such as Apache), otherwise "
-               "select another port.");
-       sprintf(https_port, "443");
-       set_value(question, https_port);
-#endif
-
-       /* Find out where Citadel is. */
-       if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
-               strcpy(hostname, "uds");
-               strcpy(portname, getenv("CITADEL"));
-       }
-       else {
-               snprintf(question, sizeof question,
-                       "Is the Citadel service running on the same host as WebCit?");
-               if (yesno(question)) {
-                       sprintf(hostname, "uds");
-                       sprintf(portname, "/usr/local/citadel");
-                       set_value("In what directory is Citadel installed?", portname);
-               }
-               else {
-                       sprintf(hostname, "127.0.0.1");
-                       sprintf(portname, "504");
-                       set_value("Enter the host name or IP address of your "
-                               "Citadel server.", hostname);
-                       set_value("Enter the port number on which Citadel is "
-                               "running (usually 504)", portname);
-               }
-       }
-
-       /* Generate unique entry names for /etc/inittab */
-       snprintf(entryname, sizeof entryname, "c0");
-       do {
-               ++entryname[1];
-               if (entryname[1] > '9') {
-                       entryname[1] = 0;
-                       ++entryname[0];
-                       if (entryname[0] > 'z') {
-                               display_error(
-                                  "Can't generate a unique entry name");
-                               return;
-                       }
-               }
-               snprintf(buf, sizeof buf,
-                    "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
-       } while (system(buf) == 0);
-       
-
-       /* Now write it out to /etc/inittab */
-       infp = fopen("/etc/inittab", "a");
-       if (infp == NULL) {
-               display_error(strerror(errno));
-       } else {
-               fprintf(infp, "# Start the WebCit server...\n");
-               fprintf(infp, "h%s:2345:respawn:%s -p%s %s %s\n",
-                       entryname, looking_for,
-                       http_port, hostname, portname);
-#ifdef HAVE_OPENSSL
-               fprintf(infp, "s%s:2345:respawn:%s -p%s -s %s %s\n",
-                       entryname, looking_for,
-                       https_port, hostname, portname);
-#endif
-               fclose(infp);
-               strcpy(init_entry, entryname);
-       }
-}
-
-
-
-
-/*
- * Figure out what type of user interface we're going to use
- */
-int discover_ui(void)
-{
-
-       /* Use "dialog" if we have it */
-       if (getenv("CTDL_DIALOG") != NULL) {
-               return UI_DIALOG;
-       }
-               
-#ifdef HAVE_NEWT
-       newtInit();
-       newtCls();
-       newtDrawRootText(0, 0, "WebCit Setup");
-       return UI_NEWT;
-#endif
-       return UI_TEXT;
-}
-
-
-
-
-
-int main(int argc, char *argv[])
-{
-       int a;
-       char aaa[256];
-       int info_only = 0;
-       strcpy(suggested_url, "http://<your_host_name>:<port>/");
-
-       /* set an invalid setup type */
-       setup_type = (-1);
-
-       /* Check to see if we're running the web installer */
-       if (getenv("CITADEL_INSTALLER") != NULL) {
-               using_web_installer = 1;
-       }
-
-       /* parse command line args */
-       for (a = 0; a < argc; ++a) {
-               if (!strncmp(argv[a], "-u", 2)) {
-                       strcpy(aaa, argv[a]);
-                       strcpy(aaa, &aaa[2]);
-                       setup_type = atoi(aaa);
-               }
-               if (!strcmp(argv[a], "-i")) {
-                       info_only = 1;
-               }
-               if (!strcmp(argv[a], "-q")) {
-                       setup_type = UI_SILENT;
-               }
-       }
-
-
-       /* If a setup type was not specified, try to determine automatically
-        * the best one to use out of all available types.
-        */
-       if (setup_type < 0) {
-               setup_type = discover_ui();
-       }
-       if (info_only == 1) {
-               important_message("WebCit Setup", "Welcome to WebCit setup");
-               cleanup(0);
-       }
-
-       /* If we're on something BSDish then we don't have inittab */
-       if (access("/etc/inittab", F_OK)) {
-               important_message("Not running SysV style init",
-                               "WebCit Setup can only run on systems that use /etc/inittab.\n"
-                               "Please manually configure your startup scripts to run WebCit\n"
-                               "when the system is booted.\n");
-               cleanup(0);
-       }
-
-       /* Get started in a valid setup directory. */
-       strcpy(setup_directory, WEBCITDIR);
-       if ( (using_web_installer) && (getenv("WEBCIT") != NULL) ) {
-               strcpy(setup_directory, getenv("WEBCIT"));
-       }
-       else {
-               set_value("In what directory is WebCit installed?",
-                       setup_directory);
-       }
-       if (chdir(setup_directory) != 0) {
-               important_message("WebCit Setup",
-                         "The directory you specified does not exist.");
-               cleanup(errno);
-       }
-
-       /* See if we need to shut down the WebCit service. */
-       for (a=0; a<=3; ++a) {
-               progress("Shutting down the WebCit service...", a, 3);
-               if (a == 0) shutdown_service();
-               sleep(1);
-       }
-
-       /* Now begin. */
-       switch (setup_type) {
-
-       case UI_TEXT:
-               printf("\n\n\n"
-                       "               *** WebCit setup program ***\n\n");
-               break;
-
-       }
-
-       check_inittab_entry();  /* Check /etc/inittab */
-
-       /* See if we can start the WebCit service. */
-       if (strlen(init_entry) > 0) {
-               for (a=0; a<=3; ++a) {
-                       progress("Starting the WebCit service...", a, 3);
-                       if (a == 0) start_the_service();
-                       sleep(1);
-               }
-               sprintf(aaa,
-                       "Setup is finished.  You may now log in.\n"
-                       "Point your web browser at %s\n", suggested_url);
-               important_message("Setup finished", aaa);
-       }
-       else {
-               important_message("Setup finished",
-                       "Setup is finished.  You may now start the server.");
-       }
-
-       cleanup(0);
-       return 0;
-}
diff --git a/webcit/setup_wizard.c b/webcit/setup_wizard.c
deleted file mode 100644 (file)
index bc2b227..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * $Id$
- *
- * First-time setup wizard
- */
-
-#include "webcit.h"
-
-
-/*
- */
-void do_setup_wizard(void)
-{
-       char *step;
-       FILE *fp;
-
-       step = bstr("step");
-
-       if (!strcasecmp(step, "Finish")) {
-               fp = fopen(wizard_filename, "w");
-               if (fp != NULL) {
-                       fprintf(fp, "%d\n", serv_info.serv_rev_level);
-                       fclose(fp);
-               }
-               do_welcome();
-               return;
-       }
-
-       output_headers(1, 1, 2, 0, 0, 0);
-
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
-       wprintf("<img src=\"static/citadel-logo.gif\" WIDTH=64 HEIGHT=64 ALT=\" \" ALIGN=MIDDLE>");
-       wprintf("<SPAN CLASS=\"titlebar\">&nbsp;First time setup");
-       wprintf("</SPAN></TD><TD ALIGN=RIGHT>");
-       wprintf("</TD></TR></TABLE>\n");
-       wprintf("</div>\n"
-               "<div id=\"content\">\n");
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<form method=\"post\" action=\"setup_wizard\">\n"
-       );
-
-       wprintf("<div align=center>"
-               "This is where the setup wizard will be placed.<br>\n"
-               "For now, just click Finish.<br><br>\n"
-       );
-
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"step\" VALUE=\"Next\">\n");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"step\" VALUE=\"Finish\">\n");
-
-       wprintf("</form></div></div>\n");
-       wDumpContent(1);
-}
-
-
diff --git a/webcit/siteconfig.c b/webcit/siteconfig.c
deleted file mode 100644 (file)
index 17a7c3c..0000000
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup AdminConfig Administrative screen for site-wide configuration
- * \ingroup CitadelConfig
- */
-/*@{*/
-
-#include "webcit.h"
-
-/**
- * \brief display all configuration items
- */
-void display_siteconfig(void)
-{
-       char buf[SIZ];
-       int i, j;
-
-       char general[SIZ];
-       char access[SIZ];
-       char network[SIZ];
-       char tuning[SIZ];
-       char directory[SIZ];
-       char purger[SIZ];
-       char idxjnl[SIZ];
-
-       /** expire policy settings */
-       int sitepolicy = 0;
-       int sitevalue = 0;
-       int mboxpolicy = 0;
-       int mboxvalue = 0;
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>"
-               "<span class=\"titlebar\">");
-       wprintf(_("Site configuration"));
-       wprintf("</span>"
-               "</td></tr></table>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       serv_printf("CONF get");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               wprintf("<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>");
-               wprintf("<span class=\"titlebar\">");
-               wprintf(_("Error"));
-               wprintf("</span>\n");
-               wprintf("</td></tr></table><br />\n");
-               wprintf("%s<br />\n", &buf[4]);
-               wDumpContent(1);
-               return;
-       }
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>");
-
-       char *tabnames[] = {
-               _("General"),
-               _("Access"),
-               _("Network"),
-               _("Tuning"),
-               _("Directory"),
-               _("Auto-purger"),
-               _("Indexing/Journaling")
-       };
-
-       sprintf(general, "<center><h1>%s</h1><table border=\"0\">",
-                       _("General site configuration items")
-       );
-
-       sprintf(access, "<center><h1>%s</h1><table border=\"0\">",
-                       _("Access controls and site policy settings")
-       );
-
-       sprintf(network, "<center><h1>%s</h1><h2>%s</h2><table border=\"0\">",
-                       _("Network services"),
-                       _("Changes made on this screen will not take effect "
-                       "until you restart the Citadel server.")
-       );
-
-       sprintf(tuning, "<center><h1>%s</h1><table border=\"0\">",
-                       _("Advanced server fine-tuning controls")
-       );
-
-       sprintf(directory, "<center><h1>%s</h1><h2>%s</h2><table border=\"0\">",
-                       _("Configure the LDAP connector for Citadel"),
-                       _("Changes made on this screen will not take effect "
-                       "until you restart the Citadel server.")
-       );
-
-       sprintf(purger, "<center><h1>%s</h1><h2>%s</h2><table border=\"0\">",
-                       _("Configure automatic expiry of old messages"),
-                       _("These settings may be overridden on a per-floor or per-room basis.")
-       );
-
-       sprintf(idxjnl, "<center><h1>%s</h1><h2>%s</h2><table border=\"0\">",
-                       _("Indexing and Journaling"),
-                       _("Warning: these facilities are resource intensive.")
-       );
-
-
-       wprintf("<form method=\"post\" action=\"siteconfig\">\n");
-
-       i = 0;
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               switch (i++) {
-               case 0:
-                       sprintf(&general[strlen(general)], "<tr><td>");
-                       sprintf(&general[strlen(general)], _("Node name"));
-                       sprintf(&general[strlen(general)], "</td><td>");
-                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_nodename\" maxlength=\"15\" value=\"%s\">", buf);
-                       sprintf(&general[strlen(general)], "</td></tr>\n");
-                       break;
-               case 1:
-                       sprintf(&general[strlen(general)], "<tr><td>");
-                       sprintf(&general[strlen(general)], _("Fully qualified domain name"));
-                       sprintf(&general[strlen(general)], "</td><td>");
-                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_fqdn\" maxlength=\"63\" value=\"%s\">", buf);
-                       sprintf(&general[strlen(general)], "</td></tr>\n");
-                       break;
-               case 2:
-                       sprintf(&general[strlen(general)], "<tr><td>");
-                       sprintf(&general[strlen(general)], _("Human-readable node name"));
-                       sprintf(&general[strlen(general)], "</td><td>");
-                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_humannode\" maxlength=\"20\" value=\"%s\">", buf);
-                       sprintf(&general[strlen(general)], "</td></tr>\n");
-                       break;
-               case 3:
-                       sprintf(&general[strlen(general)], "<tr><td>");
-                       sprintf(&general[strlen(general)], _("Telephone number"));
-                       sprintf(&general[strlen(general)], "</td><td>");
-                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_phonenum\" maxlength=\"15\" value=\"%s\">", buf);
-                       sprintf(&general[strlen(general)], "</td></tr>\n");
-                       break;
-               case 4:
-                       sprintf(&access[strlen(access)], "<tr><td>");
-                       sprintf(&access[strlen(access)], _("Automatically grant room-aide status to users who create private rooms"));
-                       sprintf(&access[strlen(access)], "</td><td>");
-                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" name=\"c_creataide\" value=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "checked" : ""));
-                       sprintf(&access[strlen(access)], "</td></tr>\n");
-                       break;
-               case 5:
-                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
-                       sprintf(&tuning[strlen(tuning)], _("Server connection idle timeout (in seconds)"));
-                       sprintf(&tuning[strlen(tuning)], "</td><td>");
-                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_sleeping\" maxlength=\"15\" value=\"%s\">", buf);
-                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
-                       break;
-               case 6:
-                       sprintf(&access[strlen(access)], "<tr><td>");
-                       sprintf(&access[strlen(access)], _("Initial access level for new users"));
-                       sprintf(&access[strlen(access)], "</td><td>");
-                       sprintf(&access[strlen(access)], "<select name=\"c_initax\" size=\"1\">\n");
-                       for (j=0; j<=6; ++j) {
-                               sprintf(&access[strlen(access)], "<option %s value=\"%d\">%d - %s</option>\n",
-                                       ((atoi(buf) == j) ? "selected" : ""),
-                                       j, j, axdefs[j]
-                               );
-                       }
-                       sprintf(&access[strlen(access)], "</select>");
-                       sprintf(&access[strlen(access)], "</td></tr>\n");
-                       break;
-               case 7:
-                       sprintf(&access[strlen(access)], "<tr><td>");
-                       sprintf(&access[strlen(access)], _("Require registration for new users"));
-                       sprintf(&access[strlen(access)], "</td><td>");
-                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" name=\"c_regiscall\" value=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "checked" : ""));
-                       sprintf(&access[strlen(access)], "</td></tr>\n");
-                       break;
-               case 8:
-                       sprintf(&access[strlen(access)], "<tr><td>");
-                       sprintf(&access[strlen(access)], _("Quarantine messages from problem users"));
-                       sprintf(&access[strlen(access)], "</td><td>");
-                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" name=\"c_twitdetect\" value=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "checked" : ""));
-                       sprintf(&access[strlen(access)], "</td></tr>\n");
-                       break;
-               case 9:
-                       sprintf(&access[strlen(access)], "<tr><td>");
-                       sprintf(&access[strlen(access)], _("Name of quarantine room"));
-                       sprintf(&access[strlen(access)], "</td><td>");
-                       sprintf(&access[strlen(access)], "<input type=\"text\" name=\"c_twitroom\" maxlength=\"63\" value=\"%s\">", buf);
-                       sprintf(&access[strlen(access)], "</td></tr>\n");
-                       break;
-               case 10:
-                       sprintf(&general[strlen(general)], "<tr><td>");
-                       sprintf(&general[strlen(general)], _("Paginator prompt (for text mode clients)"));
-                       sprintf(&general[strlen(general)], "</td><td>");
-                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_moreprompt\" maxlength=\"79\" value=\"%s\">", buf);
-                       sprintf(&general[strlen(general)], "</td></tr>\n");
-                       break;
-               case 11:
-                       sprintf(&access[strlen(access)], "<tr><td>");
-                       sprintf(&access[strlen(access)], _("Restrict access to Internet mail"));
-                       sprintf(&access[strlen(access)], "</td><td>");
-                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" name=\"c_restrict\" value=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "checked" : ""));
-                       sprintf(&access[strlen(access)], "</td></tr>\n");
-                       break;
-               case 12:
-                       sprintf(&general[strlen(general)], "<tr><td>");
-                       sprintf(&general[strlen(general)], _("Geographic location of this system"));
-                       sprintf(&general[strlen(general)], "</td><td>");
-                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_bbs_city\" maxlength=\"31\" value=\"%s\">", buf);
-                       sprintf(&general[strlen(general)], "</td></tr>\n");
-                       break;
-               case 13:
-                       sprintf(&general[strlen(general)], "<tr><td>");
-                       sprintf(&general[strlen(general)], _("Name of system administrator"));
-                       sprintf(&general[strlen(general)], "</td><td>");
-                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_sysadm\" MAXLENGTH=\"25\" VALUE=\"%s\">", buf);
-                       sprintf(&general[strlen(general)], "</td></tr>\n");
-                       break;
-               case 14:
-                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
-                       sprintf(&tuning[strlen(tuning)], _("Maximum concurrent sessions (0 = no limit)"));
-                       sprintf(&tuning[strlen(tuning)], "</td><td>");
-                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_maxsessions\" maxlength=\"5\" value=\"%s\">", buf);
-                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
-                       break;
-               case 16:
-                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
-                       sprintf(&tuning[strlen(tuning)], _("Default user purge time (days)"));
-                       sprintf(&tuning[strlen(tuning)], "</td><td>");
-                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_userpurge\" maxlength=\"5\" value=\"%s\">", buf);
-                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
-                       break;
-               case 17:
-                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
-                       sprintf(&tuning[strlen(tuning)], _("Default room purge time (days)"));
-                       sprintf(&tuning[strlen(tuning)], "</td><td>");
-                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_roompurge\" maxlength=\"5\" value=\"%s\">", buf);
-                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
-                       break;
-               case 18:
-                       sprintf(&access[strlen(access)], "<tr><td>");
-                       sprintf(&access[strlen(access)], _("Name of room to log pages"));
-                       sprintf(&access[strlen(access)], "</td><td>");
-                       sprintf(&access[strlen(access)], "<input type=\"text\" name=\"c_logpages\" maxlength=\"63\" value=\"%s\">", buf);
-                       sprintf(&access[strlen(access)], "</td></tr>\n");
-                       break;
-               case 19:
-                       sprintf(&access[strlen(access)], "<tr><td>");
-                       sprintf(&access[strlen(access)], _("Access level required to create rooms"));
-                       sprintf(&access[strlen(access)], "</td><td>");
-                       sprintf(&access[strlen(access)], "<select name=\"c_createax\" size=\"1\">\n");
-                       for (j=0; j<=6; ++j) {
-                               sprintf(&access[strlen(access)], "<option %s value=\"%d\">%d - %s</option>\n",
-                                       ((atoi(buf) == j) ? "selected" : ""),
-                                       j, j, axdefs[j]
-                               );
-                       }
-                       sprintf(&access[strlen(access)], "</select>");
-                       sprintf(&access[strlen(access)], "</td></tr>\n");
-                       break;
-               case 20:
-                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
-                       sprintf(&tuning[strlen(tuning)], _("Maximum message length"));
-                       sprintf(&tuning[strlen(tuning)], "</td><td>");
-                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_maxmsglen\" maxlength=\"20\" value=\"%s\">", buf);
-                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
-                       break;
-               case 21:
-                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
-                       sprintf(&tuning[strlen(tuning)], _("Minimum number of worker threads"));
-                       sprintf(&tuning[strlen(tuning)], "</td><td>");
-                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_min_workers\" maxlength=\"5\" value=\"%s\">", buf);
-                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
-                       break;
-               case 22:
-                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
-                       sprintf(&tuning[strlen(tuning)], _("Maximum number of worker threads"));
-                       sprintf(&tuning[strlen(tuning)], "</td><td>");
-                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_max_workers\" maxlength=\"5\" value=\"%s\">", buf);
-                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
-                       break;
-               case 23:
-                       sprintf(&network[strlen(network)], "<tr><td>");
-                       sprintf(&network[strlen(network)], _("POP3 listener port (-1 to disable)"));
-                       sprintf(&network[strlen(network)], "</td><td>");
-                       sprintf(&network[strlen(network)], "<input type=\"text\" name=\"c_pop3_port\" maxlength=\"5\" value=\"%s\">", buf);
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 24:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("SMTP MTA port (-1 to disable)"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_smtp_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 25:        /* note: reverse bool */
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("Correct forged From: lines during authenticated SMTP"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"checkbox\" NAME=\"c_rfc822_strict_from\" VALUE=\"yes\" %s>",
-                               ((atoi(buf) == 0) ? "CHECKED" : ""));
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 26:
-                       sprintf(&access[strlen(access)], "<TR><TD>");
-                       sprintf(&access[strlen(access)], _("Allow aides to zap (forget) rooms"));
-                       sprintf(&access[strlen(access)], "</TD><TD>");
-                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" NAME=\"c_aide_zap\" VALUE=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "CHECKED" : ""));
-                       sprintf(&access[strlen(access)], "</TD></TR>\n");
-                       break;
-               case 27:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("IMAP listener port (-1 to disable)"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_imap_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 28:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("Network run frequency (in seconds)"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_net_freq\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 29:
-                       sprintf(&access[strlen(access)], "<TR><TD>");
-                       sprintf(&access[strlen(access)], _("Disable self-service user account creation"));
-                       sprintf(&access[strlen(access)], "</TD><TD>");
-                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" NAME=\"c_disable_newu\" VALUE=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "CHECKED" : ""));
-                       sprintf(&access[strlen(access)], "</TD></TR>\n");
-                       break;
-               case 31:
-                       sprintf(&purger[strlen(purger)], "<TR><TD>");
-                       sprintf(&purger[strlen(purger)], _("Hour to run database auto-purge"));
-                       sprintf(&purger[strlen(purger)], "</TD><TD>");
-                       sprintf(&purger[strlen(purger)], "<SELECT NAME=\"c_purge_hour\" SIZE=\"1\">\n");
-                       for (j=0; j<=23; ++j) {
-                               sprintf(&purger[strlen(purger)], "<OPTION %s VALUE=\"%d\">%d:00%s</OPTION>\n",
-                                       ((atoi(buf) == j) ? "SELECTED" : ""),
-                                       j,
-                                       ((j == 0) ? 12 : ((j>12) ? j-12 : j)),
-                                       ((j >= 12) ? "pm" : "am")
-                               );
-                       }
-                       sprintf(&purger[strlen(purger)], "</SELECT>");
-                       sprintf(&purger[strlen(purger)], "</TD></TR>\n");
-                       break;
-               case 32:
-                       sprintf(&directory[strlen(directory)], "<TR><TD>");
-                       sprintf(&directory[strlen(directory)], _("Host name of LDAP server (blank to disable)"));
-                       sprintf(&directory[strlen(directory)], "</TD><TD>");
-                       sprintf(&directory[strlen(directory)], "<input type=\"text\" NAME=\"c_ldap_host\" MAXLENGTH=\"127\" VALUE=\"%s\">", buf);
-                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
-                       break;
-               case 33:
-                       sprintf(&directory[strlen(directory)], "<TR><TD>");
-                       sprintf(&directory[strlen(directory)], _("Port number of LDAP server (blank to disable)"));
-                       sprintf(&directory[strlen(directory)], "</TD><TD>");
-                       sprintf(&directory[strlen(directory)], "<input type=\"text\" NAME=\"c_ldap_port\" MAXLENGTH=\"127\" VALUE=\"%d\">", atoi(buf));
-                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
-                       break;
-               case 34:
-                       sprintf(&directory[strlen(directory)], "<TR><TD>");
-                       sprintf(&directory[strlen(directory)], _("Base DN"));
-                       sprintf(&directory[strlen(directory)], "</TD><TD>");
-                       sprintf(&directory[strlen(directory)], "<input type=\"text\" NAME=\"c_ldap_base_dn\" MAXLENGTH=\"255\" VALUE=\"%s\">", buf);
-                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
-                       break;
-               case 35:
-                       sprintf(&directory[strlen(directory)], "<TR><TD>");
-                       sprintf(&directory[strlen(directory)], _("Bind DN"));
-                       sprintf(&directory[strlen(directory)], "</TD><TD>");
-                       sprintf(&directory[strlen(directory)], "<input type=\"text\" NAME=\"c_ldap_bind_dn\" MAXLENGTH=\"255\" VALUE=\"%s\">", buf);
-                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
-                       break;
-               case 36:
-                       sprintf(&directory[strlen(directory)], "<TR><TD>");
-                       sprintf(&directory[strlen(directory)], _("Password for bind DN"));
-                       sprintf(&directory[strlen(directory)], "</TD><TD>");
-                       sprintf(&directory[strlen(directory)], "<input type=\"password\" NAME=\"c_ldap_bind_pw\" MAXLENGTH=\"255\" VALUE=\"%s\">",
-                               buf);
-                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
-                       break;
-               case 37:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("Server IP address (0.0.0.0 for 'any')"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_ip_addr\" MAXLENGTH=\"15\" VALUE=\"%s\">", buf);
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 38:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("SMTP MSA port (-1 to disable)"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_msa_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 39:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("IMAP over SSL port (-1 to disable)"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_imaps_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 40:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("POP3 over SSL port (-1 to disable)"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_pop3s_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 41:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("SMTP over SSL port (-1 to disable)"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_smtps_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 42:
-                               sprintf(&idxjnl[strlen(idxjnl)], "<TR><TD>");
-                               sprintf(&idxjnl[strlen(idxjnl)], _("Enable full text index"));
-                               sprintf(&idxjnl[strlen(idxjnl)], "</TD><TD>");
-                               sprintf(&idxjnl[strlen(idxjnl)], "<input type=\"checkbox\" NAME=\"c_enable_fulltext\" VALUE=\"yes\" %s>",
-                                       ((atoi(buf) != 0) ? "CHECKED" : ""));
-                               sprintf(&idxjnl[strlen(idxjnl)], "</TD></TR>\n");
-                       break;
-               case 43:
-                       sprintf(&tuning[strlen(tuning)], "<TR><TD>");
-                       sprintf(&tuning[strlen(tuning)], _("Automatically delete committed database logs"));
-                       sprintf(&tuning[strlen(tuning)], "</TD><TD>");
-                       sprintf(&tuning[strlen(tuning)], "<input type=\"checkbox\" NAME=\"c_auto_cull\" VALUE=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "CHECKED" : ""));
-                       sprintf(&tuning[strlen(tuning)], "</TD></TR>\n");
-                       break;
-               case 44:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("Instantly expunge deleted messages in IMAP"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"checkbox\" NAME=\"c_instant_expunge\" VALUE=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "CHECKED" : ""));
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 45:
-                       sprintf(&network[strlen(network)], "<TR><TD>");
-                       sprintf(&network[strlen(network)], _("Allow unauthenticated SMTP clients to spoof this site's domains"));
-                       sprintf(&network[strlen(network)], "</TD><TD>");
-                       sprintf(&network[strlen(network)], "<input type=\"checkbox\" NAME=\"c_allow_spoofing\" VALUE=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "CHECKED" : ""));
-                       sprintf(&network[strlen(network)], "</TD></TR>\n");
-                       break;
-               case 46:
-                       sprintf(&idxjnl[strlen(idxjnl)], "<TR><TD>");
-                       sprintf(&idxjnl[strlen(idxjnl)], _("Perform journaling of email messages"));
-                       sprintf(&idxjnl[strlen(idxjnl)], "</TD><TD>");
-                       sprintf(&idxjnl[strlen(idxjnl)], "<input type=\"checkbox\" NAME=\"c_journal_email\" VALUE=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "CHECKED" : ""));
-                       sprintf(&idxjnl[strlen(idxjnl)], "</TD></TR>\n");
-                       break;
-               case 47:
-                       sprintf(&idxjnl[strlen(idxjnl)], "<TR><TD>");
-                       sprintf(&idxjnl[strlen(idxjnl)], _("Perform journaling of non-email messages"));
-                       sprintf(&idxjnl[strlen(idxjnl)], "</TD><TD>");
-                       sprintf(&idxjnl[strlen(idxjnl)], "<input type=\"checkbox\" NAME=\"c_journal_pubmsgs\" VALUE=\"yes\" %s>",
-                               ((atoi(buf) != 0) ? "CHECKED" : ""));
-                       sprintf(&idxjnl[strlen(idxjnl)], "</TD></TR>\n");
-                       break;
-               case 48:
-                       sprintf(&idxjnl[strlen(idxjnl)], "<TR><TD>");
-                       sprintf(&idxjnl[strlen(idxjnl)], _("Email destination of journalized messages"));
-                       sprintf(&idxjnl[strlen(idxjnl)], "</TD><TD>");
-                       sprintf(&idxjnl[strlen(idxjnl)], "<input type=\"text\" NAME=\"c_journal_dest\" MAXLENGTH=\"127\" VALUE=\"%s\">", buf);
-                       sprintf(&idxjnl[strlen(idxjnl)], "</TD></TR>\n");
-                       break;
-               }
-       }
-
-       serv_puts("GPEX site");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               sitepolicy = extract_int(&buf[4], 0);
-               sitevalue = extract_int(&buf[4], 1);
-       }
-
-       serv_puts("GPEX mailboxes");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               mboxpolicy = extract_int(&buf[4], 0);
-               mboxvalue = extract_int(&buf[4], 1);
-       }
-
-
-       sprintf(&purger[strlen(purger)], "<TR><TD COLSPAN=2><hr /></TD></TR>\n");
-
-       sprintf(&purger[strlen(purger)], "<TR><TD>");
-       sprintf(&purger[strlen(purger)], _("Default message expire policy for public rooms"));
-       sprintf(&purger[strlen(purger)], "</TD><TD>");
-       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"sitepolicy\" VALUE=\"1\" %s>",
-               ((sitepolicy == 1) ? "CHECKED" : "") );
-       sprintf(&purger[strlen(purger)], _("Never automatically expire messages"));
-       sprintf(&purger[strlen(purger)], "<br />\n");
-       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"sitepolicy\" VALUE=\"2\" %s>",
-               ((sitepolicy == 2) ? "CHECKED" : "") );
-       sprintf(&purger[strlen(purger)], _("Expire by message count"));
-       sprintf(&purger[strlen(purger)], "<br />\n");
-       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"sitepolicy\" VALUE=\"3\" %s>",
-               ((sitepolicy == 3) ? "CHECKED" : "") );
-       sprintf(&purger[strlen(purger)], _("Expire by message age"));
-       sprintf(&purger[strlen(purger)], "<br />");
-       sprintf(&purger[strlen(purger)], _("Number of messages or days: "));
-       sprintf(&purger[strlen(purger)], "<input type=\"text\" NAME=\"sitevalue\" MAXLENGTH=\"5\" VALUE=\"%d\">", sitevalue);
-       sprintf(&purger[strlen(purger)], "</TD></TR>\n");
-
-       sprintf(&purger[strlen(purger)], "<TR><TD COLSPAN=2><hr /></TD></TR>\n");
-
-       sprintf(&purger[strlen(purger)], "<TR><TD>");
-       sprintf(&purger[strlen(purger)], _("Default message expire policy for private mailboxes"));
-       sprintf(&purger[strlen(purger)], "</TD><TD>");
-       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"mboxpolicy\" VALUE=\"0\" %s>",
-               ((mboxpolicy == 0) ? "CHECKED" : "") );
-       sprintf(&purger[strlen(purger)], _("Same policy as public rooms"));
-       sprintf(&purger[strlen(purger)], "<br />\n");
-       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"mboxpolicy\" VALUE=\"1\" %s>",
-                       ((mboxpolicy == 1) ? "CHECKED" : "") );
-       sprintf(&purger[strlen(purger)], _("Never automatically expire messages"));
-       sprintf(&purger[strlen(purger)], "<br />\n");
-       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"mboxpolicy\" VALUE=\"2\" %s>",
-               ((mboxpolicy == 2) ? "CHECKED" : "") );
-       sprintf(&purger[strlen(purger)], _("Expire by message count"));
-       sprintf(&purger[strlen(purger)], "<br />\n");
-       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"mboxpolicy\" VALUE=\"3\" %s>",
-               ((mboxpolicy == 3) ? "CHECKED" : "") );
-       sprintf(&purger[strlen(purger)], _("Expire by message age"));
-       sprintf(&purger[strlen(purger)], "<br />");
-       sprintf(&purger[strlen(purger)], _("Number of messages or days: "));
-       sprintf(&purger[strlen(purger)], "<input type=\"text\" NAME=\"mboxvalue\" MAXLENGTH=\"5\" VALUE=\"%d\">", mboxvalue);
-       sprintf(&purger[strlen(purger)], "</TD></TR>\n");
-
-       sprintf(&purger[strlen(purger)], "<TR><TD COLSPAN=2><hr /></TD></TR>\n");
-
-
-       sprintf(&general[strlen(general)], "</table>");
-       sprintf(&access[strlen(access)], "</table>");
-       sprintf(&network[strlen(network)], "</table>");
-       sprintf(&tuning[strlen(tuning)], "</table>");
-       sprintf(&directory[strlen(directory)], "</table>");
-       sprintf(&purger[strlen(purger)], "</table>");
-       sprintf(&idxjnl[strlen(idxjnl)], "</table>");
-
-       tabbed_dialog(7, tabnames);
-
-       begin_tab(0, 7);        wprintf("%s", general);          end_tab(0, 7);
-       begin_tab(1, 7);        wprintf("%s", access);           end_tab(1, 7);
-       begin_tab(2, 7);        wprintf("%s", network);          end_tab(2, 7);
-       begin_tab(3, 7);        wprintf("%s", tuning);           end_tab(3, 7);
-       begin_tab(4, 7);        wprintf("%s", directory);        end_tab(4, 7);
-       begin_tab(5, 7);        wprintf("%s", purger);           end_tab(5, 7);
-       begin_tab(6, 7);        wprintf("%s", idxjnl);   end_tab(6, 7);
-
-       wprintf("<div align=\"center\"><br>");
-       wprintf("<input type=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Save changes"));
-       wprintf("&nbsp;");
-       wprintf("<input type=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">\n", _("Cancel"));
-       wprintf("</div></FORM>\n");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-/**
- * parse siteconfig changes 
- */
-void siteconfig(void)
-{
-       char buf[256];
-
-       if (strlen(bstr("ok_button")) == 0) {
-               display_aide_menu();
-               return;
-       }
-       serv_printf("CONF set");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '4') {
-               safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage);
-               display_aide_menu();
-               return;
-       }
-       serv_printf("%s", bstr("c_nodename"));
-       serv_printf("%s", bstr("c_fqdn"));
-       serv_printf("%s", bstr("c_humannode"));
-       serv_printf("%s", bstr("c_phonenum"));
-       serv_printf("%s", ((!strcasecmp(bstr("c_creataide"), "yes") ? "1" : "0")));
-       serv_printf("%s", bstr("c_sleeping"));
-       serv_printf("%s", bstr("c_initax"));
-       serv_printf("%s", ((!strcasecmp(bstr("c_regiscall"), "yes") ? "1" : "0")));
-       serv_printf("%s", ((!strcasecmp(bstr("c_twitdetect"), "yes") ? "1" : "0")));
-       serv_printf("%s", bstr("c_twitroom"));
-       serv_printf("%s", bstr("c_moreprompt"));
-       serv_printf("%s", ((!strcasecmp(bstr("c_restrict"), "yes") ? "1" : "0")));
-       serv_printf("%s", bstr("c_bbs_city"));
-       serv_printf("%s", bstr("c_sysadm"));
-       serv_printf("%s", bstr("c_maxsessions"));
-       serv_printf("");  /* placeholder - this field is not in use */
-       serv_printf("%s", bstr("c_userpurge"));
-       serv_printf("%s", bstr("c_roompurge"));
-       serv_printf("%s", bstr("c_logpages"));
-       serv_printf("%s", bstr("c_createax"));
-       serv_printf("%s", bstr("c_maxmsglen"));
-       serv_printf("%s", bstr("c_min_workers"));
-       serv_printf("%s", bstr("c_max_workers"));
-       serv_printf("%s", bstr("c_pop3_port"));
-       serv_printf("%s", bstr("c_smtp_port"));
-       serv_printf("%s", ((!strcasecmp(bstr("c_rfc822_strict_from"), "yes") ? "0" : "1"))); /* note: reverse bool */
-       serv_printf("%s", ((!strcasecmp(bstr("c_aide_zap"), "yes") ? "1" : "0")));
-       serv_printf("%s", bstr("c_imap_port"));
-       serv_printf("%s", bstr("c_net_freq"));
-       serv_printf("%s", ((!strcasecmp(bstr("c_disable_newu"), "yes") ? "1" : "0")));
-       serv_printf("1"); /* placeholder - this field is not in use */
-       serv_printf("%s", bstr("c_purge_hour"));
-       serv_printf("%s", bstr("c_ldap_host"));
-       serv_printf("%s", bstr("c_ldap_port"));
-       serv_printf("%s", bstr("c_ldap_base_dn"));
-       serv_printf("%s", bstr("c_ldap_bind_dn"));
-       serv_printf("%s", bstr("c_ldap_bind_pw"));
-       serv_printf("%s", bstr("c_ip_addr"));
-       serv_printf("%s", bstr("c_msa_port"));
-       serv_printf("%s", bstr("c_imaps_port"));
-       serv_printf("%s", bstr("c_pop3s_port"));
-       serv_printf("%s", bstr("c_smtps_port"));
-       serv_printf("%s", ((!strcasecmp(bstr("c_enable_fulltext"), "yes") ? "1" : "0")));
-       serv_printf("%s", ((!strcasecmp(bstr("c_auto_cull"), "yes") ? "1" : "0")));
-       serv_printf("%s", ((!strcasecmp(bstr("c_instant_expunge"), "yes") ? "1" : "0")));
-       serv_printf("%s", ((!strcasecmp(bstr("c_allow_spoofing"), "yes") ? "1" : "0")));
-       serv_printf("%s", ((!strcasecmp(bstr("c_journal_email"), "yes") ? "1" : "0")));
-       serv_printf("%s", ((!strcasecmp(bstr("c_journal_pubmsgs"), "yes") ? "1" : "0")));
-       serv_printf("%s", bstr("c_journal_dest"));
-       serv_printf("000");
-
-       serv_printf("SPEX site|%d|%d", atoi(bstr("sitepolicy")), atoi(bstr("sitevalue")));
-       serv_getln(buf, sizeof buf);
-       serv_printf("SPEX mailboxes|%d|%d", atoi(bstr("mboxpolicy")), atoi(bstr("mboxvalue")));
-       serv_getln(buf, sizeof buf);
-
-       safestrncpy(WC->ImportantMessage, _("Your system configuration has been updated."),
-               sizeof WC->ImportantMessage);
-       display_aide_menu();
-}
-
-
-/*@}*/
diff --git a/webcit/snprintf.c b/webcit/snprintf.c
deleted file mode 100644 (file)
index 66e9701..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * $Id$
- */
-/** 
- * \defgroup SnprintfReplacement modified from Sten Gunterberg's BUGTRAQ post of 22 Jul 1997
- * --nathan bryant <bryant@cs.usm.maine.edu>
- * \ingroup tools
- */
-/*@{*/
-/**
- * \brief Replacements for snprintf() and vsnprintf()
- *
- * Use it only if you have the "spare" cycles needed to effectively
- * do every snprintf operation twice! Why is that? Because everything
- * is first vfprintf()'d to /dev/null to determine the number of bytes.
- * Perhaps a bit slow for demanding applications on slow machines,
- * no problem for a fast machine with some spare cycles.
- *
- * You don't have a /dev/null? Every Linux contains one for free!
- *
- * Because the format string is never even looked at, all current and
- * possible future printf-conversions should be handled just fine.
- *
- * Written July 1997 by Sten Gunterberg (gunterberg@ergon.ch)
- */
-
-#include "webcit.h"
-#include "webserver.h"
-
-/**
- * \brief is it needed????
- * \param fmt the formatstring?
- * \param argp how many params?
- */
-static int needed(const char *fmt, va_list argp)
-{
-       static FILE *sink = NULL;
-
-       /**
-        * ok, there's a small race here that could result in the sink being
-        * opened more than once if we're threaded, but I'd rather ignore it than
-        * spend cycles synchronizing :-) */
-
-       if (sink == NULL) {
-               if ((sink = fopen("/dev/null", "w")) == NULL) {
-                       perror("/dev/null");
-                       exit(1);
-               }
-       }
-       return vfprintf(sink, fmt, argp);
-}
-
-/**
- * \brief vsnprintf wrapper
- * \param buf the output charbuffer
- * \param max maximal size of the buffer
- * \param fmt the formatstring (see man printf)
- * \param argp the variable argument list 
- */
-int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp)
-{
-       char *p;
-       int size;
-
-       if ((p = malloc(needed(fmt, argp) + 1)) == NULL) {
-               lprintf(1, "vsnprintf: malloc failed, aborting\n");
-               abort();
-       }
-       if ((size = vsprintf(p, fmt, argp)) >= max)
-               size = -1;
-
-       strncpy(buf, p, max);
-       buf[max - 1] = 0;
-       free(p);
-       return size;
-}
-
-/**
- * \brief snprintf wrapper
- * \param buf the output charbuffer
- * \param max maximal size of the buffer
- * \param fmt the formatstring (see man printf)
- * \param ... the variable argument list 
- */
-int snprintf(char *buf, size_t max, const char *fmt,...)
-{
-       va_list argp;
-       int bytes;
-
-       va_start(argp, fmt);
-       bytes = vsnprintf(buf, max, fmt, argp);
-       va_end(argp);
-
-       return bytes;
-}
-
-
-
-/*@}*/
diff --git a/webcit/src/auth.c b/webcit/src/auth.c
new file mode 100644 (file)
index 0000000..0f594e1
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * $Id$
+ */
+/**
+ *
+ * \defgroup WebcitAuth WebcitAuth; Handles authentication of users to a Citadel server.
+ * \ingroup CitadelConfig
+ */
+
+/*@{*/
+#include "webcit.h"
+
+
+
+/**
+ * \brief  user states
+ * the plain text states of a user. filled in at \ function TODO initialize_ax_defs()
+ * due to NLS
+ */
+char *axdefs[7]; 
+
+void initialize_axdefs(void) {
+       axdefs[0] = _("Deleted");       /*!0: an erased user */
+       axdefs[1] = _("New User");      /*!1: a new user */
+       axdefs[2] = _("Problem User");  /*!2: a trouble maker */
+       axdefs[3] = _("Local User");    /*!3: user with normal privileges */
+       axdefs[4] = _("Network User");  /*!4: a user that may access network resources */
+       axdefs[5] = _("Preferred User");/*!5: a moderator */
+       axdefs[6] = _("Aide");          /*!6: chief */
+}
+
+
+
+
+/** 
+ * \brief Display the login screen
+ * \param mesg The error message if last attempt failed.
+ */
+void display_login(char *mesg)
+{
+       char buf[SIZ];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div style=\"position:absolute; top:20px; left:20px; right:20px\">\n");
+
+       if (mesg != NULL) if (strlen(mesg) > 0) {
+               stresc(buf, mesg, 0, 0);
+               svprintf("mesg", WCS_STRING, "%s", buf);
+       }
+
+       svprintf("LOGIN_INSTRUCTIONS", WCS_STRING,
+               _("<ul>"
+               "<li><b>If you already have an account on %s</b>, "
+               "enter your user name and password and click &quot;Login.&quot; "
+               "<li><b>If you are a new user</b>, enter the name and password "
+               "you wish to use, "
+               "and click &quot;New User.&quot; "
+               "<li>Please log off properly when finished. "
+               "<li>You must use a browser that supports <i>frames</i> and "
+               "<i>cookies</i>. "
+               "<li>Also keep in mind that if your browser is "
+               "configured to block pop-up windows, you will not be able "
+               "to receive any instant messages.<br />"
+               "</ul>"),
+               serv_info.serv_humannode
+       );
+
+       svprintf("USERNAME_BOX", WCS_STRING, "%s", _("User name:"));
+       svprintf("PASSWORD_BOX", WCS_STRING, "%s", _("Password:"));
+       svprintf("LANGUAGE_BOX", WCS_STRING, "%s", _("Language:"));
+       svprintf("LOGIN_BUTTON", WCS_STRING, "%s", _("Login"));
+       svprintf("NEWUSER_BUTTON", WCS_STRING, "%s", _("New User"));
+       svprintf("EXIT_BUTTON", WCS_STRING, "%s", _("Exit"));
+       svprintf("hello", WCS_SERVCMD, "MESG hello");
+       svprintf("BOXTITLE", WCS_STRING, _("%s - powered by Citadel"),
+               serv_info.serv_humannode);
+       svcallback("DO_LANGUAGE_BOX", offer_languages);
+       if (serv_info.serv_newuser_disabled) {
+               svprintf("NEWUSER_BUTTON_PRE", WCS_STRING, "<div style=\"display:none;\">");
+               svprintf("NEWUSER_BUTTON_POST", WCS_STRING, "</div>");
+       }
+       else {
+               svprintf("NEWUSER_BUTTON_PRE", WCS_STRING, "");
+               svprintf("NEWUSER_BUTTON_POST", WCS_STRING, "");
+       }
+
+       do_template("login");
+
+       wDumpContent(2);
+}
+
+
+
+
+/** \brief Initialize the session
+ * This function needs to get called whenever the session changes from
+ * not-logged-in to logged-in, either by an explicit login by the user or
+ * by a timed-out session automatically re-establishing with a little help
+ * from the browser cookie.  Either way, we need to load access controls and
+ * preferences from the server.
+ *
+ * \param user the username
+ * \param pass his password
+ * \param serv_response The parameters returned from a Citadel USER or NEWU command
+ */
+void become_logged_in(char *user, char *pass, char *serv_response)
+{
+       char buf[SIZ];
+
+       WC->logged_in = 1;
+       extract_token(WC->wc_fullname, &serv_response[4], 0, '|', sizeof WC->wc_fullname);
+       safestrncpy(WC->wc_username, user, sizeof WC->wc_username);
+       safestrncpy(WC->wc_password, pass, sizeof WC->wc_password);
+       WC->axlevel = extract_int(&serv_response[4], 1);
+       if (WC->axlevel >= 6) {
+               WC->is_aide = 1;
+       }
+
+       load_preferences();
+
+       serv_puts("CHEK");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               WC->new_mail = extract_int(&buf[4], 0);
+               WC->need_regi = extract_int(&buf[4], 1);
+               WC->need_vali = extract_int(&buf[4], 2);
+               extract_token(WC->cs_inet_email, &buf[4], 3, '|', sizeof WC->cs_inet_email);
+       }
+
+       get_preference("current_iconbar", buf, sizeof buf);
+       WC->current_iconbar = atoi(buf);
+
+       get_preference("floordiv_expanded", WC->floordiv_expanded, sizeof WC->floordiv_expanded);
+}
+
+
+/** 
+ * \brief Login Checks
+ * the logics to detect invalid passwords not to get on citservers nerves
+ */
+void do_login(void)
+{
+       char buf[SIZ];
+
+       if (strlen(bstr("language")) > 0) {
+               set_selected_language(bstr("language"));
+               go_selected_language();
+       }
+
+       if (strlen(bstr("exit_action")) > 0) {
+               do_logout();
+               return;
+       }
+       if (strlen(bstr("login_action")) > 0) {
+               serv_printf("USER %s", bstr("name"));
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '3') {
+                       serv_printf("PASS %s", bstr("pass"));
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] == '2') {
+                               become_logged_in(bstr("name"),
+                                                bstr("pass"), buf);
+                       } else {
+                               display_login(&buf[4]);
+                               return;
+                       }
+               } else {
+                       display_login(&buf[4]);
+                       return;
+               }
+       }
+       if (strlen(bstr("newuser_action")) > 0) {
+               if (strlen(bstr("pass")) == 0) {
+                       display_login(_("Blank passwords are not allowed."));
+                       return;
+               }
+               serv_printf("NEWU %s", bstr("name"));
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       become_logged_in(bstr("name"), bstr("pass"), buf);
+                       serv_printf("SETP %s", bstr("pass"));
+                       serv_getln(buf, sizeof buf);
+               } else {
+                       display_login(&buf[4]);
+                       return;
+               }
+       }
+       if (WC->logged_in) {
+               if (WC->need_regi) {
+                       display_reg(1);
+               } else {
+                       do_welcome();
+               }
+       } else {
+               display_login(_("Your password was not accepted."));
+       }
+
+}
+
+/**
+ * \brief display the user a welcome screen. 
+ * if this is the first time login, and the web based setup is enabled, 
+ * lead the user through the setup routines
+ */
+void do_welcome(void)
+{
+       char buf[SIZ];
+#ifdef XXX_NOT_FINISHED_YET_XXX
+       FILE *fp;
+       int i;
+
+       /**
+        * See if we have to run the first-time setup wizard
+        */
+       if (WC->is_aide) {
+               if (!setup_wizard) {
+                       sprintf(wizard_filename, "setupwiz.%s.%s",
+                               ctdlhost, ctdlport);
+                       for (i=0; i<strlen(wizard_filename); ++i) {
+                               if (    (wizard_filename[i]==' ')
+                                       || (wizard_filename[i] == '/')
+                               ) {
+                                       wizard_filename[i] = '_';
+                               }
+                       }
+       
+                       fp = fopen(wizard_filename, "r");
+                       if (fp != NULL) {
+                               fgets(buf, sizeof buf, fp);
+                               buf[strlen(buf)-1] = 0;
+                               fclose(fp);
+                               if (atoi(buf) == serv_info.serv_rev_level) {
+                                       setup_wizard = 1; /**< already run */
+                               }
+                       }
+               }
+
+               if (!setup_wizard) {
+                       http_redirect("setup_wizard");
+               }
+       }
+#endif
+
+       /**
+        * Go to the user's preferred start page
+        */
+       get_preference("startpage", buf, sizeof buf);
+       if (strlen(buf)==0) {
+               safestrncpy(buf, "dotskip&room=_BASEROOM_", sizeof buf);
+               set_preference("startpage", buf, 1);
+       }
+       if (buf[0] == '/') {
+               strcpy(buf, &buf[1]);
+       }
+       http_redirect(buf);
+}
+
+
+/**
+ * Disconnect from the Citadel server, and end this WebCit session
+ */
+void end_webcit_session(void) {
+       char buf[256];
+
+       if (WC->logged_in) {
+               sprintf(buf, "%d", WC->current_iconbar);
+               set_preference("current_iconbar", buf, 0);
+               set_preference("floordiv_expanded", WC->floordiv_expanded, 1);
+       }
+
+       serv_puts("QUIT");
+       WC->killthis = 1;
+       /* close() of citadel socket will be done by do_housekeeping() */
+}
+
+/** 
+ * execute the logout
+ */
+void do_logout(void)
+{
+       char buf[SIZ];
+
+       safestrncpy(WC->wc_username, "", sizeof WC->wc_username);
+       safestrncpy(WC->wc_password, "", sizeof WC->wc_password);
+       safestrncpy(WC->wc_roomname, "", sizeof WC->wc_roomname);
+       safestrncpy(WC->wc_fullname, "", sizeof WC->wc_fullname);
+
+       /** Calling output_headers() this way causes the cookies to be un-set */
+       output_headers(1, 1, 0, 1, 0, 0);
+
+       wprintf("<center>");
+       serv_puts("MESG goodbye");
+       serv_getln(buf, sizeof buf);
+
+       if (WC->serv_sock >= 0) {
+               if (buf[0] == '1') {
+                       fmout("CENTER");
+               } else {
+                       wprintf("Goodbye\n");
+               }
+       }
+       else {
+               wprintf(_("This program was unable to connect or stay "
+                       "connected to the Citadel server.  Please report "
+                       "this problem to your system administrator.")
+               );
+       }
+
+       wprintf("<hr /><a href=\".\">");
+       wprintf(_("Log in again"));
+       wprintf("</A>&nbsp;&nbsp;&nbsp;"
+               "<a href=\"javascript:window.close();\">");
+       wprintf(_("Close window"));
+       wprintf("</a></center>\n");
+       wDumpContent(2);
+       end_webcit_session();
+}
+
+
+/* *
+ * validate new users
+ */
+void validate(void)
+{
+       char cmd[SIZ];
+       char user[SIZ];
+       char buf[SIZ];
+       int a;
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Validate new users"));
+       wprintf("</SPAN></TD></TR></TABLE>\n</div>\n<div id=\"content\">\n");
+
+       /** If the user just submitted a validation, process it... */
+       safestrncpy(buf, bstr("user"), sizeof buf);
+       if (strlen(buf) > 0) {
+               if (strlen(bstr("axlevel")) > 0) {
+                       serv_printf("VALI %s|%s", buf, bstr("axlevel"));
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] != '2') {
+                               wprintf("<b>%s</b><br />\n", &buf[4]);
+                       }
+               }
+       }
+
+       /** Now see if any more users require validation. */
+       serv_puts("GNUR");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               wprintf("<b>");
+               wprintf(_("No users require validation at this time."));
+               wprintf("</b><br />\n");
+               wDumpContent(1);
+               return;
+       }
+       if (buf[0] != '3') {
+               wprintf("<b>%s</b><br />\n", &buf[4]);
+               wDumpContent(1);
+               return;
+       }
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+       wprintf("<center>");
+
+       safestrncpy(user, &buf[4], sizeof user);
+       serv_printf("GREG %s", user);
+       serv_getln(cmd, sizeof cmd);
+       if (cmd[0] == '1') {
+               a = 0;
+               do {
+                       serv_getln(buf, sizeof buf);
+                       ++a;
+                       if (a == 1)
+                               wprintf("#%s<br /><H1>%s</H1>",
+                                       buf, &cmd[4]);
+                       if (a == 2)
+                               wprintf("PW: %s<br />\n", buf);
+                       if (a == 3)
+                               wprintf("%s<br />\n", buf);
+                       if (a == 4)
+                               wprintf("%s<br />\n", buf);
+                       if (a == 5)
+                               wprintf("%s, ", buf);
+                       if (a == 6)
+                               wprintf("%s ", buf);
+                       if (a == 7)
+                               wprintf("%s<br />\n", buf);
+                       if (a == 8)
+                               wprintf("%s<br />\n", buf);
+                       if (a == 9)
+                               wprintf(_("Current access level: %d (%s)\n"),
+                                       atoi(buf), axdefs[atoi(buf)]);
+               } while (strcmp(buf, "000"));
+       } else {
+               wprintf("<H1>%s</H1>%s<br />\n", user, &cmd[4]);
+       }
+
+       wprintf("<hr />");
+       wprintf(_("Select access level for this user:"));
+       wprintf("<br />\n");
+       for (a = 0; a <= 6; ++a) {
+               wprintf("<a href=\"validate&user=");
+               urlescputs(user);
+               wprintf("&axlevel=%d\">%s</A>&nbsp;&nbsp;&nbsp;\n",
+                       a, axdefs[a]);
+       }
+       wprintf("<br />\n");
+
+       wprintf("</CENTER>\n");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+
+
+/** 
+ * \brief Display form for registration.
+ * (Set during_login to 1 if this registration is being performed during
+ * new user login and will require chaining to the proper screen.)
+ * \param during_login are we just in the login phase?
+ */
+void display_reg(int during_login)
+{
+       long vcard_msgnum;
+
+       if (goto_config_room() != 0) {
+               if (during_login) do_welcome();
+               else display_main_menu();
+               return;
+       }
+
+       vcard_msgnum = locate_user_vcard(WC->wc_fullname, -1);
+       if (vcard_msgnum < 0L) {
+               if (during_login) do_welcome();
+               else display_main_menu();
+               return;
+       }
+
+       if (during_login) {
+               do_edit_vcard(vcard_msgnum, "1", "do_welcome");
+       }
+       else {
+               do_edit_vcard(vcard_msgnum, "1", "display_main_menu");
+       }
+
+}
+
+
+
+
+/** 
+ * display form for changing your password
+ */
+void display_changepw(void)
+{
+       char buf[SIZ];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Change your password"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       if (strlen(WC->ImportantMessage) > 0) {
+               do_template("beginbox_nt");
+               wprintf("<SPAN CLASS=\"errormsg\">"
+                       "%s</SPAN><br />\n", WC->ImportantMessage);
+               do_template("endbox");
+               safestrncpy(WC->ImportantMessage, "", sizeof WC->ImportantMessage);
+       }
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       wprintf("<CENTER><br />");
+       serv_puts("MESG changepw");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               fmout("CENTER");
+       }
+
+       wprintf("<form name=\"changepwform\" action=\"changepw\" method=\"post\">\n");
+       wprintf("<CENTER>"
+               "<table border=\"0\" cellspacing=\"5\" cellpadding=\"5\" "
+               "BGCOLOR=\"#EEEEEE\">"
+               "<TR><TD>");
+       wprintf(_("Enter new password:"));
+       wprintf("</TD>\n");
+       wprintf("<TD><INPUT TYPE=\"password\" NAME=\"newpass1\" VALUE=\"\" MAXLENGTH=\"20\"></TD></TR>\n");
+       wprintf("<TR><TD>");
+       wprintf(_("Enter it again to confirm:"));
+       wprintf("</TD>\n");
+       wprintf("<TD><INPUT TYPE=\"password\" NAME=\"newpass2\" VALUE=\"\" MAXLENGTH=\"20\"></TD></TR>\n");
+
+       wprintf("</TABLE><br />\n");
+       wprintf("<INPUT type=\"submit\" name=\"change_action\" value=\"%s\">", _("Change password"));
+       wprintf("&nbsp;");
+       wprintf("<INPUT type=\"submit\" name=\"cancel_action\" value=\"%s\">\n", _("Cancel"));
+       wprintf("</form></center>\n");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+/**
+ * \brief change password
+ * if passwords match, propagate it to citserver.
+ */
+void changepw(void)
+{
+       char buf[SIZ];
+       char newpass1[32], newpass2[32];
+
+       if (strlen(bstr("change_action")) == 0) {
+               safestrncpy(WC->ImportantMessage, 
+                       _("Cancelled.  Password was not changed."),
+                       sizeof WC->ImportantMessage);
+               display_main_menu();
+               return;
+       }
+
+       safestrncpy(newpass1, bstr("newpass1"), sizeof newpass1);
+       safestrncpy(newpass2, bstr("newpass2"), sizeof newpass2);
+
+       if (strcasecmp(newpass1, newpass2)) {
+               safestrncpy(WC->ImportantMessage, 
+                       _("They don't match.  Password was not changed."),
+                       sizeof WC->ImportantMessage);
+               display_changepw();
+               return;
+       }
+
+       if (strlen(newpass1) == 0) {
+               safestrncpy(WC->ImportantMessage, 
+                       _("Blank passwords are not allowed."),
+                       sizeof WC->ImportantMessage);
+               display_changepw();
+               return;
+       }
+
+       serv_printf("SETP %s", newpass1);
+       serv_getln(buf, sizeof buf);
+       sprintf(WC->ImportantMessage, "%s", &buf[4]);
+       if (buf[0] == '2') {
+               safestrncpy(WC->wc_password, buf, sizeof WC->wc_password);
+               display_main_menu();
+       }
+       else {
+               display_changepw();
+       }
+}
+
+
+
+/** @} */
diff --git a/webcit/src/autocompletion.c b/webcit/src/autocompletion.c
new file mode 100644 (file)
index 0000000..0b9373e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * $Id$
+ *//**
+ * \defgroup AjaxAutoCompletion ajax-powered autocompletion...
+ * \ingroup ClientPower
+ */
+
+/*@{*/
+#include "webcit.h"
+
+/**
+ * \brief Recipient autocompletion results
+ * \param partial the account to search for ??????
+ */
+void recp_autocomplete(char *partial) {
+       char buf[1024];
+       char name[128];
+
+       output_headers(0, 0, 0, 0, 0, 0);
+
+       wprintf("Content-type: text/html\r\n"
+               "Server: %s\r\n"
+               "Connection: close\r\n"
+               "Pragma: no-cache\r\n"
+               "Cache-Control: no-store\r\n",
+               SERVER);
+       begin_burst();
+
+       wprintf("<ul>");
+
+       serv_printf("AUTO %s", partial);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       extract_token(name, buf, 0, '|', sizeof name);
+                       wprintf("<li>");
+                       escputs(name);
+                       wprintf("</li>");
+               }
+       }
+
+       wprintf("</ul>");
+
+       wprintf("\r\n\r\n");
+       wDumpContent(0);
+}
+
+
+/** @} */
diff --git a/webcit/src/availability.c b/webcit/src/availability.c
new file mode 100644 (file)
index 0000000..0c1198a
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * $Id$
+ */
+/**
+ *
+ * \defgroup CalendarAv  Check attendee availability for scheduling a meeting.
+ * \ingroup Calendaring
+ */
+/*@{*/
+
+
+#include "webcit.h"
+#include "webserver.h"
+
+/** only available if we have calendaring */
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+
+
+
+/**
+ * \brief verify users avaiability
+ * Utility function to fetch a VFREEBUSY type of thing for
+ * any specified user.
+ * \param who string of the user to search
+ */
+icalcomponent *get_freebusy_for_user(char *who) {
+       char buf[SIZ];
+       char *serialized_fb = NULL;
+       icalcomponent *fb = NULL;
+
+       serv_printf("ICAL freebusy|%s", who);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               serialized_fb = read_server_text();
+       }
+
+       if (serialized_fb == NULL) {
+               return NULL;
+       }
+       
+       fb = icalcomponent_new_from_string(serialized_fb);
+       free(serialized_fb);
+       if (fb == NULL) {
+               return NULL;
+       }
+
+       return(fb);
+}
+
+
+
+
+/**
+ * \brief Check if dates are overlapping
+ * Check to see if two events overlap.  
+ * (This function is used in both Citadel and WebCit.  If you change it in
+ * one place, change it in the other.  Better yet, put it in a library.)
+ * \param t1start date one start
+ * \param t1end  date one end
+ * \param t2start date one start
+ * \param t2end date two end
+ * \returns nonzero if they do.
+ */
+int ical_ctdl_is_overlap(
+                       struct icaltimetype t1start,
+                       struct icaltimetype t1end,
+                       struct icaltimetype t2start,
+                       struct icaltimetype t2end
+) {
+
+       if (icaltime_is_null_time(t1start)) return(0);
+       if (icaltime_is_null_time(t2start)) return(0);
+
+       /** First, check for all-day events */
+       if (t1start.is_date) {
+               if (!icaltime_compare_date_only(t1start, t2start)) {
+                       return(1);
+               }
+               if (!icaltime_is_null_time(t2end)) {
+                       if (!icaltime_compare_date_only(t1start, t2end)) {
+                               return(1);
+                       }
+               }
+       }
+
+       if (t2start.is_date) {
+               if (!icaltime_compare_date_only(t2start, t1start)) {
+                       return(1);
+               }
+               if (!icaltime_is_null_time(t1end)) {
+                       if (!icaltime_compare_date_only(t2start, t1end)) {
+                               return(1);
+                       }
+               }
+       }
+
+       /** Now check for overlaps using date *and* time. */
+
+       /** First, bail out if either event 1 or event 2 is missing end time. */
+       if (icaltime_is_null_time(t1end)) return(0);
+       if (icaltime_is_null_time(t2end)) return(0);
+
+       /** If event 1 ends before event 2 starts, we're in the clear. */
+       if (icaltime_compare(t1end, t2start) <= 0) return(0);
+
+       /** If event 2 ends before event 1 starts, we're also ok. */
+       if (icaltime_compare(t2end, t1start) <= 0) return(0);
+
+       /** Otherwise, they overlap. */
+       return(1);
+}
+
+
+
+/*
+ * \brief dig availability on citserver
+ * Back end function for check_attendee_availability()
+ * This one checks an individual attendee against a supplied
+ * event start and end time.  All these fields have already been
+ * broken out.  
+ * \param attendee_string name of the attendee
+ * \param event_start starttime of the event to check
+ * \param event_end endtime of the event to check
+ * \return The result is placed in 'annotation'.
+ */
+void check_individual_attendee(char *attendee_string,
+                               struct icaltimetype event_start,
+                               struct icaltimetype event_end,
+                               char *annotation) {
+
+       icalcomponent *fbc = NULL;
+       icalcomponent *fb = NULL;
+       icalproperty *thisfb = NULL;
+       struct icalperiodtype period;
+
+       /**
+        * Set to 'unknown' right from the beginning.  Unless we learn
+        * something else, that's what we'll go with.
+        */
+       strcpy(annotation, _("availability unknown"));
+
+       fbc = get_freebusy_for_user(attendee_string);
+       if (fbc == NULL) {
+               return;
+       }
+
+       /**
+        * Make sure we're looking at a VFREEBUSY by itself.  What we're probably
+        * looking at initially is a VFREEBUSY encapsulated in a VCALENDAR.
+        */
+       if (icalcomponent_isa(fbc) == ICAL_VCALENDAR_COMPONENT) {
+               fb = icalcomponent_get_first_component(fbc, ICAL_VFREEBUSY_COMPONENT);
+       }
+       else if (icalcomponent_isa(fbc) == ICAL_VFREEBUSY_COMPONENT) {
+               fb = fbc;
+       }
+
+       /** Iterate through all FREEBUSY's looking for conflicts. */
+       if (fb != NULL) {
+
+               strcpy(annotation, _("free"));
+
+               for (thisfb = icalcomponent_get_first_property(fb, ICAL_FREEBUSY_PROPERTY);
+                   thisfb != NULL;
+                   thisfb = icalcomponent_get_next_property(fb, ICAL_FREEBUSY_PROPERTY) ) {
+
+                       /** Do the check */
+                       period = icalproperty_get_freebusy(thisfb);
+                       if (ical_ctdl_is_overlap(period.start, period.end,
+                          event_start, event_end)) {
+                               strcpy(annotation, _("BUSY"));
+                       }
+
+               }
+       }
+
+       icalcomponent_free(fbc);
+}
+
+
+
+
+/**
+ * \brief check attendees availability
+ * Check the availability of all attendees for an event (when possible)
+ * and annotate accordingly.
+ * \param vevent the event which should be compared with attendees calendar
+ */
+void check_attendee_availability(icalcomponent *vevent) {
+       icalproperty *attendee = NULL;
+       icalproperty *dtstart_p = NULL;
+       icalproperty *dtend_p = NULL;
+       struct icaltimetype dtstart_t;
+       struct icaltimetype dtend_t;
+       char attendee_string[SIZ];
+       char annotated_attendee_string[SIZ];
+       char annotation[SIZ];
+
+       if (vevent == NULL) {
+               return;
+       }
+
+       /**
+        * If we're looking at a fully encapsulated VCALENDAR
+        * rather than a VEVENT component, attempt to use the first
+        * relevant VEVENT subcomponent.  If there is none, the
+        * NULL returned by icalcomponent_get_first_component() will
+        * tell the next iteration of this function to create a
+        * new one.
+        */
+       if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) {
+               check_attendee_availability(
+                       icalcomponent_get_first_component(
+                               vevent, ICAL_VEVENT_COMPONENT
+                       )
+               );
+               return;
+       }
+
+       ical_dezonify(vevent);          /**< Convert everything to UTC */
+
+       /**
+        * Learn the start and end times.
+        */
+       dtstart_p = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY);
+       if (dtstart_p != NULL) dtstart_t = icalproperty_get_dtstart(dtstart_p);
+
+       dtend_p = icalcomponent_get_first_property(vevent, ICAL_DTEND_PROPERTY);
+       if (dtend_p != NULL) dtend_t = icalproperty_get_dtend(dtend_p);
+
+       /**
+        * Iterate through attendees.
+        */
+       for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY);
+           attendee != NULL;
+           attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
+
+               strcpy(attendee_string, icalproperty_get_attendee(attendee));
+               if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
+
+                       /** screen name or email address */
+                       strcpy(attendee_string, &attendee_string[7]);
+                       striplt(attendee_string);
+
+                       check_individual_attendee(attendee_string,
+                                               dtstart_t, dtend_t,
+                                               annotation);
+
+                       /** Replace the attendee name with an annotated one. */
+                       snprintf(annotated_attendee_string, sizeof annotated_attendee_string,
+                               "MAILTO:%s (%s)", attendee_string, annotation);
+                       icalproperty_set_attendee(attendee, annotated_attendee_string);
+
+               }
+       }
+
+}
+
+
+#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
+
+/** @} */
diff --git a/webcit/src/calendar.c b/webcit/src/calendar.c
new file mode 100644 (file)
index 0000000..5353dfa
--- /dev/null
@@ -0,0 +1,1008 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup calav Functions which handle calendar objects and their processing/display.
+ * \ingroup Calendaring
+ */
+/* @{ */
+
+#include "webcit.h"
+#include "webserver.h"
+
+#ifndef WEBCIT_WITH_CALENDAR_SERVICE
+
+/**
+ * \brief get around non existing types
+ * Handler stubs for builds with no calendar library available
+ * \param part_source dummy pointer to the source
+ * \param msgnum number of the mesage in the db
+ * \param cal_partnum number of the calendar part
+ */
+void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) {
+
+       wprintf(_("<I>This message contains calendaring/scheduling information,"
+               " but support for calendars is not available on this "
+               "particular system.  Please ask your system administrator to "
+               "install a new version of the Citadel web service with "
+               "calendaring enabled.</I><br />\n")
+       );
+
+}
+
+/**
+ * \brief say we can't display calendar items
+ * \param msgnum number of the mesage in our db
+ */
+void display_calendar(long msgnum) {
+       wprintf(_("<i>"
+               "Cannot display calendar item.  You are seeing this error "
+               "because your WebCit service has not been installed with "
+               "calendar support.  Please contact your system administrator."
+               "</i><br />\n"));
+}
+
+/**
+ * \brief say we can't display task items
+ * \param msgnum number of the mesage in our db
+ */
+void display_task(long msgnum) {
+       wprintf(_("<i>"
+               "Cannot display to-do item.  You are seeing this error "
+               "because your WebCit service has not been installed with "
+               "calendar support.  Please contact your system administrator."
+               "</i><br />\n"));
+}
+/** ok, we have calendaring available */
+#else /* WEBCIT_WITH_CALENDAR_SERVICE */
+
+
+/******   End of handler stubs.  Everything below this line is real.   ******/
+
+
+
+
+/**
+ * \brief Process a calendar object
+ * ...at this point it's already been deserialized by cal_process_attachment()
+ * \param cal teh calendar object
+ * \param recursion_level call stack depth ??????
+ * \param msgnum number of the mesage in our db
+ * \param cal_partnum of the calendar object ???? 
+ */
+void cal_process_object(icalcomponent *cal,
+                       int recursion_level,
+                       long msgnum,
+                       char *cal_partnum
+) {
+       icalcomponent *c;
+       icalproperty *method = NULL;
+       icalproperty_method the_method = ICAL_METHOD_NONE;
+       icalproperty *p;
+       struct icaltimetype t;
+       time_t tt;
+       char buf[256];
+       char conflict_name[256];
+       char conflict_message[256];
+       int is_update = 0;
+
+       /** Leading HTML for the display of this object */
+       if (recursion_level == 0) {
+               wprintf("<CENTER><TABLE border=0>\n");
+       }
+
+       /** Look for a method */
+       method = icalcomponent_get_first_property(cal, ICAL_METHOD_PROPERTY);
+
+       /** See what we need to do with this */
+       if (method != NULL) {
+               the_method = icalproperty_get_method(method);
+               switch(the_method) {
+                   case ICAL_METHOD_REQUEST:
+                       wprintf("<tr><td colspan=\"2\">\n"
+                               "<img align=\"center\" "
+                               "src=\"static/calarea_48x.gif\">"
+                               "&nbsp;&nbsp;"  
+                               "<B>");
+                       wprintf(_("Meeting invitation"));
+                       wprintf("</B></TD></TR>\n");
+                       break;
+                   case ICAL_METHOD_REPLY:
+                       wprintf("<TR><TD COLSPAN=2>\n"
+                               "<IMG ALIGN=CENTER "
+                               "src=\"static/calarea_48x.gif\">"
+                               "&nbsp;&nbsp;"  
+                               "<B>");
+                       wprintf(_("Attendee's reply to your invitation"));
+                       wprintf("</B></TD></TR>\n");
+                       break;
+                   case ICAL_METHOD_PUBLISH:
+                       wprintf("<TR><TD COLSPAN=2>\n"
+                               "<IMG ALIGN=CENTER "
+                               "src=\"static/calarea_48x.gif\">"
+                               "&nbsp;&nbsp;"  
+                               "<B>");
+                       wprintf(_("Published event"));
+                       wprintf("</B></TD></TR>\n");
+                       break;
+                   default:
+                       wprintf("<TR><TD COLSPAN=2>");
+                       wprintf(_("This is an unknown type of calendar item."));
+                       wprintf("</TD></TR>\n");
+                       break;
+               }
+       }
+
+       p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY);
+       if (p != NULL) {
+               wprintf("<TR><TD><B>");
+               wprintf(_("Summary:"));
+               wprintf("</B></TD><TD>");
+               escputs((char *)icalproperty_get_comment(p));
+               wprintf("</TD></TR>\n");
+       }
+
+       p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY);
+       if (p != NULL) {
+               wprintf("<TR><TD><B>");
+               wprintf(_("Location:"));
+               wprintf("</B></TD><TD>");
+               escputs((char *)icalproperty_get_comment(p));
+               wprintf("</TD></TR>\n");
+       }
+
+       /**
+        * Only show start/end times if we're actually looking at the VEVENT
+        * component.  Otherwise it shows bogus dates for things like timezone.
+        */
+       if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) {
+
+               p = icalcomponent_get_first_property(cal,
+                                               ICAL_DTSTART_PROPERTY);
+               if (p != NULL) {
+                       t = icalproperty_get_dtstart(p);
+
+                       if (t.is_date) {
+                               struct tm d_tm;
+                               char d_str[32];
+                               memset(&d_tm, 0, sizeof d_tm);
+                               d_tm.tm_year = t.year - 1900;
+                               d_tm.tm_mon = t.month - 1;
+                               d_tm.tm_mday = t.day;
+                               wc_strftime(d_str, sizeof d_str, "%x", &d_tm);
+                               wprintf("<TR><TD><B>");
+                               wprintf(_("Date:"));
+                               wprintf("</B></TD><TD>%s</TD></TR>", d_str);
+                       }
+                       else {
+                               tt = icaltime_as_timet(t);
+                               fmt_date(buf, tt, 0);
+                               wprintf("<TR><TD><B>");
+                               wprintf(_("Starting date/time:"));
+                               wprintf("</B></TD><TD>%s</TD></TR>", buf);
+                       }
+               }
+       
+               p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
+               if (p != NULL) {
+                       t = icalproperty_get_dtend(p);
+                       tt = icaltime_as_timet(t);
+                       fmt_date(buf, tt, 0);
+                       wprintf("<TR><TD><B>");
+                       wprintf(_("Ending date/time:"));
+                       wprintf("</B></TD><TD>%s</TD></TR>", buf);
+               }
+
+       }
+
+       p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY);
+       if (p != NULL) {
+               wprintf("<TR><TD><B>");
+               wprintf(_("Description:"));
+               wprintf("</B></TD><TD>");
+               escputs((char *)icalproperty_get_comment(p));
+               wprintf("</TD></TR>\n");
+       }
+
+       /** If the component has attendees, iterate through them. */
+       for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY); (p != NULL); p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) {
+               wprintf("<TR><TD><B>");
+               wprintf(_("Attendee:"));
+               wprintf("</B></TD><TD>");
+               safestrncpy(buf, icalproperty_get_attendee(p), sizeof buf);
+               if (!strncasecmp(buf, "MAILTO:", 7)) {
+
+                       /** screen name or email address */
+                       strcpy(buf, &buf[7]);
+                       striplt(buf);
+                       escputs(buf);
+                       wprintf(" ");
+
+                       /** participant status */
+                       partstat_as_string(buf, p);
+                       escputs(buf);
+               }
+               wprintf("</TD></TR>\n");
+       }
+
+       /** If the component has subcomponents, recurse through them. */
+       for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
+           (c != 0);
+           c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
+               /* Recursively process subcomponent */
+               cal_process_object(c, recursion_level+1, msgnum, cal_partnum);
+       }
+
+       /** If this is a REQUEST, display conflicts and buttons */
+       if (the_method == ICAL_METHOD_REQUEST) {
+
+               /* Check for conflicts */
+               lprintf(9, "Checking server calendar for conflicts...\n");
+               serv_printf("ICAL conflicts|%ld|%s|", msgnum, cal_partnum);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '1') {
+                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                               extract_token(conflict_name, buf, 3, '|', sizeof conflict_name);
+                               is_update = extract_int(buf, 4);
+
+                               if (is_update) {
+                                       snprintf(conflict_message, sizeof conflict_message,
+                                               _("This is an update of '%s' which is already in your calendar."), conflict_name);
+                               }
+                               else {
+                                       snprintf(conflict_message, sizeof conflict_message,
+                                               _("This event would conflict with '%s' which is already in your calendar."), conflict_name);
+                               }
+
+                               wprintf("<TR><TD><B><I>%s</I></B></TD><td>",
+                                       (is_update ?
+                                               _("Update:") :
+                                               _("CONFLICT:")
+                                       )
+                               );
+                               escputs(conflict_message);
+                               wprintf("</TD></TR>\n");
+                       }
+               }
+               lprintf(9, "...done.\n");
+
+               /** Display the Accept/Decline buttons */
+               wprintf("<tr><td>%s</td>"
+                       "<td><font size=+1>"
+                       "<a href=\"respond_to_request?msgnum=%ld&cal_partnum=%s&sc=Accept\">%s</a>"
+                       " | "
+                       "<a href=\"respond_to_request?msgnum=%ld&cal_partnum=%s&sc=Tentative\">%s</a>"
+                       " | "
+                       "<a href=\"respond_to_request?msgnum=%ld&cal_partnum=%s&sc=Decline\">%s</a>"
+                       "</FONT></TD></TR>\n",
+                       _("How would you like to respond to this invitation?"),
+                       msgnum, cal_partnum, _("Accept"),
+                       msgnum, cal_partnum, _("Tentative"),
+                       msgnum, cal_partnum, _("Decline")
+               );
+
+       }
+
+       /** If this is a REPLY, display update button */
+       if (the_method == ICAL_METHOD_REPLY) {
+
+               /** \todo  In the future, if we want to validate this object before \
+                * continuing, we can do it this way:
+               serv_printf("ICAL whatever|%ld|%s|", msgnum, cal_partnum);
+               serv_getln(buf, sizeof buf);
+               }
+                ***********/
+
+               /** Display the update buttons */
+               wprintf("<TR><TD>"
+                       "%s"
+                       "</td><td><font size=+1>"
+                       "<a href=\"handle_rsvp?msgnum=%ld&cal_partnum=%s&sc=Update\">%s</a>"
+                       " | "
+                       "<a href=\"handle_rsvp?msgnum=%ld&cal_partnum=%s&sc=Ignore\">%s</a>"
+                       "</font>"
+                       "</TD></TR>\n",
+                       _("Click <i>Update</i> to accept this reply and update your calendar."),
+                       msgnum, cal_partnum, _("Update"),
+                       msgnum, cal_partnum, _("Ignore")
+               );
+
+       }
+
+       /** Trailing HTML for the display of this object */
+       if (recursion_level == 0) {
+
+               wprintf("</TR></TABLE></CENTER>\n");
+       }
+}
+
+
+/**
+ * \brief process calendar mail atachment
+ * Deserialize a calendar object in a message so it can be processed.
+ * (This is the main entry point for these things)
+ * \param part_source the part of the message we want to parse
+ * \param msgnum number of the mesage in our db
+ * \param cal_partnum the number of the calendar item
+ */
+void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) {
+       icalcomponent *cal;
+
+       cal = icalcomponent_new_from_string(part_source);
+
+       if (cal == NULL) {
+               wprintf(_("There was an error parsing this calendar item."));
+               wprintf("<br />\n");
+               return;
+       }
+
+       ical_dezonify(cal);
+       cal_process_object(cal, 0, msgnum, cal_partnum);
+
+       /* Free the memory we obtained from libical's constructor */
+       icalcomponent_free(cal);
+}
+
+
+
+
+/**
+ * \brief accept/decline meeting
+ * Respond to a meeting request
+ */
+void respond_to_request(void) {
+       char buf[SIZ];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Respond to meeting request"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+       );
+       wprintf("</div>\n<div id=\"content\">\n");
+
+       serv_printf("ICAL respond|%s|%s|%s|",
+               bstr("msgnum"),
+               bstr("cal_partnum"),
+               bstr("sc")
+       );
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] == '2') {
+               wprintf("<TABLE BORDER=0><TR><TD>"
+                       "<img src=\"static/calarea_48x.gif\" ALIGN=CENTER>"
+                       "</TD><TD>"
+               );
+               if (!strcasecmp(bstr("sc"), "accept")) {
+                       wprintf(_("You have accepted this meeting invitation.  "
+                               "It has been entered into your calendar.")
+                       );
+               } else if (!strcasecmp(bstr("sc"), "tentative")) {
+                       wprintf(_("You have tentatively accepted this meeting invitation.  "
+                               "It has been 'pencilled in' to your calendar.")
+                       );
+               } else if (!strcasecmp(bstr("sc"), "decline")) {
+                       wprintf(_("You have declined this meeting invitation.  "
+                               "It has <b>not</b> been entered into your calendar.")
+                       );
+               }
+               wprintf(" ");
+               wprintf(_("A reply has been sent to the meeting organizer."));
+               wprintf("</TD></TR></TABLE>\n");
+       } else {
+               wprintf("<img src=\"static/error.gif\" ALIGN=CENTER>"
+                       "%s\n", &buf[4]);
+       }
+
+       wprintf("<a href=\"dotskip?room=");
+       urlescputs(WC->wc_roomname);
+       wprintf("\"><br />");
+       wprintf(_("Return to messages"));
+       wprintf("</A><br />\n");
+
+       wDumpContent(1);
+}
+
+
+
+/**
+ * \brief Handle an incoming RSVP
+ */
+void handle_rsvp(void) {
+       char buf[SIZ];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Update your calendar with this RSVP"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       serv_printf("ICAL handle_rsvp|%s|%s|%s|",
+               bstr("msgnum"),
+               bstr("cal_partnum"),
+               bstr("sc")
+       );
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] == '2') {
+               wprintf("<TABLE BORDER=0><TR><TD>"
+                       "<img src=\"static/calarea_48x.gif\" ALIGN=CENTER>"
+                       "</TD><TD>"
+               );
+               if (!strcasecmp(bstr("sc"), "update")) {
+                       wprintf(_("Your calendar has been updated to reflect this RSVP."));
+               } else if (!strcasecmp(bstr("sc"), "ignore")) {
+                       wprintf(_("You have chosen to ignore this RSVP. "
+                               "Your calendar has <b>not</b> been updated.")
+                       );
+               }
+               wprintf("</TD></TR></TABLE>\n"
+               );
+       } else {
+               wprintf("<img src=\"static/error.gif\" ALIGN=CENTER>"
+                       "%s\n", &buf[4]);
+       }
+
+       wprintf("<a href=\"dotskip?room=");
+       urlescputs(WC->wc_roomname);
+       wprintf("\"><br />");
+       wprintf(_("Return to messages"));
+       wprintf("</A><br />\n");
+
+       wDumpContent(1);
+}
+
+
+
+/*@}*/
+/*-----------------------------------------------------------------------**/
+
+
+
+/**
+ * \defgroup MsgDisplayHandlers Display handlers for message reading 
+ * \ingroup Calendaring
+ */
+
+/*@{*/
+
+
+
+/**
+ * \brief get items, keep them.
+ * If we're reading calendar items, just store them for now.  We have to
+ * sort and re-output them later when we draw the calendar.
+ * \param cal Our calendar to process
+ * \param msgnum number of the mesage in our db
+ */
+void display_individual_cal(icalcomponent *cal, long msgnum) {
+
+       WC->num_cal += 1;
+
+       WC->disp_cal = realloc(WC->disp_cal,
+                       (sizeof(struct disp_cal) * WC->num_cal) );
+       WC->disp_cal[WC->num_cal - 1].cal = icalcomponent_new_clone(cal);
+
+       WC->disp_cal[WC->num_cal - 1].cal_msgnum = msgnum;
+}
+
+
+
+/*
+ * \brief edit a task
+ * Display a task by itself (for editing)
+ * \param supplied_vtodo the todo item we want to edit
+ * \param msgnum number of the mesage in our db
+ */
+void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
+       icalcomponent *vtodo;
+       icalproperty *p;
+       struct icaltimetype t;
+       time_t now;
+       int created_new_vtodo = 0;
+
+       now = time(NULL);
+
+       if (supplied_vtodo != NULL) {
+               vtodo = supplied_vtodo;
+
+               /**
+                * If we're looking at a fully encapsulated VCALENDAR
+                * rather than a VTODO component, attempt to use the first
+                * relevant VTODO subcomponent.  If there is none, the
+                * NULL returned by icalcomponent_get_first_component() will
+                * tell the next iteration of this function to create a
+                * new one.
+                */
+               if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
+                       display_edit_individual_task(
+                               icalcomponent_get_first_component(
+                                       vtodo, ICAL_VTODO_COMPONENT
+                               ), msgnum
+                       );
+                       return;
+               }
+       }
+       else {
+               vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
+               created_new_vtodo = 1;
+       }
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR>"
+               "<TD><img src=\"static/taskmanag_48x.gif\"></TD>"
+               "<td><SPAN CLASS=\"titlebar\">");
+       wprintf(_("Edit task"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>");
+       
+       wprintf("<FORM METHOD=\"POST\" action=\"save_task\">\n");
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
+               msgnum);
+
+       wprintf("<TABLE border=0>\n");
+
+       wprintf("<TR><TD>");
+       wprintf(_("Summary:"));
+       wprintf("</TD><TD>"
+               "<INPUT TYPE=\"text\" NAME=\"summary\" "
+               "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
+       p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
+       if (p != NULL) {
+               escputs((char *)icalproperty_get_comment(p));
+       }
+       wprintf("\"></TD></TR>\n");
+
+       wprintf("<TR><TD>");
+       wprintf(_("Start date:"));
+       wprintf("</TD><TD>");
+       p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY);
+       if (p != NULL) {
+               t = icalproperty_get_dtstart(p);
+       }
+       else {
+               t = icaltime_from_timet(now, 0);
+       }
+       display_icaltimetype_as_webform(&t, "dtstart");
+       wprintf("</TD></TR>\n");
+
+       wprintf("<TR><TD>");
+       wprintf(_("Due date:"));
+       wprintf("</TD><TD>");
+       p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
+       if (p != NULL) {
+               t = icalproperty_get_due(p);
+       }
+       else {
+               t = icaltime_from_timet(now, 0);
+       }
+       display_icaltimetype_as_webform(&t, "due");
+       wprintf("</TD></TR>\n");
+       wprintf("<TR><TD>");
+       wprintf(_("Description:"));
+       wprintf("</TD><TD>");
+       wprintf("<TEXTAREA NAME=\"description\" wrap=soft "
+               "ROWS=10 COLS=80 WIDTH=80>\n"
+       );
+       p = icalcomponent_get_first_property(vtodo, ICAL_DESCRIPTION_PROPERTY);
+       if (p != NULL) {
+               escputs((char *)icalproperty_get_comment(p));
+       }
+       wprintf("</TEXTAREA></TD></TR></TABLE>\n");
+
+       wprintf("<CENTER>"
+               "<INPUT TYPE=\"submit\" NAME=\"save_button\" VALUE=\"%s\">"
+               "&nbsp;&nbsp;"
+               "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
+               "&nbsp;&nbsp;"
+               "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">\n"
+               "</CENTER>\n",
+               _("Save"),
+               _("Delete"),
+               _("Cancel")
+       );
+
+       wprintf("</FORM>\n");
+
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+
+       if (created_new_vtodo) {
+               icalcomponent_free(vtodo);
+       }
+}
+
+/*
+ * \brief Save an edited task
+ * \param supplied_vtodo the task to save
+ * \param msgnum number of the mesage in our db
+ */
+void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
+       char buf[SIZ];
+       int delete_existing = 0;
+       icalproperty *prop;
+       icalcomponent *vtodo, *encaps;
+       int created_new_vtodo = 0;
+       int i;
+       int sequence = 0;
+       struct icaltimetype t;
+
+       if (supplied_vtodo != NULL) {
+               vtodo = supplied_vtodo;
+               /**
+                * If we're looking at a fully encapsulated VCALENDAR
+                * rather than a VTODO component, attempt to use the first
+                * relevant VTODO subcomponent.  If there is none, the
+                * NULL returned by icalcomponent_get_first_component() will
+                * tell the next iteration of this function to create a
+                * new one.
+                */
+               if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
+                       save_individual_task(
+                               icalcomponent_get_first_component(
+                                       vtodo, ICAL_VTODO_COMPONENT
+                               ), msgnum
+                       );
+                       return;
+               }
+       }
+       else {
+               vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
+               created_new_vtodo = 1;
+       }
+
+       if (strlen(bstr("save_button")) > 0) {
+
+               /** Replace values in the component with ones from the form */
+
+               while (prop = icalcomponent_get_first_property(vtodo,
+                     ICAL_SUMMARY_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vtodo, prop);
+                       icalproperty_free(prop);
+               }
+               icalcomponent_add_property(vtodo,
+                       icalproperty_new_summary(bstr("summary")));
+               
+               while (prop = icalcomponent_get_first_property(vtodo,
+                     ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vtodo, prop);
+                       icalproperty_free(prop);
+               }
+               icalcomponent_add_property(vtodo,
+                       icalproperty_new_description(bstr("description")));
+       
+               while (prop = icalcomponent_get_first_property(vtodo,
+                     ICAL_DTSTART_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vtodo, prop);
+                       icalproperty_free(prop);
+               }
+               icaltime_from_webform(&t, "dtstart");
+               icalcomponent_add_property(vtodo,
+                       icalproperty_new_dtstart(t)
+               );
+       
+               while (prop = icalcomponent_get_first_property(vtodo,
+                     ICAL_DUE_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vtodo, prop);
+                       icalproperty_free(prop);
+               }
+               icaltime_from_webform(&t, "due");
+               icalcomponent_add_property(vtodo,
+                       icalproperty_new_due(t)
+               );
+
+               /** Give this task a UID if it doesn't have one. */
+               lprintf(9, "Give this task a UID if it doesn't have one.\n");
+               if (icalcomponent_get_first_property(vtodo,
+                  ICAL_UID_PROPERTY) == NULL) {
+                       generate_uuid(buf);
+                       icalcomponent_add_property(vtodo,
+                               icalproperty_new_uid(buf)
+                       );
+               }
+
+               /** Increment the sequence ID */
+               lprintf(9, "Increment the sequence ID\n");
+               while (prop = icalcomponent_get_first_property(vtodo,
+                     ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
+                       i = icalproperty_get_sequence(prop);
+                       lprintf(9, "Sequence was %d\n", i);
+                       if (i > sequence) sequence = i;
+                       icalcomponent_remove_property(vtodo, prop);
+                       icalproperty_free(prop);
+               }
+               ++sequence;
+               lprintf(9, "New sequence is %d.  Adding...\n", sequence);
+               icalcomponent_add_property(vtodo,
+                       icalproperty_new_sequence(sequence)
+               );
+
+               /**
+                * Encapsulate event into full VCALENDAR component.  Clone it first,
+                * for two reasons: one, it's easier to just free the whole thing
+                * when we're done instead of unbundling, but more importantly, we
+                * can't encapsulate something that may already be encapsulated
+                * somewhere else.
+                */
+               lprintf(9, "Encapsulating into full VCALENDAR component\n");
+               encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo));
+
+               /* Serialize it and save it to the message base */
+               serv_puts("ENT0 1|||4");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '4') {
+                       serv_puts("Content-type: text/calendar");
+                       serv_puts("");
+                       serv_puts(icalcomponent_as_ical_string(encaps));
+                       serv_puts("000");
+
+                       /**
+                        * Probably not necessary; the server will see the UID
+                        * of the object and delete the old one anyway, but
+                        * just in case...
+                        */
+                       delete_existing = 1;
+               }
+               icalcomponent_free(encaps);
+       }
+
+       /**
+        * If the user clicked 'Delete' then explicitly delete the message.
+        */
+       if (strlen(bstr("delete_button")) > 0) {
+               delete_existing = 1;
+       }
+
+       if ( (delete_existing) && (msgnum > 0L) ) {
+               serv_printf("DELE %ld", atol(bstr("msgnum")));
+               serv_getln(buf, sizeof buf);
+       }
+
+       if (created_new_vtodo) {
+               icalcomponent_free(vtodo);
+       }
+
+       /** Go back to the task list */
+       readloop("readfwd");
+}
+
+
+
+/**
+ * \brief generic item handler
+ * Code common to all display handlers.  Given a message number and a MIME
+ * type, we load the message and hunt for that MIME type.  If found, we load
+ * the relevant part, deserialize it into a libical component, filter it for
+ * the requested object type, and feed it to the specified handler.
+ * \param mimetype mimetyp of our object
+ * \param which_kind sort of ical type
+ * \param msgnum number of the mesage in our db
+ * \param callback a funcion \todo
+ *
+ */
+void display_using_handler(long msgnum,
+                       char *mimetype,
+                       icalcomponent_kind which_kind,
+                       void (*callback)(icalcomponent *, long)
+       ) {
+       char buf[SIZ];
+       char mime_partnum[SIZ];
+       char mime_filename[SIZ];
+       char mime_content_type[SIZ];
+       char mime_disposition[SIZ];
+       int mime_length;
+       char relevant_partnum[SIZ];
+       char *relevant_source = NULL;
+       icalcomponent *cal, *c;
+
+       sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
+       serv_puts(buf);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') return;
+
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               if (!strncasecmp(buf, "part=", 5)) {
+                       extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
+                       extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
+                       extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
+                       extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
+                       mime_length = extract_int(&buf[5], 5);
+
+                       if (!strcasecmp(mime_content_type, "text/calendar")) {
+                               strcpy(relevant_partnum, mime_partnum);
+                       }
+
+               }
+       }
+
+       if (strlen(relevant_partnum) > 0) {
+               relevant_source = load_mimepart(msgnum, relevant_partnum);
+               if (relevant_source != NULL) {
+
+                       cal = icalcomponent_new_from_string(relevant_source);
+                       if (cal != NULL) {
+
+                               ical_dezonify(cal);
+
+                               /** Simple components of desired type */
+                               if (icalcomponent_isa(cal) == which_kind) {
+                                       callback(cal, msgnum);
+                               }
+
+                               /** Subcomponents of desired type */
+                               for (c = icalcomponent_get_first_component(cal,
+                                   which_kind);
+                                   (c != 0);
+                                   c = icalcomponent_get_next_component(cal,
+                                   which_kind)) {
+                                       callback(c, msgnum);
+                               }
+                               icalcomponent_free(cal);
+                       }
+                       free(relevant_source);
+               }
+       }
+
+}
+
+/**
+ * \brief display whole calendar
+ * \param msgnum number of the mesage in our db
+ */
+void display_calendar(long msgnum) {
+       display_using_handler(msgnum, "text/calendar",
+                               ICAL_VEVENT_COMPONENT,
+                               display_individual_cal);
+}
+
+/**
+ * \brief display whole taksview
+ * \param msgnum number of the mesage in our db
+ */
+void display_task(long msgnum) {
+       display_using_handler(msgnum, "text/calendar",
+                               ICAL_VTODO_COMPONENT,
+                               display_individual_cal);
+}
+
+/**
+ * \brief display the editor component for a task
+ */
+void display_edit_task(void) {
+       long msgnum = 0L;
+
+       /** Force change the room if we have to */
+       if (strlen(bstr("taskrm")) > 0) {
+               gotoroom(bstr("taskrm"));
+       }
+
+       msgnum = atol(bstr("msgnum"));
+       if (msgnum > 0L) {
+               /** existing task */
+               display_using_handler(msgnum, "text/calendar",
+                               ICAL_VTODO_COMPONENT,
+                               display_edit_individual_task);
+       }
+       else {
+               /** new task */
+               display_edit_individual_task(NULL, 0L);
+       }
+}
+
+/**
+ *\brief save an edited task
+ */
+void save_task(void) {
+       long msgnum = 0L;
+
+       msgnum = atol(bstr("msgnum"));
+       if (msgnum > 0L) {
+               display_using_handler(msgnum, "text/calendar",
+                               ICAL_VTODO_COMPONENT,
+                               save_individual_task);
+       }
+       else {
+               save_individual_task(NULL, 0L);
+       }
+}
+
+/**
+ * \brief display the editor component for an event
+ */
+void display_edit_event(void) {
+       long msgnum = 0L;
+
+       msgnum = atol(bstr("msgnum"));
+       if (msgnum > 0L) {
+               /* existing event */
+               display_using_handler(msgnum, "text/calendar",
+                               ICAL_VEVENT_COMPONENT,
+                               display_edit_individual_event);
+       }
+       else {
+               /* new event */
+               display_edit_individual_event(NULL, 0L);
+       }
+}
+
+/**
+ * \brief save an edited event
+ */
+void save_event(void) {
+       long msgnum = 0L;
+
+       msgnum = atol(bstr("msgnum"));
+
+       if (msgnum > 0L) {
+               display_using_handler(msgnum, "text/calendar",
+                               ICAL_VEVENT_COMPONENT,
+                               save_individual_event);
+       }
+       else {
+               save_individual_event(NULL, 0L);
+       }
+}
+
+
+
+
+
+/**
+ * \brief freebusy display (for client software)
+ * \param req dunno. ?????
+ */
+void do_freebusy(char *req) {
+       char who[SIZ];
+       char buf[SIZ];
+       char *fb;
+
+       extract_token(who, req, 1, ' ', sizeof who);
+       if (!strncasecmp(who, "/freebusy/", 10)) {
+               strcpy(who, &who[10]);
+       }
+       unescape_input(who);
+
+       if ( (!strcasecmp(&who[strlen(who)-4], ".vcf"))
+          || (!strcasecmp(&who[strlen(who)-4], ".ifb"))
+          || (!strcasecmp(&who[strlen(who)-4], ".vfb")) ) {
+               who[strlen(who)-4] = 0;
+       }
+
+       lprintf(9, "freebusy requested for <%s>\n", who);
+       serv_printf("ICAL freebusy|%s", who);
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] != '1') {
+               wprintf("HTTP/1.1 404 %s\n", &buf[4]);
+               output_headers(0, 0, 0, 0, 0, 0);
+               wprintf("Content-Type: text/plain\r\n");
+               wprintf("\r\n");
+               wprintf("%s\n", &buf[4]);
+               return;
+       }
+
+       fb = read_server_text();
+       http_transmit_thing(fb, strlen(fb), "text/calendar", 0);
+       free(fb);
+}
+
+
+
+#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
+
+
+/*@}*/
diff --git a/webcit/src/calendar_tools.c b/webcit/src/calendar_tools.c
new file mode 100644 (file)
index 0000000..d62cb82
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup MiscCal Miscellaneous functions which handle calendar components.
+ * \ingroup Calendaring
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+
+/** Hour strings */
+char *hourname[] = {
+       "12am", "1am", "2am", "3am", "4am", "5am", "6am",
+       "7am", "8am", "9am", "10am", "11am", "12pm",
+       "1pm", "2pm", "3pm", "4pm", "5pm", "6pm",
+       "7pm", "8pm", "9pm", "10pm", "11pm"
+};
+
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+
+/**
+ * \brief display and edit date/time
+ * The display_icaltimetype_as_webform() and icaltime_from_webform() functions
+ * handle the display and editing of date/time properties in web pages.  The
+ * first one converts an icaltimetype into valid HTML markup -- a series of form
+ * fields for editing the date and time.  When the user submits the form, the
+ * results can be fed back into the second function, which turns it back into
+ * an icaltimetype.  The "prefix" string required by both functions is prepended
+ * to all field names.  This allows a form to contain more than one date/time
+ * property (for example, a start and end time) by ensuring the field names are
+ * unique within the form.
+ *
+ * \todo NOTE: These functions assume that the icaltimetype being edited is in UTC, and
+ * will convert to/from local time for editing.  "local" in this case is assumed
+ * to be the time zone in which the WebCit server is running.  A future improvement
+ * might be to allow the user to specify his/her timezone.
+ * \param t the time we want to parse
+ * \param prefix ???? \todo
+ */
+
+
+void display_icaltimetype_as_webform(struct icaltimetype *t, char *prefix) {
+       int i;
+       time_t now;
+       struct tm tm_now;
+       int this_year;
+       time_t tt;
+       struct tm tm;
+       const int span = 10;
+       int all_day_event = 0;
+       time_t monthselect_time;
+       struct tm monthselect_tm;
+       char monthselect_str[32];
+       char calhourformat[16];
+
+       get_preference("calhourformat", calhourformat, sizeof calhourformat);
+
+       now = time(NULL);
+       localtime_r(&now, &tm_now);
+       this_year = tm_now.tm_year + 1900;
+
+       if (t == NULL) return;
+       if (t->is_date) all_day_event = 1;
+       tt = icaltime_as_timet(*t);
+       if (all_day_event) {
+               gmtime_r(&tt, &tm);
+       }
+       else {
+               localtime_r(&tt, &tm);
+       }
+
+       wprintf(_("Month: "));
+       wprintf("<SELECT NAME=\"%s_month\" SIZE=\"1\">\n", prefix);
+       for (i=0; i<=11; ++i) {
+               monthselect_time = 1137997451 + (i * 2592000);
+               localtime_r(&monthselect_time, &monthselect_tm);
+               wc_strftime(monthselect_str, sizeof monthselect_str, "%B", &monthselect_tm);
+               wprintf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
+                       ((tm.tm_mon == i) ? "SELECTED" : ""),
+                       i+1,
+                       monthselect_str
+               );
+       }
+       wprintf("</SELECT>\n");
+
+       wprintf(_("Day: "));
+       wprintf("<SELECT NAME=\"%s_day\" SIZE=\"1\">\n", prefix);
+       for (i=1; i<=31; ++i) {
+               wprintf("<OPTION %s VALUE=\"%d\">%d</OPTION>\n",
+                       ((tm.tm_mday == i) ? "SELECTED" : ""),
+                       i, i
+               );
+       }
+       wprintf("</SELECT>\n");
+
+       wprintf(_("Year: "));
+       wprintf("<SELECT NAME=\"%s_year\" SIZE=\"1\">\n", prefix);
+       if ((this_year - t->year) > span) {
+               wprintf("<OPTION SELECTED VALUE=\"%d\">%d</OPTION>\n",
+                       t->year, t->year);
+       }
+       for (i=(this_year-span); i<=(this_year+span); ++i) {
+               wprintf("<OPTION %s VALUE=\"%d\">%d</OPTION>\n",
+                       ((t->year == i) ? "SELECTED" : ""),
+                       i, i
+               );
+       }
+       if ((t->year - this_year) > span) {
+               wprintf("<OPTION SELECTED VALUE=\"%d\">%d</OPTION>\n",
+                       t->year, t->year);
+       }
+       wprintf("</SELECT>\n");
+
+       wprintf(_("Hour: "));
+       wprintf("<SELECT NAME=\"%s_hour\" SIZE=\"1\">\n", prefix);
+       for (i=0; i<=23; ++i) {
+
+               if (!strcasecmp(calhourformat, "24")) {
+                       wprintf("<OPTION %s VALUE=\"%d\">%d</OPTION>\n",
+                               ((tm.tm_hour == i) ? "SELECTED" : ""),
+                               i, i
+                       );
+               }
+               else {
+                       wprintf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
+                               ((tm.tm_hour == i) ? "SELECTED" : ""),
+                               i, hourname[i]
+                       );
+               }
+
+       }
+       wprintf("</SELECT>\n");
+
+       wprintf(_("Minute: "));
+       wprintf("<SELECT NAME=\"%s_minute\" SIZE=\"1\">\n", prefix);
+       for (i=0; i<=59; ++i) {
+               if ( (i % 5 == 0) || (tm.tm_min == i) ) {
+                       wprintf("<OPTION %s VALUE=\"%d\">:%02d</OPTION>\n",
+                               ((tm.tm_min == i) ? "SELECTED" : ""),
+                               i, i
+                       );
+               }
+       }
+       wprintf("</SELECT>\n");
+}
+
+/**
+ *\brief Get time from form
+ * get the time back from the user and convert it into internal structs.
+ * \param t our time element
+ * \param prefix whats that\todo ????
+ */
+void icaltime_from_webform(struct icaltimetype *t, char *prefix) {
+       char vname[32];
+        time_t tt;
+        struct tm tm;
+       struct icaltimetype t2;
+
+        tt = time(NULL);
+        localtime_r(&tt, &tm);
+
+        sprintf(vname, "%s_month", prefix);     tm.tm_mon = atoi(bstr(vname)) - 1;
+        sprintf(vname, "%s_day", prefix);       tm.tm_mday = atoi(bstr(vname));
+        sprintf(vname, "%s_year", prefix);      tm.tm_year = atoi(bstr(vname)) - 1900;
+        sprintf(vname, "%s_hour", prefix);      tm.tm_hour = atoi(bstr(vname));
+        sprintf(vname, "%s_minute", prefix);    tm.tm_min = atoi(bstr(vname));
+
+        tt = mktime(&tm);
+        t2 = icaltime_from_timet(tt, 0);
+       memcpy(t, &t2, sizeof(struct icaltimetype));
+}
+
+/**
+ *\brief Get time from form
+ * get the time back from the user and convert it into internal structs.
+ * \param t our time element
+ * \param prefix whats that\todo ????
+ */
+
+void icaltime_from_webform_dateonly(struct icaltimetype *t, char *prefix) {
+       char vname[32];
+
+       memset(t, 0, sizeof(struct icaltimetype));
+
+        sprintf(vname, "%s_month", prefix);     t->month = atoi(bstr(vname));
+        sprintf(vname, "%s_day", prefix);       t->day = atoi(bstr(vname));
+        sprintf(vname, "%s_year", prefix);      t->year = atoi(bstr(vname));
+       t->is_utc = 1;
+       t->is_date = 1;
+}
+
+
+/**
+ * \brief Render PAPSTAT
+ * Render a PARTSTAT parameter as a string (and put it in parentheses)
+ * \param buf the string to put it to
+ * \param attendee the attendee to textify
+ */
+void partstat_as_string(char *buf, icalproperty *attendee) {
+       icalparameter *partstat_param;
+       icalparameter_partstat partstat;
+
+       strcpy(buf, _("(status unknown)"));
+
+       partstat_param = icalproperty_get_first_parameter(
+                               attendee,
+                               ICAL_PARTSTAT_PARAMETER
+       );
+       if (partstat_param == NULL) {
+               return;
+       }
+
+       partstat = icalparameter_get_partstat(partstat_param);
+       switch(partstat) {
+               case ICAL_PARTSTAT_X:
+                       strcpy(buf, "(x)");
+                       break;
+               case ICAL_PARTSTAT_NEEDSACTION:
+                       strcpy(buf, _("(needs action)"));
+                       break;
+               case ICAL_PARTSTAT_ACCEPTED:
+                       strcpy(buf, _("(accepted)"));
+                       break;
+               case ICAL_PARTSTAT_DECLINED:
+                       strcpy(buf, _("(declined)"));
+                       break;
+               case ICAL_PARTSTAT_TENTATIVE:
+                       strcpy(buf, _("(tenative)"));
+                       break;
+               case ICAL_PARTSTAT_DELEGATED:
+                       strcpy(buf, _("(delegated)"));
+                       break;
+               case ICAL_PARTSTAT_COMPLETED:
+                       strcpy(buf, _("(completed)"));
+                       break;
+               case ICAL_PARTSTAT_INPROCESS:
+                       strcpy(buf, _("(in process)"));
+                       break;
+               case ICAL_PARTSTAT_NONE:
+                       strcpy(buf, _("(none)"));
+                       break;
+       }
+}
+
+
+/**
+ * \brief embedd
+ * Utility function to encapsulate a subcomponent into a full VCALENDAR
+ * \param subcomp the component to encapsulate
+ * \returns the meta object ???
+ */
+icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp) {
+       icalcomponent *encaps;
+
+       /* lprintf(9, "ical_encapsulate_subcomponent() called\n"); */
+
+       if (subcomp == NULL) {
+               lprintf(3, "ERROR: called with NULL argument!\n");
+               return NULL;
+       }
+
+       /**
+        * If we're already looking at a full VCALENDAR component,
+        * don't bother ... just return itself.
+        */
+       if (icalcomponent_isa(subcomp) == ICAL_VCALENDAR_COMPONENT) {
+               return subcomp;
+       }
+
+       /** Encapsulate the VEVENT component into a complete VCALENDAR */
+       encaps = icalcomponent_new(ICAL_VCALENDAR_COMPONENT);
+       if (encaps == NULL) {
+               lprintf(3, "%s:%d: Error - could not allocate component!\n",
+                       __FILE__, __LINE__);
+               return NULL;
+       }
+
+       /** Set the Product ID */
+       icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID));
+
+       /** Set the Version Number */
+       icalcomponent_add_property(encaps, icalproperty_new_version("2.0"));
+
+       /** Encapsulate the subcomponent inside */
+       /* lprintf(9, "Doing the encapsulation\n"); */
+       icalcomponent_add_component(encaps, subcomp);
+
+       /** Convert all timestamps to UTC so we don't have to deal with
+        * stupid VTIMEZONE crap.
+        */
+       ical_dezonify(encaps);
+
+       /** Return the object we just created. */
+       return(encaps);
+}
+
+
+
+
+#endif
+/*@}*/
diff --git a/webcit/src/calendar_view.c b/webcit/src/calendar_view.c
new file mode 100644 (file)
index 0000000..dde6f21
--- /dev/null
@@ -0,0 +1,984 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup CalHtmlHandles Handles the HTML display of calendar items.
+ * \ingroup Calendaring
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+
+#ifndef WEBCIT_WITH_CALENDAR_SERVICE
+
+/**\brief stub for non-libical builds */
+void do_calendar_view(void) {
+       wprintf("<center><i>");
+       wprintf(_("The calendar view is not available."));
+       wprintf("</i></center><br />\n");
+}
+
+/**\brief stub for non-libical builds */
+void do_tasks_view(void) {     
+       wprintf("<center><I>");
+       wprintf(_("The tasks view is not available."));
+       wprintf("</i></center><br />\n");
+}
+
+#else  /* WEBCIT_WITH_CALENDAR_SERVICE */
+
+/****************************************************************************/
+
+/**
+ * \brief Display one day of a whole month view of a calendar
+ * \param thetime the month we want to see 
+ */
+void calendar_month_view_display_events(time_t thetime) {
+       int i;
+       time_t event_tt;
+       struct tm event_tm;
+       struct tm today_tm;
+       icalproperty *p;
+       struct icaltimetype t;
+       int month, day, year;
+       int all_day_event = 0;
+
+       if (WC->num_cal == 0) {
+               wprintf("<br /><br /><br />\n");
+               return;
+       }
+
+       localtime_r(&thetime, &today_tm);
+       month = today_tm.tm_mon + 1;
+       day = today_tm.tm_mday;
+       year = today_tm.tm_year + 1900;
+
+       for (i=0; i<(WC->num_cal); ++i) {
+               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
+                                               ICAL_DTSTART_PROPERTY);
+               if (p != NULL) {
+                       t = icalproperty_get_dtstart(p);
+                       event_tt = icaltime_as_timet(t);
+
+                       if (t.is_date) all_day_event = 1;
+                       else all_day_event = 0;
+
+                       if (all_day_event) {
+                               gmtime_r(&event_tt, &event_tm);
+                       }
+                       else {
+                               localtime_r(&event_tt, &event_tm);
+                       }
+
+                       if ((event_tm.tm_year == today_tm.tm_year)
+                          && (event_tm.tm_mon == today_tm.tm_mon)
+                          && (event_tm.tm_mday == today_tm.tm_mday)) {
+
+                               p = icalcomponent_get_first_property(
+                                                       WC->disp_cal[i].cal,
+                                                       ICAL_SUMMARY_PROPERTY);
+                               if (p != NULL) {
+
+                                       if (all_day_event) {
+                                               wprintf("<table border=0 cellpadding=2><TR>"
+                                                       "<td bgcolor=\"#CCCCDD\">"
+                                               );
+                                       }
+
+                                       wprintf("<font size=-1>"
+                                               "<a href=\"display_edit_event?msgnum=%ld&calview=%s&year=%s&month=%s&day=%s\">",
+                                               WC->disp_cal[i].cal_msgnum,
+                                               bstr("calview"),
+                                               bstr("year"),
+                                               bstr("month"),
+                                               bstr("day")
+                                       );
+                                       escputs((char *)
+                                               icalproperty_get_comment(p));
+                                       wprintf("</a></font><br />\n");
+
+                                       if (all_day_event) {
+                                               wprintf("</td></tr></table>");
+                                       }
+
+                               }
+
+                       }
+
+
+               }
+       }
+}
+
+
+/**
+ * \brief Display one day of a whole month view of a calendar
+ * \param thetime the month we want to see 
+ */
+void calendar_month_view_brief_events(time_t thetime, const char *daycolor) {
+       int i;
+       time_t event_tt;
+       time_t event_tts;
+       time_t event_tte;
+       struct tm event_tms;
+       struct tm event_tme;
+       struct tm today_tm;
+       icalproperty *p;
+       icalproperty *e;
+       struct icaltimetype t;
+       int month, day, year;
+       int all_day_event = 0;
+       char calhourformat[16];
+       char *timeformat;
+
+       get_preference("calhourformat", calhourformat, sizeof calhourformat);
+       if (!strcasecmp(calhourformat, "24"))   timeformat="%k:%M";
+       else timeformat="%I:%M %p";
+
+       localtime_r(&thetime, &today_tm);
+       month = today_tm.tm_mon + 1;
+       day = today_tm.tm_mday;
+       year = today_tm.tm_year + 1900;
+
+       for (i=0; i<(WC->num_cal); ++i) {
+               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
+                                               ICAL_DTSTART_PROPERTY);
+               if (p != NULL) {
+                       t = icalproperty_get_dtstart(p);
+                       event_tt = icaltime_as_timet(t);
+                       event_tts=event_tt;
+                       if (t.is_date) all_day_event = 1;
+                       else all_day_event = 0;
+
+                       if (all_day_event) {
+                               gmtime_r(&event_tts, &event_tms);
+                       }
+                       else {
+                               localtime_r(&event_tts, &event_tms);
+                       }
+                       /** \todo epoch &! daymask */
+                       if ((event_tms.tm_year == today_tm.tm_year)
+                          && (event_tms.tm_mon == today_tm.tm_mon)
+                          && (event_tms.tm_mday == today_tm.tm_mday)) {
+                               
+                               
+                               char sbuf[255];
+                               char ebuf[255];
+
+                               p = icalcomponent_get_first_property(
+                                                       WC->disp_cal[i].cal,
+                                                       ICAL_SUMMARY_PROPERTY);
+                               e = icalcomponent_get_first_property(
+                                                       WC->disp_cal[i].cal, 
+                                                       ICAL_DTEND_PROPERTY);
+                               if ((p != NULL) && (e != NULL)) {
+                                       time_t difftime;
+                                       int hours, minutes;
+                                       t = icalproperty_get_dtend(e);
+                                       event_tte = icaltime_as_timet(t);
+                                       localtime_r(&event_tte, &event_tme);
+                                       difftime=(event_tte-event_tts)/60;
+                                       hours=(int)(difftime / 60);
+                                       minutes=difftime % 60;
+                                       wprintf("<tr><td bgcolor='%s'>%i:%2i</td><td bgcolor='%s'>"
+                                                       "<font size=-1>"
+                                                       "<a href=\"display_edit_event?msgnum=%ld&calview=%s&year=%s&month=%s&day=%s\">",
+                                                       daycolor,
+                                                       hours, minutes,
+                                                       daycolor,
+                                                       WC->disp_cal[i].cal_msgnum,
+                                                       bstr("calview"),
+                                                       bstr("year"),
+                                                       bstr("month"),
+                                                       bstr("day")
+                                                       );
+
+                                       escputs((char *)
+                                                       icalproperty_get_comment(p));
+                                       /** \todo: allso ammitime format */
+                                       wc_strftime(&sbuf[0], sizeof(sbuf), timeformat, &event_tms);
+                                       wc_strftime(&ebuf[0], sizeof(sbuf), timeformat, &event_tme);
+
+                                       wprintf("</a></font></td>"
+                                                       "<td bgcolor='%s'>%s</td><td bgcolor='%s'>%s</td></tr>",
+                                                       daycolor,
+                                                       sbuf,
+                                                       daycolor,
+                                                       ebuf);
+                                       
+                               }
+                               
+                       }
+                       
+                       
+               }
+       }
+}
+
+
+/**
+ * \brief view one month. pretty view
+ * \param year the year
+ * \param month the month
+ * \param day the actual day we want to see
+ */
+void calendar_month_view(int year, int month, int day) {
+       struct tm starting_tm;
+       struct tm tm;
+       time_t thetime;
+       int i;
+       time_t previous_month;
+       time_t next_month;
+       time_t colheader_time;
+       struct tm colheader_tm;
+       char colheader_label[32];
+
+       /** Determine what day to start.
+        * First, back up to the 1st of the month...
+        */
+       memset(&starting_tm, 0, sizeof(struct tm));
+       starting_tm.tm_year = year - 1900;
+       starting_tm.tm_mon = month - 1;
+       starting_tm.tm_mday = day;
+       thetime = mktime(&starting_tm);
+
+       memcpy(&tm, &starting_tm, sizeof(struct tm));
+       while (tm.tm_mday != 1) {
+               thetime = thetime - (time_t)86400;      /* go back 24 hours */
+               localtime_r(&thetime, &tm);
+       }
+
+       /** Determine previous and next months ... for links */
+       previous_month = thetime - (time_t)864000L;     /* back 10 days */
+       next_month = thetime + (time_t)(31L * 86400L);  /* ahead 31 days */
+
+       /** Now back up until we're on a Sunday */
+       localtime_r(&thetime, &tm);
+       while (tm.tm_wday != 0) {
+               thetime = thetime - (time_t)86400;      /* go back 24 hours */
+               localtime_r(&thetime, &tm);
+       }
+
+       /** Outer table (to get the background color) */
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table width=100%% border=0 cellpadding=0 cellspacing=0 "
+               "bgcolor=#204B78><TR><TD>\n");
+
+       wprintf("<table width=100%% border=0 cellpadding=0 cellspacing=0><tr>\n");
+
+       wprintf("<td align=center>");
+
+       localtime_r(&previous_month, &tm);
+       wprintf("<a href=\"readfwd?calview=month&year=%d&month=%d&day=1\">",
+               (int)(tm.tm_year)+1900, tm.tm_mon + 1);
+       wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>\n");
+
+       wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm);
+       wprintf("&nbsp;&nbsp;"
+               "<font size=+1 color=\"#FFFFFF\">"
+               "%s %d"
+               "</font>"
+               "&nbsp;&nbsp;", colheader_label, year);
+
+       localtime_r(&next_month, &tm);
+       wprintf("<a href=\"readfwd?calview=month&year=%d&month=%d&day=1\">",
+               (int)(tm.tm_year)+1900, tm.tm_mon + 1);
+       wprintf("<img align=middle src=\"static/nextdate_32x.gif\" border=0></A>\n");
+
+       wprintf("</td></tr></table>\n");
+
+       /** Inner table (the real one) */
+       wprintf("<table width=100%% border=0 cellpadding=1 cellspacing=1 "
+               "bgcolor=#204B78><tr>");
+       colheader_time = thetime;
+       for (i=0; i<7; ++i) {
+               colheader_time = thetime + (i * 86400) ;
+               localtime_r(&colheader_time, &colheader_tm);
+               wc_strftime(colheader_label, sizeof colheader_label, "%A", &colheader_tm);
+               wprintf("<td align=center width=14%%>"
+                       "<font color=\"#FFFFFF\">%s</font></th>", colheader_label);
+
+       }
+       wprintf("</tr>\n");
+
+       /** Now do 35 days */
+       for (i = 0; i < 35; ++i) {
+               localtime_r(&thetime, &tm);
+
+               /** Before displaying Sunday, start a new row */
+               if ((i % 7) == 0) {
+                       wprintf("<tr>");
+               }
+
+               wprintf("<td bgcolor=\"#%s\" width=14%% height=60 align=left valign=top><b>",
+                       ((tm.tm_mon != month-1) ? "DDDDDD" :
+                       ((tm.tm_wday==0 || tm.tm_wday==6) ? "EEEECC" :
+                       "FFFFFF"))
+               );
+               if ((i==0) || (tm.tm_mday == 1)) {
+                       wc_strftime(colheader_label, sizeof colheader_label, "%B", &tm);
+                       wprintf("%s ", colheader_label);
+               }
+               wprintf("<a href=\"readfwd?calview=day&year=%d&month=%d&day=%d\">"
+                       "%d</a></b><br />",
+                       tm.tm_year + 1900,
+                       tm.tm_mon + 1,
+                       tm.tm_mday,
+                       tm.tm_mday);
+
+               /** put the data here, stupid */
+               calendar_month_view_display_events(thetime);
+
+               wprintf("</td>");
+
+               /** After displaying Saturday, end the row */
+               if ((i % 7) == 6) {
+                       wprintf("</tr>\n");
+               }
+
+               thetime += (time_t)86400;               /** ahead 24 hours */
+       }
+
+       wprintf("</table>"                      /** end of inner table */
+               "</td></tr></table>"            /** end of outer table */
+               "</div>\n");
+}
+
+/**
+ * \brief view one month. brief view
+ * \param year the year
+ * \param month the month
+ * \param day the actual day we want to see
+ */
+void calendar_brief_month_view(int year, int month, int day) {
+       struct tm starting_tm;
+       struct tm tm;
+       time_t thetime;
+       int i;
+       time_t previous_month;
+       time_t next_month;
+       char month_label[32];
+
+       /** Determine what day to start.
+        * First, back up to the 1st of the month...
+        */
+       memset(&starting_tm, 0, sizeof(struct tm));
+       starting_tm.tm_year = year - 1900;
+       starting_tm.tm_mon = month - 1;
+       starting_tm.tm_mday = day;
+       thetime = mktime(&starting_tm);
+
+       memcpy(&tm, &starting_tm, sizeof(struct tm));
+       while (tm.tm_mday != 1) {
+               thetime = thetime - (time_t)86400;      /* go back 24 hours */
+               localtime_r(&thetime, &tm);
+       }
+
+       /** Determine previous and next months ... for links */
+       previous_month = thetime - (time_t)864000L;     /* back 10 days */
+       next_month = thetime + (time_t)(31L * 86400L);  /* ahead 31 days */
+
+       /** Now back up until we're on a Sunday */
+       localtime_r(&thetime, &tm);
+       while (tm.tm_wday != 0) {
+               thetime = thetime - (time_t)86400;      /* go back 24 hours */
+               localtime_r(&thetime, &tm);
+       }
+
+       /** Outer table (to get the background color) */
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table width=100%% border=0 cellpadding=0 cellspacing=0 "
+               "bgcolor=#204B78><TR><TD>\n");
+
+       wprintf("<table width=100%% border=0 cellpadding=0 cellspacing=0><tr>\n");
+
+       wprintf("<td align=center>");
+
+       localtime_r(&previous_month, &tm);
+       wprintf("<a href=\"readfwd?calview=month&year=%d&month=%d&day=1\">",
+               (int)(tm.tm_year)+1900, tm.tm_mon + 1);
+       wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>\n");
+
+       wc_strftime(month_label, sizeof month_label, "%B", &tm);
+       wprintf("&nbsp;&nbsp;"
+               "<font size=+1 color=\"#FFFFFF\">"
+               "%s %d"
+               "</font>"
+               "&nbsp;&nbsp;", month_label, year);
+
+       localtime_r(&next_month, &tm);
+       wprintf("<a href=\"readfwd?calview=month&year=%d&month=%d&day=1\">",
+               (int)(tm.tm_year)+1900, tm.tm_mon + 1);
+       wprintf("<img align=middle src=\"static/nextdate_32x.gif\" border=0></A>\n");
+
+       wprintf("</td></tr></table>\n");
+
+       /** Inner table (the real one) */
+       wprintf("<table width=100%% border=0 cellpadding=1 cellspacing=1 "
+               "bgcolor=#EEEECC><TR>");
+       wprintf("</tr>\n");
+       wprintf("<tr><td colspan=\"100%\">\n");
+
+       /** Now do 35 days */
+       for (i = 0; i < 35; ++i) {
+               char weeknumber[255];
+               char weekday_name[32];
+               char *daycolor;
+               localtime_r(&thetime, &tm);
+
+
+               /** Before displaying Sunday, start a new CELL */
+               if ((i % 7) == 0) {
+                       wc_strftime(&weeknumber[0], sizeof(weeknumber), "%U", &tm);
+                       wprintf("<table border='0' bgcolor=\"#EEEECC\" width='100%'> <tr><th colspan='4'>%s %s</th></tr>"
+                                       "   <tr><td>%s</td><td width='70%'>%s</td><td>%s</td><td>%s</td></tr>\n",
+                                       _("Week"), 
+                                       weeknumber,
+                                       _("Hours"),
+                                       _("Subject"),
+                                       _("Start"),
+                                       _("End")
+                                       );
+               }
+               
+               daycolor=((tm.tm_mon != month-1) ? "DDDDDD" :
+                                 ((tm.tm_wday==0 || tm.tm_wday==6) ? "EEEECC" :
+                                  "FFFFFF"));
+               
+               /** Day Header */
+               wc_strftime(weekday_name, sizeof weekday_name, "%A", &tm);
+               wprintf("<tr><td bgcolor='%s' colspan='1' align='left'> %s,%i."
+                               "</td><td bgcolor='%s' colspan='3'><hr></td></tr>\n",
+                               daycolor,
+                               weekday_name,tm.tm_mday,
+                               daycolor);
+
+               /** put the data of one day  here, stupid */
+               calendar_month_view_brief_events(thetime, daycolor);
+
+
+               /** After displaying Saturday, end the row */
+               if ((i % 7) == 6) {
+                       wprintf("</td></tr></table>\n");
+               }
+
+               thetime += (time_t)86400;               /** ahead 24 hours */
+       }
+
+       wprintf("</table>"                      /** end of inner table */
+               "</td></tr></table>"            /** end of outer table */
+               "</div>\n");
+}
+
+/** 
+ * \brief view one week
+ * this should view just one week, but it's not here yet.
+ * \todo ny implemented
+ * \param year the year
+ * \param month the month
+ * \param day the day which we want to see the week around
+ */
+void calendar_week_view(int year, int month, int day) {
+       wprintf("<center><i>week view FIXME</i></center><br />\n");
+}
+
+
+/**
+ * \brief display one day
+ * Display events for a particular hour of a particular day.
+ * (Specify hour < 0 to show "all day" events)
+ * \param year the year
+ * \param month the month
+ * \param day the day
+ * \param hour the hour we want to start displaying?????
+ */
+void calendar_day_view_display_events(int year, int month,
+                                       int day, int hour) {
+       int i;
+       icalproperty *p;
+       struct icaltimetype t;
+       time_t event_tt;
+       struct tm *event_tm;
+       int all_day_event = 0;
+
+       if (WC->num_cal == 0) {
+               // \todo FIXME wprintf("<br /><br /><br />\n");
+               return;
+       }
+
+       for (i=0; i<(WC->num_cal); ++i) {
+               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
+                                               ICAL_DTSTART_PROPERTY);
+               if (p != NULL) {
+                       t = icalproperty_get_dtstart(p);
+                       event_tt = icaltime_as_timet(t);
+                       if (t.is_date) {
+                               all_day_event = 1;
+                       }
+                       else {
+                               all_day_event = 0;
+                       }
+
+                       if (all_day_event) {
+                               event_tm = gmtime(&event_tt);
+                       }
+                       else {
+                               event_tm = localtime(&event_tt);
+                       }
+
+                       if ((event_tm->tm_year == (year-1900))
+                          && (event_tm->tm_mon == (month-1))
+                          && (event_tm->tm_mday == day)
+                          && ( ((event_tm->tm_hour == hour)&&(!t.is_date)) || ((hour<0)&&(t.is_date)) )
+                          ) {
+
+
+                               p = icalcomponent_get_first_property(
+                                                       WC->disp_cal[i].cal,
+                                                       ICAL_SUMMARY_PROPERTY);
+                               if (p != NULL) {
+
+                                       if (all_day_event) {
+                                               wprintf("<table border=1 cellpadding=2><TR>"
+                                                       "<td bgcolor=\"#CCCCCC\">"
+                                               );
+                                       }
+
+                                       wprintf("<font size=-1>"
+                                               "<a href=\"display_edit_event?msgnum=%ld&calview=day&year=%d&month=%d&day=%d\">",
+                                               WC->disp_cal[i].cal_msgnum,
+                                               year, month, day
+                                       );
+                                       escputs((char *)
+                                               icalproperty_get_comment(p));
+                                       wprintf("</a></font><br />\n");
+
+                                       if (all_day_event) {
+                                               wprintf("</td></tr></table>");
+                                       }
+                               }
+
+                       }
+
+
+               }
+       }
+}
+
+
+/**
+ * \brief view one day
+ * \param year the year
+ * \param month the month 
+ * \param day the day we want to display
+ */
+void calendar_day_view(int year, int month, int day) {
+       int hour;
+       struct icaltimetype today, yesterday, tomorrow;
+       char calhourformat[16];
+       int daystart = 8;
+       int dayend = 17;
+       char daystart_str[16], dayend_str[16];
+       struct tm d_tm;
+       char d_str[128];
+
+       get_preference("calhourformat", calhourformat, sizeof calhourformat);
+       get_preference("daystart", daystart_str, sizeof daystart_str);
+       if (strlen(daystart_str) > 0) daystart = atoi(daystart_str);
+       get_preference("dayend", dayend_str, sizeof dayend_str);
+       if (strlen(dayend_str) > 0) dayend = atoi(dayend_str);
+       
+
+       /** Figure out the dates for "yesterday" and "tomorrow" links */
+
+       memset(&today, 0, sizeof(struct icaltimetype));
+       today.year = year;
+       today.month = month;
+       today.day = day;
+       today.is_date = 1;
+
+       memcpy(&yesterday, &today, sizeof(struct icaltimetype));
+       --yesterday.day;
+       yesterday = icaltime_normalize(yesterday);
+
+       memcpy(&tomorrow, &today, sizeof(struct icaltimetype));
+       ++tomorrow.day;
+       tomorrow = icaltime_normalize(tomorrow);
+
+
+       /** Outer table (to get the background color) */
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table width=100%% border=0 cellpadding=0 cellspacing=0 "
+               "bgcolor=#204B78><tr><td>\n");
+
+       /** Inner table (the real one) */
+       wprintf("<table width=100%% border=0 cellpadding=1 cellspacing=1 "
+               "bgcolor=#204B78><tr>\n");
+
+       /** Innermost table (contains hours etc.) */
+       wprintf("<td width=80%%>"
+               "<table width=100%% border=0 cellpadding=1 cellspacing=1 "
+               "bgcolor=#204B78>\n");
+
+       /** Display events before 8:00 (hour=-1 is all-day events) */
+       wprintf("<tr>"
+               "<td bgcolor=\"#CCCCDD\" valign=middle width=10%%></td>"
+               "<td bgcolor=\"#FFFFFF\" valign=top>");
+       for (hour = (-1); hour <= (daystart-1); ++hour) {
+               calendar_day_view_display_events(year, month, day, hour);
+       }
+       wprintf("</td></tr>\n");
+
+       /** Now the middle of the day... */     
+       for (hour = daystart; hour <= dayend; ++hour) { /* could do HEIGHT=xx */
+               wprintf("<tr height=30><td bgcolor=\"#CCCCDD\" align=middle "
+                       "valign=middle width=10%%>");
+               wprintf("<a href=\"display_edit_event?msgnum=0"
+                       "&year=%d&month=%d&day=%d&hour=%d&minute=0\">",
+                       year, month, day, hour
+               );
+
+               if (!strcasecmp(calhourformat, "24")) {
+                       wprintf("%2d:00</a> ", hour);
+               }
+               else {
+                       wprintf("%d:00%s</a> ",
+                               (hour <= 12 ? hour : hour-12),
+                               (hour < 12 ? "am" : "pm")
+                       );
+               }
+
+               wprintf("</td><td bgcolor=\"#FFFFFF\" valign=top>");
+
+               /* put the data here, stupid */
+               calendar_day_view_display_events(year, month, day, hour);
+
+               wprintf("</td></tr>\n");
+       }
+
+       /** Display events after 5:00... */
+       wprintf("<tr>"
+               "<td bgcolor=\"#CCCCDD\" valign=middle width=10%%></td>"
+               "<td bgcolor=\"#FFFFFF\" valign=top>");
+       for (hour = (dayend+1); hour <= 23; ++hour) {
+               calendar_day_view_display_events(year, month, day, hour);
+       }
+       wprintf("</td></tr>\n");
+
+
+       wprintf("</table>"                      /* end of innermost table */
+               "</td>"
+       );
+
+       wprintf("<td width=20%% valign=top>");  /* begin stuff-on-the-right */
+
+
+       /** Begin todays-date-with-left-and-right-arrows */
+       wprintf("<table border=0 width=100%% "
+               "cellspacing=0 cellpadding=0 bgcolor=\"#FFFFFF\">\n");
+       wprintf("<tr>");
+
+       /** Left arrow */       
+       wprintf("<td align=center>");
+       wprintf("<a href=\"readfwd?calview=day&year=%d&month=%d&day=%d\">",
+               yesterday.year, yesterday.month, yesterday.day);
+       wprintf("<img align=middle src=\"static/prevdate_32x.gif\" border=0></A>");
+       wprintf("</td>");
+
+       /** Today's date */
+       memset(&d_tm, 0, sizeof d_tm);
+       d_tm.tm_year = year - 1900;
+       d_tm.tm_mon = month - 1;
+       d_tm.tm_mday = day;
+       wc_strftime(d_str, sizeof d_str,
+               "<td align=center>"
+               "<font size=+2>%B</font><br />"
+               "<font size=+3>%d</font><br />"
+               "<font size=+2>%Y</font><br />"
+               "</td>",
+               &d_tm
+       );
+       wprintf("%s", d_str);
+
+       /** Right arrow */
+       wprintf("<td align=center>");
+       wprintf("<a href=\"readfwd?calview=day&year=%d&month=%d&day=%d\">",
+               tomorrow.year, tomorrow.month, tomorrow.day);
+       wprintf("<img align=middle src=\"static/nextdate_32x.gif\""
+               " border=0></A>\n");
+       wprintf("</td>");
+
+       wprintf("</tr></table>\n");
+       /** End todays-date-with-left-and-right-arrows */
+
+       /** \todo In the future we might want to put a month-o-matic here */
+
+       wprintf("</font></center>\n");
+
+       wprintf("</td>");                       /** end stuff-on-the-right */
+
+
+
+       wprintf("</tr></table>"                 /** end of inner table */
+               "</td></tr></table></div>"      /** end of outer table */
+       );
+
+
+
+}
+
+/**
+ * \brief Display today's events.
+ */
+void calendar_summary_view(void) {
+       int i;
+       icalproperty *p;
+       struct icaltimetype t;
+       time_t event_tt;
+       struct tm event_tm;
+       struct tm today_tm;
+       time_t now;
+       int all_day_event = 0;
+       char timestring[SIZ];
+
+       if (WC->num_cal == 0) {
+               return;
+       }
+
+       now = time(NULL);
+       localtime_r(&now, &today_tm);
+
+       for (i=0; i<(WC->num_cal); ++i) {
+               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
+                                               ICAL_DTSTART_PROPERTY);
+               if (p != NULL) {
+                       t = icalproperty_get_dtstart(p);
+                       event_tt = icaltime_as_timet(t);
+                       if (t.is_date) {
+                               all_day_event = 1;
+                       }
+                       else {
+                               all_day_event = 0;
+                       }
+                       fmt_time(timestring, event_tt);
+
+                       if (all_day_event) {
+                               gmtime_r(&event_tt, &event_tm);
+                       }
+                       else {
+                               localtime_r(&event_tt, &event_tm);
+                       }
+
+                       if ( (event_tm.tm_year == today_tm.tm_year)
+                          && (event_tm.tm_mon == today_tm.tm_mon)
+                          && (event_tm.tm_mday == today_tm.tm_mday)
+                          ) {
+
+
+                               p = icalcomponent_get_first_property(
+                                                       WC->disp_cal[i].cal,
+                                                       ICAL_SUMMARY_PROPERTY);
+                               if (p != NULL) {
+                                       escputs((char *)
+                                               icalproperty_get_comment(p));
+                                       wprintf(" (%s)<br />\n", timestring);
+                               }
+                       }
+               }
+       }
+       free_calendar_buffer();
+}
+
+
+/**
+ * \brief clean up ical memory
+ * \todo this could get troubel with future ical versions
+ */
+void free_calendar_buffer(void) {
+       int i;
+       if (WC->num_cal) for (i=0; i<(WC->num_cal); ++i) {
+               icalcomponent_free(WC->disp_cal[i].cal);
+       }
+       WC->num_cal = 0;
+       free(WC->disp_cal);
+       WC->disp_cal = NULL;
+}
+
+
+
+/**
+ * \brief do the whole calendar page
+ * view any part of the calender. decide which way, etc.
+ */
+void do_calendar_view(void) {
+       time_t now;
+       struct tm tm;
+       int year, month, day;
+       char calview[SIZ];
+
+       /** In case no date was specified, go with today */
+       now = time(NULL);
+       localtime_r(&now, &tm);
+       year = tm.tm_year + 1900;
+       month = tm.tm_mon + 1;
+       day = tm.tm_mday;
+
+       /** Now see if a date was specified */
+       if (strlen(bstr("year")) > 0) year = atoi(bstr("year"));
+       if (strlen(bstr("month")) > 0) month = atoi(bstr("month"));
+       if (strlen(bstr("day")) > 0) day = atoi(bstr("day"));
+
+       /** How would you like that cooked? */
+       if (strlen(bstr("calview")) > 0) {
+               strcpy(calview, bstr("calview"));
+       }
+       else {
+               strcpy(calview, "month");
+       }
+
+       /** Display the selected view */
+       if (!strcasecmp(calview, "day")) {
+               calendar_day_view(year, month, day);
+       }
+       else if (!strcasecmp(calview, "week")) {
+               calendar_week_view(year, month, day);
+       }
+       else {
+               if (WC->wc_view == VIEW_CALBRIEF) {
+                       calendar_brief_month_view(year, month, day);
+               }
+               else {
+                       calendar_month_view(year, month, day);
+               }
+       }
+
+       /** Free the calendar stuff */
+       free_calendar_buffer();
+
+}
+
+
+/**
+ * \brief get task due date
+ * Helper function for do_tasks_view().  
+ * \param vtodo a task to get the due date
+ * \return the date/time due.
+ */
+time_t get_task_due_date(icalcomponent *vtodo) {
+       icalproperty *p;
+
+       if (vtodo == NULL) {
+               return(0L);
+       }
+
+       /**
+        * If we're looking at a fully encapsulated VCALENDAR
+        * rather than a VTODO component, recurse into the data
+        * structure until we get a VTODO.
+        */
+       if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
+               return get_task_due_date(
+                       icalcomponent_get_first_component(
+                               vtodo, ICAL_VTODO_COMPONENT
+                       )
+               );
+       }
+
+       p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
+       if (p != NULL) {
+               return(icaltime_as_timet(icalproperty_get_due(p)));
+       }
+       else {
+               return(0L);
+       }
+}
+
+
+/**
+ * \brief Compare the due dates of two tasks (this is for sorting)
+ * \param task1 first task to compare
+ * \param task2 second task to compare
+ */
+int task_due_cmp(const void *task1, const void *task2) {
+       time_t t1;
+       time_t t2;
+
+       t1 =  get_task_due_date(((struct disp_cal *)task1)->cal);
+       t2 =  get_task_due_date(((struct disp_cal *)task2)->cal);
+
+       if (t1 < t2) return(-1);
+       if (t1 > t2) return(1);
+       return(0);
+}
+
+
+
+
+/**
+ * \brief do the whole task view stuff
+ */
+void do_tasks_view(void) {
+       int i;
+       time_t due;
+       int bg = 0;
+       char buf[SIZ];
+       icalproperty *p;
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 cellspacing=0 width=100%% bgcolor=\"#FFFFFF\">\n<tr>\n"
+               "<th>");
+       wprintf(_("Name of task"));
+       wprintf("</th><th>");
+       wprintf(_("Date due"));
+       wprintf("</th></tr>\n"
+       );
+
+       /** Sort them if necessary */
+       if (WC->num_cal > 1) {
+               qsort(WC->disp_cal,
+                       WC->num_cal,
+                       sizeof(struct disp_cal),
+                       task_due_cmp
+               );
+       }
+
+       if (WC->num_cal) for (i=0; i<(WC->num_cal); ++i) {
+
+               bg = 1 - bg;
+               wprintf("<tr bgcolor=\"#%s\"><td>",
+                       (bg ? "DDDDDD" : "FFFFFF")
+               );
+
+               p = icalcomponent_get_first_property(WC->disp_cal[i].cal,
+                                                       ICAL_SUMMARY_PROPERTY);
+               wprintf("<a href=\"display_edit_task?msgnum=%ld&taskrm=",
+                       WC->disp_cal[i].cal_msgnum );
+               urlescputs(WC->wc_roomname);
+               wprintf("\">");
+               wprintf("<img align=middle "
+                       "src=\"static/taskmanag_16x.gif\" border=0>&nbsp;");
+               if (p != NULL) {
+                       escputs((char *)icalproperty_get_comment(p));
+               }
+               wprintf("</a>\n");
+               wprintf("</td>\n");
+
+               due = get_task_due_date(WC->disp_cal[i].cal);
+               fmt_date(buf, due, 0);
+               wprintf("<td><font");
+               if (due < time(NULL)) {
+                       wprintf(" color=\"#FF0000\"");
+               }
+               wprintf(">%s</font></td></tr>\n", buf);
+       }
+
+       wprintf("</table></div>\n");
+
+       /** Free the list */
+       free_calendar_buffer();
+
+}
+
+#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
+
+/** @} */
diff --git a/webcit/src/context_loop.c b/webcit/src/context_loop.c
new file mode 100644 (file)
index 0000000..e276383
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup WebServerII some of the webserver stuff.
+ * This is the other half of the webserver.  It handles the task of hooking
+ * up HTTP requests with the sessions they belong to, using HTTP cookies to
+ * keep track of things.  If the HTTP request doesn't belong to any currently
+ * active session, a new session is started.
+ * \ingroup WebcitHttpServer 
+ *
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+
+/** Only one thread may manipulate SessionList at a time... */
+pthread_mutex_t SessionListMutex;
+
+struct wcsession *SessionList = NULL; /**< our sessions ????*/
+
+pthread_key_t MyConKey;         /**< TSD key for MySession() */
+
+
+/**
+ * \brief free the memory used for viewing atachments
+ * \param sess the session object to destroy
+ */
+void free_attachments(struct wcsession *sess) {
+       struct wc_attachment *att;
+
+       while (sess->first_attachment != NULL) {
+               att = sess->first_attachment;
+               sess->first_attachment = sess->first_attachment->next;
+               free(att->data);
+               free(att);
+       }
+}
+
+/**
+ * \brief what??????
+ */
+void do_housekeeping(void)
+{
+       struct wcsession *sptr, *ss;
+       struct wcsession *sessions_to_kill = NULL;
+       int num_sessions = 0;
+       static int num_threads = MIN_WORKER_THREADS;
+
+       /**
+        * Lock the session list, moving any candidates for euthanasia into
+        * a separate list.
+        */
+       pthread_mutex_lock(&SessionListMutex);
+       num_sessions = 0;
+       for (sptr = SessionList; sptr != NULL; sptr = sptr->next) {
+               ++num_sessions;
+
+               /** Kill idle sessions */
+               if ((time(NULL) - (sptr->lastreq)) >
+                  (time_t) WEBCIT_TIMEOUT) {
+                       sptr->killthis = 1;
+               }
+
+               /** Remove sessions flagged for kill */
+               if (sptr->killthis) {
+
+                       /** remove session from linked list */
+                       if (sptr == SessionList) {
+                               SessionList = SessionList->next;
+                       }
+                       else for (ss=SessionList;ss!=NULL;ss=ss->next) {
+                               if (ss->next == sptr) {
+                                       ss->next = ss->next->next;
+                               }
+                       }
+
+                       sptr->next = sessions_to_kill;
+                       sessions_to_kill = sptr;
+               }
+       }
+       pthread_mutex_unlock(&SessionListMutex);
+
+       /**
+        * Now free up and destroy the culled sessions.
+        */
+       while (sessions_to_kill != NULL) {
+               lprintf(3, "Destroying session %d\n", sessions_to_kill->wc_session);
+               pthread_mutex_lock(&sessions_to_kill->SessionMutex);
+               close(sessions_to_kill->serv_sock);
+               close(sessions_to_kill->chat_sock);
+               if (sessions_to_kill->preferences != NULL) {
+                       free(sessions_to_kill->preferences);
+               }
+               if (sessions_to_kill->cache_fold != NULL) {
+                       free(sessions_to_kill->cache_fold);
+               }
+               free_attachments(sessions_to_kill);
+               free_march_list(sessions_to_kill);
+               pthread_mutex_unlock(&sessions_to_kill->SessionMutex);
+               sptr = sessions_to_kill->next;
+               free(sessions_to_kill);
+               sessions_to_kill = sptr;
+               --num_sessions;
+       }
+
+       /**
+        * If there are more sessions than threads, then we should spawn
+        * more threads ... up to a predefined maximum.
+        */
+       while ( (num_sessions > num_threads)
+             && (num_threads <= MAX_WORKER_THREADS) ) {
+               spawn_another_worker_thread();
+               ++num_threads;
+               lprintf(3, "There are %d sessions and %d threads active.\n",
+                       num_sessions, num_threads);
+       }
+}
+
+
+/**
+ * \brief Wake up occasionally and clean house
+ */
+void housekeeping_loop(void)
+{
+       while (1) {
+               sleeeeeeeeeep(HOUSEKEEPING);
+               do_housekeeping();
+       }
+}
+
+
+/**
+ * \brief Create a Session id
+ * Generate a unique WebCit session ID (which is not the same thing as the
+ * Citadel session ID).
+ *
+ * \todo FIXME ... ensure that session number is truly unique
+ *
+ */
+int GenerateSessionID(void)
+{
+       static int seq = (-1);
+
+       if (seq < 0) {
+               seq = (int) time(NULL);
+       }
+               
+       return ++seq;
+}
+
+
+/**
+ * \brief Collapse multiple cookies on one line
+ * \param sock a socket?
+ * \param buf some bunch of chars?
+ * \param hold hold what?
+ */
+int req_gets(int sock, char *buf, char *hold)
+{
+       int a;
+
+       if (strlen(hold) == 0) {
+               strcpy(buf, "");
+               a = client_getln(sock, buf, SIZ);
+               if (a<1) return(-1);
+       } else {
+               safestrncpy(buf, hold, SIZ);
+       }
+       strcpy(hold, "");
+
+       if (!strncasecmp(buf, "Cookie: ", 8)) {
+               for (a = 0; a < strlen(buf); ++a)
+                       if (buf[a] == ';') {
+                               sprintf(hold, "Cookie: %s", &buf[a + 1]);
+                               buf[a] = 0;
+                               while (isspace(hold[8]))
+                                       strcpy(&hold[8], &hold[9]);
+                               return(0);
+                       }
+       }
+
+       return(0);
+}
+
+/**
+ * \brief close some fd for some reason???
+ * \param fd the fd to close??????
+ * lingering_close() a`la Apache. see
+ * http://www.apache.org/docs/misc/fin_wait_2.html for rationale
+ */
+
+int lingering_close(int fd)
+{
+       char buf[SIZ];
+       int i;
+       fd_set set;
+       struct timeval tv, start;
+
+       gettimeofday(&start, NULL);
+       shutdown(fd, 1);
+       do {
+               do {
+                       gettimeofday(&tv, NULL);
+                       tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec);
+                       tv.tv_usec = start.tv_usec - tv.tv_usec;
+                       if (tv.tv_usec < 0) {
+                               tv.tv_sec--;
+                               tv.tv_usec += 1000000;
+                       }
+                       FD_ZERO(&set);
+                       FD_SET(fd, &set);
+                       i = select(fd + 1, &set, NULL, NULL, &tv);
+               } while (i == -1 && errno == EINTR);
+
+               if (i <= 0)
+                       break;
+
+               i = read(fd, buf, sizeof buf);
+       } while (i != 0 && (i != -1 || errno == EINTR));
+
+       return close(fd);
+}
+
+
+
+/**
+ * \brief      sanity requests
+ *             Check for bogus requests coming from brain-dead Windows boxes.
+ *
+ * \param      http_cmd        The HTTP request to check
+ */
+int is_bogus(char *http_cmd) {
+       char *url;
+
+       url = strstr(http_cmd, " ");
+       if (url == NULL) return(1);
+       ++url;
+
+       /** Worms and trojans and viruses, oh my! */
+       if (!strncasecmp(url, "/scripts/root.exe", 17)) return(2);
+       if (!strncasecmp(url, "/c/winnt", 8)) return(2);
+       if (!strncasecmp(url, "/MSADC/", 7)) return(2);
+
+       /** Broken Microsoft DAV implementation */
+       if (!strncasecmp(url, "/_vti", 5)) return(3);
+
+       return(0);      /* probably ok */
+}
+
+
+
+/**
+ * \brief handle one request
+ * This loop gets called once for every HTTP connection made to WebCit.  At
+ * this entry point we have an HTTP socket with a browser allegedly on the
+ * other end, but we have not yet bound to a WebCit session.
+ *
+ * The job of this function is to locate the correct session and bind to it,
+ * or create a session if necessary and bind to it, then run the WebCit
+ * transaction loop.  Afterwards, we unbind from the session.  When this
+ * function returns, the worker thread is then free to handle another
+ * transaction.
+ * \param sock the socket we will put our answer to
+ */
+void context_loop(int sock)
+{
+       struct httprequest *req = NULL;
+       struct httprequest *last = NULL;
+       struct httprequest *hptr;
+       char buf[SIZ], hold[SIZ];
+       int desired_session = 0;
+       int got_cookie = 0;
+       int gzip_ok = 0;
+       struct wcsession *TheSession, *sptr;
+       char httpauth_string[1024];
+       char httpauth_user[1024];
+       char httpauth_pass[1024];
+       char accept_language[256];
+       char *ptr = NULL;
+       int session_is_new = 0;
+
+       strcpy(httpauth_string, "");
+       strcpy(httpauth_user, DEFAULT_HTTPAUTH_USER);
+       strcpy(httpauth_pass, DEFAULT_HTTPAUTH_PASS);
+
+       /**
+        * Find out what it is that the web browser is asking for
+        */
+       memset(hold, 0, sizeof(hold));
+       do {
+               if (req_gets(sock, buf, hold) < 0) return;
+
+               /**
+                * Can we compress?
+                */
+               if (!strncasecmp(buf, "Accept-encoding:", 16)) {
+                       if (strstr(&buf[16], "gzip")) {
+                               gzip_ok = 1;
+                       }
+               }
+
+               /**
+                * Browser-based sessions use cookies for session authentication
+                */
+               if (!strncasecmp(buf, "Cookie: webcit=", 15)) {
+                       cookie_to_stuff(&buf[15], &desired_session,
+                               NULL, 0, NULL, 0, NULL, 0);
+                       got_cookie = 1;
+               }
+
+               /**
+                * GroupDAV-based sessions use HTTP authentication
+                */
+               if (!strncasecmp(buf, "Authorization: Basic ", 21)) {
+                       CtdlDecodeBase64(httpauth_string, &buf[21], strlen(&buf[21]));
+                       extract_token(httpauth_user, httpauth_string, 0, ':', sizeof httpauth_user);
+                       extract_token(httpauth_pass, httpauth_string, 1, ':', sizeof httpauth_pass);
+               }
+
+               if (!strncasecmp(buf, "If-Modified-Since: ", 19)) {
+                       if_modified_since = httpdate_to_timestamp(&buf[19]);
+               }
+
+               if (!strncasecmp(buf, "Accept-Language: ", 17)) {
+                       safestrncpy(accept_language, &buf[17], sizeof accept_language);
+               }
+
+               /**
+                * Read in the request
+                */
+               hptr = (struct httprequest *)
+                       malloc(sizeof(struct httprequest));
+               if (req == NULL)
+                       req = hptr;
+               else
+                       last->next = hptr;
+               hptr->next = NULL;
+               last = hptr;
+
+               safestrncpy(hptr->line, buf, sizeof hptr->line);
+
+       } while (strlen(buf) > 0);
+
+       /**
+        * If the request is prefixed by "/webcit" then chop that off.  This
+        * allows a front end web server to forward all /webcit requests to us
+        * while still using the same web server port for other things.
+        */
+       
+       ptr = strstr(req->line, " /webcit ");   /*< Handle "/webcit" */
+       if (ptr != NULL) {
+               strcpy(ptr+2, ptr+8);
+       }
+
+       ptr = strstr(req->line, " /webcit");    /*< Handle "/webcit/" */
+       if (ptr != NULL) {
+               strcpy(ptr+1, ptr+8);
+       }
+
+       /** Begin parsing the request. */
+
+       safestrncpy(buf, req->line, sizeof buf);
+       lprintf(5, "HTTP: %s\n", buf);
+
+       /** Check for bogus requests */
+       if (is_bogus(buf)) {
+               strcpy(req->line, "GET /404 HTTP/1.1");
+               strcpy(buf, "GET /404 HTTP/1.1");
+       }
+
+       /**
+        * Strip out the method, leaving the URL up front...
+        */
+       remove_token(buf, 0, ' ');
+       if (buf[1]==' ') buf[1]=0;
+
+       /**
+        * While we're at it, gracefully handle requests for the
+        * robots.txt and favicon.ico files.
+        */
+       if (!strncasecmp(buf, "/robots.txt", 11)) {
+               strcpy(req->line, "GET /static/robots.txt"
+                               "?force_close_session=yes HTTP/1.1");
+       }
+       else if (!strncasecmp(buf, "/favicon.ico", 12)) {
+               strcpy(req->line, "GET /static/favicon.ico");
+       }
+
+       /**
+        * These are the URL's which may be executed without a
+        * session cookie already set.  If it's not one of these,
+        * force the session to close because cookies are
+        * probably disabled on the client browser.
+        */
+       else if ( (strcmp(buf, "/"))
+               && (strncasecmp(buf, "/listsub", 8))
+               && (strncasecmp(buf, "/freebusy", 9))
+               && (strncasecmp(buf, "/do_logout", 10))
+               && (strncasecmp(buf, "/groupdav", 9))
+               && (strncasecmp(buf, "/static", 7))
+               && (strncasecmp(buf, "/rss", 4))
+               && (strncasecmp(buf, "/404", 4))
+               && (got_cookie == 0)) {
+               strcpy(req->line, "GET /static/nocookies.html"
+                               "?force_close_session=yes HTTP/1.1");
+       }
+
+       /**
+        * See if there's an existing session open with the desired ID or user/pass
+        */
+       TheSession = NULL;
+
+       if (TheSession == NULL) {
+               pthread_mutex_lock(&SessionListMutex);
+               for (sptr = SessionList; sptr != NULL; sptr = sptr->next) {
+
+                       /** If HTTP-AUTH, look for a session with matching credentials */
+                       if ( (strlen(httpauth_user) > 0)
+                          &&(!strcasecmp(sptr->httpauth_user, httpauth_user))
+                          &&(!strcasecmp(sptr->httpauth_pass, httpauth_pass)) ) {
+                               TheSession = sptr;
+                       }
+
+                       /** If cookie-session, look for a session with matching session ID */
+                       if ( (desired_session != 0) && (sptr->wc_session == desired_session)) {
+                               TheSession = sptr;
+                       }
+
+               }
+               pthread_mutex_unlock(&SessionListMutex);
+       }
+
+       /**
+        * Create a new session if we have to
+        */
+       if (TheSession == NULL) {
+               lprintf(3, "Creating a new session\n");
+               TheSession = (struct wcsession *)
+                       malloc(sizeof(struct wcsession));
+               memset(TheSession, 0, sizeof(struct wcsession));
+               TheSession->serv_sock = (-1);
+               TheSession->chat_sock = (-1);
+               TheSession->wc_session = GenerateSessionID();
+               strcpy(TheSession->httpauth_user, httpauth_user);
+               strcpy(TheSession->httpauth_pass, httpauth_pass);
+               pthread_mutex_init(&TheSession->SessionMutex, NULL);
+               pthread_mutex_lock(&SessionListMutex);
+               TheSession->next = SessionList;
+               SessionList = TheSession;
+               pthread_mutex_unlock(&SessionListMutex);
+               session_is_new = 1;
+       }
+
+       /**
+        * A future improvement might be to check the session integrity
+        * at this point before continuing.
+        */
+
+       /**
+        * Bind to the session and perform the transaction
+        */
+       pthread_mutex_lock(&TheSession->SessionMutex);          /*< bind */
+       pthread_setspecific(MyConKey, (void *)TheSession);
+       TheSession->http_sock = sock;
+       TheSession->lastreq = time(NULL);                       /*< log */
+       TheSession->gzip_ok = gzip_ok;
+#ifdef ENABLE_NLS
+       if (session_is_new) {
+               httplang_to_locale(accept_language);
+       }
+       go_selected_language();                         /*< set locale */
+#endif
+       session_loop(req);                              /*< do transaction */
+#ifdef ENABLE_NLS
+       stop_selected_language();                       /*< unset locale */
+#endif
+       pthread_mutex_unlock(&TheSession->SessionMutex);        /*< unbind */
+
+       /** Free the request buffer */
+       while (req != NULL) {
+               hptr = req->next;
+               free(req);
+               req = hptr;
+       }
+
+       /**
+        * Free up any session-local substitution variables which
+        * were set during this transaction
+        */
+       clear_local_substs();
+}
+/*@}*/
diff --git a/webcit/src/cookie_conversion.c b/webcit/src/cookie_conversion.c
new file mode 100644 (file)
index 0000000..24e29ce
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup CookieConversion Grep Cookies
+ * Utility functions which convert the HTTP cookie format we use to and
+ * from user/password/room strings.
+ *
+ * \ingroup WebcitHttpServer 
+ */
+/*@{*/
+#include "webcit.h"
+
+
+#define TRUE  1    /**< for sure? */
+#define FALSE 0    /**< nope. */
+
+typedef unsigned char byte;          /**< Byte type */
+
+/**
+ * \brief find cookie
+ * Pack all session info into one easy-to-digest cookie. Healthy and delicious!
+ * \param cookie cookie string to create???
+ * \param session the session we want to convert into a cookie
+ * \param user the user to be associated with the cookie
+ * \param pass his passphrase
+ * \param room the room he wants to enter
+ */
+void stuff_to_cookie(char *cookie, int session,
+               char *user, char *pass, char *room)
+{
+       char buf[SIZ];
+       int i;
+
+       sprintf(buf, "%d|%s|%s|%s|", session, user, pass, room);
+       strcpy(cookie, "");
+       for (i=0; i<strlen(buf); ++i) {
+               sprintf(&cookie[i*2], "%02X", buf[i]);
+       }
+}
+
+/**
+ * \brief      Convert unpacked hex string to an integer
+ * \param      in      Input hex string
+ * \param      len     the length of the string
+ * \return     the corrosponding integer value
+ */
+int xtoi(char *in, size_t len)
+{
+       int val = 0;
+       char c = 0;
+       while (isxdigit((byte) *in) && (len-- > 0))
+       {
+               c = *in++;
+               val <<= 4;
+               val += isdigit((unsigned char)c)
+               ? (c - '0')
+               : (tolower((unsigned char)c) - 'a' + 10);
+       }
+       return val;
+}
+
+/**
+ * \brief Extract all that fun stuff out of the cookie.
+ * \param cookie the cookie string
+ * \param session the corrosponding session to return
+ * \param user the user string
+ * \param user_len the user stringlength
+ * \param pass the passphrase
+ * \param pass_len length of the passphrase string 
+ * \param room the room he is in
+ * \param room_len the length of the room string
+ */
+void cookie_to_stuff(char *cookie, int *session,
+               char *user, size_t user_len,
+               char *pass, size_t pass_len,
+               char *room, size_t room_len)
+{
+       char buf[SIZ];
+       int i, len;
+
+       strcpy(buf, "");
+       len = strlen(cookie) * 2 ;
+       for (i=0; i<len; ++i) {
+               buf[i] = xtoi(&cookie[i*2], 2);
+               buf[i+1] = 0;
+       }
+
+       if (session != NULL)
+               *session = extract_int(buf, 0);
+       if (user != NULL)
+               extract_token(user, buf, 1, '|', user_len);
+       if (pass != NULL)
+               extract_token(pass, buf, 2, '|', pass_len);
+       if (room != NULL)
+               extract_token(room, buf, 3, '|', room_len);
+}
+/*@}*/
diff --git a/webcit/src/crypto.c b/webcit/src/crypto.c
new file mode 100644 (file)
index 0000000..ae3e417
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup https  Provides HTTPS, when the OpenSSL library is available.
+ * \ingroup WebcitHttpServer 
+ */
+
+/*@{*/
+#ifdef HAVE_OPENSSL
+
+#include "webcit.h"
+#include "webserver.h"
+/** \todo dirify */
+/** where to find the keys */
+#define        CTDL_CRYPTO_DIR         "./keys" 
+#define CTDL_KEY_PATH          CTDL_CRYPTO_DIR "/citadel.key" /**< the key */
+#define CTDL_CSR_PATH          CTDL_CRYPTO_DIR "/citadel.csr" /**< the csr file */
+#define CTDL_CER_PATH          CTDL_CRYPTO_DIR "/citadel.cer" /**< the cer file */
+#define SIGN_DAYS              365 /**< how long our certificate should live */
+
+SSL_CTX *ssl_ctx;              /**< SSL context */
+pthread_mutex_t **SSLCritters; /**< Things needing locking */
+
+pthread_key_t ThreadSSL;       /**< Per-thread SSL context */
+
+/**
+ * \brief what?????
+ * \return thread id??? 
+ */
+static unsigned long id_callback(void)
+{
+       return (unsigned long) pthread_self();
+}
+
+/**
+ * \brief initialize ssl engine
+ * load certs and initialize openssl internals
+ */
+void init_ssl(void)
+{
+       SSL_METHOD *ssl_method;
+       RSA *rsa=NULL;
+       X509_REQ *req = NULL;
+       X509 *cer = NULL;
+       EVP_PKEY *pk = NULL;
+       EVP_PKEY *req_pkey = NULL;
+       X509_NAME *name = NULL;
+       FILE *fp;
+       char buf[SIZ];
+
+       if (!access("/var/run/egd-pool", F_OK))
+               RAND_egd("/var/run/egd-pool");
+
+       if (!RAND_status()) {
+               lprintf(3,
+                       "PRNG not adequately seeded, won't do SSL/TLS\n");
+               return;
+       }
+       SSLCritters =
+           malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t *));
+       if (!SSLCritters) {
+               lprintf(1, "citserver: can't allocate memory!!\n");
+               /* Nothing's been initialized, just die */
+               exit(1);
+       } else {
+               int a;
+
+               for (a = 0; a < CRYPTO_num_locks(); a++) {
+                       SSLCritters[a] = malloc(sizeof(pthread_mutex_t));
+                       if (!SSLCritters[a]) {
+                               lprintf(1,
+                                       "citserver: can't allocate memory!!\n");
+                               /** Nothing's been initialized, just die */
+                               exit(1);
+                       }
+                       pthread_mutex_init(SSLCritters[a], NULL);
+               }
+       }
+
+       /**
+        * Initialize SSL transport layer
+        */
+       SSL_library_init();
+       SSL_load_error_strings();
+       ssl_method = SSLv23_server_method();
+       if (!(ssl_ctx = SSL_CTX_new(ssl_method))) {
+               lprintf(3, "SSL_CTX_new failed: %s\n",
+                       ERR_reason_error_string(ERR_get_error()));
+               return;
+       }
+
+       CRYPTO_set_locking_callback(ssl_lock);
+       CRYPTO_set_id_callback(id_callback);
+
+       /**
+        * Get our certificates in order. \todo dirify. this is a setup job.
+        * First, create the key/cert directory if it's not there already...
+        */
+       mkdir(CTDL_CRYPTO_DIR, 0700);
+
+       /**
+        * Before attempting to generate keys/certificates, first try
+        * link to them from the Citadel server if it's on the same host.
+        * We ignore any error return because it either meant that there
+        * was nothing in Citadel to link from (in which case we just
+        * generate new files) or the target files already exist (which
+        * is not fatal either). \todo dirify
+        */
+       if (!strcasecmp(ctdlhost, "uds")) {
+               sprintf(buf, "%s/keys/citadel.key", ctdlport);
+               symlink(buf, CTDL_KEY_PATH);
+               sprintf(buf, "%s/keys/citadel.csr", ctdlport);
+               symlink(buf, CTDL_CSR_PATH);
+               sprintf(buf, "%s/keys/citadel.cer", ctdlport);
+               symlink(buf, CTDL_CER_PATH);
+       }
+
+       /**
+        * If we still don't have a private key, generate one.
+        */
+       if (access(CTDL_KEY_PATH, R_OK) != 0) {
+               lprintf(5, "Generating RSA key pair.\n");
+               rsa = RSA_generate_key(1024,    /**< modulus size */
+                                       65537,  /**< exponent */
+                                       NULL,   /**< no callback */
+                                       NULL);  /**< no callback */
+               if (rsa == NULL) {
+                       lprintf(3, "Key generation failed: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               }
+               if (rsa != NULL) {
+                       fp = fopen(CTDL_KEY_PATH, "w");
+                       if (fp != NULL) {
+                               chmod(CTDL_KEY_PATH, 0600);
+                               if (PEM_write_RSAPrivateKey(fp, /**< the file */
+                                                       rsa,    /**< the key */
+                                                       NULL,   /**< no enc */
+                                                       NULL,   /**< no passphr */
+                                                       0,      /**< no passphr */
+                                                       NULL,   /**< no callbk */
+                                                       NULL    /**< no callbk */
+                               ) != 1) {
+                                       lprintf(3, "Cannot write key: %s\n",
+                                               ERR_reason_error_string(ERR_get_error()));
+                                       unlink(CTDL_KEY_PATH);
+                               }
+                               fclose(fp);
+                       }
+                       RSA_free(rsa);
+               }
+       }
+
+       /**
+        * Generate a CSR if we don't have one.
+        */
+       if (access(CTDL_CSR_PATH, R_OK) != 0) {
+               lprintf(5, "Generating a certificate signing request.\n");
+
+               /**
+                * Read our key from the file.  No, we don't just keep this
+                * in memory from the above key-generation function, because
+                * there is the possibility that the key was already on disk
+                * and we didn't just generate it now.
+                */
+               fp = fopen(CTDL_KEY_PATH, "r");
+               if (fp) {
+                       rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+                       fclose(fp);
+               }
+
+               if (rsa) {
+
+                       /** Create a public key from the private key */
+                       if (pk=EVP_PKEY_new(), pk != NULL) {
+                               EVP_PKEY_assign_RSA(pk, rsa);
+                               if (req = X509_REQ_new(), req != NULL) {
+
+                                       /** Set the public key */
+                                       X509_REQ_set_pubkey(req, pk);
+                                       X509_REQ_set_version(req, 0L);
+
+                                       name = X509_REQ_get_subject_name(req);
+
+                                       /** Tell it who we are */
+
+                                       /* \todo whats this?
+                                       X509_NAME_add_entry_by_txt(name, "C",
+                                               MBSTRING_ASC, "US", -1, -1, 0);
+
+                                       X509_NAME_add_entry_by_txt(name, "ST",
+                                               MBSTRING_ASC, "New York", -1, -1, 0);
+
+                                       X509_NAME_add_entry_by_txt(name, "L",
+                                               MBSTRING_ASC, "Mount Kisco", -1, -1, 0);
+                                       */
+
+                                       X509_NAME_add_entry_by_txt(name, "O",
+                                               MBSTRING_ASC, "FIXME.FIXME.org", -1, -1, 0);
+
+                                       X509_NAME_add_entry_by_txt(name, "OU",
+                                               MBSTRING_ASC, "Citadel server", -1, -1, 0);
+
+                                       X509_NAME_add_entry_by_txt(name, "CN",
+                                               MBSTRING_ASC, "FIXME.FIXME.org", -1, -1, 0);
+                               
+                                       X509_REQ_set_subject_name(req, name);
+
+                                       /** Sign the CSR */
+                                       if (!X509_REQ_sign(req, pk, EVP_md5())) {
+                                               lprintf(3, "X509_REQ_sign(): error\n");
+                                       }
+                                       else {
+                                               /** Write it to disk. */        
+                                               fp = fopen(CTDL_CSR_PATH, "w");
+                                               if (fp != NULL) {
+                                                       chmod(CTDL_CSR_PATH, 0600);
+                                                       PEM_write_X509_REQ(fp, req);
+                                                       fclose(fp);
+                                               }
+                                       }
+
+                                       X509_REQ_free(req);
+                               }
+                       }
+
+                       RSA_free(rsa);
+               }
+
+               else {
+                       lprintf(3, "Unable to read private key.\n");
+               }
+       }
+
+
+
+       /**
+        * Generate a self-signed certificate if we don't have one.
+        */
+       if (access(CTDL_CER_PATH, R_OK) != 0) {
+               lprintf(5, "Generating a self-signed certificate.\n");
+
+               /** Same deal as before: always read the key from disk because
+                * it may or may not have just been generated.
+                */
+               fp = fopen(CTDL_KEY_PATH, "r");
+               if (fp) {
+                       rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+                       fclose(fp);
+               }
+
+               /** This also holds true for the CSR. */
+               req = NULL;
+               cer = NULL;
+               pk = NULL;
+               if (rsa) {
+                       if (pk=EVP_PKEY_new(), pk != NULL) {
+                               EVP_PKEY_assign_RSA(pk, rsa);
+                       }
+
+                       fp = fopen(CTDL_CSR_PATH, "r");
+                       if (fp) {
+                               req = PEM_read_X509_REQ(fp, NULL, NULL, NULL);
+                               fclose(fp);
+                       }
+
+                       if (req) {
+                               if (cer = X509_new(), cer != NULL) {
+
+                                       ASN1_INTEGER_set(X509_get_serialNumber(cer), 0);
+                                       X509_set_issuer_name(cer, req->req_info->subject);
+                                       X509_set_subject_name(cer, req->req_info->subject);
+                                       X509_gmtime_adj(X509_get_notBefore(cer), 0);
+                                       X509_gmtime_adj(X509_get_notAfter(cer),(long)60*60*24*SIGN_DAYS);
+
+                                       req_pkey = X509_REQ_get_pubkey(req);
+                                       X509_set_pubkey(cer, req_pkey);
+                                       EVP_PKEY_free(req_pkey);
+                                       
+                                       /** Sign the cert */
+                                       if (!X509_sign(cer, pk, EVP_md5())) {
+                                               lprintf(3, "X509_sign(): error\n");
+                                       }
+                                       else {
+                                               /** Write it to disk. */        
+                                               fp = fopen(CTDL_CER_PATH, "w");
+                                               if (fp != NULL) {
+                                                       chmod(CTDL_CER_PATH, 0600);
+                                                       PEM_write_X509(fp, cer);
+                                                       fclose(fp);
+                                               }
+                                       }
+                                       X509_free(cer);
+                               }
+                       }
+
+                       RSA_free(rsa);
+               }
+       }
+
+       /**
+        * Now try to bind to the key and certificate.
+        * Note that we use SSL_CTX_use_certificate_chain_file() which allows
+        * the certificate file to contain intermediate certificates.
+        */
+       SSL_CTX_use_certificate_chain_file(ssl_ctx, CTDL_CER_PATH);
+       SSL_CTX_use_PrivateKey_file(ssl_ctx, CTDL_KEY_PATH, SSL_FILETYPE_PEM);
+       if ( !SSL_CTX_check_private_key(ssl_ctx) ) {
+               lprintf(3, "Cannot install certificate: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+       }
+       
+}
+
+
+/**
+ * \brief starts SSL/TLS encryption for the current session.
+ * \param sock the socket connection
+ * \return Zero if the SSL/TLS handshake succeeded, non-zero otherwise.
+ */
+int starttls(int sock) {
+       int retval, bits, alg_bits;
+       SSL *newssl;
+
+       pthread_setspecific(ThreadSSL, NULL);
+
+       if (!ssl_ctx) {
+               return(1);
+       }
+       if (!(newssl = SSL_new(ssl_ctx))) {
+               lprintf(3, "SSL_new failed: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               return(2);
+       }
+       if (!(SSL_set_fd(newssl, sock))) {
+               lprintf(3, "SSL_set_fd failed: %s\n",
+                       ERR_reason_error_string(ERR_get_error()));
+               SSL_free(newssl);
+               return(3);
+       }
+       retval = SSL_accept(newssl);
+       if (retval < 1) {
+               /**
+                * Can't notify the client of an error here; they will
+                * discover the problem at the SSL layer and should
+                * revert to unencrypted communications.
+                */
+               long errval;
+
+               errval = SSL_get_error(newssl, retval);
+               lprintf(3, "SSL_accept failed: %s\n",
+                       ERR_reason_error_string(ERR_get_error()));
+               SSL_free(newssl);
+               newssl = NULL;
+               return(4);
+       }
+       BIO_set_close(newssl->rbio, BIO_NOCLOSE);
+       bits =
+           SSL_CIPHER_get_bits(SSL_get_current_cipher(newssl),
+                               &alg_bits);
+       lprintf(5, "SSL/TLS using %s on %s (%d of %d bits)\n",
+               SSL_CIPHER_get_name(SSL_get_current_cipher(newssl)),
+               SSL_CIPHER_get_version(SSL_get_current_cipher(newssl)),
+               bits, alg_bits);
+
+       pthread_setspecific(ThreadSSL, newssl);
+       return(0);
+}
+
+
+
+/**
+ * \brief shuts down the TLS connection
+ *
+ * WARNING:  This may make your session vulnerable to a known plaintext
+ * attack in the current implmentation.
+ */
+void endtls(void)
+{
+       if (THREADSSL == NULL) return;
+
+       lprintf(5, "Ending SSL/TLS\n");
+       SSL_shutdown(THREADSSL);
+       SSL_free(THREADSSL);
+       pthread_setspecific(ThreadSSL, NULL);
+}
+
+
+/**
+ * \brief callback for OpenSSL mutex locks
+ * \param mode which mode??????
+ * \param n  how many???
+ * \param file which filename ???
+ * \param line what line????
+ */
+void ssl_lock(int mode, int n, const char *file, int line)
+{
+       if (mode & CRYPTO_LOCK)
+               pthread_mutex_lock(SSLCritters[n]);
+       else
+               pthread_mutex_unlock(SSLCritters[n]);
+}
+
+/**
+ * \brief Send binary data to the client encrypted.
+ * \param buf chars to send to the client
+ * \param nbytes how many chars
+ */
+void client_write_ssl(char *buf, int nbytes)
+{
+       int retval;
+       int nremain;
+       char junk[1];
+
+       if (THREADSSL == NULL) return;
+
+       nremain = nbytes;
+
+       while (nremain > 0) {
+               if (SSL_want_write(THREADSSL)) {
+                       if ((SSL_read(THREADSSL, junk, 0)) < 1) {
+                               lprintf(9, "SSL_read in client_write: %s\n",
+                                               ERR_reason_error_string(ERR_get_error()));
+                       }
+               }
+               retval = SSL_write(THREADSSL, &buf[nbytes - nremain], nremain);
+               if (retval < 1) {
+                       long errval;
+
+                       errval = SSL_get_error(THREADSSL, retval);
+                       if (errval == SSL_ERROR_WANT_READ ||
+                           errval == SSL_ERROR_WANT_WRITE) {
+                               sleep(1);
+                               continue;
+                       }
+                       lprintf(9, "SSL_write got error %ld, ret %d\n", errval, retval);
+                       if (retval == -1) {
+                               lprintf(9, "errno is %d\n", errno);
+                       }
+                       endtls();
+                       return;
+               }
+               nremain -= retval;
+       }
+}
+
+
+/**
+ * \brief read data from the encrypted layer.
+ * \param buf charbuffer to read to 
+ * \param bytes how many
+ * \param timeout how long should we wait?
+ * \returns what???
+ */
+int client_read_ssl(char *buf, int bytes, int timeout)
+{
+#if 0
+       fd_set rfds;
+       struct timeval tv;
+       int retval;
+       int s;
+#endif
+       int len, rlen;
+       char junk[1];
+
+       if (THREADSSL == NULL) return(0);
+
+       len = 0;
+       while (len < bytes) {
+#if 0
+               /**
+                * This code is disabled because we don't need it when
+                * using blocking reads (which we are). -IO
+                */
+               FD_ZERO(&rfds);
+               s = BIO_get_fd(THREADSSL->rbio, NULL);
+               FD_SET(s, &rfds);
+               tv.tv_sec = timeout;
+               tv.tv_usec = 0;
+
+               retval = select(s + 1, &rfds, NULL, NULL, &tv);
+
+               if (FD_ISSET(s, &rfds) == 0) {
+                       return (0);
+               }
+
+#endif
+               if (SSL_want_read(THREADSSL)) {
+                       if ((SSL_write(THREADSSL, junk, 0)) < 1) {
+                               lprintf(9, "SSL_write in client_read: %s\n", ERR_reason_error_string(ERR_get_error()));
+                       }
+               }
+               rlen = SSL_read(THREADSSL, &buf[len], bytes - len);
+               if (rlen < 1) {
+                       long errval;
+
+                       errval = SSL_get_error(THREADSSL, rlen);
+                       if (errval == SSL_ERROR_WANT_READ ||
+                           errval == SSL_ERROR_WANT_WRITE) {
+                               sleep(1);
+                               continue;
+                       }
+                       lprintf(9, "SSL_read got error %ld\n", errval);
+                       endtls();
+                       return (0);
+               }
+               len += rlen;
+       }
+       return (1);
+}
+
+
+#endif                         /* HAVE_OPENSSL */
+/*@}*/
diff --git a/webcit/src/doxygen_groups.c b/webcit/src/doxygen_groups.c
new file mode 100644 (file)
index 0000000..3fed3e6
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * ok, hacky, but gets us nice groups. so we define sub parts to join from other 
+ * files here. NO CODE IN HERE! This is comment shouldn't appear in doxygen.
+ * we have: 
+ * CitadelConfig; WebcitDisplayItems; WebcitHttpServer; WebcitHttpServerGDav;
+ * ClientPower; Calendaring; MenuInfrastructure; CitadelCommunitacion;
+ * VCards
+ * WebcitHttpServerRSS; tools;
+ */
+
+
+/**
+ * \defgroup CitadelConfig Configuration Mechanisms
+ * \brief Functions about configuring citadel / webcit
+ */
+
+/*@{*/
+/*@}*/
+
+/**
+ * \defgroup tools  Utility Functions
+ * \brief Functions that aren't related to webcit topics
+ */
+
+/*@{*/
+/*@}*/
+
+
+/**
+ * \defgroup WebcitDisplayItems Display some mime types through webcit
+ * \brief Functions that format mime types into HTML to the user
+ */
+
+/*@{*/
+/*@}*/
+
+/**
+ * \defgroup WebcitHttpServer the Webserver part
+ * \brief Functions that run the HTTP-Deamon
+ */
+
+/*@{*/
+/*@}*/
+
+/**
+ * \defgroup WebcitHttpServerGDav Groupdav Mechanisms
+ * \ingroup WebcitHttpServer
+ * \brief Functions that handle groupdav requests
+ */
+/*@{*/
+/*@}*/
+
+
+/**
+ * \defgroup WebcitHttpServerRSS RSS Mechanisms
+ * \ingroup WebcitHttpServer
+ * \brief Functions that handle RSS requests
+ */
+
+/*@{*/
+/*@}*/
+
+/**
+ * \defgroup ClientPower Client powered Functionality
+ * \brief Functions that spawn things on the webbrowser
+ */
+
+/*@{*/
+/*@}*/
+
+/**
+ * \defgroup Calendaring Calendaring background
+ * \brief Functions that make the Business-logic of the calendaring items
+ * \ingroup WebcitDisplayItems
+ */
+
+/*@{*/
+/*@}*/
+
+/**
+ * \defgroup VCards showing / editing VCards
+ * \brief Functions that make the Business-logic of the vcard stuff
+ * \ingroup WebcitDisplayItems
+ */
+
+/*@{*/
+/*@}*/
+
+/**
+ * \defgroup MenuInfrastructure Things that guide you through the webcit parts
+ * \brief Functions that display menues, trees etc. to connect the parts of the 
+ *        ui to a whole thing
+ * \ingroup WebcitDisplayItems
+ */
+
+/*@{*/
+/*@}*/
+
+/**
+ * \defgroup CitadelCommunitacion Talk to the citadel server
+ * \brief Functions that talk to the citadel server and process reviewed entities
+ * \ingroup WebcitDisplayItems
+ */
+
+/*@{*/
+/*@}*/
+
+
+
diff --git a/webcit/src/event.c b/webcit/src/event.c
new file mode 100644 (file)
index 0000000..94fac68
--- /dev/null
@@ -0,0 +1,721 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup EditCal Editing calendar events.
+ * \ingroup Calendaring
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+
+
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+
+/**
+ * \brief Display an event by itself (for editing)
+ * \param supplied_vevent the event to edit
+ * \param msgnum reference on the citserver
+ */
+void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum) {
+       icalcomponent *vevent;
+       icalproperty *p;
+       icalvalue *v;
+       struct icaltimetype t_start, t_end;
+       time_t now;
+       struct tm tm_now;
+       int created_new_vevent = 0;
+       icalproperty *organizer = NULL;
+       char organizer_string[SIZ];
+       icalproperty *attendee = NULL;
+       char attendee_string[SIZ];
+       char buf[SIZ];
+       int organizer_is_me = 0;
+       int i;
+       int sequence = 0;
+
+       now = time(NULL);
+       strcpy(organizer_string, "");
+       strcpy(attendee_string, "");
+
+       if (supplied_vevent != NULL) {
+               vevent = supplied_vevent;
+               /**
+                * If we're looking at a fully encapsulated VCALENDAR
+                * rather than a VEVENT component, attempt to use the first
+                * relevant VEVENT subcomponent.  If there is none, the
+                * NULL returned by icalcomponent_get_first_component() will
+                * tell the next iteration of this function to create a
+                * new one.
+                */
+               if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) {
+                       display_edit_individual_event(
+                               icalcomponent_get_first_component(
+                                       vevent, ICAL_VEVENT_COMPONENT
+                               ), msgnum
+                       );
+                       return;
+               }
+       }
+       else {
+               vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
+               created_new_vevent = 1;
+       }
+
+       /** Learn the sequence */
+       p = icalcomponent_get_first_property(vevent, ICAL_SEQUENCE_PROPERTY);
+       if (p != NULL) {
+               sequence = icalproperty_get_sequence(p);
+       }
+
+       /** Begin output */
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Add or edit an event"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<script type=\"text/javascript\">"
+               "function grey_all_day() { "
+                       "if (document.EventForm.alldayevent.checked) {"
+                               "document.EventForm.dtstart_hour.value='0';"
+                               "document.EventForm.dtstart_hour.disabled = true;"
+                               "document.EventForm.dtstart_minute.value='0';"
+                               "document.EventForm.dtstart_minute.disabled = true;"
+                               "document.EventForm.dtend_hour.value='0';"
+                               "document.EventForm.dtend_hour.disabled = true;"
+                               "document.EventForm.dtend_minute.value='0';"
+                               "document.EventForm.dtend_minute.disabled = true;"
+                               "document.EventForm.dtend_month.disabled = true;"
+                               "document.EventForm.dtend_day.disabled = true;"
+                               "document.EventForm.dtend_year.disabled = true;"
+                       "}"
+                       "else {"
+                               "document.EventForm.dtstart_hour.disabled = false;"
+                               "document.EventForm.dtstart_minute.disabled = false;"
+                               "document.EventForm.dtend_hour.disabled = false;"
+                               "document.EventForm.dtend_minute.disabled = false;"
+                               "document.EventForm.dtend_month.disabled = false;"
+                               "document.EventForm.dtend_day.disabled = false;"
+                               "document.EventForm.dtend_year.disabled = false;"
+                       "}"
+               "}"
+               "</script>\n"
+       );
+
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       /************************************************************
+        * Uncomment this to see the UID in calendar events for debugging
+       wprintf("UID == ");
+       p = icalcomponent_get_first_property(vevent, ICAL_UID_PROPERTY);
+       if (p != NULL) {
+               escputs((char *)icalproperty_get_comment(p));
+       }
+       wprintf("<br />\n");
+       wprintf("SEQUENCE == %d<br />\n", sequence);
+       *************************************************************/
+
+       wprintf("<FORM NAME=\"EventForm\" METHOD=\"POST\" action=\"save_event\">\n");
+
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
+               msgnum);
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"calview\" VALUE=\"%s\">\n",
+               bstr("calview"));
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"year\" VALUE=\"%s\">\n",
+               bstr("year"));
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"month\" VALUE=\"%s\">\n",
+               bstr("month"));
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"day\" VALUE=\"%s\">\n",
+               bstr("day"));
+
+       /** Put it in a borderless table so it lines up nicely */
+       wprintf("<TABLE border=0 width=100%%>\n");
+
+       wprintf("<TR><TD><B>");
+       wprintf(_("Summary"));
+       wprintf("</B></TD><TD>\n"
+               "<INPUT TYPE=\"text\" NAME=\"summary\" "
+               "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
+       p = icalcomponent_get_first_property(vevent, ICAL_SUMMARY_PROPERTY);
+       if (p != NULL) {
+               escputs((char *)icalproperty_get_comment(p));
+       }
+       wprintf("\"></TD></TR>\n");
+
+       wprintf("<TR><TD><B>");
+       wprintf(_("Location"));
+       wprintf("</B></TD><TD>\n"
+               "<INPUT TYPE=\"text\" NAME=\"location\" "
+               "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
+       p = icalcomponent_get_first_property(vevent, ICAL_LOCATION_PROPERTY);
+       if (p != NULL) {
+               escputs((char *)icalproperty_get_comment(p));
+       }
+       wprintf("\"></TD></TR>\n");
+
+       wprintf("<TR><TD><B>");
+       wprintf(_("Start"));
+       wprintf("</B></TD><TD>\n");
+       p = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY);
+       if (p != NULL) {
+               t_start = icalproperty_get_dtstart(p);
+               if (t_start.is_date) {
+                       t_start.hour = 0;
+                       t_start.minute = 0;
+                       t_start.second = 0;
+               }
+       }
+       else {
+               localtime_r(&now, &tm_now);
+               if (strlen(bstr("year")) > 0) {
+                       tm_now.tm_year = atoi(bstr("year")) - 1900;
+                       tm_now.tm_mon = atoi(bstr("month")) - 1;
+                       tm_now.tm_mday = atoi(bstr("day"));
+               }
+               if (strlen(bstr("hour")) > 0) {
+                       tm_now.tm_hour = atoi(bstr("hour"));
+                       tm_now.tm_min = atoi(bstr("minute"));
+                       tm_now.tm_sec = 0;
+               }
+               else {
+                       tm_now.tm_hour = 9;
+                       tm_now.tm_min = 0;
+                       tm_now.tm_sec = 0;
+               }
+
+               t_start = icaltime_from_timet_with_zone(
+                       mktime(&tm_now),
+                       ((!strcasecmp(bstr("alldayevent"), "yes")) ? 1 : 0),
+                       icaltimezone_get_utc_timezone()
+               );
+               t_start.is_utc = 1;
+
+       }
+       display_icaltimetype_as_webform(&t_start, "dtstart");
+
+       wprintf("<INPUT TYPE=\"checkbox\" NAME=\"alldayevent\" "
+               "VALUE=\"yes\" onClick=\"grey_all_day();\""
+               " %s >%s",
+               (t_start.is_date ? "CHECKED" : "" ),
+               _("All day event")
+       );
+
+       wprintf("</TD></TR>\n");
+
+       /**
+        * If this is an all-day-event, set the end time to be identical to
+        * the start time (the hour/minute/second will be set to midnight).
+        * Otherwise extract or create it.
+        */
+       wprintf("<TR><TD><B>");
+       wprintf(_("End"));
+       wprintf("</B></TD><TD>\n");
+       if (t_start.is_date) {
+               t_end = t_start;
+       }
+       else {
+               p = icalcomponent_get_first_property(vevent,
+                                                       ICAL_DTEND_PROPERTY);
+               if (p != NULL) {
+                       t_end = icalproperty_get_dtend(p);
+               }
+               else {
+                       /**
+                        * If this is not an all-day event and there is no
+                        * end time specified, make the default one hour
+                        * from the start time.
+                        */
+                       t_end = t_start;
+                       t_end.hour += 1;
+                       t_end.second = 0;
+                       t_end = icaltime_normalize(t_end);
+                       /* t_end = icaltime_from_timet(now, 0); */
+               }
+       }
+       display_icaltimetype_as_webform(&t_end, "dtend");
+       wprintf("</TD></TR>\n");
+
+       wprintf("<TR><TD><B>");
+       wprintf(_("Notes"));
+       wprintf("</B></TD><TD>\n"
+               "<TEXTAREA NAME=\"description\" wrap=soft "
+               "ROWS=5 COLS=80 WIDTH=80>\n"
+       );
+       p = icalcomponent_get_first_property(vevent, ICAL_DESCRIPTION_PROPERTY);
+       if (p != NULL) {
+               escputs((char *)icalproperty_get_comment(p));
+       }
+       wprintf("</TEXTAREA></TD></TR>");
+
+       /**
+        * For a new event, the user creating the event should be the
+        * organizer.  Set this field accordingly.
+        */
+       if (icalcomponent_get_first_property(vevent, ICAL_ORGANIZER_PROPERTY)
+          == NULL) {
+               sprintf(organizer_string, "MAILTO:%s", WC->cs_inet_email);
+               icalcomponent_add_property(vevent,
+                       icalproperty_new_organizer(organizer_string)
+               );
+       }
+
+       /**
+        * Determine who is the organizer of this event.
+        * We need to determine "me" or "not me."
+        */
+       organizer = icalcomponent_get_first_property(vevent, ICAL_ORGANIZER_PROPERTY);
+       if (organizer != NULL) {
+               strcpy(organizer_string, icalproperty_get_organizer(organizer));
+               if (!strncasecmp(organizer_string, "MAILTO:", 7)) {
+                       strcpy(organizer_string, &organizer_string[7]);
+                       striplt(organizer_string);
+                       serv_printf("ISME %s", organizer_string);
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] == '2') {
+                               organizer_is_me = 1;
+                       }
+               }
+       }
+
+       wprintf("<TR><TD><B>");
+       wprintf(_("Organizer"));
+       wprintf("</B></TD><TD>");
+       escputs(organizer_string);
+       if (organizer_is_me) {
+               wprintf(" <FONT SIZE=-1><I>");
+               wprintf(_("(you are the organizer)"));
+               wprintf("</I></FONT>\n");
+       }
+
+       /**
+        * Transmit the organizer as a hidden field.   We don't want the user
+        * to be able to change it, but we do want it fed back to the server,
+        * especially if this is a new event and there is no organizer already
+        * in the calendar object.
+        */
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"organizer\" VALUE=\"");
+       escputs(organizer_string);
+       wprintf("\">");
+
+       wprintf("</TD></TR>\n");
+
+       /** Transparency */
+       wprintf("<TR><TD><B>");
+       wprintf(_("Show time as:"));
+       wprintf("</B></TD><TD>");
+
+       p = icalcomponent_get_first_property(vevent, ICAL_TRANSP_PROPERTY);
+       if (p == NULL) {
+               /** No transparency found.  Default to opaque (busy). */
+               p = icalproperty_new_transp(ICAL_TRANSP_OPAQUE);
+               if (p != NULL) {
+                       icalcomponent_add_property(vevent, p);
+               }
+       }
+       if (p != NULL) {
+               v = icalproperty_get_value(p);
+       }
+       else {
+               v = NULL;
+       }
+
+       wprintf("<INPUT TYPE=\"radio\" NAME=\"transp\" VALUE=\"transparent\"");
+       if (v != NULL) if (icalvalue_get_transp(v) == ICAL_TRANSP_TRANSPARENT)
+               wprintf(" CHECKED");
+       wprintf(">");
+       wprintf(_("Free"));
+       wprintf("&nbsp;&nbsp;");
+
+       wprintf("<INPUT TYPE=\"radio\" NAME=\"transp\" VALUE=\"opaque\"");
+       if (v != NULL) if (icalvalue_get_transp(v) == ICAL_TRANSP_OPAQUE)
+               wprintf(" CHECKED");
+       wprintf(">");
+       wprintf(_("Busy"));
+
+       wprintf("</TD></TR>\n");
+
+       /** Attendees */
+       wprintf("<TR><TD><B>");
+       wprintf(_("Attendees"));
+       wprintf("</B><br />"
+               "<FONT SIZE=-2>");
+       wprintf(_("(One per line)"));
+       wprintf("</FONT></TD><TD>"
+               "<TEXTAREA %s NAME=\"attendees\" wrap=soft "
+               "ROWS=3 COLS=80 WIDTH=80>\n",
+               (organizer_is_me ? "" : "DISABLED ")
+       );
+       i = 0;
+       for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY);
+           attendee != NULL;
+           attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
+               strcpy(attendee_string, icalproperty_get_attendee(attendee));
+               if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
+
+                       /** screen name or email address */
+                       strcpy(attendee_string, &attendee_string[7]);
+                       striplt(attendee_string);
+                       if (i++) wprintf("\n");
+                       escputs(attendee_string);
+                       wprintf(" ");
+
+                       /** participant status */
+                       partstat_as_string(buf, attendee);
+                       escputs(buf);
+               }
+       }
+       wprintf("</TEXTAREA></TD></TR>\n");
+
+       /** Done with properties. */
+       wprintf("</TABLE>\n<CENTER>"
+               "<INPUT TYPE=\"submit\" NAME=\"save_button\" VALUE=\"%s\">"
+               "&nbsp;&nbsp;"
+               "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
+               "&nbsp;&nbsp;"
+               "<INPUT TYPE=\"submit\" NAME=\"check_button\" "
+                               "VALUE=\"%s\">\n"
+               "&nbsp;&nbsp;"
+               "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">\n"
+               "</CENTER>\n",
+               _("Save"),
+               _("Delete"),
+               _("Check attendee availability"),
+               _("Cancel")
+       );
+
+       wprintf("</FORM>\n");
+       
+       wprintf("</td></tr></table></div>\n");
+       wprintf("<script type=\"text/javascript\">"
+               "grey_all_day();"
+               "</script>\n"
+       );
+       wDumpContent(1);
+
+       if (created_new_vevent) {
+               icalcomponent_free(vevent);
+       }
+}
+
+/**
+ * \brief Save an edited event
+ * \param supplied_vevent the event to save
+ * \param msgnum the index on the citserver
+ */
+void save_individual_event(icalcomponent *supplied_vevent, long msgnum) {
+       char buf[SIZ];
+       icalproperty *prop;
+       icalcomponent *vevent, *encaps;
+       int created_new_vevent = 0;
+       int all_day_event = 0;
+       struct icaltimetype event_start, t;
+       icalproperty *attendee = NULL;
+       char attendee_string[SIZ];
+       int i;
+       int foundit;
+       char form_attendees[SIZ];
+       char organizer_string[SIZ];
+       int sequence = 0;
+       enum icalproperty_transp formtransp = ICAL_TRANSP_NONE;
+
+       if (supplied_vevent != NULL) {
+               vevent = supplied_vevent;
+               /**
+                * If we're looking at a fully encapsulated VCALENDAR
+                * rather than a VEVENT component, attempt to use the first
+                * relevant VEVENT subcomponent.  If there is none, the
+                * NULL returned by icalcomponent_get_first_component() will
+                * tell the next iteration of this function to create a
+                * new one.
+                */
+               if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) {
+                       save_individual_event(
+                               icalcomponent_get_first_component(
+                                       vevent, ICAL_VEVENT_COMPONENT
+                               ), msgnum
+                       );
+                       return;
+               }
+       }
+       else {
+               vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
+               created_new_vevent = 1;
+       }
+
+       if ( (strlen(bstr("save_button")) > 0)
+          || (strlen(bstr("check_button")) > 0) ) {
+
+               /** Replace values in the component with ones from the form */
+
+               while (prop = icalcomponent_get_first_property(vevent,
+                     ICAL_SUMMARY_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vevent, prop);
+                       icalproperty_free(prop);
+               }
+               icalcomponent_add_property(vevent,
+                       icalproperty_new_summary(bstr("summary")));
+
+               while (prop = icalcomponent_get_first_property(vevent,
+                     ICAL_LOCATION_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vevent, prop);
+                       icalproperty_free(prop);
+               }
+               icalcomponent_add_property(vevent,
+                       icalproperty_new_location(bstr("location")));
+               
+               while (prop = icalcomponent_get_first_property(vevent,
+                     ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vevent, prop);
+                       icalproperty_free(prop);
+               }
+               icalcomponent_add_property(vevent,
+                       icalproperty_new_description(bstr("description")));
+       
+               while (prop = icalcomponent_get_first_property(vevent,
+                     ICAL_DTSTART_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vevent, prop);
+                       icalproperty_free(prop);
+               }
+
+               if (!strcmp(bstr("alldayevent"), "yes")) {
+                       all_day_event = 1;
+               }
+               else {
+                       all_day_event = 0;
+               }
+
+               if (all_day_event) {
+                       icaltime_from_webform_dateonly(&event_start, "dtstart");
+               }
+               else {
+                       icaltime_from_webform(&event_start, "dtstart");
+               }
+
+               /**
+                * The following odd-looking snippet of code looks like it
+                * takes some unnecessary steps.  It is done this way because
+                * libical incorrectly turns an "all day event" into a normal
+                * event starting at midnight (i.e. it serializes as date/time
+                * instead of just date) unless icalvalue_new_date() is used.
+                * So we force it, if this is an all day event.
+                */
+               prop = icalproperty_new_dtstart(event_start);
+               if (all_day_event) {
+                       icalproperty_set_value(prop,
+                               icalvalue_new_date(event_start)
+                       );
+               }
+
+               if (prop) icalcomponent_add_property(vevent, prop);
+               else icalproperty_free(prop);
+
+               while (prop = icalcomponent_get_first_property(vevent,
+                     ICAL_DTEND_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vevent, prop);
+                       icalproperty_free(prop);
+               }
+               while (prop = icalcomponent_get_first_property(vevent,
+                     ICAL_DURATION_PROPERTY), prop != NULL) {
+                       icalcomponent_remove_property(vevent, prop);
+                       icalproperty_free(prop);
+               }
+
+               if (all_day_event == 0) {
+                       icaltime_from_webform(&t, "dtend");     
+                       icalcomponent_add_property(vevent,
+                               icalproperty_new_dtend(icaltime_normalize(t)
+                               )
+                       );
+               }
+
+               /** See if transparency is indicated */
+               if (strlen(bstr("transp")) > 0) {
+                       if (!strcasecmp(bstr("transp"), "opaque")) {
+                               formtransp = ICAL_TRANSP_OPAQUE;
+                       }
+                       else if (!strcasecmp(bstr("transp"), "transparent")) {
+                               formtransp = ICAL_TRANSP_TRANSPARENT;
+                       }
+
+                       while (prop = icalcomponent_get_first_property(vevent, ICAL_TRANSP_PROPERTY),
+                             (prop != NULL)) {
+                               icalcomponent_remove_property(vevent, prop);
+                               icalproperty_free(prop);
+                       }
+
+                       lprintf(9, "adding new property...\n");
+                       icalcomponent_add_property(vevent, icalproperty_new_transp(formtransp));
+                       lprintf(9, "...added it.\n");
+               }
+
+               /** Give this event a UID if it doesn't have one. */
+               lprintf(9, "Give this event a UID if it doesn't have one.\n");
+               if (icalcomponent_get_first_property(vevent,
+                  ICAL_UID_PROPERTY) == NULL) {
+                       generate_uuid(buf);
+                       icalcomponent_add_property(vevent,
+                               icalproperty_new_uid(buf)
+                       );
+               }
+
+               /** Increment the sequence ID */
+               lprintf(9, "Increment the sequence ID\n");
+               while (prop = icalcomponent_get_first_property(vevent,
+                     ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
+                       i = icalproperty_get_sequence(prop);
+                       lprintf(9, "Sequence was %d\n", i);
+                       if (i > sequence) sequence = i;
+                       icalcomponent_remove_property(vevent, prop);
+                       icalproperty_free(prop);
+               }
+               ++sequence;
+               lprintf(9, "New sequence is %d.  Adding...\n", sequence);
+               icalcomponent_add_property(vevent,
+                       icalproperty_new_sequence(sequence)
+               );
+               
+               /**
+                * Set the organizer, only if one does not already exist *and*
+                * the form is supplying one
+                */
+               lprintf(9, "Setting the organizer...\n");
+               strcpy(buf, bstr("organizer"));
+               if ( (icalcomponent_get_first_property(vevent,
+                  ICAL_ORGANIZER_PROPERTY) == NULL) 
+                  && (strlen(buf) > 0) ) {
+
+                       /** set new organizer */
+                       sprintf(organizer_string, "MAILTO:%s", buf);
+                       icalcomponent_add_property(vevent,
+                               icalproperty_new_organizer(organizer_string)
+                       );
+
+               }
+
+               /**
+                * Add any new attendees listed in the web form
+                */
+               lprintf(9, "Add any new attendees\n");
+
+               /* First, strip out the parenthesized partstats.  */
+               strcpy(form_attendees, bstr("attendees"));
+               stripout(form_attendees, '(', ')');
+
+               /** Now iterate! */
+               for (i=0; i<num_tokens(form_attendees, '\n'); ++i) {
+                       extract_token(buf, form_attendees, i, '\n', sizeof buf);
+                       striplt(buf);
+                       if (strlen(buf) > 0) {
+                               lprintf(9, "Attendee: <%s>\n", buf);
+                               sprintf(attendee_string, "MAILTO:%s", buf);
+                               foundit = 0;
+
+                               for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
+                                       if (!strcasecmp(attendee_string,
+                                          icalproperty_get_attendee(attendee)))
+                                               ++foundit;
+                               }
+
+
+                               if (foundit == 0) {
+                                       icalcomponent_add_property(vevent,
+                                               icalproperty_new_attendee(attendee_string)
+                                       );
+                               }
+                       }
+               }
+
+               /**
+                * Remove any attendees *not* listed in the web form
+                */
+STARTOVER:     lprintf(9, "Remove unlisted attendees\n");
+               for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
+                       strcpy(attendee_string, icalproperty_get_attendee(attendee));
+                       if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
+                               strcpy(attendee_string, &attendee_string[7]);
+                               striplt(attendee_string);
+                               foundit = 0;
+                               for (i=0; i<num_tokens(form_attendees, '\n'); ++i) {
+                                       extract_token(buf, form_attendees, i, '\n', sizeof buf);
+                                       striplt(buf);
+                                       if (!strcasecmp(buf, attendee_string)) ++foundit;
+                               }
+                               if (foundit == 0) {
+                                       icalcomponent_remove_property(vevent, attendee);
+                                       icalproperty_free(attendee);
+                                       goto STARTOVER;
+                               }
+                       }
+               }
+
+               /**
+                * Encapsulate event into full VCALENDAR component.  Clone it first,
+                * for two reasons: one, it's easier to just free the whole thing
+                * when we're done instead of unbundling, but more importantly, we
+                * can't encapsulate something that may already be encapsulated
+                * somewhere else.
+                */
+               lprintf(9, "Encapsulating into full VCALENDAR component\n");
+               encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vevent));
+
+               /** If the user clicked 'Save' then save it to the server. */
+               lprintf(9, "Serializing it for saving\n");
+               if ( (encaps != NULL) && (strlen(bstr("save_button")) > 0) ) {
+                       serv_puts("ENT0 1|||4|||1|");
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] == '8') {
+                               serv_puts("Content-type: text/calendar");
+                               serv_puts("");
+                               serv_puts(icalcomponent_as_ical_string(encaps));
+                               serv_puts("000");
+                       }
+                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                               lprintf(9, "ENT0 REPLY: %s\n", buf);
+                       }
+                       icalcomponent_free(encaps);
+               }
+
+               /** Or, check attendee availability if the user asked for that. */
+               if ( (encaps != NULL) && (strlen(bstr("check_button")) > 0) ) {
+
+                       /** Call this function, which does the real work */
+                       check_attendee_availability(encaps);
+
+                       /** This displays the form again, with our annotations */
+                       display_edit_individual_event(encaps, msgnum);
+
+                       icalcomponent_free(encaps);
+               }
+
+       }
+
+       /**
+        * If the user clicked 'Delete' then delete it.
+        */
+       lprintf(9, "Checking to see if we have to delete an old event\n");
+       if ( (strlen(bstr("delete_button")) > 0) && (msgnum > 0L) ) {
+               serv_printf("DELE %ld", atol(bstr("msgnum")));
+               serv_getln(buf, sizeof buf);
+       }
+
+       if (created_new_vevent) {
+               icalcomponent_free(vevent);
+       }
+
+       /** If this was a save or deelete, go back to the calendar view. */
+       if (strlen(bstr("check_button")) == 0) {
+               readloop("readfwd");
+       }
+}
+
+
+#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
+
+/*@}*/
diff --git a/webcit/src/floors.c b/webcit/src/floors.c
new file mode 100644 (file)
index 0000000..d919285
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup AdminFloor Administrative screens for floor maintenance
+ * \ingroup CitadelConfig
+ */
+/*@{*/
+
+#include "webcit.h"
+#include "webserver.h"
+
+
+
+
+/**
+ * \brief Display floor config
+ * Display floor configuration.  If prepend_html is not NULL, its contents
+ * will be displayed at the top of the screen.
+ * \param prepend_html pagetitle to prepend
+ */
+void display_floorconfig(char *prepend_html)
+{
+       char buf[SIZ];
+
+       int floornum;
+       char floorname[SIZ];
+       int refcount;
+
+        output_headers(1, 1, 2, 0, 0, 0);
+        wprintf("<div id=\"banner\">\n"
+                "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+                "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Add/change/delete floors"));
+       wprintf("</SPAN>"
+                "</TD></TR></TABLE>\n"
+                "</div>\n<div id=\"content\">\n"
+        );
+                                                                                                                             
+       if (prepend_html != NULL) {
+               wprintf("<br /><b><i>");
+               client_write(prepend_html, strlen(prepend_html));
+               wprintf("</i></b><br /><br />\n");
+       }
+
+       serv_printf("LFLR");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#770000\"><TR><TD>");
+               wprintf("<SPAN CLASS=\"titlebar\">");
+               wprintf(_("Error"));
+               wprintf("</SPAN>\n");
+               wprintf("</TD></TR></TABLE>\n");
+               wprintf("%s<br />\n", &buf[4]);
+               wDumpContent(1);
+               return;
+       }
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<TABLE BORDER=1 WIDTH=100%% bgcolor=\"#ffffff\">\n"
+               "<TR><TH>");
+       wprintf(_("Floor number"));
+       wprintf("</TH><TH>");
+       wprintf(_("Floor name"));
+       wprintf("</TH><TH>");
+       wprintf(_("Number of rooms"));
+       wprintf("</TH><TH>");
+       wprintf(_("Floor CSS"));
+       wprintf("</TH></TR>\n");
+
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               floornum = extract_int(buf, 0);
+               extract_token(floorname, buf, 1, '|', sizeof floorname);
+               refcount = extract_int(buf, 2);
+
+               wprintf("<TR><TD><TABLE border=0><TR><TD>%d", floornum);
+               if (refcount == 0) {
+                       wprintf("</TD><TD>"
+                               "<a href=\"delete_floor?floornum=%d\">"
+                               "<FONT SIZE=-1>", floornum);
+                       wprintf(_("(delete floor)"));
+                       wprintf("</A></FONT><br />");
+               }
+               wprintf("<FONT SIZE=-1>"
+                       "<a href=\"display_editfloorpic&"
+                       "which_floor=%d\">", floornum);
+               wprintf(_("(edit graphic)"));
+               wprintf("</A></TD></TR></TABLE>");
+               wprintf("</TD>");
+
+               wprintf("<TD>"
+                       "<FORM METHOD=\"POST\" action=\"rename_floor\">"
+                       "<INPUT TYPE=\"hidden\" NAME=\"floornum\" "
+                       "VALUE=\"%d\">"
+                       "<INPUT TYPE=\"text\" NAME=\"floorname\" "
+                       "VALUE=\"%s\" MAXLENGTH=\"250\">\n",
+                       floornum, floorname);
+               wprintf("<INPUT TYPE=\"SUBMIT\" NAME=\"sc\" "
+                       "VALUE=\"%s\">"
+                       "</FORM></TD>", _("Change name"));
+
+               wprintf("<TD>%d</TD>\n", refcount);
+
+               wprintf("<TD>"
+                       "<FORM METHOD=\"POST\" action=\"set_floor_css\">"
+                       "<INPUT TYPE=\"hidden\" NAME=\"floornum\" "
+                       "VALUE=\"%d\">"
+                       "<INPUT TYPE=\"text\" NAME=\"floorcss\" "
+                       "VALUE=\"%s\" MAXLENGTH=\"250\">\n",
+                       floornum, floorname);
+               wprintf("<INPUT TYPE=\"SUBMIT\" NAME=\"sc\" "
+                       "VALUE=\"%s\">"
+                       "</FORM></TD>", _("Change CSS"));
+
+               wprintf("</TR>\n");
+       }
+
+       wprintf("<TR><TD>&nbsp;</TD>"
+               "<TD><FORM METHOD=\"POST\" action=\"create_floor\">"
+               "<INPUT TYPE=\"text\" NAME=\"floorname\" "
+               "MAXLENGTH=\"250\">\n"
+               "<INPUT TYPE=\"SUBMIT\" NAME=\"sc\" "
+               "VALUE=\"%s\">"
+               "</FORM></TD>"
+               "<TD>&nbsp;</TD></TR>\n", _("Create new floor"));
+
+       wprintf("</table></div>\n");
+       wDumpContent(1);
+}
+
+
+/**
+ * \brief delete the actual floor
+ */
+void delete_floor(void) {
+       int floornum;
+       char buf[SIZ];
+       char message[SIZ];
+
+       floornum = atoi(bstr("floornum"));
+
+       serv_printf("KFLR %d|1", floornum);
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] == '2') {
+               sprintf(message, _("Floor has been deleted."));
+       }
+       else {
+               sprintf(message, "%s", &buf[4]);
+       }
+
+       display_floorconfig(message);
+}
+
+/**
+ * \brief tart creating a new floor
+ */
+void create_floor(void) {
+       char buf[SIZ];
+       char message[SIZ];
+       char floorname[SIZ];
+
+       strcpy(floorname, bstr("floorname"));
+
+       serv_printf("CFLR %s|1", floorname);
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] == '2') {
+               sprintf(message, _("New floor has been created."));
+       } else {
+               sprintf(message, "%s", &buf[4]);
+       }
+
+       display_floorconfig(message);
+}
+
+/**
+ * \brief rename this floor
+ */
+void rename_floor(void) {
+       int floornum;
+       char buf[SIZ];
+       char message[SIZ];
+       char floorname[SIZ];
+
+       floornum = atoi(bstr("floornum"));
+       strcpy(floorname, bstr("floorname"));
+
+       serv_printf("EFLR %d|%s", floornum, floorname);
+       serv_getln(buf, sizeof buf);
+
+       sprintf(message, "%s", &buf[4]);
+
+       display_floorconfig(message);
+}
+
+
+/*@}*/
diff --git a/webcit/src/fmt_date.c b/webcit/src/fmt_date.c
new file mode 100644 (file)
index 0000000..2c7c9f6
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup FormatDates Miscellaneous routines formating dates
+ * \ingroup Calendaring
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+
+typedef unsigned char byte; /**< a byte. */
+
+#define FALSE 0 /**< no. */
+#define TRUE 1 /**< yes. */
+
+/**
+ * \brief      Wrapper around strftime() or strftime_l()
+ *             depending upon how our build is configured.
+ *
+ * \param      s       String target buffer
+ * \param      max     Maximum size of string target buffer
+ * \param      format  strftime() format
+ * \param      tm      Input date/time
+ */
+size_t wc_strftime(char *s, size_t max, const char *format, const struct tm *tm)
+{
+#ifdef ENABLE_NLS
+       if (wc_locales[WC->selected_language] == NULL) {
+               return strftime(s, max, format, tm);
+       }
+       else {
+               return strftime_l(s, max, format, tm, wc_locales[WC->selected_language]);
+       }
+#else
+       return strftime(s, max, format, tm);
+#endif
+}
+
+
+/**
+ * \brief Format a date/time stamp for output 
+ * \param buf the output buffer
+ * \param thetime time to convert to string 
+ * \param brief do we want compact view?????
+ */
+void fmt_date(char *buf, time_t thetime, int brief)
+{
+       struct tm tm;
+       struct tm today_tm;
+       time_t today_timet;
+       int hour;
+       char calhourformat[16];
+
+       get_preference("calhourformat", calhourformat, sizeof calhourformat);
+
+       today_timet = time(NULL);
+       localtime_r(&today_timet, &today_tm);
+
+       localtime_r(&thetime, &tm);
+       hour = tm.tm_hour;
+       if (hour == 0)
+               hour = 12;
+       else if (hour > 12)
+               hour = hour - 12;
+
+       buf[0] = 0;
+
+       if (brief) {
+
+               /** If date == today, show only the time */
+               if ((tm.tm_year == today_tm.tm_year)
+                 &&(tm.tm_mon == today_tm.tm_mon)
+                 &&(tm.tm_mday == today_tm.tm_mday)) {
+                       wc_strftime(buf, 32, "%l:%M%p", &tm);
+               }
+               /** Otherwise, for messages up to 6 months old, show the
+                * month and day, and the time */
+               else if (today_timet - thetime < 15552000) {
+                       wc_strftime(buf, 32, "%b %d %l:%M%p", &tm);
+               }
+               /** older than 6 months, show only the date */
+               else {
+                       wc_strftime(buf, 32, "%b %d %Y", &tm);
+               }
+       }
+       else {
+               wc_strftime(buf, 32, "%c", &tm);
+       }
+}
+
+
+/**
+ * \brief Format TIME ONLY for output 
+ * \param buf the output buffer
+ * \param thetime time to format into buf
+ */
+void fmt_time(char *buf, time_t thetime)
+{
+       struct tm *tm;
+       int hour;
+       char calhourformat[16];
+
+       get_preference("calhourformat", calhourformat, sizeof calhourformat);
+
+       buf[0] = 0;
+       tm = localtime(&thetime);
+       hour = tm->tm_hour;
+       if (hour == 0)
+               hour = 12;
+       else if (hour > 12)
+               hour = hour - 12;
+
+       if (!strcasecmp(calhourformat, "24")) {
+               sprintf(buf, "%2d:%02d",
+                       tm->tm_hour, tm->tm_min
+               );
+       }
+       else {
+               sprintf(buf, "%d:%02d%s",
+                       hour, tm->tm_min, ((tm->tm_hour > 12) ? "pm" : "am")
+               );
+       }
+}
+
+
+
+
+/**
+ * \brief Break down the timestamp used in HTTP headers
+ * Should read rfc1123 and rfc850 dates OK
+ * \todo FIXME won't read asctime
+ * Doesn't understand timezone, but we only should be using GMT/UTC anyway
+ * \param buf time to parse
+ * \return the time found in buf
+ */
+time_t httpdate_to_timestamp(char *buf)
+{
+       time_t t = 0;
+       struct tm tt;
+       char *c;
+       char tz[256];
+
+       /** Skip day of week, to number */
+       for (c = buf; *c != ' '; c++)
+               ;
+       c++;
+
+       /* Get day of month */
+       tt.tm_mday = atoi(c);
+       for (; *c != ' ' && *c != '-'; c++);
+       c++;
+
+       /** Get month */
+       switch (*c) {
+       case 'A':       /** April, August */
+               tt.tm_mon = (c[1] == 'p') ? 3 : 7;
+               break;
+       case 'D':       /** December */
+               tt.tm_mon = 11;
+               break;
+       case 'F':       /** February */
+               tt.tm_mon = 1;
+               break;
+       case 'M':       /** March, May */
+               tt.tm_mon = (c[2] == 'r') ? 2 : 4;
+               break;
+       case 'J':       /** January, June, July */
+               tt.tm_mon = (c[2] == 'n') ? ((c[1] == 'a') ? 0 : 5) : 6;
+               break;
+       case 'N':       /** November */
+               tt.tm_mon = 10;
+               break;
+       case 'O':       /** October */
+               tt.tm_mon = 9;
+               break;
+       case 'S':       /** September */
+               tt.tm_mon = 8;
+               break;
+       default:
+               return 42;
+               break;  /** NOTREACHED */
+       }
+       c += 4;
+
+       tt.tm_year = 0;
+       /** Get year */
+       tt.tm_year = atoi(c);
+       for (; *c != ' '; c++);
+       c++;
+       if (tt.tm_year >= 1900)
+               tt.tm_year -= 1900;
+
+       /** Get hour */
+       tt.tm_hour = atoi(c);
+       for (; *c != ':'; c++);
+       c++;
+
+       /** Get minute */
+       tt.tm_min = atoi(c);
+       for (; *c != ':'; c++);
+       c++;
+
+       /** Get second */
+       tt.tm_sec = atoi(c);
+       for (; *c && *c != ' '; c++);
+
+       /** Got everything; let's go */
+       /** First, change to UTC */
+       if (getenv("TZ"))
+               sprintf(tz, "TZ=%s", getenv("TZ"));
+       else
+               strcpy(tz, "TZ=");
+       putenv("TZ=UTC");
+       tzset();
+       t = mktime(&tt);
+       putenv(tz);
+       tzset();
+       return t;
+}
+
+
+
+
+/*@}*/
diff --git a/webcit/src/gettext.c b/webcit/src/gettext.c
new file mode 100644 (file)
index 0000000..ddee2f8
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * $Id
+ */
+/**
+ * \defgroup LocaleHeaderParser Parse the browser http locale headers and set the NLS stuff.
+ * \ingroup WebcitHttpServer 
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+
+#ifdef ENABLE_NLS
+
+#define NUM_LANGS 5 /**< how many different locales do we know? */
+#define SEARCH_LANG 20 /**< how many langs should we parse? */
+
+/** actual supported locales */
+char *AvailLang[NUM_LANGS] = {
+       "C",
+       "en_US",
+       "de_DE",
+       "it_IT",
+       "en_GB"
+};
+
+locale_t wc_locales[NUM_LANGS]; /**< here we keep the parsed stuff */
+
+/** Keep information about one locale */
+typedef struct _lang_pref{
+       char lang[16];          /**< the language locale string */
+       char region[16];        /**< the region locale string */
+       long priority;          /**< which priority does it have */
+       int availability;       /**< do we know it? */
+       int selectedlang;       /**< is this the selected language? */
+} LangStruct;
+
+/* \brief parse browser locale header 
+ * seems as most browsers just do a one after coma value even if more than 10 locales are available. Sample strings:
+ * opera: 
+ * Accept-Language: sq;q=1.0,de;q=0.9,as;q=0.8,ar;q=0.7,bn;q=0.6,zh-cn;q=0.5,kn;q=0.4,ch;q=0.3,fo;q=0.2,gn;q=0.1,ce;q=0.1,ie;q=0.1 
+ * Firefox 
+ * Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' 
+ * Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 
+ * Accept-Language: de,en-us;q=0.9,it;q=0.9,de-de;q=0.8,en-ph;q=0.7,de-at;q=0.7,zh-cn;q=0.6,cy;q=0.5,ar-om;q=0.5,en-tt;q=0.4,xh;q=0.3,nl-be;q=0.3,cs;q=0.2,sv;q=0.1,tk;q=0.1 
+ * \param LocaleString the string from the browser http headers
+ */
+
+void httplang_to_locale(char *LocaleString)
+{
+       LangStruct wanted_locales[SEARCH_LANG];
+       LangStruct *ls;
+
+       int i = 0;
+       int j = 0;
+       size_t len = strlen(LocaleString);
+       long prio;
+       int av;
+       int nBest;
+       int nParts;
+       char *search = (char *) malloc(len);
+       
+       memcpy(search, LocaleString, len);
+       search[len] = '\0';
+       nParts=num_tokens(search,',');
+       for (i=0; ((i<nParts)&&(i<SEARCH_LANG)); i++)
+        {
+                       char buf[16];
+                       char sbuf[16];
+                       char lbuf[16];
+                       int blen;
+                       
+                       ls=&wanted_locales[i];
+
+                       extract_token(&buf[0],search, i,',',16);
+                       /** we are searching, if this list item has something like ;q=n*/
+                       if (num_tokens(&buf[0],'=')>1) {
+                               int sbuflen, k;
+                               extract_token(&sbuf[0],&buf[0], 1,'=',16);
+                               sbuflen=strlen(&sbuf[0]);
+                               for (k=0; k<sbuflen; k++) if (sbuf[k]=='.') sbuf[k]='0';
+                               ls->priority=atol(&sbuf[0]);
+                       }
+                       else {
+                               ls->priority=1000;
+                       }
+                       /** get the locale part */
+                       extract_token(&sbuf[0],&buf[0],0,';',16);
+                       /** get the lang part, which should be allways there */
+                       extract_token(&ls->lang[0],&sbuf[0],0,'-',16);
+                       /** get the area code if any. */
+                       if (num_tokens(&sbuf[0],'-')>1) {
+                               extract_token(&ls->region[0],&sbuf[0],1,'-',16);
+                       }
+                       else { /** no ara code? use lang code */
+                               blen=strlen(&ls->lang[0]);
+                               memcpy(&ls->region[0], ls->lang,blen);
+                               ls->region[blen]='\0';
+                       } /** area codes are uppercase */
+                       blen=strlen(&ls->region[0]);
+                       for (j=0; j<blen; j++)
+                               {
+                                       int chars=toupper(ls->region[j]);
+                                       ls->region[j]=(char)chars;/** \todo ?! */
+                               }
+                       sprintf(&lbuf[0],"%s_%s",&ls->lang[0],&ls->region[0]);
+                       
+                       /** check if we have this lang */
+                       ls->availability=1;
+                       ls->selectedlang=-1;
+                       for (j=0; j<NUM_LANGS; j++) {
+                               int result;
+                               /** match against the LANG part */
+                               result=strcasecmp(&ls->lang[0], AvailLang[j]);
+                               if ((result<0)&&(result<ls->availability)){
+                                       ls->availability=result;
+                                       ls->selectedlang=j;
+                               }
+                               /** match against lang and locale */
+                               if (0==strcasecmp(&lbuf[0], AvailLang[j])){
+                                       ls->availability=0;
+                                       ls->selectedlang=j;
+                                       j=NUM_LANGS;
+                               }
+                       }
+        }
+       
+       prio=0;
+       av=-1000;
+       nBest=-1;
+       for (i=0; ((i<nParts)&&(i<SEARCH_LANG)); i++)
+               {
+                       ls=&wanted_locales[i];
+                       if ((ls->availability<=0)&& 
+                               (av<ls->availability)&&
+                               (prio<ls->priority)&&
+                               (ls->selectedlang!=-1)){
+                               nBest=ls->selectedlang;
+                               av=ls->availability;
+                               prio=ls->priority;
+                       }
+               }
+       if (nBest==-1) /** fall back to C */
+               nBest=0;
+       WC->selected_language=nBest;
+       lprintf(9, "language found: %s\n", AvailLang[WC->selected_language]);
+       if (search != NULL) {
+               free(search);
+       }
+}
+
+/* TODO: we skip the language weightening so far. */
+/* Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' */
+/* Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 */
+//void httplang_to_locale(char *LocaleString)
+//{
+//     char selected_locale[16];
+//     int i, j;
+//     char lang[64];
+//     int num_accept = 0;
+//
+//     lprintf(9, "languageAccept: %s\n", LocaleString);
+//
+//     strcpy(selected_locale, "C");
+//     num_accept = num_tokens(LocaleString, ',');
+//
+//     for (i=num_accept-1; i>=0; --i) {
+//             extract_token(lang, LocaleString, i, ',', sizeof lang);
+//
+//             /* Strip out the weights; we don't use them.  Also convert
+//              * hyphens to underscores.
+//              */
+//             for (j=0; j<strlen(lang); ++j) {
+//                     if (lang[j] == '-') lang[j] = '_';
+//                     if (lang[j] == ';') lang[j] = 0;
+//             }
+//
+//             for (j=0; j<NUM_LANGS; ++j) {
+//                     if (!strncasecmp(lang, AvailLang[j], strlen(lang))) {
+//                             strcpy(selected_locale, AvailLang[j]);
+//                     }
+//             }
+//     }
+//
+//     lprintf(9, "language found: %s\n", selected_locale);
+//     set_selected_language(selected_locale);
+//}
+
+
+/**
+ * \brief show the language chooser on the login dialog
+ * depending on the browser locale change the sequence of the 
+ * language chooser.
+ */
+void offer_languages(void) {
+       int i;
+
+       wprintf("<select name=\"language\" size=\"1\">\n");
+
+       for (i=0; i < NUM_LANGS; ++i) {
+               wprintf("<option %s value=%s>%s</option>\n",
+                       ((WC->selected_language == i) ? "selected" : ""),
+                       AvailLang[i],
+                       AvailLang[i]
+               );
+       }
+
+       wprintf("</select>\n");
+}
+
+/**
+ * \brief Set the selected language for this session.
+ * \param lang the locale to set.
+ */
+void set_selected_language(char *lang) {
+       int i;
+
+       for (i=0; i<NUM_LANGS; ++i) {
+               if (!strcasecmp(lang, AvailLang[i])) {
+                       WC->selected_language = i;
+               }
+       }
+}
+
+/**
+ * \brief Activate the selected language for this session.
+ */
+void go_selected_language(void) {
+       if (WC->selected_language < 0) return;
+       uselocale(wc_locales[WC->selected_language]);   /** switch locales */
+       textdomain(textdomain(NULL));                   /** clear the cache */
+}
+
+/**
+ * \brief Deactivate the selected language for this session.
+ */
+void stop_selected_language(void) {
+       uselocale(LC_GLOBAL_LOCALE);                    /** switch locales */
+       textdomain(textdomain(NULL));                   /** clear the cache */
+}
+
+
+/**
+ * \brief Create a locale_t for each available language
+ */
+void initialize_locales(void) {
+       int i;
+       locale_t Empty_Locale;
+       char buf[32];
+
+       /* create default locale */
+       Empty_Locale = newlocale(LC_ALL_MASK, NULL, NULL);
+
+       for (i = 0; i < NUM_LANGS; ++i) {
+               if (i == 0) {
+                       sprintf(buf, "%s", AvailLang[i]);       // locale 0 (C) is ascii, not utf-8
+               }
+               else {
+                       sprintf(buf, "%s.UTF8", AvailLang[i]);
+               }
+               wc_locales[i] = newlocale(
+                       (LC_MESSAGES_MASK|LC_TIME_MASK),
+                       buf,
+                       (((i > 0) && (wc_locales[0] != NULL)) ? wc_locales[0] : Empty_Locale)
+               );
+               if (wc_locales[i] == NULL) {
+                       lprintf(1, "Error configuring locale for %s: %s\n",
+                               buf,
+                               strerror(errno)
+                       );
+               }
+               else {
+                       lprintf(3, "Configured available locale: %s\n", buf);
+               }
+       }
+}
+
+
+#else  /* ENABLE_NLS */
+/** \brief dummy for non NLS enabled systems */
+void offer_languages(void) {
+       wprintf("English (US)");
+}
+
+/** \brief dummy for non NLS enabled systems */
+void set_selected_language(char *lang) {
+}
+
+/** \brief dummy for non NLS enabled systems */
+void go_selected_language(void) {
+}
+
+/** \brief dummy for non NLS enabled systems */
+void stop_selected_language(void) {
+}
+
+#endif /* ENABLE_NLS */
+
+
+/*@}*/
diff --git a/webcit/src/graphics.c b/webcit/src/graphics.c
new file mode 100644 (file)
index 0000000..00f256b
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * $Id$
+ *
+ * Handles HTTP upload of graphics files into the system.
+ * \ingroup WebcitHttpServer
+ */
+
+#include "webcit.h"
+
+void display_graphics_upload(char *description, char *check_cmd, char *uplurl)
+{
+       char buf[SIZ];
+
+       serv_puts(check_cmd);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               strcpy(WC->ImportantMessage, &buf[4]);
+               display_main_menu();
+               return;
+       }
+       output_headers(1, 1, 0, 0, 0, 0);
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Image upload"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       wprintf("<CENTER>\n");
+
+       wprintf("<FORM ENCTYPE=\"multipart/form-data\" action=\"%s\" "
+               "METHOD=\"POST\" NAME=\"graphicsupload\">\n", uplurl);
+
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"which_room\" VALUE=\"");
+       urlescputs(bstr("which_room"));
+       wprintf("\">\n");
+
+       wprintf(_("You can upload any image directly from your computer, "
+               "as long as it is in GIF format (JPEG, PNG, etc. won't "
+               "work)."));
+       wprintf("<br /><br />\n");
+
+       wprintf(_("Please select a file to upload:"));
+       wprintf("<br /><br />\n");
+       wprintf("<INPUT TYPE=\"FILE\" NAME=\"filename\" SIZE=\"35\">\n");
+       wprintf("<br /><br />");
+       wprintf("<INPUT TYPE=\"SUBMIT\" NAME=\"upload_button\" VALUE=\"%s\">\n", _("Upload"));
+       wprintf("&nbsp;");
+       wprintf("<INPUT TYPE=\"RESET\" VALUE=\"%s\">\n", _("Reset form"));
+       wprintf("&nbsp;");
+       wprintf("<INPUT TYPE=\"SUBMIT\" NAME=\"cancel_button\" VALUE=\"%s\">\n", _("Cancel"));
+       wprintf("</FORM>\n");
+       wprintf("</CENTER>\n");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+void do_graphics_upload(char *upl_cmd)
+{
+       char buf[SIZ];
+       int bytes_remaining;
+       int pos = 0;
+       int thisblock;
+
+       if (strlen(bstr("cancel_button")) > 0) {
+               strcpy(WC->ImportantMessage,
+                       _("Graphics upload has been cancelled."));
+               display_main_menu();
+               return;
+       }
+
+       if (WC->upload_length == 0) {
+               strcpy(WC->ImportantMessage,
+                       _("You didn't upload a file."));
+               display_main_menu();
+               return;
+       }
+       serv_puts(upl_cmd);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               strcpy(WC->ImportantMessage, &buf[4]);
+               display_main_menu();
+               return;
+       }
+       bytes_remaining = WC->upload_length;
+       while (bytes_remaining) {
+               thisblock = ((bytes_remaining > 4096) ? 4096 : bytes_remaining);
+               serv_printf("WRIT %d", thisblock);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '7') {
+                       strcpy(WC->ImportantMessage, &buf[4]);
+                       serv_puts("UCLS 0");
+                       serv_getln(buf, sizeof buf);
+                       display_main_menu();
+                       return;
+               }
+               thisblock = extract_int(&buf[4], 0);
+               serv_write(&WC->upload[pos], thisblock);
+               pos = pos + thisblock;
+               bytes_remaining = bytes_remaining - thisblock;
+       }
+
+       serv_puts("UCLS 1");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != 'x') {
+               display_success(&buf[4]);
+               return;
+       }
+}
diff --git a/webcit/src/groupdav.h b/webcit/src/groupdav.h
new file mode 100644 (file)
index 0000000..2a933ad
--- /dev/null
@@ -0,0 +1,14 @@
+/* $Id$ */
+
+void groupdav_common_headers(void);
+void groupdav_main(struct httprequest *, char *, int, char *);
+void groupdav_get(char *);
+void groupdav_put(char *, char *, char *, char *, int);
+void groupdav_delete(char *, char *);
+void groupdav_propfind(char *, int, char *, char *);
+void groupdav_options(char *);
+long locate_message_by_uid(char *);
+void groupdav_folder_list(void);
+void euid_escapize(char *, char *);
+void euid_unescapize(char *, char *);
+void groupdav_identify_host(void);
diff --git a/webcit/src/groupdav_delete.c b/webcit/src/groupdav_delete.c
new file mode 100644 (file)
index 0000000..2d44b8f
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * $Id$
+ *
+ * Handles GroupDAV DELETE requests.
+ *
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+#include "groupdav.h"
+
+
+/*
+ * The pathname is always going to be /groupdav/room_name/euid
+ */
+void groupdav_delete(char *dav_pathname, char *dav_ifmatch) {
+       char dav_roomname[SIZ];
+       char dav_uid[SIZ];
+       long dav_msgnum = (-1);
+       char buf[SIZ];
+       int n = 0;
+
+       /* First, break off the "/groupdav/" prefix */
+       remove_token(dav_pathname, 0, '/');
+       remove_token(dav_pathname, 0, '/');
+
+       /* Now extract the message euid */
+       n = num_tokens(dav_pathname, '/');
+       extract_token(dav_uid, dav_pathname, n-1, '/', sizeof dav_uid);
+       remove_token(dav_pathname, n-1, '/');
+
+       /* What's left is the room name.  Remove trailing slashes. */
+       if (dav_pathname[strlen(dav_pathname)-1] == '/') {
+               dav_pathname[strlen(dav_pathname)-1] = 0;
+       }
+       strcpy(dav_roomname, dav_pathname);
+
+       /* Go to the correct room. */
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               gotoroom(dav_roomname);
+       }
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               wprintf("HTTP/1.1 404 not found\r\n");
+               groupdav_common_headers();
+               wprintf("Content-Length: 0\r\n\r\n");
+               return;
+       }
+
+       dav_msgnum = locate_message_by_uid(dav_uid);
+
+       /*
+        * If no item exists with the requested uid ... simple error.
+        */
+       if (dav_msgnum < 0L) {
+               wprintf("HTTP/1.1 404 Not Found\r\n");
+               groupdav_common_headers();
+               wprintf("Content-Length: 0\r\n\r\n");
+               return;
+       }
+
+       /*
+        * It's there ... check the ETag and make sure it matches
+        * the message number.
+        */
+       if (strlen(dav_ifmatch) > 0) {
+               if (atol(dav_ifmatch) != dav_msgnum) {
+                       wprintf("HTTP/1.1 412 Precondition Failed\r\n");
+                       groupdav_common_headers();
+                       wprintf("Content-Length: 0\r\n\r\n");
+                       return;
+               }
+       }
+
+       /*
+        * Ok, attempt to delete the item.
+        */
+       serv_printf("DELE %ld", dav_msgnum);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               wprintf("HTTP/1.1 204 No Content\r\n"); /* success */
+               groupdav_common_headers();
+               wprintf("Content-Length: 0\r\n\r\n");
+       }
+       else {
+               wprintf("HTTP/1.1 403 Forbidden\r\n");  /* access denied */
+               groupdav_common_headers();
+               wprintf("Content-Length: 0\r\n\r\n");
+       }
+       return;
+}
diff --git a/webcit/src/groupdav_get.c b/webcit/src/groupdav_get.c
new file mode 100644 (file)
index 0000000..d773fe2
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * $Id$
+ *
+ * Handles GroupDAV GET requests.
+ *
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+#include "groupdav.h"
+
+
+/*
+ * Fetch the entire contents of the room as one big ics file.
+ * This is for "webcal://" type access.
+ */    
+void groupdav_get_big_ics(void) {
+       char buf[1024];
+
+       serv_puts("ICAL getics");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               wprintf("HTTP/1.1 404 not found\r\n");
+               groupdav_common_headers();
+               wprintf(
+                       "Content-Type: text/plain\r\n"
+                       "\r\n"
+                       "%s\r\n",
+                       &buf[4]
+               );
+               return;
+       }
+
+       wprintf("HTTP/1.1 200 OK\r\n");
+       groupdav_common_headers();
+       wprintf("Content-type: text/calendar; charset=UTF-8\r\n");
+       begin_burst();
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               wprintf("%s\r\n", buf);
+       }
+       end_burst();
+}
+
+
+/*
+ * The pathname is always going to take one of two formats:
+ * /groupdav/room_name/euid    (GroupDAV)
+ * /groupdav/room_name         (webcal)
+ */
+void groupdav_get(char *dav_pathname) {
+       char dav_roomname[1024];
+       char dav_uid[1024];
+       long dav_msgnum = (-1);
+       char buf[1024];
+       int in_body = 0;
+       int found_content_type = 0;
+
+       if (num_tokens(dav_pathname, '/') < 3) {
+               wprintf("HTTP/1.1 404 not found\r\n");
+               groupdav_common_headers();
+               wprintf(
+                       "Content-Type: text/plain\r\n"
+                       "\r\n"
+                       "The object you requested was not found.\r\n"
+               );
+               return;
+       }
+
+       extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
+       extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
+       if ((!strcasecmp(dav_uid, "ics")) || (!strcasecmp(dav_uid, "calendar.ics"))) {
+               strcpy(dav_uid, "");
+       }
+
+       /* Go to the correct room. */
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               gotoroom(dav_roomname);
+       }
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               wprintf("HTTP/1.1 404 not found\r\n");
+               groupdav_common_headers();
+               wprintf(
+                       "Content-Type: text/plain\r\n"
+                       "\r\n"
+                       "There is no folder called \"%s\" on this server.\r\n",
+                       dav_roomname
+               );
+               return;
+       }
+
+       /** GET on the collection itself returns an ICS of the entire collection.
+        */
+       if (!strcasecmp(dav_uid, "")) {
+               groupdav_get_big_ics();
+               return;
+       }
+
+       dav_msgnum = locate_message_by_uid(dav_uid);
+       serv_printf("MSG2 %ld", dav_msgnum);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               wprintf("HTTP/1.1 404 not found\r\n");
+               groupdav_common_headers();
+               wprintf(
+                       "Content-Type: text/plain\r\n"
+                       "\r\n"
+                       "Object \"%s\" was not found in the \"%s\" folder.\r\n",
+                       dav_uid,
+                       dav_roomname
+               );
+               return;
+       }
+
+       wprintf("HTTP/1.1 200 OK\r\n");
+       groupdav_common_headers();
+       wprintf("etag: \"%ld\"\r\n", dav_msgnum);
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               if (in_body) {
+                       wprintf("%s\r\n", buf);
+               }
+               else if (!strncasecmp(buf, "Date: ", 6)) {
+                       wprintf("%s\r\n", buf);
+               }
+               else if (!strncasecmp(buf, "Content-type: ", 14)) {
+                       wprintf("%s", buf);
+                       if (bmstrcasestr(buf, "charset=")) {
+                               wprintf("%s\r\n", buf);
+                       }
+                       else {
+                               wprintf("%s;charset=UTF-8\r\n", buf);
+                       }
+                       found_content_type = 1;
+               }
+               else if ((strlen(buf) == 0) && (in_body == 0)) {
+                       if (!found_content_type) {
+                               wprintf("Content-type: text/plain\r\n");
+                       }
+                       in_body = 1;
+                       begin_burst();
+               }
+       }
+       end_burst();
+}
diff --git a/webcit/src/groupdav_main.c b/webcit/src/groupdav_main.c
new file mode 100644 (file)
index 0000000..ca31fe2
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * $Id$
+ *
+ * Entry point for GroupDAV functions
+ *
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+#include "groupdav.h"
+
+
+/*
+ * Output HTTP headers which are common to all requests.
+ *
+ * Please observe that we don't use the usual output_headers()
+ * and wDumpContent() functions in the GroupDAV subsystem, so we
+ * do our own header stuff here.
+ *
+ */
+void groupdav_common_headers(void) {
+       wprintf(
+               "Server: %s / %s\r\n"
+               "Connection: close\r\n",
+               SERVER, serv_info.serv_software
+       );
+}
+
+
+
+/*
+ * string conversion function
+ */
+void euid_escapize(char *target, char *source) {
+       int i;
+       int target_length = 0;
+
+       strcpy(target, "");
+       for (i=0; i<strlen(source); ++i) {
+               if ( (isalnum(source[i])) || (source[i]=='-') || (source[i]=='_') ) {
+                       target[target_length] = source[i];
+                       target[++target_length] = 0;
+               }
+               else {
+                       sprintf(&target[target_length], "=%02X", source[i]);
+                       target_length += 3;
+               }
+       }
+}
+
+/*
+ * string conversion function
+ */
+void euid_unescapize(char *target, char *source) {
+       int a, b;
+       char hex[3];
+       int target_length = 0;
+
+       strcpy(target, "");
+
+       for (a = 0; a < strlen(source); ++a) {
+               if (source[a] == '=') {
+                       hex[0] = source[a + 1];
+                       hex[1] = source[a + 2];
+                       hex[2] = 0;
+                       b = 0;
+                       sscanf(hex, "%02x", &b);
+                       target[target_length] = b;
+                       target[++target_length] = 0;
+                       a += 2;
+               }
+               else {
+                       target[target_length] = source[a];
+                       target[++target_length] = 0;
+               }
+       }
+}
+
+
+
+
+/*
+ * Main entry point for GroupDAV requests
+ */
+void groupdav_main(struct httprequest *req,
+                       char *dav_content_type,
+                       int dav_content_length,
+                       char *dav_content
+) {
+       struct httprequest *rptr;
+       char dav_method[256];
+       char dav_pathname[256];
+       char dav_ifmatch[256];
+       int dav_depth;
+       char *ds;
+       int i;
+
+       strcpy(dav_method, "");
+       strcpy(dav_pathname, "");
+       strcpy(dav_ifmatch, "");
+       dav_depth = 0;
+
+       for (rptr=req; rptr!=NULL; rptr=rptr->next) {
+               if (!strncasecmp(rptr->line, "Host: ", 6)) {
+                       if (strlen(WC->http_host) == 0) {
+                               safestrncpy(WC->http_host, &rptr->line[6],
+                                       sizeof WC->http_host);
+                       }
+                }
+               if (!strncasecmp(rptr->line, "If-Match: ", 10)) {
+                        safestrncpy(dav_ifmatch, &rptr->line[10],
+                               sizeof dav_ifmatch);
+                }
+               if (!strncasecmp(rptr->line, "Depth: ", 7)) {
+                       if (!strcasecmp(&rptr->line[7], "infinity")) {
+                               dav_depth = 32767;
+                       }
+                       else if (!strcmp(&rptr->line[7], "0")) {
+                               dav_depth = 0;
+                       }
+                       else if (!strcmp(&rptr->line[7], "1")) {
+                               dav_depth = 1;
+                       }
+                }
+       }
+
+       if (!WC->logged_in) {
+               wprintf("HTTP/1.1 401 Unauthorized\r\n");
+               groupdav_common_headers();
+               wprintf("WWW-Authenticate: Basic realm=\"%s\"\r\n",
+                       serv_info.serv_humannode);
+               wprintf("Content-Length: 0\r\n\r\n");
+               return;
+       }
+
+       extract_token(dav_method, req->line, 0, ' ', sizeof dav_method);
+       extract_token(dav_pathname, req->line, 1, ' ', sizeof dav_pathname);
+       unescape_input(dav_pathname);
+
+       /* If the request does not begin with "/groupdav", prepend it.  If
+        * we happen to introduce a double-slash, that's ok; we'll strip it
+        * in the next step.
+        * 
+        * (THIS IS DISABLED BECAUSE WE ARE NOW TRYING TO DO REAL DAV.)
+        *
+       if (strncasecmp(dav_pathname, "/groupdav", 9)) {
+               char buf[512];
+               snprintf(buf, sizeof buf, "/groupdav/%s", dav_pathname);
+               safestrncpy(dav_pathname, buf, sizeof dav_pathname);
+       }
+        *
+        */
+       
+       /* Remove any stray double-slashes in pathname */
+       while (ds=strstr(dav_pathname, "//"), ds != NULL) {
+               strcpy(ds, ds+1);
+       }
+
+       /*
+        * If there's an If-Match: header, strip out the quotes if present, and
+        * then if all that's left is an asterisk, make it go away entirely.
+        */
+       if (strlen(dav_ifmatch) > 0) {
+               striplt(dav_ifmatch);
+               if (dav_ifmatch[0] == '\"') {
+                       strcpy(dav_ifmatch, &dav_ifmatch[1]);
+                       for (i=0; i<strlen(dav_ifmatch); ++i) {
+                               if (dav_ifmatch[i] == '\"') {
+                                       dav_ifmatch[i] = 0;
+                               }
+                       }
+               }
+               if (!strcmp(dav_ifmatch, "*")) {
+                       strcpy(dav_ifmatch, "");
+               }
+       }
+
+       /*
+        * The OPTIONS method is not required by GroupDAV.  This is an
+        * experiment to determine what might be involved in supporting
+        * other variants of DAV in the future.
+        */
+       if (!strcasecmp(dav_method, "OPTIONS")) {
+               groupdav_options(dav_pathname);
+               return;
+       }
+
+       /*
+        * The PROPFIND method is basically used to list all objects in a
+        * room, or to list all relevant rooms on the server.
+        */
+       if (!strcasecmp(dav_method, "PROPFIND")) {
+               groupdav_propfind(dav_pathname, dav_depth,
+                               dav_content_type, dav_content);
+               return;
+       }
+
+       /*
+        * The GET method is used for fetching individual items.
+        */
+       if (!strcasecmp(dav_method, "GET")) {
+               groupdav_get(dav_pathname);
+               return;
+       }
+
+       /*
+        * The PUT method is used to add or modify items.
+        */
+       if (!strcasecmp(dav_method, "PUT")) {
+               groupdav_put(dav_pathname, dav_ifmatch,
+                               dav_content_type, dav_content,
+                               dav_content_length);
+               return;
+       }
+
+       /*
+        * The DELETE method kills, maims, and destroys.
+        */
+       if (!strcasecmp(dav_method, "DELETE")) {
+               groupdav_delete(dav_pathname, dav_ifmatch);
+               return;
+       }
+
+       /*
+        * Couldn't find what we were looking for.  Die in a car fire.
+        */
+       wprintf("HTTP/1.1 501 Method not implemented\r\n");
+       groupdav_common_headers();
+       wprintf("Content-Type: text/plain\r\n"
+               "\r\n"
+               "GroupDAV method \"%s\" is not implemented.\r\n",
+               dav_method
+       );
+}
+
+
+/*
+ * Output our host prefix for globally absolute URL's.
+ */  
+void groupdav_identify_host(void) {
+       if (strlen(WC->http_host) > 0) {
+               wprintf("%s://%s",
+                       (is_https ? "https" : "http"),
+                       WC->http_host);
+       }
+}
diff --git a/webcit/src/groupdav_options.c b/webcit/src/groupdav_options.c
new file mode 100644 (file)
index 0000000..b92b794
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * $Id$
+ *
+ * Handles DAV OPTIONS requests (experimental -- not required by GroupDAV)
+ *
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+#include "groupdav.h"
+
+/*
+ * The pathname is always going to be /groupdav/room_name/msg_num
+ */
+void groupdav_options(char *dav_pathname) {
+       char dav_roomname[256];
+       char dav_uid[256];
+       long dav_msgnum = (-1);
+       char datestring[256];
+       time_t now;
+
+       now = time(NULL);
+       http_datestring(datestring, sizeof datestring, now);
+
+       extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
+       extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
+
+       /*
+        * If the room name is blank, the client is doing a top-level OPTIONS.
+        */
+       if (strlen(dav_roomname) == 0) {
+               wprintf("HTTP/1.1 200 OK\r\n");
+               groupdav_common_headers();
+               wprintf("Date: %s\r\n", datestring);
+               wprintf("DAV: 1\r\n");
+               wprintf("Allow: OPTIONS, PROPFIND\r\n");
+               wprintf("\r\n");
+               return;
+       }
+
+       /* Go to the correct room. */
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               gotoroom(dav_roomname);
+       }
+
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               wprintf("HTTP/1.1 404 not found\r\n");
+               groupdav_common_headers();
+               wprintf("Date: %s\r\n", datestring);
+               wprintf(
+                       "Content-Type: text/plain\r\n"
+                       "\r\n"
+                       "There is no folder called \"%s\" on this server.\r\n",
+                       dav_roomname
+               );
+               return;
+       }
+
+       /* If dav_uid is non-empty, client is requesting an OPTIONS on
+        * a specific item in the room.
+        */
+       if (strlen(dav_uid) > 0) {
+
+               dav_msgnum = locate_message_by_uid(dav_uid);
+               if (dav_msgnum < 0) {
+                       wprintf("HTTP/1.1 404 not found\r\n");
+                       groupdav_common_headers();
+                       wprintf(
+                               "Content-Type: text/plain\r\n"
+                               "\r\n"
+                               "Object \"%s\" was not found in the \"%s\" folder.\r\n",
+                               dav_uid,
+                               dav_roomname
+                       );
+                       return;
+               }
+
+               wprintf("HTTP/1.1 200 OK\r\n");
+               groupdav_common_headers();
+               wprintf("Date: %s\r\n", datestring);
+               wprintf("DAV: 1\r\n");
+               wprintf("Allow: OPTIONS, PROPFIND, GET, PUT, DELETE\r\n");
+               wprintf("\r\n");
+               return;
+       }
+
+       /*
+        * We got to this point, which means that the client is requesting
+        * an OPTIONS on the room itself.
+        */
+       wprintf("HTTP/1.1 200 OK\r\n");
+       groupdav_common_headers();
+       wprintf("Date: %s\r\n", datestring);
+       wprintf("DAV: 1\r\n");
+       wprintf("Allow: OPTIONS, PROPFIND, GET, PUT\r\n");
+       wprintf("\r\n");
+}
diff --git a/webcit/src/groupdav_propfind.c b/webcit/src/groupdav_propfind.c
new file mode 100644 (file)
index 0000000..8e6154d
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * $Id$
+ *
+ * Handles GroupDAV PROPFIND requests.
+ *
+ * A few notes about our XML output:
+ *
+ * --> Yes, we are spewing tags directly instead of using an XML library.
+ *     If you would like to rewrite this using libxml2, code it up and submit
+ *     a patch.  Whining will be summarily ignored.
+ *
+ * --> XML is deliberately output with no whitespace/newlines between tags.
+ *     This makes it difficult to read, but we have discovered clients which
+ *     crash when you try to pretty it up.
+ *
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+#include "groupdav.h"
+
+/*
+ * Given an encoded UID, translate that to an unencoded Citadel EUID and
+ * then search for it in the current room.  Return a message number or -1
+ * if not found.
+ *
+ */
+long locate_message_by_uid(char *uid) {
+       char buf[256];
+       char decoded_uid[1024];
+       long retval = (-1L);
+
+       /* Decode the uid */
+       euid_unescapize(decoded_uid, uid);
+
+/**************  THE NEW WAY ***********************/
+       serv_printf("EUID %s", decoded_uid);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               retval = atol(&buf[4]);
+       }
+/***************************************************/
+
+/**************  THE OLD WAY ***********************
+       serv_puts("MSGS ALL|0|1");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '8') {
+               serv_printf("exti|%s", decoded_uid);
+               serv_puts("000");
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       retval = atol(buf);
+               }
+       }
+ ***************************************************/
+
+       return(retval);
+}
+
+
+
+/*
+ * List rooms (or "collections" in DAV terminology) which contain
+ * interesting groupware objects.
+ */
+void groupdav_collection_list(char *dav_pathname, int dav_depth)
+{
+       char buf[256];
+       char roomname[256];
+       int view;
+       char datestring[256];
+       time_t now;
+       time_t mtime;
+       int is_groupware_collection = 0;
+       int starting_point = 1;         /**< 0 for /, 1 for /groupdav/ */
+
+       if (!strcmp(dav_pathname, "/")) {
+               starting_point = 0;
+       }
+       else if (!strcasecmp(dav_pathname, "/groupdav")) {
+               starting_point = 1;
+       }
+       else if (!strcasecmp(dav_pathname, "/groupdav/")) {
+               starting_point = 1;
+       }
+       else if ( (!strncasecmp(dav_pathname, "/groupdav/", 10)) && (strlen(dav_pathname) > 10) ) {
+               starting_point = 2;
+       }
+
+       now = time(NULL);
+       http_datestring(datestring, sizeof datestring, now);
+
+       /**
+        * Be rude.  Completely ignore the XML request and simply send them
+        * everything we know about.  Let the client sort it out.
+        */
+       wprintf("HTTP/1.0 207 Multi-Status\r\n");
+       groupdav_common_headers();
+       wprintf("Date: %s\r\n", datestring);
+       wprintf("Content-type: text/xml\r\n");
+       wprintf("Content-encoding: identity\r\n");
+
+       begin_burst();
+
+       wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+               "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
+       );
+
+       /**
+        *      If the client is requesting the root, show a root node.
+        */
+       if (starting_point == 0) {
+               wprintf("<response>");
+                       wprintf("<href>");
+                               groupdav_identify_host();
+                               wprintf("/");
+                       wprintf("</href>");
+                       wprintf("<propstat>");
+                               wprintf("<status>HTTP/1.1 200 OK</status>");
+                               wprintf("<prop>");
+                                       wprintf("<displayname>/</displayname>");
+                                       wprintf("<resourcetype><collection/></resourcetype>");
+                                       wprintf("<getlastmodified>");
+                                               escputs(datestring);
+                                       wprintf("</getlastmodified>");
+                               wprintf("</prop>");
+                       wprintf("</propstat>");
+               wprintf("</response>");
+       }
+
+       /**
+        *      If the client is requesting "/groupdav", show a /groupdav subdirectory.
+        */
+       if ((starting_point + dav_depth) >= 1) {
+               wprintf("<response>");
+                       wprintf("<href>");
+                               groupdav_identify_host();
+                               wprintf("/groupdav");
+                       wprintf("</href>");
+                       wprintf("<propstat>");
+                               wprintf("<status>HTTP/1.1 200 OK</status>");
+                               wprintf("<prop>");
+                                       wprintf("<displayname>GroupDAV</displayname>");
+                                       wprintf("<resourcetype><collection/></resourcetype>");
+                                       wprintf("<getlastmodified>");
+                                               escputs(datestring);
+                                       wprintf("</getlastmodified>");
+                               wprintf("</prop>");
+                       wprintf("</propstat>");
+               wprintf("</response>");
+       }
+
+       /**
+        *      Now go through the list and make it look like a DAV collection
+        */
+       serv_puts("LKRA");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+
+               extract_token(roomname, buf, 0, '|', sizeof roomname);
+               view = extract_int(buf, 7);
+               mtime = extract_long(buf, 8);
+               http_datestring(datestring, sizeof datestring, mtime);
+
+               /*
+                * For now, only list rooms that we know a GroupDAV client
+                * might be interested in.  In the future we may add
+                * the rest.
+                *
+                * We determine the type of objects which are stored in each
+                * room by looking at the *default* view for the room.  This
+                * allows, for example, a Calendar room to appear as a
+                * GroupDAV calendar even if the user has switched it to a
+                * Calendar List view.
+                */
+               if ((view == VIEW_CALENDAR) || (view == VIEW_TASKS) || (view == VIEW_ADDRESSBOOK) ) {
+                       is_groupware_collection = 1;
+               }
+               else {
+                       is_groupware_collection = 0;
+               }
+
+               if ( (is_groupware_collection) && ((starting_point + dav_depth) >= 2) ) {
+                       wprintf("<response>");
+
+                       wprintf("<href>");
+                       groupdav_identify_host();
+                       wprintf("/groupdav/");
+                       urlescputs(roomname);
+                       wprintf("/</href>");
+
+                       wprintf("<propstat>");
+                       wprintf("<status>HTTP/1.1 200 OK</status>");
+                       wprintf("<prop>");
+                       wprintf("<displayname>");
+                       escputs(roomname);
+                       wprintf("</displayname>");
+                       wprintf("<resourcetype><collection/>");
+
+                       switch(view) {
+                               case VIEW_CALENDAR:
+                                       wprintf("<G:vevent-collection />");
+                                       break;
+                               case VIEW_TASKS:
+                                       wprintf("<G:vtodo-collection />");
+                                       break;
+                               case VIEW_ADDRESSBOOK:
+                                       wprintf("<G:vcard-collection />");
+                                       break;
+                       }
+
+                       wprintf("</resourcetype>");
+                       wprintf("<getlastmodified>");
+                               escputs(datestring);
+                       wprintf("</getlastmodified>");
+                       wprintf("</prop>");
+                       wprintf("</propstat>");
+                       wprintf("</response>");
+               }
+       }
+       wprintf("</multistatus>\n");
+
+       end_burst();
+}
+
+
+
+/*
+ * The pathname is always going to be /groupdav/room_name/msg_num
+ */
+void groupdav_propfind(char *dav_pathname, int dav_depth, char *dav_content_type, char *dav_content) {
+       char dav_roomname[256];
+       char dav_uid[256];
+       char msgnum[256];
+       long dav_msgnum = (-1);
+       char buf[256];
+       char uid[256];
+       char encoded_uid[256];
+       long *msgs = NULL;
+       int num_msgs = 0;
+       int i;
+       char datestring[256];
+       time_t now;
+
+       now = time(NULL);
+       http_datestring(datestring, sizeof datestring, now);
+
+       extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
+       extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
+
+       /*
+        * If the room name is blank, the client is requesting a
+        * folder list.
+        */
+       if (strlen(dav_roomname) == 0) {
+               groupdav_collection_list(dav_pathname, dav_depth);
+               return;
+       }
+
+       /* Go to the correct room. */
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               gotoroom(dav_roomname);
+       }
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               wprintf("HTTP/1.1 404 not found\r\n");
+               groupdav_common_headers();
+               wprintf("Date: %s\r\n", datestring);
+               wprintf(
+                       "Content-Type: text/plain\r\n"
+                       "\r\n"
+                       "There is no folder called \"%s\" on this server.\r\n",
+                       dav_roomname
+               );
+               return;
+       }
+
+       /* If dav_uid is non-empty, client is requesting a PROPFIND on
+        * a specific item in the room.  This is not valid GroupDAV, but
+        * it is valid WebDAV.
+        */
+       if (strlen(dav_uid) > 0) {
+
+               dav_msgnum = locate_message_by_uid(dav_uid);
+               if (dav_msgnum < 0) {
+                       wprintf("HTTP/1.1 404 not found\r\n");
+                       groupdav_common_headers();
+                       wprintf(
+                               "Content-Type: text/plain\r\n"
+                               "\r\n"
+                               "Object \"%s\" was not found in the \"%s\" folder.\r\n",
+                               dav_uid,
+                               dav_roomname
+                       );
+                       return;
+               }
+
+               /* Be rude.  Completely ignore the XML request and simply send them
+                * everything we know about (which is going to simply be the ETag and
+                * nothing else).  Let the client-side parser sort it out.
+                */
+               wprintf("HTTP/1.0 207 Multi-Status\r\n");
+               groupdav_common_headers();
+               wprintf("Date: %s\r\n", datestring);
+               wprintf("Content-type: text/xml\r\n");
+               wprintf("Content-encoding: identity\r\n");
+       
+               begin_burst();
+       
+               wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+                       "<multistatus xmlns=\"DAV:\">"
+               );
+
+               wprintf("<response>");
+
+               wprintf("<href>");
+               groupdav_identify_host();
+               wprintf("/groupdav/");
+               urlescputs(WC->wc_roomname);
+               euid_escapize(encoded_uid, dav_uid);
+               wprintf("/%s", encoded_uid);
+               wprintf("</href>");
+               wprintf("<propstat>");
+               wprintf("<status>HTTP/1.1 200 OK</status>");
+               wprintf("<prop><getetag>\"%ld\"</getetag></prop>", dav_msgnum);
+               wprintf("</propstat>");
+
+               wprintf("</response>\n");
+               wprintf("</multistatus>\n");
+               end_burst();
+               return;
+       }
+
+
+       /*
+        * We got to this point, which means that the client is requesting
+        * a 'collection' (i.e. a list of all items in the room).
+        *
+        * Be rude.  Completely ignore the XML request and simply send them
+        * everything we know about (which is going to simply be the ETag and
+        * nothing else).  Let the client-side parser sort it out.
+        */
+       wprintf("HTTP/1.0 207 Multi-Status\r\n");
+       groupdav_common_headers();
+       wprintf("Date: %s\r\n", datestring);
+       wprintf("Content-type: text/xml\r\n");
+       wprintf("Content-encoding: identity\r\n");
+
+       begin_burst();
+
+       wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+               "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
+       );
+
+
+       /** Transmit the collection resource (FIXME check depth and starting point) */
+       wprintf("<response>");
+
+       wprintf("<href>");
+               groupdav_identify_host();
+               wprintf("/groupdav/");
+               urlescputs(WC->wc_roomname);
+       wprintf("</href>");
+
+       wprintf("<propstat>");
+       wprintf("<status>HTTP/1.1 200 OK</status>");
+       wprintf("<prop>");
+       wprintf("<displayname>");
+       escputs(WC->wc_roomname);
+       wprintf("</displayname>");
+       wprintf("<resourcetype><collection/>");
+
+       switch(WC->wc_default_view) {
+               case VIEW_CALENDAR:
+                       wprintf("<G:vevent-collection />");
+                       break;
+               case VIEW_TASKS:
+                       wprintf("<G:vtodo-collection />");
+                       break;
+               case VIEW_ADDRESSBOOK:
+                       wprintf("<G:vcard-collection />");
+                       break;
+       }
+
+       wprintf("</resourcetype>");
+       /* FIXME get the mtime
+       wprintf("<getlastmodified>");
+               escputs(datestring);
+       wprintf("</getlastmodified>");
+       */
+       wprintf("</prop>");
+       wprintf("</propstat>");
+       wprintf("</response>");
+
+       /** Transmit the collection listing (FIXME check depth and starting point) */
+
+       serv_puts("MSGS ALL");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') while (serv_getln(msgnum, sizeof msgnum), strcmp(msgnum, "000")) {
+               msgs = realloc(msgs, ++num_msgs * sizeof(long));
+               msgs[num_msgs-1] = atol(msgnum);
+       }
+
+       if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
+
+               strcpy(uid, "");
+               serv_printf("MSG0 %ld|3", msgs[i]);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       if (!strncasecmp(buf, "exti=", 5)) {
+                               strcpy(uid, &buf[5]);
+                       }
+               }
+
+               if (strlen(uid) > 0) {
+                       wprintf("<response>");
+                               wprintf("<href>");
+                                       groupdav_identify_host();
+                                       wprintf("/groupdav/");
+                                       urlescputs(WC->wc_roomname);
+                                       euid_escapize(encoded_uid, uid);
+                                       wprintf("/%s", encoded_uid);
+                               wprintf("</href>");
+                               wprintf("<propstat>");
+                                       wprintf("<status>HTTP/1.1 200 OK</status>");
+                                       wprintf("<prop>");
+                                               wprintf("<getetag>\"%ld\"</getetag>", msgs[i]);
+                                       wprintf("</prop>");
+                               wprintf("</propstat>");
+                       wprintf("</response>");
+               }
+       }
+
+       wprintf("</multistatus>\n");
+       end_burst();
+
+       if (msgs != NULL) {
+               free(msgs);
+       }
+}
diff --git a/webcit/src/groupdav_put.c b/webcit/src/groupdav_put.c
new file mode 100644 (file)
index 0000000..21eaefb
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * $Id$
+ *
+ * Handles GroupDAV PUT requests.
+ *
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+#include "groupdav.h"
+
+
+/*
+ * This function is for uploading an ENTIRE calendar, not just one
+ * component.  This would be for webcal:// 'publish' operations, not
+ * for GroupDAV.
+ */
+void groupdav_put_bigics(char *dav_content, int dav_content_length)
+{
+       char buf[1024];
+
+       serv_puts("ICAL putics");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '4') {
+               wprintf("HTTP/1.1 502 Bad Gateway\r\n");
+               groupdav_common_headers();
+               wprintf("Content-type: text/plain\r\n"
+                       "\r\n"
+                       "%s\r\n", &buf[4]
+               );
+               return;
+       }
+
+       serv_write(dav_content, dav_content_length);
+       serv_printf("\n000");
+
+       /* Report success and not much else. */
+       wprintf("HTTP/1.1 204 No Content\r\n");
+       lprintf(9, "HTTP/1.1 204 No Content\r\n");
+       groupdav_common_headers();
+       wprintf("Content-Length: 0\r\n\r\n");
+}
+
+
+
+/*
+ * The pathname is always going to take one of two formats:
+ * /groupdav/room_name/euid    (GroupDAV)
+ * /groupdav/room_name         (webcal)
+ */
+void groupdav_put(char *dav_pathname, char *dav_ifmatch,
+               char *dav_content_type, char *dav_content,
+               int dav_content_length
+) {
+       char dav_roomname[1024];
+       char dav_uid[1024];
+       long new_msgnum = (-2L);
+       long old_msgnum = (-1L);
+       char buf[SIZ];
+       int n = 0;
+
+       if (num_tokens(dav_pathname, '/') < 3) {
+               wprintf("HTTP/1.1 404 not found\r\n");
+               groupdav_common_headers();
+               wprintf(
+                       "Content-Type: text/plain\r\n"
+                       "\r\n"
+                       "The object you requested was not found.\r\n"
+               );
+               return;
+       }
+
+       extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
+       extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
+       if ((!strcasecmp(dav_uid, "ics")) || (!strcasecmp(dav_uid, "calendar.ics"))) {
+               strcpy(dav_uid, "");
+       }
+
+       /* Go to the correct room. */
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               gotoroom(dav_roomname);
+       }
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               wprintf("HTTP/1.1 404 not found\r\n");
+               groupdav_common_headers();
+               wprintf(
+                       "Content-Type: text/plain\r\n"
+                       "\r\n"
+                       "There is no folder called \"%s\" on this server.\r\n",
+                       dav_roomname
+               );
+               return;
+       }
+
+       /*
+        * If an HTTP If-Match: header is present, the client is attempting
+        * to replace an existing item.  We have to check to see if the
+        * message number associated with the supplied uid matches what the
+        * client is expecting.  If not, the server probably contains a newer
+        * version, so we fail...
+        */
+       if (strlen(dav_ifmatch) > 0) {
+               lprintf(9, "dav_ifmatch: %s\n", dav_ifmatch);
+               old_msgnum = locate_message_by_uid(dav_uid);
+               lprintf(9, "old_msgnum:  %ld\n", old_msgnum);
+               if (atol(dav_ifmatch) != old_msgnum) {
+                       wprintf("HTTP/1.1 412 Precondition Failed\r\n");
+                       lprintf(9, "HTTP/1.1 412 Precondition Failed (ifmatch=%ld, old_msgnum=%ld)\r\n",
+                               atol(dav_ifmatch), old_msgnum);
+                       groupdav_common_headers();
+                       wprintf("Content-Length: 0\r\n\r\n");
+                       return;
+               }
+       }
+
+       /** PUT on the collection itself uploads an ICS of the entire collection.
+        */
+       if (!strcasecmp(dav_uid, "")) {
+               groupdav_put_bigics(dav_content, dav_content_length);
+               return;
+       }
+
+       /*
+        * We are cleared for upload!  We use the new calling syntax for ENT0
+        * which allows a confirmation to be sent back to us.  That's how we
+        * extract the message ID.
+        */
+       serv_puts("ENT0 1|||4|||1|");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '8') {
+               wprintf("HTTP/1.1 502 Bad Gateway\r\n");
+               groupdav_common_headers();
+               wprintf("Content-type: text/plain\r\n"
+                       "\r\n"
+                       "%s\r\n", &buf[4]
+               );
+               return;
+       }
+
+       /* Send the content to the Citadel server */
+       serv_printf("Content-type: %s\n\n", dav_content_type);
+       serv_puts(dav_content);
+       serv_puts("\n000");
+
+       /* Fetch the reply from the Citadel server */
+       n = 0;
+       strcpy(dav_uid, "");
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               switch(n++) {
+                       case 0: new_msgnum = atol(buf);
+                               break;
+                       case 1: lprintf(9, "new_msgnum=%ld (%s)\n", new_msgnum, buf);
+                               break;
+                       case 2: strcpy(dav_uid, buf);
+                               break;
+                       default:
+                               break;
+               }
+       }
+
+       /* Tell the client what happened. */
+
+       /* Citadel failed in some way? */
+       if (new_msgnum < 0L) {
+               wprintf("HTTP/1.1 502 Bad Gateway\r\n");
+               groupdav_common_headers();
+               wprintf("Content-type: text/plain\r\n"
+                       "\r\n"
+                       "new_msgnum is %ld\r\n"
+                       "\r\n", new_msgnum
+               );
+               return;
+       }
+
+       /* We created this item for the first time. */
+       if (old_msgnum < 0L) {
+               wprintf("HTTP/1.1 201 Created\r\n");
+               lprintf(9, "HTTP/1.1 201 Created\r\n");
+               groupdav_common_headers();
+               wprintf("etag: \"%ld\"\r\n", new_msgnum);
+               wprintf("Content-Length: 0\r\n");
+               wprintf("Location: ");
+               groupdav_identify_host();
+               wprintf("/groupdav/");
+               urlescputs(dav_roomname);
+               wprintf("/%s\r\n", dav_uid);
+               wprintf("\r\n");
+               return;
+       }
+
+       /* We modified an existing item. */
+       wprintf("HTTP/1.1 204 No Content\r\n");
+       lprintf(9, "HTTP/1.1 204 No Content\r\n");
+       groupdav_common_headers();
+       wprintf("etag: \"%ld\"\r\n", new_msgnum);
+       wprintf("Content-Length: 0\r\n\r\n");
+
+       /* The item we replaced has probably already been deleted by
+        * the Citadel server, but we'll do this anyway, just in case.
+        */
+       serv_printf("DELE %ld", old_msgnum);
+       serv_getln(buf, sizeof buf);
+
+       return;
+}
diff --git a/webcit/src/html2html.c b/webcit/src/html2html.c
new file mode 100644 (file)
index 0000000..ca8804c
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup HTML2HTML Output an HTML message, modifying it slightly to make sure it plays nice
+ * with the rest of our web framework.
+ * \ingroup WebcitHttpServer
+ */
+/*@{*/
+#include "webcit.h"
+#include "vcard.h"
+#include "webserver.h"
+
+
+/**
+ * \brief      Strip surrounding single or double quotes from a string.
+ *
+ * \param s    String to be stripped.
+ */
+void stripquotes(char *s)
+{
+       int len;
+
+       if (!s) return;
+
+       len = strlen(s);
+       if (len < 2) return;
+
+       if ( ( (s[0] == '\"') && (s[len-1] == '\"') ) || ( (s[0] == '\'') && (s[len-1] == '\'') ) ) {
+               s[len-1] = 0;
+               strcpy(s, &s[1]);
+       }
+}
+
+
+/**
+ * \brief Check to see if a META tag has overridden the declared MIME character set.
+ *
+ * \param charset              Character set name (left unchanged if we don't do anything)
+ * \param meta_http_equiv      Content of the "http-equiv" portion of the META tag
+ * \param meta_content         Content of the "content" portion of the META tag
+ */
+void extract_charset_from_meta(char *charset, char *meta_http_equiv, char *meta_content)
+{
+       char *ptr;
+       char buf[64];
+
+       if (!charset) return;
+       if (!meta_http_equiv) return;
+       if (!meta_content) return;
+
+
+       if (strcasecmp(meta_http_equiv, "Content-type")) return;
+
+       ptr = strchr(meta_content, ';');
+       if (!ptr) return;
+
+       safestrncpy(buf, ++ptr, sizeof buf);
+       striplt(buf);
+       if (!strncasecmp(buf, "charset=", 8)) {
+               strcpy(charset, &buf[8]);
+       }
+}
+
+
+
+/**
+ * \brief Sanitize and enhance an HTML message for display.
+ *        Also convert weird character sets to UTF-8 if necessary.
+ *
+ * \param supplied_charset the input charset as declared in the MIME headers
+ */
+void output_html(char *supplied_charset, int treat_as_wiki) {
+       char buf[SIZ];
+       char *msg;
+       char *ptr;
+       char *msgstart;
+       char *msgend;
+       char *converted_msg;
+       int buffer_length = 1;
+       int line_length = 0;
+       int content_length = 0;
+       int output_length = 0;
+       char new_window[SIZ];
+       int brak = 0;
+       int alevel = 0;
+       int i;
+       int linklen;
+       char charset[128];
+#ifdef HAVE_ICONV
+       iconv_t ic = (iconv_t)(-1) ;
+       char *ibuf;                   /**< Buffer of characters to be converted */
+       char *obuf;                   /**< Buffer for converted characters      */
+       size_t ibuflen;               /**< Length of input buffer               */
+       size_t obuflen;               /**< Length of output buffer              */
+       char *osav;                   /**< Saved pointer to output buffer       */
+#endif
+
+       safestrncpy(charset, supplied_charset, sizeof charset);
+       msg = strdup("");
+       sprintf(new_window, "<a target=\"%s\" href=", TARGET);
+
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               line_length = strlen(buf);
+               buffer_length = content_length + line_length + 2;
+               ptr = realloc(msg, buffer_length);
+               if (ptr == NULL) {
+                       wprintf("<b>");
+                       wprintf(_("realloc() error! couldn't get %d bytes: %s"),
+                               buffer_length + 1,
+                               strerror(errno));
+                       wprintf("</b><br /><br />\n");
+                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                               /** flush */
+                       }
+                       free(msg);
+                       return;
+               }
+               msg = ptr;
+               strcpy(&msg[content_length], buf);
+               content_length += line_length;
+               strcpy(&msg[content_length], "\n");
+               content_length += 1;
+       }
+
+       /** Do a first pass to isolate the message body */
+       ptr = msg;
+       msgstart = msg;
+       msgend = &msg[content_length];
+
+       while (ptr < msgend) {
+
+               /** Advance to next tag */
+               ptr = strchr(ptr, '<');
+               if ((ptr == NULL) || (ptr >= msgend)) break;
+               ++ptr;
+               if ((ptr == NULL) || (ptr >= msgend)) break;
+
+               /**
+                *  Look for META tags.  Some messages (particularly in
+                *  Asian locales) illegally declare a message's character
+                *  set in the HTML instead of in the MIME headers.  This
+                *  is wrong but we have to work around it anyway.
+                */
+               if (!strncasecmp(ptr, "META", 4)) {
+
+                       char *meta_start;
+                       char *meta_end;
+                       int meta_length;
+                       char *meta;
+                       char *meta_http_equiv;
+                       char *meta_content;
+                       char *spaceptr;
+
+                       meta_start = &ptr[4];
+                       meta_end = strchr(ptr, '>');
+                       if ((meta_end != NULL) && (meta_end <= msgend)) {
+                               meta_length = meta_end - meta_start + 1;
+                               meta = malloc(meta_length + 1);
+                               safestrncpy(meta, meta_start, meta_length);
+                               meta[meta_length] = 0;
+                               striplt(meta);
+                               if (!strncasecmp(meta, "HTTP-EQUIV=", 11)) {
+                                       meta_http_equiv = strdup(&meta[11]);
+                                       spaceptr = strchr(meta_http_equiv, ' ');
+                                       if (spaceptr != NULL) {
+                                               *spaceptr = 0;
+                                               meta_content = strdup(++spaceptr);
+                                               if (!strncasecmp(meta_content, "content=", 8)) {
+                                                       strcpy(meta_content, &meta_content[8]);
+                                                       stripquotes(meta_http_equiv);
+                                                       stripquotes(meta_content);
+                                                       extract_charset_from_meta(charset,
+                                                               meta_http_equiv, meta_content);
+                                               }
+                                               free(meta_content);
+                                       }
+                                       free(meta_http_equiv);
+                               }
+                               free(meta);
+                       }
+               }
+
+               /**
+                * Any of these tags cause everything up to and including
+                * the tag to be removed.
+                */     
+               if ( (!strncasecmp(ptr, "HTML", 4))
+                  ||(!strncasecmp(ptr, "HEAD", 4))
+                  ||(!strncasecmp(ptr, "/HEAD", 5))
+                  ||(!strncasecmp(ptr, "BODY", 4)) ) {
+                       ptr = strchr(ptr, '>');
+                       if ((ptr == NULL) || (ptr >= msgend)) break;
+                       ++ptr;
+                       if ((ptr == NULL) || (ptr >= msgend)) break;
+                       msgstart = ptr;
+               }
+
+               /**
+                * Any of these tags cause everything including and following
+                * the tag to be removed.
+                */
+               if ( (!strncasecmp(ptr, "/HTML", 5))
+                  ||(!strncasecmp(ptr, "/BODY", 5)) ) {
+                       --ptr;
+                       msgend = ptr;
+                       strcpy(ptr, "");
+                       
+               }
+
+               ++ptr;
+       }
+       if (msgstart > msg) {
+               strcpy(msg, msgstart);
+       }
+
+       /** Convert foreign character sets to UTF-8 if necessary. */
+#ifdef HAVE_ICONV
+       if ( (strcasecmp(charset, "us-ascii"))
+          && (strcasecmp(charset, "UTF-8"))
+          && (strcasecmp(charset, ""))
+       ) {
+               lprintf(9, "Converting %s to UTF-8\n", charset);
+               ic = ctdl_iconv_open("UTF-8", charset);
+               if (ic == (iconv_t)(-1) ) {
+                       lprintf(5, "%s:%d iconv_open() failed: %s\n",
+                               __FILE__, __LINE__, strerror(errno));
+               }
+       }
+       if (ic != (iconv_t)(-1) ) {
+               ibuf = msg;
+               ibuflen = content_length;
+               obuflen = content_length + (content_length / 2) ;
+               obuf = (char *) malloc(obuflen);
+               osav = obuf;
+               iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
+               content_length = content_length + (content_length / 2) - obuflen;
+               osav[content_length] = 0;
+               free(msg);
+               msg = osav;
+               iconv_close(ic);
+       }
+#endif
+
+       /**
+        *      At this point, the message has been stripped down to
+        *      only the content inside the <BODY></BODY> tags, and has
+        *      been converted to UTF-8 if it was originally in a foreign
+        *      character set.  The text is also guaranteed to be null
+        *      terminated now.
+        */
+
+       /** Now go through the message, parsing tags as necessary. */
+       converted_msg = malloc(content_length);
+       strcpy(converted_msg, "");
+       ptr = msg;
+       msgend = strchr(msg, 0);
+       while (ptr < msgend) {
+
+               /**
+                * Change mailto: links to WebCit mail, by replacing the
+                * link with one that points back to our mail room.  Due to
+                * the way we parse URL's, it'll even handle mailto: links
+                * that have "?subject=" in them.
+                */
+               if (!strncasecmp(ptr, "<a href=\"mailto:", 16)) {
+                       content_length += 64;
+                       converted_msg = realloc(converted_msg, content_length);
+                       sprintf(&converted_msg[output_length],
+                               "<a href=\"display_enter"
+                               "?force_room=_MAIL_&recp=");
+                       output_length += 47;
+                       ptr = &ptr[16];
+                       ++alevel;
+               }
+               /** Make external links open in a separate window */
+               else if (!strncasecmp(ptr, "<a href=\"", 9)) {
+                       ++alevel;
+                       if ( ((strchr(ptr, ':') < strchr(ptr, '/')))
+                            &&  ((strchr(ptr, '/') < strchr(ptr, '>'))) 
+                            ) {
+                               /* open external links to new window */
+                               content_length += 64;
+                               converted_msg = realloc(converted_msg, content_length);
+                               sprintf(&converted_msg[output_length], new_window);
+                               output_length += strlen(new_window);
+                               ptr = &ptr[8];
+                       }
+                       else if ( (treat_as_wiki) && (strncasecmp(ptr, "<a href=\"wiki?", 14)) ) {
+                               content_length += 64;
+                               converted_msg = realloc(converted_msg, content_length);
+                               sprintf(&converted_msg[output_length], "<a href=\"wiki?page=");
+                               output_length += 19;
+                               ptr = &ptr[9];
+                       }
+                       else {
+                               sprintf(&converted_msg[output_length], "<a href=\"");
+                               output_length += 9;
+                               ptr = &ptr[9];
+                       }
+               }
+               /**
+                * Turn anything that looks like a URL into a real link, as long
+                * as it's not inside a tag already
+                */
+               else if ( (brak == 0) && (alevel == 0)
+                    && (!strncasecmp(ptr, "http://", 7))) {
+                               linklen = 0;
+                               /** Find the end of the link */
+                               for (i=0; i<=strlen(ptr); ++i) {
+                                       if ((ptr[i]==0)
+                                          ||(isspace(ptr[i]))
+                                          ||(ptr[i]==10)
+                                          ||(ptr[i]==13)
+                                          ||(ptr[i]=='(')
+                                          ||(ptr[i]==')')
+                                          ||(ptr[i]=='<')
+                                          ||(ptr[i]=='>')
+                                          ||(ptr[i]=='[')
+                                          ||(ptr[i]==']')
+                                       ) linklen = i;
+                                       if (linklen > 0) break;
+                               }
+                               if (linklen > 0) {
+                                       content_length += (32 + linklen);
+                                       converted_msg = realloc(converted_msg, content_length);
+                                       sprintf(&converted_msg[output_length], new_window);
+                                       output_length += strlen(new_window);
+                                       converted_msg[output_length] = '\"';
+                                       converted_msg[++output_length] = 0;
+                                       for (i=0; i<linklen; ++i) {
+                                               converted_msg[output_length] = ptr[i];
+                                               converted_msg[++output_length] = 0;
+                                       }
+                                       sprintf(&converted_msg[output_length], "\">");
+                                       output_length += 2;
+                                       for (i=0; i<linklen; ++i) {
+                                               converted_msg[output_length] = *ptr++;
+                                               converted_msg[++output_length] = 0;
+                                       }
+                                       sprintf(&converted_msg[output_length], "</A>");
+                                       output_length += 4;
+                               }
+               }
+               else {
+                       /**
+                        * We need to know when we're inside a tag,
+                        * so we don't turn things that look like URL's into
+                        * links, when they're already links - or image sources.
+                        */
+                       if (*ptr == '<') ++brak;
+                       if (*ptr == '>') --brak;
+                       if (!strncasecmp(ptr, "</A>", 3)) --alevel;
+                       converted_msg[output_length] = *ptr++;
+                       converted_msg[++output_length] = 0;
+               }
+       }
+
+       /**     uncomment these two lines to override conversion        */
+       /**     memcpy(converted_msg, msg, content_length);             */
+       /**     output_length = content_length;                         */
+
+       /** Output our big pile of markup */
+       client_write(converted_msg, output_length);
+
+       /** A little trailing vertical whitespace... */
+       wprintf("<br /><br />\n");
+
+       /** Now give back the memory */
+       free(converted_msg);
+       free(msg);
+}
+
+/*@}*/
diff --git a/webcit/src/http_datestring.c b/webcit/src/http_datestring.c
new file mode 100644 (file)
index 0000000..33f5784
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup HTTPDateTime Function to generate HTTP-compliant textual time/date stamp
+ * (This module was lifted directly from the Citadel server source)
+ *
+ * \ingroup WebcitHttpServer
+ */
+/*@{*/
+#include "webcit.h"
+
+/** HTTP Months - do not translate - these are not for human consumption */
+static char *httpdate_months[] = {
+       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/** HTTP Weekdays - do not translate - these are not for human consumption */
+static char *httpdate_weekdays[] = {
+       "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+
+/**
+ * \brief Supplied with a unix timestamp, generate a textual time/date stamp
+ * \param buf the return buffer
+ * \param n the size of the buffer
+ * \param xtime the time to format as string
+ */
+void http_datestring(char *buf, size_t n, time_t xtime) {
+       struct tm t;
+
+       long offset;
+       char offsign;
+
+       localtime_r(&xtime, &t);
+
+       /** Convert "seconds west of GMT" to "hours/minutes offset" */
+#ifdef HAVE_STRUCT_TM_TM_GMTOFF
+       offset = t.tm_gmtoff;
+#else
+       offset = timezone;
+#endif
+       if (offset > 0) {
+               offsign = '+';
+       }
+       else {
+               offset = 0L - offset;
+               offsign = '-';
+       }
+       offset = ( (offset / 3600) * 100 ) + ( offset % 60 );
+
+       snprintf(buf, n, "%s, %02d %s %04d %02d:%02d:%02d %c%04ld",
+               httpdate_weekdays[t.tm_wday],
+               t.tm_mday,
+               httpdate_months[t.tm_mon],
+               t.tm_year + 1900,
+               t.tm_hour,
+               t.tm_min,
+               t.tm_sec,
+               offsign, offset
+       );
+}
+
+
+/*@}*/
diff --git a/webcit/src/ical_dezonify.c b/webcit/src/ical_dezonify.c
new file mode 100644 (file)
index 0000000..c2042fd
--- /dev/null
@@ -0,0 +1,171 @@
+/* 
+ * $Id$ 
+ */
+/**
+ * \defgroup IcalDezonify normalize ical dates to UTC
+ * Function to go through an ical component set and convert all non-UTC
+ * date/time properties to UTC.  It also strips out any VTIMEZONE
+ * subcomponents afterwards, because they're irrelevant.
+ *
+ * Everything here will work on both a fully encapsulated VCALENDAR component
+ * or any type of subcomponent.
+ *
+ * \ingroup Calendaring
+ */
+/*@{*/
+
+#include "webcit.h"
+#include "webserver.h"
+
+
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+
+
+/**
+ * \brief Back end function for ical_dezonify()
+ *
+ * We supply this with the master component, the relevant component,
+ * and the property (which will be a DTSTART, DTEND, etc.)
+ * which we want to convert to UTC.
+ * \param cal dunno ???
+ * \param rcal dunno ???
+ * \param prop dunno ???
+ */
+void ical_dezonify_backend(icalcomponent *cal,
+                       icalcomponent *rcal,
+                       icalproperty *prop) {
+
+       icaltimezone *t = NULL;
+       icalparameter *param;
+       const char *tzid;
+       struct icaltimetype TheTime;
+
+       /** Give me nothing and I will give you nothing in return. */
+       if (cal == NULL) return;
+
+       /** Hunt for a TZID parameter in this property. */
+       param = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER);
+
+       /** Get the stringish name of this TZID. */
+       if (param != NULL) {
+               tzid = icalparameter_get_tzid(param);
+
+               /** Convert it to an icaltimezone type. */
+               if (tzid != NULL) {
+                       t = icalcomponent_get_timezone(cal, tzid);
+               }
+
+       }
+
+       /** Now we know the timezone.  Convert to UTC. */
+
+       if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) {
+               TheTime = icalproperty_get_dtstart(prop);
+       }
+       else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) {
+               TheTime = icalproperty_get_dtend(prop);
+       }
+       else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) {
+               TheTime = icalproperty_get_due(prop);
+       }
+       else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) {
+               TheTime = icalproperty_get_exdate(prop);
+       }
+       else {
+               return;
+       }
+
+       /** Do the conversion. */
+       if (t != NULL) {
+               icaltimezone_convert_time(&TheTime,
+                                       t,
+                                       icaltimezone_get_utc_timezone()
+               );
+       }
+       TheTime.is_utc = 1;
+       icalproperty_remove_parameter_by_kind(prop, ICAL_TZID_PARAMETER);
+
+       /** Now add the converted property back in. */
+       if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) {
+               icalproperty_set_dtstart(prop, TheTime);
+       }
+       else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) {
+               icalproperty_set_dtend(prop, TheTime);
+       }
+       else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) {
+               icalproperty_set_due(prop, TheTime);
+       }
+       else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) {
+               icalproperty_set_exdate(prop, TheTime);
+       }
+}
+
+
+/**
+ * \brief Recursive portion of ical_dezonify()
+ * \param cal dunno ???
+ * \param rcal dunno ???
+ */
+void ical_dezonify_recur(icalcomponent *cal, icalcomponent *rcal) {
+       icalcomponent *c;
+       icalproperty *p;
+
+       /**
+        * Recurse through all subcomponents *except* VTIMEZONE ones.
+        */
+       for (c=icalcomponent_get_first_component(
+                                       rcal, ICAL_ANY_COMPONENT);
+               c != NULL;
+               c = icalcomponent_get_next_component(
+                                       rcal, ICAL_ANY_COMPONENT)
+       ) {
+               if (icalcomponent_isa(c) != ICAL_VTIMEZONE_COMPONENT) {
+                       ical_dezonify_recur(cal, c);
+               }
+       }
+
+       /**
+        * Now look for DTSTART and DTEND properties
+        */
+       for (p=icalcomponent_get_first_property(
+                               rcal, ICAL_ANY_PROPERTY);
+               p != NULL;
+               p = icalcomponent_get_next_property(
+                               rcal, ICAL_ANY_PROPERTY)
+       ) {
+               if (
+                       (icalproperty_isa(p) == ICAL_DTSTART_PROPERTY)
+                       || (icalproperty_isa(p) == ICAL_DTEND_PROPERTY)
+                       || (icalproperty_isa(p) == ICAL_DUE_PROPERTY)
+                       || (icalproperty_isa(p) == ICAL_EXDATE_PROPERTY)
+                  ) {
+                       ical_dezonify_backend(cal, rcal, p);
+               }
+       }
+}
+
+
+/**
+ * \brief Convert all DTSTART and DTEND properties in all subcomponents to UTC.
+ * This function will search any VTIMEZONE subcomponents to learn the
+ * relevant timezone information.
+ * \param cal item to process
+ */
+void ical_dezonify(icalcomponent *cal) {
+       icalcomponent *vt = NULL;
+
+       /** Convert all times to UTC */
+       ical_dezonify_recur(cal, cal);
+
+       /** Strip out VTIMEZONE subcomponents -- we don't need them anymore */
+       while (vt = icalcomponent_get_first_component(
+                       cal, ICAL_VTIMEZONE_COMPONENT), vt != NULL) {
+               icalcomponent_remove_component(cal, vt);
+               icalcomponent_free(vt);
+       }
+
+}
+
+
+#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
+/*@}*/
diff --git a/webcit/src/iconbar.c b/webcit/src/iconbar.c
new file mode 100644 (file)
index 0000000..bb9a4e4
--- /dev/null
@@ -0,0 +1,774 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup IconBar Displays and customizes the iconbar.
+ * \ingroup MenuInfrastructure
+ */
+/*@{*/
+#include "webcit.h"
+
+
+/** Values for ib_displayas */
+#define IB_PICTEXT     0 /**< picture and text */
+#define IB_PICONLY     1 /**< just a picture */
+#define IB_TEXTONLY    2 /**< just text */
+
+
+/**
+ * \brief draw the icon bar?????
+ */
+void do_selected_iconbar(void) {
+       if (WC->current_iconbar == current_iconbar_roomlist) {
+               do_iconbar_roomlist();
+       }
+       else {
+               do_iconbar();
+       }
+}
+
+/**
+ * \brief draw the icon bar???
+ */
+void do_iconbar(void) {
+       char iconbar[SIZ];
+       char buf[SIZ];
+       char key[SIZ], value[SIZ];
+       int i;
+
+       WC->current_iconbar = current_iconbar_menu;
+
+       /**
+        * The initialized values of these variables also happen to
+        * specify the default values for users who haven't customized
+        * their iconbars.  These should probably be set in a master
+        * configuration somewhere.
+        */
+       int ib_displayas = 0;   /**< pictures and text, pictures, text */
+       int ib_logo = 0;        /**< Site logo */
+       int ib_summary = 1;     /**< Summary page icon */
+       int ib_inbox = 1;       /**< Inbox icon */
+       int ib_calendar = 1;    /**< Calendar icon */
+       int ib_contacts = 1;    /**< Contacts icon */
+       int ib_notes = 1;       /**< Notes icon */
+       int ib_tasks = 1;       /**< Tasks icon */
+       int ib_rooms = 1;       /**< Rooms icon */
+       int ib_users = 1;       /**< Users icon */
+       int ib_chat = 1;        /**< Chat icon */
+       int ib_advanced = 1;    /**< Advanced Options icon */
+       int ib_citadel = 1;     /**< 'Powered by Citadel' logo */
+       /*
+        */
+
+       get_preference("iconbar", iconbar, sizeof iconbar);
+       for (i=0; i<num_tokens(iconbar, ','); ++i) {
+               extract_token(buf, iconbar, i, ',', sizeof buf);
+               extract_token(key, buf, 0, '=', sizeof key);
+               extract_token(value, buf, 1, '=', sizeof value);
+
+               if (!strcasecmp(key, "ib_displayas")) ib_displayas = atoi(value);
+               if (!strcasecmp(key, "ib_logo")) ib_logo = atoi(value);
+               if (!strcasecmp(key, "ib_summary")) ib_summary = atoi(value);
+               if (!strcasecmp(key, "ib_inbox")) ib_inbox = atoi(value);
+               if (!strcasecmp(key, "ib_calendar")) ib_calendar = atoi(value);
+               if (!strcasecmp(key, "ib_contacts")) ib_contacts = atoi(value);
+               if (!strcasecmp(key, "ib_notes")) ib_notes = atoi(value);
+               if (!strcasecmp(key, "ib_tasks")) ib_tasks = atoi(value);
+               if (!strcasecmp(key, "ib_rooms")) ib_rooms = atoi(value);
+               if (!strcasecmp(key, "ib_users")) ib_users = atoi(value);
+               if (!strcasecmp(key, "ib_chat")) ib_chat = atoi(value);
+               if (!strcasecmp(key, "ib_advanced")) ib_advanced = atoi(value);
+               if (!strcasecmp(key, "ib_citadel")) ib_citadel = atoi(value);
+       }
+
+       wprintf("<div id=\"button\">\n"
+               "<ul>\n"
+       );
+
+       if (ib_logo) {
+               wprintf("<li>");
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" "
+                               "HEIGHT=\"32\" src=\"image&name=hello\" ALT=\"&nbsp;\">\n"
+                       );
+               }
+               wprintf("</li>\n");
+       }
+
+       if (ib_citadel) if (ib_displayas != IB_TEXTONLY) wprintf(
+               "<li><div align=\"center\">"
+               "<a href=\"http://www.citadel.org\" "
+               "title=\"%s\" target=\"aboutcit\">"
+               "<img border=\"0\" "
+               "src=\"static/citadel-logo.gif\" ALT=\"%s\"></a>"
+               "</div></li>\n",
+               _("Find out more about Citadel"),
+               _("CITADEL")
+       );
+
+       wprintf("<li><div align=\"center\"><a href=\"javascript:switch_to_room_list()\">");
+       wprintf(_("switch to room list"));
+       wprintf("</a></div>");
+
+       if (ib_summary) {
+               wprintf("<li><a href=\"summary\" "
+                       "TITLE=\"%s\" "
+                       ">", _("Your summary page")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                               "src=\"static/summscreen_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Summary"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if (ib_inbox) {
+               wprintf("<li>"
+                       "<a href=\"dotgoto?room=_MAIL_\" "
+                       "TITLE=\"%s\" "
+                       ">",
+                       _("Go to your email inbox")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                               "src=\"static/privatemess_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Mail"));
+                       if (WC->new_mail != WC->remember_new_mail) {
+/*
+                               if (WC->new_mail > 0) {
+                                       wprintf(" <b>(%d)</b>", WC->new_mail);
+                               }
+*/
+                               WC->remember_new_mail = WC->new_mail;
+                       }
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if (ib_calendar) {
+               wprintf("<li>"
+                       "<a href=\"dotgoto?room=_CALENDAR_\" "
+                       "TITLE=\"%s\" "
+                       ">",
+                       _("Go to your personal calendar")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                       "src=\"static/calarea_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Calendar"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if (ib_contacts) {
+               wprintf("<li>"
+                       "<a href=\"dotgoto?room=_CONTACTS_\" "
+                       "TITLE=\"%s\" "
+                       ">",
+                       _("Go to your personal address book")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                       "src=\"static/viewcontacts_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Contacts"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if (ib_notes) {
+               wprintf("<li>"
+                       "<a href=\"dotgoto?room=_NOTES_\" "
+                       "TITLE=\"%s\" "
+                       ">",
+                       _("Go to your personal notes")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                       "src=\"static/storenotes_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Notes"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if (ib_tasks)  {
+               wprintf("<li>"
+                       "<a href=\"dotgoto?room=_TASKS_\" "
+                       "TITLE=\"%s\" "
+                       ">",
+                       _("Go to your personal task list")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                       "src=\"static/taskmanag_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Tasks"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if (ib_rooms) {
+               wprintf("<li>"
+                       "<a href=\"knrooms\" TITLE=\"%s\" >",
+                       _("List all of your accessible rooms")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                       "src=\"static/chatrooms_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Rooms"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if (ib_users) {
+               wprintf("<li>"
+                       "<a href=\"who\" TITLE=\"%s\" "
+                       ">",
+                       _("See who is online right now")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                       "src=\"static/usermanag_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Who is online?"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if (ib_chat) {
+               wprintf("<li>"
+                       "<a href=\"#\" onClick=\"window.open('chat', "
+                       "'ctdl_chat_window', "
+                       "'toolbar=no,location=no,directories=no,copyhistory=no,"
+                       "status=no,scrollbars=yes,resizable=yes');\""
+                       ">"
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                       "src=\"static/citadelchat_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Chat"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if (ib_advanced) {
+               wprintf("<li>"
+                       "<a href=\"display_main_menu\" "
+                       "TITLE=\"%s\" "
+                       ">",
+                       _("Advanced Options Menu: Advanced Room commands, Account Info, and Chat")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                       "src=\"static/advanpage2_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Advanced"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       if ((WC->axlevel >= 6) || (WC->is_room_aide)) {
+               wprintf("<li>"
+                       "<a href=\"display_aide_menu\" "
+                       "TITLE=\"%s\" "
+                       ">",
+                       _("Room and system administration functions")
+               );
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+                       "src=\"static/advanpage2_32x.gif\">");
+               }
+               if (ib_displayas != IB_PICONLY) {
+                       wprintf(_("Administration"));
+               }
+               wprintf("</A></li>\n");
+       }
+
+       wprintf("<li>"
+               "<a href=\"termquit\" TITLE=\"%s\" "
+               "onClick=\"return confirm('%s');\">",
+               _("Log off"),
+               _("Log off now?")
+               
+       );
+       if (ib_displayas != IB_TEXTONLY) {
+       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+               "src=\"static/logoff_32x.gif\">");
+       }
+       if (ib_displayas != IB_PICONLY) {
+               wprintf(_("Log off"));
+       }
+       wprintf("</A></li>\n");
+
+       wprintf(
+               "<li><div align=\"center\">"
+               "<a href=\"display_customize_iconbar\" "
+               "TITLE=\"%s\" "
+               ">%s"
+               "</A></div></li>\n",
+               _("Customize this menu"),
+               _("customize this menu")
+       );
+
+       wprintf("</ul></div>\n");
+}
+
+
+/**
+ * \brief roomtree view of the iconbar
+ * If the user has toggled the icon bar over to a room list, here's where
+ * we generate its innerHTML...
+ */
+void do_iconbar_roomlist(void) {
+       char iconbar[SIZ];
+       char buf[SIZ];
+       char key[SIZ], value[SIZ];
+       int i;
+
+       WC->current_iconbar = current_iconbar_roomlist;
+
+       /**
+        * The initialized values of these variables also happen to
+        * specify the default values for users who haven't customized
+        * their iconbars.  These should probably be set in a master
+        * configuration somewhere.
+        */
+       int ib_displayas = 0;   /* pictures and text, pictures, text */
+       int ib_logo = 0;        /* Site logo */
+       int ib_citadel = 1;     /* 'Powered by Citadel' logo */
+       /*
+        */
+
+       get_preference("iconbar", iconbar, sizeof iconbar);
+       for (i=0; i<num_tokens(iconbar, ','); ++i) {
+               extract_token(buf, iconbar, i, ',', sizeof buf);
+               extract_token(key, buf, 0, '=', sizeof key);
+               extract_token(value, buf, 1, '=', sizeof value);
+
+               if (!strcasecmp(key, "ib_displayas")) ib_displayas = atoi(value);
+               if (!strcasecmp(key, "ib_logo")) ib_logo = atoi(value);
+               if (!strcasecmp(key, "ib_citadel")) ib_citadel = atoi(value);
+       }
+
+       wprintf("<div id=\"button\">\n"
+               "<ul>\n"
+       );
+
+       if (ib_logo) {
+               wprintf("<li>");
+               if (ib_displayas != IB_TEXTONLY) {
+                       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" "
+                               "HEIGHT=\"32\" src=\"image&name=hello\" ALT=\"&nbsp;\">\n"
+                       );
+               }
+               wprintf("</li>\n");
+       }
+
+       if (ib_citadel) if (ib_displayas != IB_TEXTONLY) wprintf(
+               "<li><div align=\"center\">"
+               "<a href=\"http://www.citadel.org\" "
+               "title=\"%s\" target=\"aboutcit\">"
+               "<img border=\"0\" "
+               "src=\"static/citadel-logo.gif\" ALT=\"%s\"></a>"
+               "</div></li>\n",
+               _("Find out more about Citadel"),
+               _("CITADEL")
+       );
+
+       wprintf("<li><div align=\"center\"><a href=\"javascript:switch_to_menu_buttons()\">");
+       wprintf(_("switch to menu"));
+       wprintf("</a></div>");
+
+       wprintf("<li>"
+               "<a href=\"termquit\" TITLE=\"%s\" "
+               "onClick=\"return confirm('%s');\">",
+               _("Log off"),
+               _("Log off now?")
+               
+       );
+       if (ib_displayas != IB_TEXTONLY) {
+       wprintf("<IMG BORDER=\"0\" WIDTH=\"32\" HEIGHT=\"32\" "
+               "src=\"static/logoff_32x.gif\">");
+       }
+       if (ib_displayas != IB_PICONLY) {
+               wprintf(_("Log off"));
+       }
+       wprintf("</A></li>\n");
+
+       wprintf("</ul></div>\n");
+
+       /** embed the room list */
+       list_all_rooms_by_floor("iconbar");
+
+       wprintf("</div>\n");
+}
+
+
+/**
+ * \brief display a customized version of the iconbar
+ */
+void display_customize_iconbar(void) {
+       char iconbar[SIZ];
+       char buf[SIZ];
+       char key[SIZ], value[SIZ];
+       int i;
+       int bar = 0;
+
+       /**
+        * The initialized values of these variables also happen to
+        * specify the default values for users who haven't customized
+        * their iconbars.  These should probably be set in a master
+        * configuration somewhere.
+        */
+       int ib_displayas = IB_PICTEXT;  /**< pictures and text, pictures, text */
+       int ib_logo = 0;        /**< Site logo */
+       int ib_summary = 1;     /**< Summary page icon */
+       int ib_inbox = 1;       /**< Inbox icon */
+       int ib_calendar = 1;    /**< Calendar icon */
+       int ib_contacts = 1;    /**< Contacts icon */
+       int ib_notes = 1;       /**< Notes icon */
+       int ib_tasks = 1;       /**< Tasks icon */
+       int ib_rooms = 1;       /**< Rooms icon */
+       int ib_users = 1;       /**< Users icon */
+       int ib_chat = 1;        /**< Chat icon */
+       int ib_advanced = 1;    /**< Advanced Options icon */
+       int ib_citadel = 1;     /**< 'Powered by Citadel' logo */
+       /*
+        */
+
+       get_preference("iconbar", iconbar, sizeof iconbar);
+       for (i=0; i<num_tokens(iconbar, ','); ++i) {
+               extract_token(buf, iconbar, i, ',', sizeof buf);
+               extract_token(key, buf, 0, '=', sizeof key);
+               extract_token(value, buf, 1, '=', sizeof value);
+
+               if (!strcasecmp(key, "ib_displayas")) ib_displayas = atoi(value);
+               if (!strcasecmp(key, "ib_logo")) ib_logo = atoi(value);
+               if (!strcasecmp(key, "ib_summary")) ib_summary = atoi(value);
+               if (!strcasecmp(key, "ib_inbox")) ib_inbox = atoi(value);
+               if (!strcasecmp(key, "ib_calendar")) ib_calendar = atoi(value);
+               if (!strcasecmp(key, "ib_contacts")) ib_contacts = atoi(value);
+               if (!strcasecmp(key, "ib_notes")) ib_notes = atoi(value);
+               if (!strcasecmp(key, "ib_tasks")) ib_tasks = atoi(value);
+               if (!strcasecmp(key, "ib_rooms")) ib_rooms = atoi(value);
+               if (!strcasecmp(key, "ib_users")) ib_users = atoi(value);
+               if (!strcasecmp(key, "ib_chat")) ib_chat = atoi(value);
+               if (!strcasecmp(key, "ib_advanced")) ib_advanced = atoi(value);
+               if (!strcasecmp(key, "ib_citadel")) ib_citadel = atoi(value);
+       }
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Customize the icon bar"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>");
+
+       wprintf("<FORM METHOD=\"POST\" action=\"commit_iconbar\">\n");
+
+       wprintf("<CENTER>");
+       wprintf(_("Display icons as:"));
+       wprintf(" ");
+       for (i=0; i<=2; ++i) {
+               wprintf("<INPUT TYPE=\"radio\" NAME=\"ib_displayas\" VALUE=\"%d\"", i);
+               if (ib_displayas == i) wprintf(" CHECKED");
+               wprintf(">");
+               if (i == IB_PICTEXT)    wprintf(_("pictures and text"));
+               if (i == IB_PICONLY)    wprintf(_("pictures only"));
+               if (i == IB_TEXTONLY)   wprintf(_("text only"));
+               wprintf("\n");
+       }
+       wprintf("<br /><br />\n");
+
+       wprintf(_("Select the icons you would like to see displayed "
+               "in the 'icon bar' menu on the left side of the "
+               "screen."));
+       wprintf("</CENTER><br />\n");
+
+       wprintf("<TABLE border=0 cellspacing=0 cellpadding=3 width=100%%>\n");
+
+       wprintf("<TR BGCOLOR=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_logo\" VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"image&name=hello\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_logo ? "CHECKED" : ""),
+               _("Site logo"),
+               _("An icon describing this site")
+       );
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_summary\" VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/summscreen_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_summary ? "CHECKED" : ""),
+               _("Summary"),
+               _("Your summary page")
+       );
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_inbox\" VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/privatemess_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_inbox ? "CHECKED" : ""),
+               _("Mail (inbox)"),
+               _("A shortcut to your email Inbox")
+       );
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_contacts\" "
+               "VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/viewcontacts_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_contacts ? "CHECKED" : ""),
+               _("Contacts"),
+               _("Your personal address book")
+       );
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_notes\" "
+               "VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/storenotes_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_notes ? "CHECKED" : ""),
+               _("Notes"),
+               _("Your personal notes")
+       );
+
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_calendar\" "
+               "VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/calarea_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_calendar ? "CHECKED" : ""),
+               _("Calendar"),
+               _("A shortcut to your personal calendar")
+       );
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_tasks\" VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/taskmanag_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_tasks ? "CHECKED" : ""),
+               _("Tasks"),
+               _("A shortcut to your personal task list")
+       );
+#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_rooms\" VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/chatrooms_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_rooms ? "CHECKED" : ""),
+               _("Rooms"),
+               _("Clicking this icon displays a list of all accessible "
+               "rooms (or folders) available.")
+       );
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_users\" VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/usermanag_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_users ? "CHECKED" : ""),
+               _("Who is online?"),
+               _("Clicking this icon displays a list of all users "
+               "currently logged in.")
+       );
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_chat\" VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/citadelchat_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_chat ? "CHECKED" : ""),
+               _("Chat"),
+               _("Clicking this icon enters real-time chat mode "
+               "with other users in the same room.")
+               
+       );
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_advanced\" "
+               "VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/advanpage2_48x.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_advanced ? "CHECKED" : ""),
+               _("Advanced options"),
+               _("Access to the complete menu of Citadel functions.")
+
+       );
+
+       wprintf("<TR bgcolor=%s><TD>"
+               "<INPUT TYPE=\"checkbox\" NAME=\"ib_citadel\" "
+               "VALUE=\"yes\" %s>"
+               "</TD><TD>"
+               "<IMG BORDER=\"0\" WIDTH=\"48\" HEIGHT=\"48\" "
+               "src=\"static/citadel-logo.gif\" ALT=\"&nbsp;\">"
+               "</TD><TD>"
+               "<B>%s</B><br />"
+               "%s"
+               "</TD></TR>\n",
+               ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")),
+               (ib_citadel ? "CHECKED" : ""),
+               _("Citadel logo"),
+               _("Displays the 'Powered by Citadel' icon")
+       );
+
+       wprintf("</TABLE><br />\n"
+               "<CENTER>"
+               "<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">"
+               "&nbsp;"
+               "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">"
+               "</CENTER></FORM>\n",
+               _("Save changes"),
+               _("Cancel")
+       );
+
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(2);
+}
+
+/**
+ * \brief commit the changes of an edited iconbar ????
+ */
+void commit_iconbar(void) {
+       char iconbar[SIZ];
+       int i;
+
+       char *boxen[] = {
+               "ib_logo",
+               "ib_summary",
+               "ib_inbox",
+               "ib_calendar",
+               "ib_contacts",
+               "ib_notes",
+               "ib_tasks",
+               "ib_rooms",
+               "ib_users",
+               "ib_chat",
+               "ib_advanced",
+               "ib_logoff",
+               "ib_citadel"
+       };
+
+       if (strlen(bstr("ok_button")) == 0) {
+               display_main_menu();
+               return;
+       }
+
+       sprintf(iconbar, "ib_displayas=%d", atoi(bstr("ib_displayas")));
+
+       for (i=0; i<(sizeof(boxen)/sizeof(char *)); ++i) {
+               sprintf(&iconbar[strlen(iconbar)], ",%s=", boxen[i]);
+               if (!strcasecmp(bstr(boxen[i]), "yes")) {
+                       sprintf(&iconbar[strlen(iconbar)], "1");
+               }
+               else {
+                       sprintf(&iconbar[strlen(iconbar)], "0");
+               }
+       }
+
+       set_preference("iconbar", iconbar, 1);
+
+       output_headers(1, 1, 0, 0, 0, 0);
+       wprintf(
+               "<center><table border=1 bgcolor=\"#ffffff\"><tr><td>"
+               "<img src=\"static/advanpage2_48x.gif\">"
+               "&nbsp;");
+       wprintf(_("Your icon bar has been updated.  Please select any of its "
+               "choices to continue."));
+       wprintf("</td></tr></table>\n");
+       wDumpContent(2);
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/inetconf.c b/webcit/src/inetconf.c
new file mode 100644 (file)
index 0000000..85f9815
--- /dev/null
@@ -0,0 +1,202 @@
+/* 
+ * $Id$
+ */
+/**
+ * \defgroup InetCfg Functions which handle Internet domain configuration etc.
+ * \ingroup CitadelConfig
+ */
+/*@{*/
+#include "webcit.h"
+
+
+/**
+ * \brief display the inet config dialog 
+ */
+void display_inetconf(void)
+{
+       char buf[SIZ];
+       char ename[SIZ];
+       char etype[SIZ];
+       int i;
+       int which;
+
+       enum {
+               ic_localhost,
+               ic_directory,
+               ic_gwdom,
+               ic_smarthost,
+               ic_rbl,
+               ic_spamass,
+               ic_max
+       };
+       char *ic_spec[ic_max];
+       char *ic_misc;
+       char *ic_keyword[ic_max];
+       char *ic_boxtitle[ic_max];
+       char *ic_desc[ic_max];
+
+       ic_keyword[0] = _("localhost");
+       ic_keyword[1] = _("directory");
+       ic_keyword[2] = _("gatewaydomain");
+       ic_keyword[3] = _("smarthost");
+       ic_keyword[4] = _("rbl");
+       ic_keyword[5] = _("spamassassin");
+
+       ic_boxtitle[0] = _("Local host aliases");
+       ic_boxtitle[1] = _("Directory domains");
+       ic_boxtitle[2] = _("Gateway domains");
+       ic_boxtitle[3] = _("Smart hosts");
+       ic_boxtitle[4] = _("RBL hosts");
+       ic_boxtitle[5] = _("SpamAssassin hosts");
+
+       ic_desc[0] = _("(domains for which this host receives mail)");
+       ic_desc[1] = _("(domains mapped with the Global Address Book)");
+       ic_desc[2] = _("(domains whose subdomains match Citadel hosts)");
+       ic_desc[3] = _("(if present, forward all outbound mail to one of these hosts)");
+       ic_desc[4] = _("(hosts running a Realtime Blackhole List)");
+       ic_desc[5] = _("(hosts running the SpamAssassin service)");
+
+       for (i=0; i<ic_max; ++i) {
+               ic_spec[i] = strdup("");
+       }
+       ic_misc = strdup("");
+
+       serv_printf("CONF GETSYS|application/x-citadel-internet-config");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+
+               extract_token(ename, buf, 0, '|', sizeof ename);
+               extract_token(etype, buf, 1, '|', sizeof etype);
+               which = (-1);
+               for (i=0; i<ic_max; ++i) {
+                       if (!strcasecmp(etype, ic_keyword[i])) {
+                               which = i;
+                       }
+               }
+
+               if (which >= 0) {
+                       ic_spec[which] = realloc(ic_spec[which], strlen(ic_spec[which]) + strlen(ename) + 2);
+                       if (strlen(ic_spec[which]) > 0) strcat(ic_spec[which], "\n");
+                       strcat(ic_spec[which], ename);
+               }
+               else {
+                       ic_misc = realloc(ic_misc, strlen(ic_misc) + strlen(buf) + 2);
+                       if (strlen(ic_misc) > 0) strcat(ic_misc, "\n");
+                       strcat(ic_misc, buf);
+               }
+
+       }
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
+       wprintf("<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Internet configuration"));
+       wprintf("</SPAN>\n");
+       wprintf("</TD></TR></TABLE>\n");
+       wprintf("</div>\n<div id=\"content\">\n");
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%%><tr><td valign=top>\n");
+       for (which=0; which<ic_max; ++which) {
+               if (which == (ic_max / 2)) {
+                       wprintf("</TD><TD VALIGN=TOP>");
+               }
+               svprintf("BOXTITLE", WCS_STRING, ic_boxtitle[which]);
+               do_template("beginbox");
+               wprintf("<span class=\"menudesc\">");
+               escputs(ic_desc[which]);
+               wprintf("</span><br />");
+               wprintf("<TABLE border=0 cellspacing=0 cellpadding=0 width=100%%>\n");
+               if (strlen(ic_spec[which]) > 0) {
+                       for (i=0; i<num_tokens(ic_spec[which], '\n'); ++i) {
+                               wprintf("<TR><TD ALIGN=LEFT>");
+                               extract_token(buf, ic_spec[which], i, '\n', sizeof buf);
+                               escputs(buf);
+                               wprintf("</TD><TD ALIGN=RIGHT>"
+                                       "<a href=\"save_inetconf?oper=delete&ename=");
+                               escputs(buf);
+                               wprintf("&etype=%s\" ", ic_keyword[which]);
+                               wprintf("onClick=\"return confirm('%s');\">",
+                                       _("Delete this entry?"));
+                               wprintf("<font size=-1>");
+                               wprintf(_("(Delete)"));
+                               wprintf("</font></a></TD></TR>\n");
+                       }
+               }
+               wprintf("<FORM METHOD=\"POST\" action=\"save_inetconf\">\n"
+                       "<TR><TD>"
+                       "<INPUT TYPE=\"text\" NAME=\"ename\" MAXLENGTH=\"64\">"
+                       "<INPUT TYPE=\"hidden\" NAME=\"etype\" VALUE=\"%s\">", ic_keyword[which]);
+               wprintf("</TD><TD ALIGN=RIGHT>"
+                       "<INPUT TYPE=\"submit\" NAME=\"oper\" VALUE=\"Add\">"
+                       "</TD></TR></TABLE></FORM>\n");
+               do_template("endbox");
+       }
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+
+       for (i=0; i<ic_max; ++i) {
+               free(ic_spec[i]);
+       }
+       free(ic_misc);
+}
+
+
+/**
+ * \brief save changes to the inet config
+ */
+void save_inetconf(void) {
+       char *buf;
+       char *ename;
+       char *etype;
+       char *newconfig;
+
+       buf = malloc(SIZ);
+       ename = malloc(SIZ);
+       etype = malloc(SIZ);
+       newconfig = malloc(65536);
+
+       strcpy(newconfig, "");
+       serv_printf("CONF GETSYS|application/x-citadel-internet-config");
+       serv_getln(buf, SIZ);
+       if (buf[0] == '1') while (serv_getln(buf, SIZ), strcmp(buf, "000")) {
+               extract_token(ename, buf, 0, '|', SIZ);
+               extract_token(etype, buf, 1, '|', SIZ);
+               if (strlen(buf) == 0) {
+                       /** skip blank lines */
+               }
+               else if ((!strcasecmp(ename, bstr("ename")))
+                  &&   (!strcasecmp(etype, bstr("etype")))
+                  &&   (!strcasecmp(bstr("oper"), "delete"))
+               ) {
+                       sprintf(WC->ImportantMessage, _("%s has been deleted."), ename);
+               }
+               else {
+                       if (strlen(newconfig) > 0) strcat(newconfig, "\n");
+                       strcat(newconfig, buf);
+               }
+       }
+
+       serv_printf("CONF PUTSYS|application/x-citadel-internet-config");
+       serv_getln(buf, SIZ);
+       if (buf[0] == '4') {
+               serv_puts(newconfig);
+               if (!strcasecmp(bstr("oper"), "add")) {
+                       serv_printf("%s|%s", bstr("ename"), bstr("etype") );
+                       sprintf(WC->ImportantMessage, "%s added.", bstr("ename"));
+               }
+               serv_puts("000");
+       }
+       
+       display_inetconf();
+
+       free(buf);
+       free(ename);
+       free(etype);
+       free(newconfig);
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/listsub.c b/webcit/src/listsub.c
new file mode 100644 (file)
index 0000000..3f7e9cf
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup ListSubForms Web forms for handling mailing list subscribe/unsubscribe requests.
+ * \ingroup WebcitDisplayItems
+ */
+
+/*@{*/
+#include "webcit.h"
+
+
+
+/**
+ * \brief List subscription handling
+ */
+void do_listsub(void)
+{
+       char cmd[256];
+       char room[256];
+       char token[256];
+       char email[256];
+       char subtype[256];
+       char escaped_email[256];
+       char escaped_room[256];
+
+       char buf[SIZ];
+       int self;
+       char sroom[SIZ];
+
+       strcpy(WC->wc_fullname, "");
+       strcpy(WC->wc_username, "");
+       strcpy(WC->wc_password, "");
+       strcpy(WC->wc_roomname, "");
+
+       output_headers(1, 0, 0, 1, 1, 0);
+       begin_burst();
+
+       wprintf("<HTML><HEAD>\n"
+               "<meta name=\"MSSmartTagsPreventParsing\" content=\"TRUE\" />\n"
+               "<link href=\"static/webcit.css\" rel=\"stylesheet\" type=\"text/css\">\n"
+               "<TITLE>\n"
+       );
+       wprintf(_("List subscription"));
+       wprintf("</TITLE></HEAD><BODY>\n");
+
+       strcpy(cmd, bstr("cmd"));
+       strcpy(room, bstr("room"));
+       strcpy(token, bstr("token"));
+       strcpy(email, bstr("email"));
+       strcpy(subtype, bstr("subtype"));
+
+       wprintf("<CENTER>"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("List subscribe/unsubscribe"));
+       wprintf("</SPAN></TD></TR></TABLE><br />\n");
+
+       /**
+        * Subscribe command
+        */
+       if (!strcasecmp(cmd, "subscribe")) {
+               serv_printf("SUBS subscribe|%s|%s|%s|%s://%s/listsub",
+                       room,
+                       email,
+                       subtype,
+                       (is_https ? "https" : "http"),
+                       WC->http_host
+               );
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       stresc(escaped_email, email, 0, 0);
+                       stresc(escaped_room, room, 0, 0);
+
+                       wprintf("<CENTER><H1>");
+                       wprintf(_("Confirmation request sent"));
+                       wprintf("</H1>");
+                       wprintf(_("You are subscribing <TT>%s"
+                               "</TT> to the <b>%s</b> mailing list.  "
+                               "The listserver has "
+                               "sent you an e-mail with one additional "
+                               "Web link for you to click on to confirm "
+                               "your subscription.  This extra step is for "
+                               "your protection, as it prevents others from "
+                               "being able to subscribe you to lists "
+                               "without your consent.<br /><br />"
+                               "Please click on the link which is being "
+                               "e-mailed to you and your subscription will "
+                               "be confirmed.<br />\n"),
+                               escaped_email, escaped_room);
+                       wprintf("<a href=\"listsub\">%s</A></CENTER>\n", _("Go back..."));
+               }
+               else {
+                       wprintf("<FONT SIZE=+1><B>ERROR: %s</B>"
+                               "</FONT><br /><br />\n",
+                               &buf[4]);
+                       goto FORM;
+               }
+       }
+
+       /**
+        * Unsubscribe command
+        */
+       else if (!strcasecmp(cmd, "unsubscribe")) {
+               serv_printf("SUBS unsubscribe|%s|%s|%s://%s/listsub",
+                       room,
+                       email,
+                       (is_https ? "https" : "http"),
+                       WC->http_host
+               );
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       wprintf("<CENTER><H1>Confirmation request sent</H1>"
+                               "You are unsubscribing <TT>");
+                       escputs(email);
+                       wprintf("</TT> from the &quot;");
+                       escputs(room);
+                       wprintf("&quot; mailing list.  The listserver has "
+                               "sent you an e-mail with one additional "
+                               "Web link for you to click on to confirm "
+                               "your unsubscription.  This extra step is for "
+                               "your protection, as it prevents others from "
+                               "being able to unsubscribe you from "
+                               "lists without your consent.<br /><br />"
+                               "Please click on the link which is being "
+                               "e-mailed to you and your unsubscription will "
+                               "be confirmed.<br />\n"
+                               "<a href=\"listsub\">Back...</A></CENTER>\n"
+                       );
+               }
+               else {
+                       wprintf("<FONT SIZE=+1><B>ERROR: %s</B>"
+                               "</FONT><br /><br />\n",
+                               &buf[4]);
+                       goto FORM;
+               }
+       }
+
+       /**
+        * Confirm command
+        */
+       else if (!strcasecmp(cmd, "confirm")) {
+               serv_printf("SUBS confirm|%s|%s",
+                       room,
+                       token
+               );
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       wprintf("<CENTER><H1>Confirmation successful!</H1>");
+               }
+               else {
+                       wprintf("<CENTER><H1>Confirmation failed.</H1>"
+                               "This could mean one of two things:<UL>\n"
+                               "<LI>You waited too long to confirm your "
+                               "subscribe/unsubscribe request (the "
+                               "confirmation link is only valid for three "
+                               "days)\n<LI>You have <i>already</i> "
+                               "successfully confirmed your "
+                               "subscribe/unsubscribe request and are "
+                               "attempting to do it again.</UL>\n"
+                               "The error returned by the server was: "
+                       );
+               }
+               wprintf("%s</CENTER><br />\n", &buf[4]);
+       }
+
+       /**
+        * Any other (invalid) command causes the form to be displayed
+        */
+       else {
+FORM:          wprintf("<FORM METHOD=\"POST\" action=\"listsub\">\n"
+                       "<TABLE BORDER=0>\n"
+               );
+
+               wprintf("<TR><TD>Name of list</TD><TD>"
+                       "<SELECT NAME=\"room\" SIZE=1>\n");
+
+               serv_puts("LPRM");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '1') {
+                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                               extract_token(sroom, buf, 0, '|', sizeof sroom);
+                               self = extract_int(buf, 4) & QR2_SELFLIST ;
+                               if (self) {
+                                       wprintf("<OPTION VALUE=\"");
+                                       escputs(sroom);
+                                       wprintf("\">");
+                                       escputs(sroom);
+                                       wprintf("</OPTION>\n");
+                               }
+                       }
+               }
+               wprintf("</SELECT>"
+                       "</TD></TR>\n");
+
+               wprintf("<TR><TD>Your e-mail address</TD><TD>"
+                       "<INPUT TYPE=\"text\" NAME=\"email\" "
+                       "VALUE=\""
+               );
+               escputs(email);
+               wprintf("\" MAXLENGTH=128></TD></TR>\n");
+
+               wprintf("</TABLE>"
+                       "(If subscribing) preferred format: "
+                       "<INPUT TYPE=\"radio\" NAME=\"subtype\""
+                       "VALUE=\"list\">One message at a time&nbsp; "
+                       "<INPUT TYPE=\"radio\" NAME=\"subtype\""
+                       "VALUE=\"digest\" CHECKED>Digest format&nbsp; "
+                       "<br />\n"
+                       "<INPUT TYPE=\"submit\" NAME=\"cmd\""
+                       " VALUE=\"subscribe\">\n"
+                       "<INPUT TYPE=\"submit\" NAME=\"cmd\""
+                       " VALUE=\"unsubscribe\">\n"
+                       "</FORM>\n"
+               );
+
+               wprintf("<br />When you attempt to subscribe or unsubscribe to "
+                       "a mailing list, you will receive an e-mail containing"
+                       " one additional web link to click on for final "
+                       "confirmation.  This extra step is for your "
+                       "protection, as it prevents others from being able to "
+                       "subscribe or unsubscribe you to lists.<br />\n"
+               );
+
+       }
+
+       wprintf("</BODY></HTML>\n");
+       wDumpContent(0);
+       end_webcit_session();
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/locate_host.c b/webcit/src/locate_host.c
new file mode 100644 (file)
index 0000000..7b58699
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup Hostlookup Examine a socket and determine the name/address of the originating host.
+ * \ingroup WebcitHttpServer
+ */
+/*@{*/
+
+#include "webcit.h"
+
+/**
+ * \brief get a hostname 
+ * \todo buffersize?
+ * \param tbuf the returnbuffer
+ * \param client_socket the sock fd where the client is connected
+ */
+void locate_host(char *tbuf, int client_socket)
+{
+       struct sockaddr_in cs;
+       struct hostent *ch;
+       socklen_t len;
+       char *i;
+       int a1, a2, a3, a4;
+
+       len = sizeof(cs);
+       if (getpeername(client_socket, (struct sockaddr *) &cs, &len) < 0) {
+               strcpy(tbuf, "<unknown>");
+               return;
+       }
+       if ((ch = gethostbyaddr((char *) &cs.sin_addr, sizeof(cs.sin_addr),
+                               AF_INET)) == NULL) {
+               i = (char *) &cs.sin_addr;
+               a1 = ((*i++) & 0xff);
+               a2 = ((*i++) & 0xff);
+               a3 = ((*i++) & 0xff);
+               a4 = ((*i++) & 0xff);
+               sprintf(tbuf, "%d.%d.%d.%d", a1, a2, a3, a4);
+               return;
+       }
+       safestrncpy(tbuf, ch->h_name, 64);
+}
+
+/*@}*/
diff --git a/webcit/src/mainmenu.c b/webcit/src/mainmenu.c
new file mode 100644 (file)
index 0000000..3f66e6d
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup DispAdvancedMenu Displays the "advanced" (main) menu.
+ * \ingroup MenuInfrastructure
+ *
+ */
+/*@{*/
+#include "webcit.h"
+
+/**
+ * \brief The Main Menu
+ */
+void display_main_menu(void)
+{
+       output_headers(1, 1, 1, 0, 0, 0);
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<TABLE WIDTH=100%%>"
+               "<TR><TD COLSPAN=2>\n");
+
+       svprintf("BOXTITLE", WCS_STRING, _("Basic commands"));
+       do_template("beginbox");
+
+       wprintf("\n"
+               "<TABLE border=0 cellspacing=1 cellpadding=1 width=100%%>"
+               "<TR>"
+               "<TD>");        /**< start of first column */
+
+       wprintf("<a href=\"knrooms\"><span class=\"mainmenu\">");
+       wprintf(_("List known rooms"));
+       wprintf("</span></A><br /><span class=\"menudesc\">");
+       wprintf(_("Where can I go from here?"));
+       wprintf("</span><br />\n");
+
+       wprintf("<a href=\"gotonext\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Goto next room"));
+       wprintf("</span></A><br />"
+               "<span class=\"menudesc\">");
+       wprintf(_("...with <EM>unread</EM> messages"));
+       wprintf("</span><br />\n");
+
+       wprintf("<a href=\"skip\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Skip to next room"));
+       wprintf("</span></a><br />"
+               "<span class=\"menudesc\">");
+       wprintf(_("(come back here later)"));
+       wprintf("</span>\n");
+
+       if ((strlen(WC->ugname) > 0) && (strcasecmp(WC->ugname, WC->wc_roomname))) {
+               wprintf("<br />"
+                       "<a href=\"ungoto\">"
+                       "<span class=\"mainmenu\">");
+               wprintf(_("Ungoto"));
+               wprintf("</span></A><br />"
+                       "<span class=\"menudesc\">");
+               wprintf(_("(oops! Back to %s)"), WC->ugname);
+               wprintf("</span>\n");
+       }
+
+       wprintf("</TD><TD>\n"); /* start of second column */
+
+       wprintf("<a href=\"readnew\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Read new messages"));
+       wprintf("</span></A><br />"
+               "<span class=\"menudesc\">");
+       wprintf(_("...in this room"));
+       wprintf("</span><br />\n");
+
+       wprintf("<a href=\"readfwd\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Read all messages"));
+       wprintf("</span></A><br />"
+               "<span class=\"menudesc\">");
+       wprintf(_("...old <EM>and</EM> new"));
+       wprintf("</span><br />\n");
+
+       wprintf("<a href=\"display_enter\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Enter a message"));
+       wprintf("</span></A><br />"
+               "<span class=\"menudesc\">");
+       wprintf(_("(post in this room)"));
+       wprintf("</span>\n");
+
+       wprintf("</TD><TD>");   /* start of third column */
+
+       wprintf("<a href=\"summary\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Summary page"));
+       wprintf("</span></A><br />"
+               "<span class=\"menudesc\">");
+       wprintf(_("Summary of my account"));
+       wprintf("</span><br />\n");
+
+       wprintf("<a href=\"userlist\">\n"
+               "<span class=\"mainmenu\">");
+       wprintf(_("User list"));
+       wprintf("</span></A><br />"
+               "<span class=\"menudesc\">");
+       wprintf(_("(all registered users)"));
+       wprintf("</span><br />\n");
+
+       wprintf("<a href=\"termquit\" TARGET=\"_top\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Log off"));
+       wprintf("</span></A><br />"
+               "<span class=\"menudesc\">");
+       wprintf(_("Bye!"));
+       wprintf("</span>\n");
+
+       wprintf("</TD></TR></TABLE>\n");
+       do_template("endbox");
+
+       wprintf("</TD></TR>"
+               "<TR VALIGN=TOP><TD>");
+
+       svprintf("BOXTITLE", WCS_STRING, _("Your info"));
+       do_template("beginbox");
+
+       wprintf("<a href=\"display_preferences\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Change your preferences and settings"));
+       wprintf("</span><br />\n");
+
+       wprintf("<a href=\"display_reg\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Update your contact information"));
+       wprintf("</span><br />\n");
+
+       wprintf("<a href=\"display_changepw\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Change your password"));
+       wprintf("</span></A><br />\n");
+
+       wprintf("<a href=\"display_editbio\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Enter your 'bio'"));
+       wprintf("</span></a><br />\n");
+
+       wprintf("<a href=\"display_editpic\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Edit your online photo"));
+       wprintf("</span></a>\n");
+
+       do_template("endbox");
+
+       wprintf("</TD><TD>");
+
+       svprintf("BOXTITLE", WCS_STRING, _("Advanced room commands"));
+       do_template("beginbox");
+
+       if ((WC->axlevel >= 6) || (WC->is_room_aide)) {
+               wprintf("<a href=\"display_editroom\">"
+                       "<span class=\"mainmenu\">");
+               wprintf(_("Edit or delete this room"));
+               wprintf("</span></A><br />\n");
+       }
+
+       wprintf("<a href=\"display_private\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Go to a 'hidden' room"));
+       wprintf("</span></A><br />\n");
+
+       wprintf("<a href=\"display_entroom\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Create a new room"));
+       wprintf("</span></A><br />\n");
+
+       wprintf("<a href=\"display_zap\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Zap (forget) this room (%s)"), WC->wc_roomname);
+       wprintf("</span></A><br />\n");
+
+       wprintf("<a href=\"zapped_list\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("List all forgotten rooms"));
+       wprintf("</span></A>\n");
+
+       do_template("endbox");
+
+       wprintf("</td></tr></table></div>");
+       wDumpContent(2);
+}
+
+
+/**
+ * \brief System administration menu
+ */
+void display_aide_menu(void)
+{
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("System Administration Menu"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%%><tr valign=top><td>");
+
+       svprintf("BOXTITLE", WCS_STRING, _("Global Configuration"));
+       do_template("beginbox");
+
+       wprintf("<a href=\"display_siteconfig\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Edit site-wide configuration"));
+       wprintf("</span></A><br />\n");
+
+       wprintf("<a href=\"display_inetconf\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Domain names and Internet mail configuration"));
+       wprintf("</span></a><br />\n");
+
+       wprintf("<a href=\"display_netconf\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Configure replication with other Citadel servers"));
+       wprintf("</span></A>\n");
+
+       do_template("endbox");
+
+       wprintf("</td><td>");
+
+       svprintf("BOXTITLE", WCS_STRING, _("User account management"));
+       do_template("beginbox");
+
+       wprintf("<a href=\"select_user_to_edit\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Add, change, delete user accounts"));
+       wprintf("</span></A><br />\n");
+
+       wprintf("<a href=\"validate\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Validate new users"));
+       wprintf("</span></A><br />\n");
+
+       do_template("endbox");
+
+       svprintf("BOXTITLE", WCS_STRING, _("Rooms and Floors"));
+       do_template("beginbox");
+
+       wprintf("<a href=\"display_floorconfig\">"
+               "<span class=\"mainmenu\">");
+       wprintf(_("Add, change, or delete floors"));
+       wprintf("</span></A>\n");
+
+       do_template("endbox");
+
+       wprintf("</td></tr></table></div>");
+       wDumpContent(2);
+}
+
+
+
+
+
+/**
+ * \brief Display the screen to enter a generic server command
+ */
+void display_generic(void)
+{
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Enter a server command"));
+       wprintf("</SPAN></TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       wprintf("<CENTER>");
+       wprintf(_("This screen allows you to enter Citadel server commands which are "
+               "not supported by WebCit.  If you do not know what that means, "
+               "then this screen will not be of much use to you."));
+       wprintf("<br />\n");
+
+       wprintf("<FORM METHOD=\"POST\" action=\"do_generic\">\n");
+
+       wprintf(_("Enter command:"));
+       wprintf("<br /><INPUT TYPE=\"text\" NAME=\"g_cmd\" SIZE=80 MAXLENGTH=\"250\"><br />\n");
+
+       wprintf(_("Command input (if requesting SEND_LISTING transfer mode):"));
+       wprintf("<br /><TEXTAREA NAME=\"g_input\" ROWS=10 COLS=80 WIDTH=80></TEXTAREA><br />\n");
+
+       wprintf("<FONT SIZE=-2>");
+       wprintf(_("Detected host header is %s://%s"), (is_https ? "https" : "http"), WC->http_host);
+       wprintf("</FONT>\n");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"sc_button\" VALUE=\"%s\">", _("Send command"));
+       wprintf("&nbsp;");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\"><br />\n", _("Cancel"));
+
+       wprintf("</FORM></CENTER>\n");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+/**
+ * \brief Interactive window to perform generic Citadel server commands.
+ */
+void do_generic(void)
+{
+       char buf[SIZ];
+       char gcontent[SIZ];
+       char *junk;
+       size_t len;
+
+       if (strlen(bstr("sc_button")) == 0) {
+               display_main_menu();
+               return;
+       }
+
+       output_headers(1, 1, 0, 0, 0, 0);
+
+       serv_printf("%s", bstr("g_cmd"));
+       serv_getln(buf, sizeof buf);
+
+       svprintf("BOXTITLE", WCS_STRING, _("Server command results"));
+       do_template("beginbox");
+
+       wprintf("<TABLE border=0><TR><TD>Command:</TD><TD><TT>");
+       escputs(bstr("g_cmd"));
+       wprintf("</TT></TD></TR><TR><TD>Result:</TD><TD><TT>");
+       escputs(buf);
+       wprintf("</TT></TD></TR></TABLE><br />\n");
+
+       if (buf[0] == '8') {
+               serv_printf("\n\n000");
+       }
+       if ((buf[0] == '1') || (buf[0] == '8')) {
+               while (serv_getln(gcontent, sizeof gcontent), strcmp(gcontent, "000")) {
+                       escputs(gcontent);
+                       wprintf("<br />\n");
+               }
+               wprintf("000");
+       }
+       if (buf[0] == '4') {
+               text_to_server(bstr("g_input"));
+               serv_puts("000");
+       }
+       if (buf[0] == '6') {
+               len = atol(&buf[4]);
+               junk = malloc(len);
+               serv_read(junk, len);
+               free(junk);
+       }
+       if (buf[0] == '7') {
+               len = atol(&buf[4]);
+               junk = malloc(len);
+               memset(junk, 0, len);
+               serv_write(junk, len);
+               free(junk);
+       }
+       wprintf("<hr />");
+       wprintf("<a href=\"display_generic\">Enter another command</A><br />\n");
+       wprintf("<a href=\"display_advanced\">Return to menu</A>\n");
+       do_template("endbox");
+       wDumpContent(1);
+}
+
+
+/**
+ * \brief Display the menubar.  
+ * \param as_single_page Set to display HTML headers and footers -- otherwise it's assumed
+ * that the menubar is being embedded in another page.
+ */
+void display_menubar(int as_single_page) {
+
+       if (as_single_page) {
+               output_headers(0, 0, 0, 0, 0, 0);
+               wprintf("<HTML>\n"
+                       "<HEAD>\n"
+                       "<TITLE>MenuBar</TITLE>\n"
+                       "<STYLE TYPE=\"text/css\">\n"
+                       "BODY   { text-decoration: none; }\n"
+                       "</STYLE>\n"
+                       "</HEAD>\n");
+               do_template("background");
+       }
+
+       do_template("menubar");
+
+       if (as_single_page) {
+               wDumpContent(2);
+       }
+
+
+}
+
+
+/*@}*/
diff --git a/webcit/src/messages.c b/webcit/src/messages.c
new file mode 100644 (file)
index 0000000..1be8639
--- /dev/null
@@ -0,0 +1,3183 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup MsgDisp Functions which deal with the fetching and displaying of messages.
+ * \ingroup WebcitDisplayItems
+ *
+ */
+/*@{*/
+#include "webcit.h"
+#include "vcard.h"
+#include "webserver.h"
+#include "groupdav.h"
+
+#define SUBJ_COL_WIDTH_PCT             50      /**< Mailbox view column width */
+#define SENDER_COL_WIDTH_PCT           30      /**< Mailbox view column width */
+#define DATE_PLUS_BUTTONS_WIDTH_PCT    20      /**< Mailbox view column width */
+
+/**
+ * Address book entry (keep it short and sweet, it's just a quickie lookup
+ * which we can use to get to the real meat and bones later)
+ */
+struct addrbookent {
+       char ab_name[64]; /**< name string */
+       long ab_msgnum;   /**< message number of address book entry */
+};
+
+
+
+#ifdef HAVE_ICONV
+
+/**
+ * \brief      Wrapper around iconv_open()
+ *             Our version adds aliases for non-standard Microsoft charsets
+ *              such as 'MS950', aliasing them to names like 'CP950'
+ *
+ * \param      tocode          Target encoding
+ * \param      fromcode        Source encoding
+ */
+iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode)
+{
+       iconv_t ic = (iconv_t)(-1) ;
+       ic = iconv_open(tocode, fromcode);
+       if (ic == (iconv_t)(-1) ) {
+               char alias_fromcode[64];
+               if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
+                       safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
+                       alias_fromcode[0] = 'C';
+                       alias_fromcode[1] = 'P';
+                       ic = iconv_open(tocode, alias_fromcode);
+               }
+       }
+       return(ic);
+}
+
+
+/**
+ * \brief  Handle subjects with RFC2047 encoding
+ *  such as:
+ * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
+ * \param buf the stringbuffer to process
+ */
+void utf8ify_rfc822_string(char *buf) {
+       char *start, *end;
+       char newbuf[1024];
+       char charset[128];
+       char encoding[16];
+       char istr[1024];
+       iconv_t ic = (iconv_t)(-1) ;
+       char *ibuf;                     /**< Buffer of characters to be converted */
+       char *obuf;                     /**< Buffer for converted characters */
+       size_t ibuflen;                 /**< Length of input buffer */
+       size_t obuflen;                 /**< Length of output buffer */
+       char *isav;                     /**< Saved pointer to input buffer */
+       char *osav;                     /**< Saved pointer to output buffer */
+       int passes = 0;
+       int i;
+       int illegal_non_rfc2047_encoding = 0;
+
+       /** Sometimes, badly formed messages contain strings which were simply
+        *  written out directly in some foreign character set instead of
+        *  using RFC2047 encoding.  This is illegal but we will attempt to
+        *  handle it anyway by converting from a user-specified default
+        *  charset to UTF-8 if we see any nonprintable characters.
+        */
+       for (i=0; i<strlen(buf); ++i) {
+               if ((buf[i] < 32) || (buf[i] > 126)) {
+                       illegal_non_rfc2047_encoding = 1;
+               }
+       }
+       if (illegal_non_rfc2047_encoding) {
+               char default_header_charset[128];
+               get_preference("default_header_charset", default_header_charset, sizeof default_header_charset);
+               if ( (strcasecmp(default_header_charset, "UTF-8")) && (strcasecmp(default_header_charset, "us-ascii")) ) {
+                       ic = ctdl_iconv_open("UTF-8", default_header_charset);
+                       if (ic != (iconv_t)(-1) ) {
+                               ibuf = malloc(1024);
+                               isav = ibuf;
+                               safestrncpy(ibuf, buf, 1024);
+                               ibuflen = strlen(ibuf);
+                               obuflen = 1024;
+                               obuf = (char *) malloc(obuflen);
+                               osav = obuf;
+                               iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
+                               osav[1024-obuflen] = 0;
+                               strcpy(buf, osav);
+                               free(osav);
+                               iconv_close(ic);
+                               free(isav);
+                       }
+               }
+       }
+
+       /** Now we handle foreign character sets properly encoded
+        *  in RFC2047 format.
+        */
+       while (start=strstr(buf, "=?"), end=strstr(buf, "?="),
+               ((start != NULL) && (end != NULL) && (end > start)) )
+       {
+               extract_token(charset, start, 1, '?', sizeof charset);
+               extract_token(encoding, start, 2, '?', sizeof encoding);
+               extract_token(istr, start, 3, '?', sizeof istr);
+
+               ibuf = malloc(1024);
+               isav = ibuf;
+               if (!strcasecmp(encoding, "B")) {       /**< base64 */
+                       ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr));
+               }
+               else if (!strcasecmp(encoding, "Q")) {  /**< quoted-printable */
+                       ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, strlen(istr));
+               }
+               else {
+                       strcpy(ibuf, istr);             /**< unknown encoding */
+                       ibuflen = strlen(istr);
+               }
+
+               ic = ctdl_iconv_open("UTF-8", charset);
+               if (ic != (iconv_t)(-1) ) {
+                       obuflen = 1024;
+                       obuf = (char *) malloc(obuflen);
+                       osav = obuf;
+                       iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
+                       osav[1024-obuflen] = 0;
+
+                       end = start;
+                       end++;
+                       strcpy(start, "");
+                       remove_token(end, 0, '?');
+                       remove_token(end, 0, '?');
+                       remove_token(end, 0, '?');
+                       remove_token(end, 0, '?');
+                       strcpy(end, &end[1]);
+
+                       snprintf(newbuf, sizeof newbuf, "%s%s%s", buf, osav, end);
+                       strcpy(buf, newbuf);
+                       free(osav);
+                       iconv_close(ic);
+               }
+               else {
+                       end = start;
+                       end++;
+                       strcpy(start, "");
+                       remove_token(end, 0, '?');
+                       remove_token(end, 0, '?');
+                       remove_token(end, 0, '?');
+                       remove_token(end, 0, '?');
+                       strcpy(end, &end[1]);
+
+                       snprintf(newbuf, sizeof newbuf, "%s(unreadable)%s", buf, end);
+                       strcpy(buf, newbuf);
+               }
+
+               free(isav);
+
+               /**
+                * Since spammers will go to all sorts of absurd lengths to get their
+                * messages through, there are LOTS of corrupt headers out there.
+                * So, prevent a really badly formed RFC2047 header from throwing
+                * this function into an infinite loop.
+                */
+               ++passes;
+               if (passes > 20) return;
+       }
+
+}
+#endif
+
+
+
+
+/**
+ * \brief      RFC2047-encode a header field if necessary.
+ *             If no non-ASCII characters are found, the string
+ *             will be copied verbatim without encoding.
+ *
+ * \param      target          Target buffer.
+ * \param      maxlen          Maximum size of target buffer.
+ * \param      source          Source string to be encoded.
+ */
+void rfc2047encode(char *target, int maxlen, char *source)
+{
+       int need_to_encode = 0;
+       int i;
+       unsigned char ch;
+
+       if (target == NULL) return;
+
+       for (i=0; i<strlen(source); ++i) {
+               if ((source[i] < 32) || (source[i] > 126)) {
+                       need_to_encode = 1;
+               }
+       }
+
+       if (!need_to_encode) {
+               safestrncpy(target, source, maxlen);
+               return;
+       }
+
+       strcpy(target, "=?UTF-8?Q?");
+       for (i=0; i<strlen(source); ++i) {
+               ch = (unsigned char) source[i];
+               if ((ch < 32) || (ch > 126) || (ch == 61)) {
+                       sprintf(&target[strlen(target)], "=%02X", ch);
+               }
+               else {
+                       sprintf(&target[strlen(target)], "%c", ch);
+               }
+       }
+       
+       strcat(target, "?=");
+}
+
+
+
+
+/**
+ * \brief Look for URL's embedded in a buffer and make them linkable.  We use a
+ * target window in order to keep the BBS session in its own window.
+ * \param buf the message buffer
+ */
+void url(char *buf)
+{
+
+       int pos;
+       int start, end;
+       char ench;
+       char urlbuf[SIZ];
+       char outbuf[1024];
+
+       start = (-1);
+       end = strlen(buf);
+       ench = 0;
+
+       for (pos = 0; pos < strlen(buf); ++pos) {
+               if (!strncasecmp(&buf[pos], "http://", 7))
+                       start = pos;
+               if (!strncasecmp(&buf[pos], "ftp://", 6))
+                       start = pos;
+       }
+
+       if (start < 0)
+               return;
+
+       if ((start > 0) && (buf[start - 1] == '<'))
+               ench = '>';
+       if ((start > 0) && (buf[start - 1] == '['))
+               ench = ']';
+       if ((start > 0) && (buf[start - 1] == '('))
+               ench = ')';
+       if ((start > 0) && (buf[start - 1] == '{'))
+               ench = '}';
+
+       for (pos = strlen(buf); pos > start; --pos) {
+               if ((buf[pos] == ' ') || (buf[pos] == ench))
+                       end = pos;
+       }
+
+       strncpy(urlbuf, &buf[start], end - start);
+       urlbuf[end - start] = 0;
+
+       strncpy(outbuf, buf, start);
+       sprintf(&outbuf[start], "%ca href=%c%s%c TARGET=%c%s%c%c%s%c/A%c",
+               LB, QU, urlbuf, QU, QU, TARGET, QU, RB, urlbuf, LB, RB);
+       strcat(outbuf, &buf[end]);
+       if ( strlen(outbuf) < 250 )
+               strcpy(buf, outbuf);
+}
+
+
+/**
+ * \brief Turn a vCard "n" (name) field into something displayable.
+ * \param name the name field to convert
+ */
+void vcard_n_prettyize(char *name)
+{
+       char *original_name;
+       int i;
+
+       original_name = strdup(name);
+       for (i=0; i<5; ++i) {
+               if (strlen(original_name) > 0) {
+                       if (original_name[strlen(original_name)-1] == ' ') {
+                               original_name[strlen(original_name)-1] = 0;
+                       }
+                       if (original_name[strlen(original_name)-1] == ';') {
+                               original_name[strlen(original_name)-1] = 0;
+                       }
+               }
+       }
+       strcpy(name, "");
+       for (i=0; i<strlen(original_name); ++i) {
+               if (original_name[i] == ';') {
+                       strcat(name, ", ");
+               }
+               else {
+                       name[strlen(name)+1] = 0;
+                       name[strlen(name)] = original_name[i];
+               }
+       }
+       free(original_name);
+}
+
+
+
+
+/**
+ * \brief preparse a vcard name
+ * display_vcard() calls this after parsing the textual vCard into
+ * our 'struct vCard' data object.
+ * This gets called instead of display_parsed_vcard() if we are only looking
+ * to extract the person's name instead of displaying the card.
+ * \param v the vcard to retrieve the name from
+ * \param storename where to put the name at
+ */
+void fetchname_parsed_vcard(struct vCard *v, char *storename) {
+       char *name;
+
+       strcpy(storename, "");
+
+       name = vcard_get_prop(v, "n", 1, 0, 0);
+       if (name != NULL) {
+               strcpy(storename, name);
+               /* vcard_n_prettyize(storename); */
+       }
+
+}
+
+
+
+/**
+ * \brief html print a vcard
+ * display_vcard() calls this after parsing the textual vCard into
+ * our 'struct vCard' data object.
+ *
+ * Set 'full' to nonzero to display the full card, otherwise it will only
+ * show a summary line.
+ *
+ * This code is a bit ugly, so perhaps an explanation is due: we do this
+ * in two passes through the vCard fields.  On the first pass, we process
+ * fields we understand, and then render them in a pretty fashion at the
+ * end.  Then we make a second pass, outputting all the fields we don't
+ * understand in a simple two-column name/value format.
+ * \param v the vCard to display
+ * \param full display all items of the vcard?
+ */
+void display_parsed_vcard(struct vCard *v, int full) {
+       int i, j;
+       char buf[SIZ];
+       char *name;
+       int is_qp = 0;
+       int is_b64 = 0;
+       char *thisname, *thisvalue;
+       char firsttoken[SIZ];
+       int pass;
+
+       char fullname[SIZ];
+       char title[SIZ];
+       char org[SIZ];
+       char phone[SIZ];
+       char mailto[SIZ];
+
+       strcpy(fullname, "");
+       strcpy(phone, "");
+       strcpy(mailto, "");
+       strcpy(title, "");
+       strcpy(org, "");
+
+       if (!full) {
+               wprintf("<TD>");
+               name = vcard_get_prop(v, "fn", 1, 0, 0);
+               if (name != NULL) {
+                       escputs(name);
+               }
+               else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) {
+                       strcpy(fullname, name);
+                       vcard_n_prettyize(fullname);
+                       escputs(fullname);
+               }
+               else {
+                       wprintf("&nbsp;");
+               }
+               wprintf("</TD>");
+               return;
+       }
+
+       wprintf("<div align=center><table bgcolor=#aaaaaa width=50%%>");
+       for (pass=1; pass<=2; ++pass) {
+
+               if (v->numprops) for (i=0; i<(v->numprops); ++i) {
+
+                       thisname = strdup(v->prop[i].name);
+                       extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken);
+       
+                       for (j=0; j<num_tokens(thisname, ';'); ++j) {
+                               extract_token(buf, thisname, j, ';', sizeof buf);
+                               if (!strcasecmp(buf, "encoding=quoted-printable")) {
+                                       is_qp = 1;
+                                       remove_token(thisname, j, ';');
+                               }
+                               if (!strcasecmp(buf, "encoding=base64")) {
+                                       is_b64 = 1;
+                                       remove_token(thisname, j, ';');
+                               }
+                       }
+       
+                       if (is_qp) {
+                               thisvalue = malloc(strlen(v->prop[i].value) + 50);
+                               j = CtdlDecodeQuotedPrintable(
+                                       thisvalue, v->prop[i].value,
+                                       strlen(v->prop[i].value) );
+                               thisvalue[j] = 0;
+                       }
+                       else if (is_b64) {
+                               thisvalue = malloc(strlen(v->prop[i].value) + 50);
+                               CtdlDecodeBase64(
+                                       thisvalue, v->prop[i].value,
+                                       strlen(v->prop[i].value) );
+                       }
+                       else {
+                               thisvalue = strdup(v->prop[i].value);
+                       }
+       
+                       /** Various fields we may encounter ***/
+       
+                       /** N is name, but only if there's no FN already there */
+                       if (!strcasecmp(firsttoken, "n")) {
+                               if (strlen(fullname) == 0) {
+                                       strcpy(fullname, thisvalue);
+                                       vcard_n_prettyize(fullname);
+                               }
+                       }
+       
+                       /** FN (full name) is a true 'display name' field */
+                       else if (!strcasecmp(firsttoken, "fn")) {
+                               strcpy(fullname, thisvalue);
+                       }
+
+                       /** title */
+                       else if (!strcasecmp(firsttoken, "title")) {
+                               strcpy(title, thisvalue);
+                       }
+       
+                       /** organization */
+                       else if (!strcasecmp(firsttoken, "org")) {
+                               strcpy(org, thisvalue);
+                       }
+       
+                       else if (!strcasecmp(firsttoken, "email")) {
+                               if (strlen(mailto) > 0) strcat(mailto, "<br />");
+                               strcat(mailto,
+                                       "<a href=\"display_enter"
+                                       "?force_room=_MAIL_?recp=");
+
+                               urlesc(&mailto[strlen(mailto)], fullname);
+                               urlesc(&mailto[strlen(mailto)], " <");
+                               urlesc(&mailto[strlen(mailto)], thisvalue);
+                               urlesc(&mailto[strlen(mailto)], ">");
+
+                               strcat(mailto, "\">");
+                               stresc(&mailto[strlen(mailto)], thisvalue, 1, 1);
+                               strcat(mailto, "</A>");
+                       }
+                       else if (!strcasecmp(firsttoken, "tel")) {
+                               if (strlen(phone) > 0) strcat(phone, "<br />");
+                               strcat(phone, thisvalue);
+                               for (j=0; j<num_tokens(thisname, ';'); ++j) {
+                                       extract_token(buf, thisname, j, ';', sizeof buf);
+                                       if (!strcasecmp(buf, "tel"))
+                                               strcat(phone, "");
+                                       else if (!strcasecmp(buf, "work"))
+                                               strcat(phone, _(" (work)"));
+                                       else if (!strcasecmp(buf, "home"))
+                                               strcat(phone, _(" (home)"));
+                                       else if (!strcasecmp(buf, "cell"))
+                                               strcat(phone, _(" (cell)"));
+                                       else {
+                                               strcat(phone, " (");
+                                               strcat(phone, buf);
+                                               strcat(phone, ")");
+                                       }
+                               }
+                       }
+                       else if (!strcasecmp(firsttoken, "adr")) {
+                               if (pass == 2) {
+                                       wprintf("<TR><TD>");
+                                       wprintf(_("Address:"));
+                                       wprintf("</TD><TD>");
+                                       for (j=0; j<num_tokens(thisvalue, ';'); ++j) {
+                                               extract_token(buf, thisvalue, j, ';', sizeof buf);
+                                               if (strlen(buf) > 0) {
+                                                       escputs(buf);
+                                                       if (j<3) wprintf("<br />");
+                                                       else wprintf(" ");
+                                               }
+                                       }
+                                       wprintf("</TD></TR>\n");
+                               }
+                       }
+                       else if (!strcasecmp(firsttoken, "version")) {
+                               /* ignore */
+                       }
+                       else if (!strcasecmp(firsttoken, "rev")) {
+                               /* ignore */
+                       }
+                       else if (!strcasecmp(firsttoken, "label")) {
+                               /* ignore */
+                       }
+                       else {
+
+                               /*** Don't show extra fields.  They're ugly.
+                               if (pass == 2) {
+                                       wprintf("<TR><TD>");
+                                       escputs(thisname);
+                                       wprintf("</TD><TD>");
+                                       escputs(thisvalue);
+                                       wprintf("</TD></TR>\n");
+                               }
+                               ***/
+                       }
+       
+                       free(thisname);
+                       free(thisvalue);
+               }
+       
+               if (pass == 1) {
+                       wprintf("<TR BGCOLOR=\"#AAAAAA\">"
+                       "<TD COLSPAN=2 BGCOLOR=\"#FFFFFF\">"
+                       "<IMG ALIGN=CENTER src=\"static/viewcontacts_48x.gif\">"
+                       "<FONT SIZE=+1><B>");
+                       escputs(fullname);
+                       wprintf("</B></FONT>");
+                       if (strlen(title) > 0) {
+                               wprintf("<div align=right>");
+                               escputs(title);
+                               wprintf("</div>");
+                       }
+                       if (strlen(org) > 0) {
+                               wprintf("<div align=right>");
+                               escputs(org);
+                               wprintf("</div>");
+                       }
+                       wprintf("</TD></TR>\n");
+               
+                       if (strlen(phone) > 0) {
+                               wprintf("<tr><td>");
+                               wprintf(_("Telephone:"));
+                               wprintf("</td><td>%s</td></tr>\n", phone);
+                       }
+                       if (strlen(mailto) > 0) {
+                               wprintf("<tr><td>");
+                               wprintf(_("E-mail:"));
+                               wprintf("</td><td>%s</td></tr>\n", mailto);
+                       }
+               }
+
+       }
+
+       wprintf("</table></div>\n");
+}
+
+
+
+/**
+ * \brief  Display a textual vCard
+ * (Converts to a vCard object and then calls the actual display function)
+ * Set 'full' to nonzero to display the whole card instead of a one-liner.
+ * Or, if "storename" is non-NULL, just store the person's name in that
+ * buffer instead of displaying the card at all.
+ * \param vcard_source the buffer containing the vcard text
+ * \param alpha what???
+ * \param full should we usse all lines?
+ * \param storename where to store???
+ */
+void display_vcard(char *vcard_source, char alpha, int full, char *storename) {
+       struct vCard *v;
+       char *name;
+       char buf[SIZ];
+       char this_alpha = 0;
+
+       v = vcard_load(vcard_source);
+       if (v == NULL) return;
+
+       name = vcard_get_prop(v, "n", 1, 0, 0);
+       if (name != NULL) {
+               strcpy(buf, name);
+               this_alpha = buf[0];
+       }
+
+       if (storename != NULL) {
+               fetchname_parsed_vcard(v, storename);
+       }
+       else if (       (alpha == 0)
+                       || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) )
+                       || ((!isalpha(alpha)) && (!isalpha(this_alpha)))
+               ) {
+               display_parsed_vcard(v, full);
+       }
+
+       vcard_free(v);
+}
+
+
+/**
+ * \brief I wanna SEE that message!  
+ * \param msgnum the citadel number of the message to display
+ * \param printable_view are we doing a print view?
+ * \param section Optional for encapsulated message/rfc822 submessage)
+ */
+void read_message(long msgnum, int printable_view, char *section) {
+       char buf[SIZ];
+       char mime_partnum[256];
+       char mime_filename[256];
+       char mime_content_type[256];
+       char mime_charset[256];
+       char mime_disposition[256];
+       int mime_length;
+       char mime_http[SIZ];
+       char mime_submessages[256];
+       char m_subject[256];
+       char m_cc[1024];
+       char from[256];
+       char node[256];
+       char rfca[256];
+       char reply_to[512];
+       char reply_all[4096];
+       char now[64];
+       int format_type = 0;
+       int nhdr = 0;
+       int bq = 0;
+       int i = 0;
+       char vcard_partnum[256];
+       char cal_partnum[256];
+       char *part_source = NULL;
+#ifdef HAVE_ICONV
+       iconv_t ic = (iconv_t)(-1) ;
+       char *ibuf;                /**< Buffer of characters to be converted */
+       char *obuf;                /**< Buffer for converted characters      */
+       size_t ibuflen;    /**< Length of input buffer         */
+       size_t obuflen;    /**< Length of output buffer       */
+       char *osav;                /**< Saved pointer to output buffer       */
+#endif
+
+       strcpy(from, "");
+       strcpy(node, "");
+       strcpy(rfca, "");
+       strcpy(reply_to, "");
+       strcpy(reply_all, "");
+       strcpy(vcard_partnum, "");
+       strcpy(cal_partnum, "");
+       strcpy(mime_http, "");
+       strcpy(mime_content_type, "text/plain");
+       strcpy(mime_charset, "us-ascii");
+       strcpy(mime_submessages, "");
+
+       serv_printf("MSG4 %ld|%s", msgnum, section);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               wprintf("<STRONG>");
+               wprintf(_("ERROR:"));
+               wprintf("</STRONG> %s<br />\n", &buf[4]);
+               return;
+       }
+
+       /** begin everythingamundo table */
+       if (!printable_view) {
+               wprintf("<div class=\"fix_scrollbar_bug\">\n");
+               wprintf("<table width=100%% border=1 cellspacing=0 "
+                       "cellpadding=0><TR><TD>\n");
+       }
+
+       /** begin message header table */
+       wprintf("<table width=100%% border=0 cellspacing=0 "
+               "cellpadding=1 bgcolor=\"#CCCCCC\"><tr><td>\n");
+
+       wprintf("<span class=\"message_header\">");
+       strcpy(m_subject, "");
+       strcpy(m_cc, "");
+
+       while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
+               if (!strcmp(buf, "000")) {
+                       wprintf("<i>");
+                       wprintf(_("unexpected end of message"));
+                       wprintf("</i><br /><br />\n");
+                       wprintf("</span>\n");
+                       return;
+               }
+               if (!strncasecmp(buf, "nhdr=yes", 8))
+                       nhdr = 1;
+               if (nhdr == 1)
+                       buf[0] = '_';
+               if (!strncasecmp(buf, "type=", 5))
+                       format_type = atoi(&buf[5]);
+               if (!strncasecmp(buf, "from=", 5)) {
+                       strcpy(from, &buf[5]);
+                       wprintf(_("from "));
+                       wprintf("<a href=\"showuser?who=");
+#ifdef HAVE_ICONV
+                       utf8ify_rfc822_string(from);
+#endif
+                       urlescputs(from);
+                       wprintf("\">");
+                       escputs(from);
+                       wprintf("</a> ");
+               }
+               if (!strncasecmp(buf, "subj=", 5)) {
+                       safestrncpy(m_subject, &buf[5], sizeof m_subject);
+               }
+               if (!strncasecmp(buf, "cccc=", 5)) {
+                       safestrncpy(m_cc, &buf[5], sizeof m_cc);
+                       if (strlen(reply_all) > 0) {
+                               strcat(reply_all, ", ");
+                       }
+                       safestrncpy(&reply_all[strlen(reply_all)], &buf[5],
+                               (sizeof reply_all - strlen(reply_all)) );
+               }
+               if ((!strncasecmp(buf, "hnod=", 5))
+                   && (strcasecmp(&buf[5], serv_info.serv_humannode))) {
+                       wprintf("(%s) ", &buf[5]);
+               }
+               if ((!strncasecmp(buf, "room=", 5))
+                   && (strcasecmp(&buf[5], WC->wc_roomname))
+                   && (strlen(&buf[5])>0) ) {
+                       wprintf(_("in "));
+                       wprintf("%s&gt; ", &buf[5]);
+               }
+               if (!strncasecmp(buf, "rfca=", 5)) {
+                       strcpy(rfca, &buf[5]);
+                       wprintf("&lt;");
+                       escputs(rfca);
+                       wprintf("&gt; ");
+               }
+
+               if (!strncasecmp(buf, "node=", 5)) {
+                       strcpy(node, &buf[5]);
+                       if ( ((WC->room_flags & QR_NETWORK)
+                       || ((strcasecmp(&buf[5], serv_info.serv_nodename)
+                       && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
+                       && (strlen(rfca)==0)
+                       ) {
+                               wprintf("@%s ", &buf[5]);
+                       }
+               }
+               if (!strncasecmp(buf, "rcpt=", 5)) {
+                       wprintf(_("to "));
+                       if (strlen(reply_all) > 0) {
+                               strcat(reply_all, ", ");
+                       }
+                       safestrncpy(&reply_all[strlen(reply_all)], &buf[5],
+                               (sizeof reply_all - strlen(reply_all)) );
+#ifdef HAVE_ICONV
+                       utf8ify_rfc822_string(&buf[5]);
+#endif
+                       escputs(&buf[5]);
+                       wprintf(" ");
+               }
+               if (!strncasecmp(buf, "time=", 5)) {
+                       fmt_date(now, atol(&buf[5]), 0);
+                       wprintf("%s ", now);
+               }
+
+               if (!strncasecmp(buf, "part=", 5)) {
+                       extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
+                       extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
+                       extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
+                       extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
+                       mime_length = extract_int(&buf[5], 5);
+
+                       if (!strcasecmp(mime_content_type, "message/rfc822")) {
+                               if (strlen(mime_submessages) > 0) {
+                                       strcat(mime_submessages, "|");
+                               }
+                               strcat(mime_submessages, mime_partnum);
+                       }
+                       else if ((!strcasecmp(mime_disposition, "inline"))
+                          && (!strncasecmp(mime_content_type, "image/", 6)) ){
+                               snprintf(&mime_http[strlen(mime_http)],
+                                       (sizeof(mime_http) - strlen(mime_http) - 1),
+                                       "<img src=\"mimepart/%ld/%s/%s\">",
+                                       msgnum, mime_partnum, mime_filename);
+                       }
+                       else if ( (!strcasecmp(mime_disposition, "attachment")) 
+                            || (!strcasecmp(mime_disposition, "inline")) ) {
+                               snprintf(&mime_http[strlen(mime_http)],
+                                       (sizeof(mime_http) - strlen(mime_http) - 1),
+                                       "<img src=\"static/diskette_24x.gif\" "
+                                       "border=0 align=middle>\n"
+                                       "%s (%s, %d bytes) [ "
+                                       "<a href=\"mimepart/%ld/%s/%s\""
+                                       "target=\"wc.%ld.%s\">%s</a>"
+                                       " | "
+                                       "<a href=\"mimepart_download/%ld/%s/%s\">%s</a>"
+                                       " ]<br />\n",
+                                       mime_filename,
+                                       mime_content_type, mime_length,
+                                       msgnum, mime_partnum, mime_filename,
+                                       msgnum, mime_partnum,
+                                       _("View"),
+                                       msgnum, mime_partnum, mime_filename,
+                                       _("Download")
+                               );
+                       }
+
+                       /** begin handler prep ***/
+                       if (!strcasecmp(mime_content_type, "text/x-vcard")) {
+                               strcpy(vcard_partnum, mime_partnum);
+                       }
+
+                       if (!strcasecmp(mime_content_type, "text/calendar")) {
+                               strcpy(cal_partnum, mime_partnum);
+                       }
+
+                       /** end handler prep ***/
+
+               }
+
+       }
+
+       /** Generate a reply-to address */
+       if (strlen(rfca) > 0) {
+               strcpy(reply_to, rfca);
+       }
+       else {
+               if ( (strlen(node) > 0)
+                  && (strcasecmp(node, serv_info.serv_nodename))
+                  && (strcasecmp(node, serv_info.serv_humannode)) ) {
+                       snprintf(reply_to, sizeof(reply_to), "%s @ %s",
+                               from, node);
+               }
+               else {
+                       snprintf(reply_to, sizeof(reply_to), "%s", from);
+               }
+       }
+
+       if (nhdr == 1) {
+               wprintf("****");
+       }
+
+       wprintf("</span>");
+#ifdef HAVE_ICONV
+       utf8ify_rfc822_string(m_cc);
+       utf8ify_rfc822_string(m_subject);
+#endif
+       if (strlen(m_cc) > 0) {
+               wprintf("<br />"
+                       "<span class=\"message_subject\">");
+               wprintf(_("CC:"));
+               wprintf(" ");
+               escputs(m_cc);
+               wprintf("</span>");
+       }
+       if (strlen(m_subject) > 0) {
+               wprintf("<br />"
+                       "<span class=\"message_subject\">");
+               wprintf(_("Subject:"));
+               wprintf(" ");
+               escputs(m_subject);
+               wprintf("</span>");
+       }
+       wprintf("</td>\n");
+
+       /** start msg buttons */
+       if (!printable_view) {
+               wprintf("<td align=right><span class=\"msgbuttons\">\n");
+
+               /** Reply */
+               if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) {
+                       wprintf("<a href=\"display_enter");
+                       if (WC->is_mailbox) {
+                               wprintf("?replyquote=%ld", msgnum);
+                       }
+                       wprintf("?recp=");
+                       urlescputs(reply_to);
+                       if (strlen(m_subject) > 0) {
+                               wprintf("?subject=");
+                               if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
+                               urlescputs(m_subject);
+                       }
+                       wprintf("\">[%s]</a> ", _("Reply"));
+               }
+
+               /** ReplyQuoted */
+               if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) {
+                       if (!WC->is_mailbox) {
+                               wprintf("<a href=\"display_enter");
+                               wprintf("?replyquote=%ld", msgnum);
+                               wprintf("?recp=");
+                               urlescputs(reply_to);
+                               if (strlen(m_subject) > 0) {
+                                       wprintf("?subject=");
+                                       if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
+                                       urlescputs(m_subject);
+                               }
+                               wprintf("\">[%s]</a> ", _("ReplyQuoted"));
+                       }
+               }
+
+               /** ReplyAll */
+               if (WC->wc_view == VIEW_MAILBOX) {
+                       wprintf("<a href=\"display_enter");
+                       wprintf("?replyquote=%ld", msgnum);
+                       wprintf("?recp=");
+                       urlescputs(reply_to);
+                       wprintf("?cc=");
+                       urlescputs(reply_all);
+                       if (strlen(m_subject) > 0) {
+                               wprintf("?subject=");
+                               if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
+                               urlescputs(m_subject);
+                       }
+                       wprintf("\">[%s]</a> ", _("ReplyAll"));
+               }
+
+               /** Forward */
+               if (WC->wc_view == VIEW_MAILBOX) {
+                       wprintf("<a href=\"display_enter?fwdquote=%ld?subject=", msgnum);
+                       if (strncasecmp(m_subject, "Fwd:", 4)) wprintf("Fwd:%20");
+                       urlescputs(m_subject);
+                       wprintf("\">[%s]</a> ", _("Forward"));
+               }
+
+               /** If this is one of my own rooms, or if I'm an Aide or Room Aide, I can move/delete */
+               if ( (WC->is_room_aide) || (WC->is_mailbox) ) {
+                       /** Move */
+                       wprintf("<a href=\"confirm_move_msg?msgid=%ld\">[%s]</a> ",
+                               msgnum, _("Move"));
+       
+                       /** Delete */
+                       wprintf("<a href=\"delete_msg?msgid=%ld\" "
+                               "onClick=\"return confirm('%s');\">"
+                               "[%s]</a> ", msgnum, _("Delete this message?"), _("Delete")
+                       );
+               }
+
+               /** Headers */
+               wprintf("<a href=\"#\" onClick=\"window.open('msgheaders/%ld', 'headers%ld', 'toolbar=no,location=no,directories=no,copyhistory=no,status=yes,scrollbars=yes,resizable=yes,width=600,height=400'); \" >"
+                       "[%s]</a>", msgnum, msgnum, _("Headers"));
+
+
+               /** Print */
+               wprintf("<a href=\"#\" onClick=\"window.open('printmsg/%ld', 'print%ld', 'toolbar=no,location=no,directories=no,copyhistory=no,status=yes,scrollbars=yes,resizable=yes,width=600,height=400'); \" >"
+                       "[%s]</a>", msgnum, msgnum, _("Print"));
+
+               wprintf("</span></td>");
+       }
+
+       wprintf("</tr></table>\n");
+
+       /** Begin body */
+       wprintf("<table border=0 width=100%% bgcolor=\"#FFFFFF\" "
+               "cellpadding=1 cellspacing=0><tr><td>");
+
+       /**
+        * Learn the content type
+        */
+       strcpy(mime_content_type, "text/plain");
+       while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) {
+               if (!strcmp(buf, "000")) {
+                       wprintf("<i>");
+                       wprintf(_("unexpected end of message"));
+                       wprintf("</i><br /><br />\n");
+                       goto ENDBODY;
+               }
+               if (!strncasecmp(buf, "Content-type: ", 14)) {
+                       safestrncpy(mime_content_type, &buf[14],
+                               sizeof(mime_content_type));
+                       for (i=0; i<strlen(mime_content_type); ++i) {
+                               if (!strncasecmp(&mime_content_type[i], "charset=", 8)) {
+                                       safestrncpy(mime_charset, &mime_content_type[i+8],
+                                               sizeof mime_charset);
+                               }
+                       }
+                       for (i=0; i<strlen(mime_content_type); ++i) {
+                               if (mime_content_type[i] == ';') {
+                                       mime_content_type[i] = 0;
+                               }
+                       }
+                       for (i=0; i<strlen(mime_charset); ++i) {
+                               if (mime_charset[i] == ';') {
+                                       mime_charset[i] = 0;
+                               }
+                       }
+               }
+       }
+
+       /** Set up a character set conversion if we need to (and if we can) */
+#ifdef HAVE_ICONV
+       if (strchr(mime_charset, ';')) strcpy(strchr(mime_charset, ';'), "");
+       if ( (strcasecmp(mime_charset, "us-ascii"))
+          && (strcasecmp(mime_charset, "UTF-8"))
+          && (strcasecmp(mime_charset, ""))
+       ) {
+               ic = ctdl_iconv_open("UTF-8", mime_charset);
+               if (ic == (iconv_t)(-1) ) {
+                       lprintf(5, "%s:%d iconv_open(UTF-8, %s) failed: %s\n",
+                               __FILE__, __LINE__, mime_charset, strerror(errno));
+               }
+       }
+#endif
+
+       /** Messages in legacy Citadel variformat get handled thusly... */
+       if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
+               fmout("JUSTIFY");
+       }
+
+       /** Boring old 80-column fixed format text gets handled this way... */
+       else if ( (!strcasecmp(mime_content_type, "text/plain"))
+               || (!strcasecmp(mime_content_type, "text")) ) {
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
+                       if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
+
+#ifdef HAVE_ICONV
+                       if (ic != (iconv_t)(-1) ) {
+                               ibuf = buf;
+                               ibuflen = strlen(ibuf);
+                               obuflen = SIZ;
+                               obuf = (char *) malloc(obuflen);
+                               osav = obuf;
+                               iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
+                               osav[SIZ-obuflen] = 0;
+                               safestrncpy(buf, osav, sizeof buf);
+                               free(osav);
+                       }
+#endif
+
+                       while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
+                               buf[strlen(buf) - 1] = 0;
+                       if ((bq == 0) &&
+                       ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) {
+                               wprintf("<blockquote>");
+                               bq = 1;
+                       } else if ((bq == 1) &&
+                               (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) {
+                               wprintf("</blockquote>");
+                               bq = 0;
+                       }
+                       wprintf("<tt>");
+                       url(buf);
+                       escputs(buf);
+                       wprintf("</tt><br />\n");
+               }
+               wprintf("</i><br />");
+       }
+
+       else /** HTML is fun, but we've got to strip it first */
+       if (!strcasecmp(mime_content_type, "text/html")) {
+               output_html(mime_charset, (WC->wc_view == VIEW_WIKI ? 1 : 0));
+       }
+
+       /** Unknown weirdness */
+       else {
+               wprintf(_("I don't know how to display %s"), mime_content_type);
+               wprintf("<br />\n", mime_content_type);
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { }
+       }
+
+       /** If there are attached submessages, display them now... */
+       if ( (strlen(mime_submessages) > 0) && (!section[0]) ) {
+               for (i=0; i<num_tokens(mime_submessages, '|'); ++i) {
+                       extract_token(buf, mime_submessages, i, '|', sizeof buf);
+                       /** use printable_view to suppress buttons */
+                       wprintf("<blockquote>");
+                       read_message(msgnum, 1, buf);
+                       wprintf("</blockquote>");
+               }
+       }
+
+
+       /** Afterwards, offer links to download attachments 'n' such */
+       if ( (strlen(mime_http) > 0) && (!section[0]) ) {
+               wprintf("%s", mime_http);
+       }
+
+       /** Handler for vCard parts */
+       if (strlen(vcard_partnum) > 0) {
+               part_source = load_mimepart(msgnum, vcard_partnum);
+               if (part_source != NULL) {
+
+                       /** If it's my vCard I can edit it */
+                       if (    (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
+                               || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
+                               || (WC->wc_view == VIEW_ADDRESSBOOK)
+                       ) {
+                               wprintf("<a href=\"edit_vcard?"
+                                       "msgnum=%ld?partnum=%s\">",
+                                       msgnum, vcard_partnum);
+                               wprintf("[%s]</a>", _("edit"));
+                       }
+
+                       /** In all cases, display the full card */
+                       display_vcard(part_source, 0, 1, NULL);
+               }
+       }
+
+       /** Handler for calendar parts */
+       if (strlen(cal_partnum) > 0) {
+               part_source = load_mimepart(msgnum, cal_partnum);
+               if (part_source != NULL) {
+                       cal_process_attachment(part_source,
+                                               msgnum, cal_partnum);
+               }
+       }
+
+       if (part_source) {
+               free(part_source);
+               part_source = NULL;
+       }
+
+ENDBODY:
+       wprintf("</td></tr></table>\n");
+
+       /** end everythingamundo table */
+       if (!printable_view) {
+               wprintf("</td></tr></table>\n");
+               wprintf("</div><br />\n");
+       }
+
+#ifdef HAVE_ICONV
+       if (ic != (iconv_t)(-1) ) {
+               iconv_close(ic);
+       }
+#endif
+}
+
+
+
+/**
+ * \brief Unadorned HTML output of an individual message, suitable
+ * for placing in a hidden iframe, for printing, or whatever
+ *
+ * \param msgnum_as_string Message number, as a string instead of as a long int
+ */
+void embed_message(char *msgnum_as_string) {
+       long msgnum = 0L;
+
+       msgnum = atol(msgnum_as_string);
+       begin_ajax_response();
+       read_message(msgnum, 0, "");
+       end_ajax_response();
+}
+
+
+/**
+ * \brief Printable view of a message
+ *
+ * \param msgnum_as_string Message number, as a string instead of as a long int
+ */
+void print_message(char *msgnum_as_string) {
+       long msgnum = 0L;
+
+       msgnum = atol(msgnum_as_string);
+       output_headers(0, 0, 0, 0, 0, 0);
+
+       wprintf("Content-type: text/html\r\n"
+               "Server: %s\r\n"
+               "Connection: close\r\n",
+               SERVER);
+       begin_burst();
+
+       wprintf("\r\n\r\n<html>\n"
+               "<head><title>Printable view</title></head>\n"
+               "<body onLoad=\" window.print(); window.close(); \">\n"
+       );
+       
+       read_message(msgnum, 1, "");
+
+       wprintf("\n</body></html>\n\n");
+       wDumpContent(0);
+}
+
+
+
+/**
+ * \brief Display a message's headers
+ *
+ * \param msgnum_as_string Message number, as a string instead of as a long int
+ */
+void display_headers(char *msgnum_as_string) {
+       long msgnum = 0L;
+       char buf[1024];
+
+       msgnum = atol(msgnum_as_string);
+       output_headers(0, 0, 0, 0, 0, 0);
+
+       wprintf("Content-type: text/plain\r\n"
+               "Server: %s\r\n"
+               "Connection: close\r\n",
+               SERVER);
+       begin_burst();
+
+       serv_printf("MSG2 %ld|3", msgnum);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       wprintf("%s\n", buf);
+               }
+       }
+
+       wDumpContent(0);
+}
+
+
+
+/**
+ * \brief Read message in simple, JavaScript-embeddable form for 'forward'
+ *        or 'reply quoted' operations.
+ *
+ * NOTE: it is VITALLY IMPORTANT that we output no single-quotes or linebreaks
+ *       in this function.  Doing so would throw a JavaScript error in the
+ *       'supplied text' argument to the editor.
+ *
+ * \param msgnum Message number of the message we want to quote
+ * \param forward_attachments Nonzero if we want attachments to be forwarded
+ */
+void pullquote_message(long msgnum, int forward_attachments, int include_headers) {
+       char buf[SIZ];
+       char mime_partnum[256];
+       char mime_filename[256];
+       char mime_content_type[256];
+       char mime_charset[256];
+       char mime_disposition[256];
+       int mime_length;
+       char *attachments = NULL;
+       char *ptr = NULL;
+       int num_attachments = 0;
+       struct wc_attachment *att, *aptr;
+       char m_subject[256];
+       char from[256];
+       char node[256];
+       char rfca[256];
+       char reply_to[512];
+       char now[256];
+       int format_type = 0;
+       int nhdr = 0;
+       int bq = 0;
+       int i = 0;
+#ifdef HAVE_ICONV
+       iconv_t ic = (iconv_t)(-1) ;
+       char *ibuf;                /**< Buffer of characters to be converted */
+       char *obuf;                /**< Buffer for converted characters      */
+       size_t ibuflen;    /**< Length of input buffer         */
+       size_t obuflen;    /**< Length of output buffer       */
+       char *osav;                /**< Saved pointer to output buffer       */
+#endif
+
+       strcpy(from, "");
+       strcpy(node, "");
+       strcpy(rfca, "");
+       strcpy(reply_to, "");
+       strcpy(mime_content_type, "text/plain");
+       strcpy(mime_charset, "us-ascii");
+
+       serv_printf("MSG4 %ld", msgnum);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               wprintf(_("ERROR:"));
+               wprintf("%s<br />", &buf[4]);
+               return;
+       }
+
+       strcpy(m_subject, "");
+
+       while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
+               if (!strcmp(buf, "000")) {
+                       wprintf(_("unexpected end of message"));
+                       return;
+               }
+               if (include_headers) {
+                       if (!strncasecmp(buf, "nhdr=yes", 8))
+                               nhdr = 1;
+                       if (nhdr == 1)
+                               buf[0] = '_';
+                       if (!strncasecmp(buf, "type=", 5))
+                               format_type = atoi(&buf[5]);
+                       if (!strncasecmp(buf, "from=", 5)) {
+                               strcpy(from, &buf[5]);
+                               wprintf(_("from "));
+#ifdef HAVE_ICONV
+                               utf8ify_rfc822_string(from);
+#endif
+                               msgescputs(from);
+                       }
+                       if (!strncasecmp(buf, "subj=", 5)) {
+                               strcpy(m_subject, &buf[5]);
+                       }
+                       if ((!strncasecmp(buf, "hnod=", 5))
+                           && (strcasecmp(&buf[5], serv_info.serv_humannode))) {
+                               wprintf("(%s) ", &buf[5]);
+                       }
+                       if ((!strncasecmp(buf, "room=", 5))
+                           && (strcasecmp(&buf[5], WC->wc_roomname))
+                           && (strlen(&buf[5])>0) ) {
+                               wprintf(_("in "));
+                               wprintf("%s&gt; ", &buf[5]);
+                       }
+                       if (!strncasecmp(buf, "rfca=", 5)) {
+                               strcpy(rfca, &buf[5]);
+                               wprintf("&lt;");
+                               msgescputs(rfca);
+                               wprintf("&gt; ");
+                       }
+       
+                       if (!strncasecmp(buf, "node=", 5)) {
+                               strcpy(node, &buf[5]);
+                               if ( ((WC->room_flags & QR_NETWORK)
+                               || ((strcasecmp(&buf[5], serv_info.serv_nodename)
+                               && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
+                               && (strlen(rfca)==0)
+                               ) {
+                                       wprintf("@%s ", &buf[5]);
+                               }
+                       }
+                       if (!strncasecmp(buf, "rcpt=", 5)) {
+                               wprintf(_("to "));
+                               wprintf("%s ", &buf[5]);
+                       }
+                       if (!strncasecmp(buf, "time=", 5)) {
+                               fmt_date(now, atol(&buf[5]), 0);
+                               wprintf("%s ", now);
+                       }
+               }
+
+               /**
+                * Save attachment info for later.  We can't start downloading them
+                * yet because we're in the middle of a server transaction.
+                */
+               if (!strncasecmp(buf, "part=", 5)) {
+                       ptr = malloc( (strlen(buf) + ((attachments != NULL) ? strlen(attachments) : 0)) ) ;
+                       if (ptr != NULL) {
+                               ++num_attachments;
+                               sprintf(ptr, "%s%s\n",
+                                       ((attachments != NULL) ? attachments : ""),
+                                       &buf[5]
+                               );
+                               free(attachments);
+                               attachments = ptr;
+                               lprintf(9, "attachments=<%s>\n", attachments);
+                       }
+               }
+
+       }
+
+       if (include_headers) {
+               wprintf("<br>");
+
+#ifdef HAVE_ICONV
+               utf8ify_rfc822_string(m_subject);
+#endif
+               if (strlen(m_subject) > 0) {
+                       wprintf(_("Subject:"));
+                       wprintf(" ");
+                       msgescputs(m_subject);
+                       wprintf("<br />");
+               }
+
+               /**
+                * Begin body
+                */
+               wprintf("<br />");
+       }
+
+       /**
+        * Learn the content type
+        */
+       strcpy(mime_content_type, "text/plain");
+       while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) {
+               if (!strcmp(buf, "000")) {
+                       wprintf(_("unexpected end of message"));
+                       goto ENDBODY;
+               }
+               if (!strncasecmp(buf, "Content-type: ", 14)) {
+                       safestrncpy(mime_content_type, &buf[14],
+                               sizeof(mime_content_type));
+                       for (i=0; i<strlen(mime_content_type); ++i) {
+                               if (!strncasecmp(&mime_content_type[i], "charset=", 8)) {
+                                       safestrncpy(mime_charset, &mime_content_type[i+8],
+                                               sizeof mime_charset);
+                               }
+                       }
+                       for (i=0; i<strlen(mime_content_type); ++i) {
+                               if (mime_content_type[i] == ';') {
+                                       mime_content_type[i] = 0;
+                               }
+                       }
+                       for (i=0; i<strlen(mime_charset); ++i) {
+                               if (mime_charset[i] == ';') {
+                                       mime_charset[i] = 0;
+                               }
+                       }
+               }
+       }
+
+       /** Set up a character set conversion if we need to (and if we can) */
+#ifdef HAVE_ICONV
+       if ( (strcasecmp(mime_charset, "us-ascii"))
+          && (strcasecmp(mime_charset, "UTF-8"))
+          && (strcasecmp(mime_charset, ""))
+       ) {
+               ic = ctdl_iconv_open("UTF-8", mime_charset);
+               if (ic == (iconv_t)(-1) ) {
+                       lprintf(5, "%s:%d iconv_open(%s, %s) failed: %s\n",
+                               __FILE__, __LINE__, "UTF-8", mime_charset, strerror(errno));
+               }
+       }
+#endif
+
+       /** Messages in legacy Citadel variformat get handled thusly... */
+       if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
+               pullquote_fmout();
+       }
+
+       /* Boring old 80-column fixed format text gets handled this way... */
+       else if (!strcasecmp(mime_content_type, "text/plain")) {
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
+                       if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
+
+#ifdef HAVE_ICONV
+                       if (ic != (iconv_t)(-1) ) {
+                               ibuf = buf;
+                               ibuflen = strlen(ibuf);
+                               obuflen = SIZ;
+                               obuf = (char *) malloc(obuflen);
+                               osav = obuf;
+                               iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
+                               osav[SIZ-obuflen] = 0;
+                               safestrncpy(buf, osav, sizeof buf);
+                               free(osav);
+                       }
+#endif
+
+                       while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
+                               buf[strlen(buf) - 1] = 0;
+                       if ((bq == 0) &&
+                       ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) {
+                               wprintf("<blockquote>");
+                               bq = 1;
+                       } else if ((bq == 1) &&
+                               (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) {
+                               wprintf("</blockquote>");
+                               bq = 0;
+                       }
+                       wprintf("<tt>");
+                       url(buf);
+                       msgescputs(buf);
+                       wprintf("</tt><br />");
+               }
+               wprintf("</i><br />");
+       }
+
+       /** HTML just gets escaped and stuffed back into the editor */
+       else if (!strcasecmp(mime_content_type, "text/html")) {
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       strcat(buf, "\n");
+                       msgescputs(buf);
+               }
+       }
+
+       /** Unknown weirdness ... don't know how to handle this content type */
+       else {
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { }
+       }
+
+ENDBODY:
+       /** end of body handler */
+
+       /*
+        * If there were attachments, we have to download them and insert them
+        * into the attachment chain for the forwarded message we are composing.
+        */
+       if ( (forward_attachments) && (num_attachments) ) {
+               for (i=0; i<num_attachments; ++i) {
+                       extract_token(buf, attachments, i, '\n', sizeof buf);
+                       extract_token(mime_filename, buf, 1, '|', sizeof mime_filename);
+                       extract_token(mime_partnum, buf, 2, '|', sizeof mime_partnum);
+                       extract_token(mime_disposition, buf, 3, '|', sizeof mime_disposition);
+                       extract_token(mime_content_type, buf, 4, '|', sizeof mime_content_type);
+                       mime_length = extract_int(buf, 5);
+
+                       /*
+                        * tracing  ... uncomment if necessary
+                        *
+                        */
+                       lprintf(9, "fwd filename: %s\n", mime_filename);
+                       lprintf(9, "fwd partnum : %s\n", mime_partnum);
+                       lprintf(9, "fwd conttype: %s\n", mime_content_type);
+                       lprintf(9, "fwd dispose : %s\n", mime_disposition);
+                       lprintf(9, "fwd length  : %d\n", mime_length);
+
+                       if ( (!strcasecmp(mime_disposition, "inline"))
+                          || (!strcasecmp(mime_disposition, "attachment")) ) {
+               
+                               /* Create an attachment struct from this mime part... */
+                               att = malloc(sizeof(struct wc_attachment));
+                               memset(att, 0, sizeof(struct wc_attachment));
+                               att->length = mime_length;
+                               strcpy(att->content_type, mime_content_type);
+                               strcpy(att->filename, mime_filename);
+                               att->next = NULL;
+                               att->data = load_mimepart(msgnum, mime_partnum);
+               
+                               /* And add it to the list. */
+                               if (WC->first_attachment == NULL) {
+                                       WC->first_attachment = att;
+                               }
+                               else {
+                                       aptr = WC->first_attachment;
+                                       while (aptr->next != NULL) aptr = aptr->next;
+                                       aptr->next = att;
+                               }
+                       }
+
+               }
+       }
+
+#ifdef HAVE_ICONV
+       if (ic != (iconv_t)(-1) ) {
+               iconv_close(ic);
+       }
+#endif
+
+       if (attachments != NULL) {
+               free(attachments);
+       }
+}
+
+/**
+ * \brief Display one row in the mailbox summary view
+ *
+ * \param num The row number to be displayed
+ */
+void display_summarized(int num) {
+       char datebuf[64];
+
+       wprintf("<tr id=\"m%ld\" style=\"width:100%%;font-weight:%s;background-color:#ffffff\" "
+               "onMouseDown=\"CtdlMoveMsgMouseDown(event,%ld)\">",
+               WC->summ[num].msgnum,
+               (WC->summ[num].is_new ? "bold" : "normal"),
+               WC->summ[num].msgnum
+       );
+
+       wprintf("<td width=%d%%>", SUBJ_COL_WIDTH_PCT);
+       escputs(WC->summ[num].subj);
+       wprintf("</td>");
+
+       wprintf("<td width=%d%%>", SENDER_COL_WIDTH_PCT);
+       escputs(WC->summ[num].from);
+       wprintf("</td>");
+
+       wprintf("<td width=%d%%>", DATE_PLUS_BUTTONS_WIDTH_PCT);
+       fmt_date(datebuf, WC->summ[num].date, 1);       /* brief */
+       escputs(datebuf);
+       wprintf("</td>");
+
+       wprintf("</tr>\n");
+}
+
+
+
+/**
+ * \brief display the adressbook overview
+ * \param msgnum the citadel message number
+ * \param alpha what????
+ */
+void display_addressbook(long msgnum, char alpha) {
+       char buf[SIZ];
+       char mime_partnum[SIZ];
+       char mime_filename[SIZ];
+       char mime_content_type[SIZ];
+       char mime_disposition[SIZ];
+       int mime_length;
+       char vcard_partnum[SIZ];
+       char *vcard_source = NULL;
+       struct message_summary summ;
+
+       memset(&summ, 0, sizeof(summ));
+       safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj);
+
+       sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
+       serv_puts(buf);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') return;
+
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               if (!strncasecmp(buf, "part=", 5)) {
+                       extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
+                       extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
+                       extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
+                       extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
+                       mime_length = extract_int(&buf[5], 5);
+
+                       if (!strcasecmp(mime_content_type, "text/x-vcard")) {
+                               strcpy(vcard_partnum, mime_partnum);
+                       }
+
+               }
+       }
+
+       if (strlen(vcard_partnum) > 0) {
+               vcard_source = load_mimepart(msgnum, vcard_partnum);
+               if (vcard_source != NULL) {
+
+                       /** Display the summary line */
+                       display_vcard(vcard_source, alpha, 0, NULL);
+
+                       /** If it's my vCard I can edit it */
+                       if (    (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
+                               || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
+                               || (WC->wc_view == VIEW_ADDRESSBOOK)
+                       ) {
+                               wprintf("<a href=\"edit_vcard?"
+                                       "msgnum=%ld?partnum=%s\">",
+                                       msgnum, vcard_partnum);
+                               wprintf("[%s]</a>", _("edit"));
+                       }
+
+                       free(vcard_source);
+               }
+       }
+
+}
+
+
+
+/**
+ * \brief  If it's an old "Firstname Lastname" style record, try to convert it.
+ * \param namebuf name to analyze, reverse if nescessary
+ */
+void lastfirst_firstlast(char *namebuf) {
+       char firstname[SIZ];
+       char lastname[SIZ];
+       int i;
+
+       if (namebuf == NULL) return;
+       if (strchr(namebuf, ';') != NULL) return;
+
+       i = num_tokens(namebuf, ' ');
+       if (i < 2) return;
+
+       extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
+       remove_token(namebuf, i-1, ' ');
+       strcpy(firstname, namebuf);
+       sprintf(namebuf, "%s; %s", lastname, firstname);
+}
+
+/**
+ * \brief fetch what??? name
+ * \param msgnum the citadel message number
+ * \param namebuf where to put the name in???
+ */
+void fetch_ab_name(long msgnum, char *namebuf) {
+       char buf[SIZ];
+       char mime_partnum[SIZ];
+       char mime_filename[SIZ];
+       char mime_content_type[SIZ];
+       char mime_disposition[SIZ];
+       int mime_length;
+       char vcard_partnum[SIZ];
+       char *vcard_source = NULL;
+       int i;
+       struct message_summary summ;
+
+       if (namebuf == NULL) return;
+       strcpy(namebuf, "");
+
+       memset(&summ, 0, sizeof(summ));
+       safestrncpy(summ.subj, "(no subject)", sizeof summ.subj);
+
+       sprintf(buf, "MSG0 %ld|1", msgnum);     /** ask for headers only */
+       serv_puts(buf);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') return;
+
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               if (!strncasecmp(buf, "part=", 5)) {
+                       extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
+                       extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
+                       extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
+                       extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
+                       mime_length = extract_int(&buf[5], 5);
+
+                       if (!strcasecmp(mime_content_type, "text/x-vcard")) {
+                               strcpy(vcard_partnum, mime_partnum);
+                       }
+
+               }
+       }
+
+       if (strlen(vcard_partnum) > 0) {
+               vcard_source = load_mimepart(msgnum, vcard_partnum);
+               if (vcard_source != NULL) {
+
+                       /* Grab the name off the card */
+                       display_vcard(vcard_source, 0, 0, namebuf);
+
+                       free(vcard_source);
+               }
+       }
+
+       lastfirst_firstlast(namebuf);
+       striplt(namebuf);
+       for (i=0; i<strlen(namebuf); ++i) {
+               if (namebuf[i] != ';') return;
+       }
+       strcpy(namebuf, _("(no name)"));
+}
+
+
+
+/**
+ * \brief Record compare function for sorting address book indices
+ * \param ab1 adressbook one
+ * \param ab2 adressbook two
+ */
+int abcmp(const void *ab1, const void *ab2) {
+       return(strcasecmp(
+               (((const struct addrbookent *)ab1)->ab_name),
+               (((const struct addrbookent *)ab2)->ab_name)
+       ));
+}
+
+
+/**
+ * \brief Helper function for do_addrbook_view()
+ * Converts a name into a three-letter tab label
+ * \param tabbuf the tabbuffer to add name to
+ * \param name the name to add to the tabbuffer
+ */
+void nametab(char *tabbuf, char *name) {
+       stresc(tabbuf, name, 0, 0);
+       tabbuf[0] = toupper(tabbuf[0]);
+       tabbuf[1] = tolower(tabbuf[1]);
+       tabbuf[2] = tolower(tabbuf[2]);
+       tabbuf[3] = 0;
+}
+
+
+/**
+ * \brief Render the address book using info we gathered during the scan
+ * \param addrbook the addressbook to render
+ * \param num_ab the number of the addressbook
+ */
+void do_addrbook_view(struct addrbookent *addrbook, int num_ab) {
+       int i = 0;
+       int displayed = 0;
+       int bg = 0;
+       static int NAMESPERPAGE = 60;
+       int num_pages = 0;
+       int page = 0;
+       int tabfirst = 0;
+       char tabfirst_label[SIZ];
+       int tablast = 0;
+       char tablast_label[SIZ];
+
+       if (num_ab == 0) {
+               wprintf("<br /><br /><br /><div align=\"center\"><i>");
+               wprintf(_("This address book is empty."));
+               wprintf("</i></div>\n");
+               return;
+       }
+
+       if (num_ab > 1) {
+               qsort(addrbook, num_ab, sizeof(struct addrbookent), abcmp);
+       }
+
+       num_pages = num_ab / NAMESPERPAGE;
+
+       page = atoi(bstr("page"));
+
+       wprintf("Page: ");
+       for (i=0; i<=num_pages; ++i) {
+               if (i != page) {
+                       wprintf("<a href=\"readfwd?page=%d\">", i);
+               }
+               else {
+                       wprintf("<B>");
+               }
+               tabfirst = i * NAMESPERPAGE;
+               tablast = tabfirst + NAMESPERPAGE - 1;
+               if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
+               nametab(tabfirst_label, addrbook[tabfirst].ab_name);
+               nametab(tablast_label, addrbook[tablast].ab_name);
+               wprintf("[%s&nbsp;-&nbsp;%s]",
+                       tabfirst_label, tablast_label
+               );
+               if (i != page) {
+                       wprintf("</A>\n");
+               }
+               else {
+                       wprintf("</B>\n");
+               }
+       }
+       wprintf("<br />\n");
+
+       wprintf("<table border=0 cellspacing=0 "
+               "cellpadding=3 width=100%%>\n"
+       );
+
+       for (i=0; i<num_ab; ++i) {
+
+               if ((i / NAMESPERPAGE) == page) {
+
+                       if ((displayed % 4) == 0) {
+                               if (displayed > 0) {
+                                       wprintf("</tr>\n");
+                               }
+                               bg = 1 - bg;
+                               wprintf("<tr bgcolor=\"#%s\">",
+                                       (bg ? "DDDDDD" : "FFFFFF")
+                               );
+                       }
+       
+                       wprintf("<td>");
+       
+                       wprintf("<a href=\"readfwd?startmsg=%ld&is_singlecard=1",
+                               addrbook[i].ab_msgnum);
+                       wprintf("?maxmsgs=1?summary=0?alpha=%s\">", bstr("alpha"));
+                       vcard_n_prettyize(addrbook[i].ab_name);
+                       escputs(addrbook[i].ab_name);
+                       wprintf("</a></td>\n");
+                       ++displayed;
+               }
+       }
+
+       wprintf("</tr></table>\n");
+}
+
+
+
+/**
+ * \brief load message pointers from the server
+ * \param servcmd the citadel command to send to the citserver
+ * \param with_headers what headers???
+ */
+int load_msg_ptrs(char *servcmd, int with_headers)
+{
+       char buf[1024];
+       time_t datestamp;
+       char fullname[128];
+       char nodename[128];
+       char inetaddr[128];
+       char subject[256];
+       int nummsgs;
+       int maxload = 0;
+
+       int num_summ_alloc = 0;
+
+       if (WC->summ != NULL) {
+               free(WC->summ);
+               WC->num_summ = 0;
+               WC->summ = NULL;
+       }
+       num_summ_alloc = 100;
+       WC->num_summ = 0;
+       WC->summ = malloc(num_summ_alloc * sizeof(struct message_summary));
+
+       nummsgs = 0;
+       maxload = sizeof(WC->msgarr) / sizeof(long) ;
+       serv_puts(servcmd);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               return (nummsgs);
+       }
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               if (nummsgs < maxload) {
+                       WC->msgarr[nummsgs] = extract_long(buf, 0);
+                       datestamp = extract_long(buf, 1);
+                       extract_token(fullname, buf, 2, '|', sizeof fullname);
+                       extract_token(nodename, buf, 3, '|', sizeof nodename);
+                       extract_token(inetaddr, buf, 4, '|', sizeof inetaddr);
+                       extract_token(subject, buf, 5, '|', sizeof subject);
+                       ++nummsgs;
+
+                       if (with_headers) {
+                               if (nummsgs > num_summ_alloc) {
+                                       num_summ_alloc *= 2;
+                                       WC->summ = realloc(WC->summ,
+                                               num_summ_alloc * sizeof(struct message_summary));
+                               }
+                               ++WC->num_summ;
+
+                               memset(&WC->summ[nummsgs-1], 0, sizeof(struct message_summary));
+                               WC->summ[nummsgs-1].msgnum = WC->msgarr[nummsgs-1];
+                               safestrncpy(WC->summ[nummsgs-1].subj,
+                                       _("(no subject)"), sizeof WC->summ[nummsgs-1].subj);
+                               if (strlen(fullname) > 0) {
+                                       safestrncpy(WC->summ[nummsgs-1].from,
+                                               fullname, sizeof WC->summ[nummsgs-1].from);
+                               }
+                               if (strlen(subject) > 0) {
+                               safestrncpy(WC->summ[nummsgs-1].subj, subject,
+                                       sizeof WC->summ[nummsgs-1].subj);
+                               }
+#ifdef HAVE_ICONV
+                               /** Handle subjects with RFC2047 encoding */
+                               utf8ify_rfc822_string(WC->summ[nummsgs-1].subj);
+#endif
+                               if (strlen(WC->summ[nummsgs-1].subj) > 75) {
+                                       strcpy(&WC->summ[nummsgs-1].subj[72], "...");
+                               }
+
+                               if (strlen(nodename) > 0) {
+                                       if ( ((WC->room_flags & QR_NETWORK)
+                                          || ((strcasecmp(nodename, serv_info.serv_nodename)
+                                          && (strcasecmp(nodename, serv_info.serv_fqdn)))))
+                                       ) {
+                                               strcat(WC->summ[nummsgs-1].from, " @ ");
+                                               strcat(WC->summ[nummsgs-1].from, nodename);
+                                       }
+                               }
+
+                               WC->summ[nummsgs-1].date = datestamp;
+       
+#ifdef HAVE_ICONV
+                               /** Handle senders with RFC2047 encoding */
+                               utf8ify_rfc822_string(WC->summ[nummsgs-1].from);
+#endif
+                               if (strlen(WC->summ[nummsgs-1].from) > 25) {
+                                       strcpy(&WC->summ[nummsgs-1].from[22], "...");
+                               }
+                       }
+               }
+       }
+       return (nummsgs);
+}
+
+/**
+ * \brief qsort() compatible function to compare two longs in descending order.
+ *
+ * \param s1 first number to compare 
+ * \param s2 second number to compare
+ */
+int longcmp_r(const void *s1, const void *s2) {
+       long l1;
+       long l2;
+
+       l1 = *(long *)s1;
+       l2 = *(long *)s2;
+
+       if (l1 > l2) return(-1);
+       if (l1 < l2) return(+1);
+       return(0);
+}
+
+/**
+ * \brief qsort() compatible function to compare two message summary structs by ascending subject.
+ *
+ * \param s1 first item to compare 
+ * \param s2 second item to compare
+ */
+int summcmp_subj(const void *s1, const void *s2) {
+       struct message_summary *summ1;
+       struct message_summary *summ2;
+       
+       summ1 = (struct message_summary *)s1;
+       summ2 = (struct message_summary *)s2;
+       return strcasecmp(summ1->subj, summ2->subj);
+}
+
+/**
+ * \brief qsort() compatible function to compare two message summary structs by descending subject.
+ *
+ * \param s1 first item to compare 
+ * \param s2 second item to compare
+ */
+int summcmp_rsubj(const void *s1, const void *s2) {
+       struct message_summary *summ1;
+       struct message_summary *summ2;
+       
+       summ1 = (struct message_summary *)s1;
+       summ2 = (struct message_summary *)s2;
+       return strcasecmp(summ2->subj, summ1->subj);
+}
+
+/**
+ * \brief qsort() compatible function to compare two message summary structs by ascending sender.
+ *
+ * \param s1 first item to compare 
+ * \param s2 second item to compare
+ */
+int summcmp_sender(const void *s1, const void *s2) {
+       struct message_summary *summ1;
+       struct message_summary *summ2;
+       
+       summ1 = (struct message_summary *)s1;
+       summ2 = (struct message_summary *)s2;
+       return strcasecmp(summ1->from, summ2->from);
+}
+
+/**
+ * \brief qsort() compatible function to compare two message summary structs by descending sender.
+ *
+ * \param s1 first item to compare 
+ * \param s2 second item to compare
+ */
+int summcmp_rsender(const void *s1, const void *s2) {
+       struct message_summary *summ1;
+       struct message_summary *summ2;
+       
+       summ1 = (struct message_summary *)s1;
+       summ2 = (struct message_summary *)s2;
+       return strcasecmp(summ2->from, summ1->from);
+}
+
+/**
+ * \brief qsort() compatible function to compare two message summary structs by ascending date.
+ *
+ * \param s1 first item to compare 
+ * \param s2 second item to compare
+ */
+int summcmp_date(const void *s1, const void *s2) {
+       struct message_summary *summ1;
+       struct message_summary *summ2;
+       
+       summ1 = (struct message_summary *)s1;
+       summ2 = (struct message_summary *)s2;
+
+       if (summ1->date < summ2->date) return -1;
+       else if (summ1->date > summ2->date) return +1;
+       else return 0;
+}
+
+/**
+ * \brief qsort() compatible function to compare two message summary structs by descending date.
+ *
+ * \param s1 first item to compare 
+ * \param s2 second item to compare
+ */
+int summcmp_rdate(const void *s1, const void *s2) {
+       struct message_summary *summ1;
+       struct message_summary *summ2;
+       
+       summ1 = (struct message_summary *)s1;
+       summ2 = (struct message_summary *)s2;
+
+       if (summ1->date < summ2->date) return +1;
+       else if (summ1->date > summ2->date) return -1;
+       else return 0;
+}
+
+
+
+/**
+ * \brief command loop for reading messages
+ *
+ * \param oper Set to "readnew" or "readold" or "readfwd" or "headers"
+ */
+void readloop(char *oper)
+{
+       char cmd[SIZ];
+       char buf[SIZ];
+       char old_msgs[SIZ];
+       int a, b;
+       int nummsgs;
+       long startmsg;
+       int maxmsgs;
+       long *displayed_msgs = NULL;
+       int num_displayed = 0;
+       int is_summary = 0;
+       int is_addressbook = 0;
+       int is_singlecard = 0;
+       int is_calendar = 0;
+       int is_tasks = 0;
+       int is_notes = 0;
+       int is_bbview = 0;
+       int lo, hi;
+       int lowest_displayed = (-1);
+       int highest_displayed = 0;
+       struct addrbookent *addrbook = NULL;
+       int num_ab = 0;
+       char *sortby = NULL;
+       char sortpref_name[128];
+       char sortpref_value[128];
+       char *subjsort_button;
+       char *sendsort_button;
+       char *datesort_button;
+       int bbs_reverse = 0;
+
+       if (WC->wc_view == VIEW_WIKI) {
+               sprintf(buf, "wiki?room=%s?page=home", WC->wc_roomname);
+               http_redirect(buf);
+               return;
+       }
+
+       startmsg = atol(bstr("startmsg"));
+       maxmsgs = atoi(bstr("maxmsgs"));
+       is_summary = atoi(bstr("summary"));
+       if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS;
+
+       snprintf(sortpref_name, sizeof sortpref_name, "sort %s", WC->wc_roomname);
+       get_preference(sortpref_name, sortpref_value, sizeof sortpref_value);
+
+       sortby = bstr("sortby");
+       if ( (strlen(sortby) > 0) && (strcasecmp(sortby, sortpref_value)) ) {
+               set_preference(sortpref_name, sortby, 1);
+       }
+       if (strlen(sortby) == 0) sortby = sortpref_value;
+
+       /** mailbox sort */
+       if (strlen(sortby) == 0) sortby = "rdate";
+
+       /** message board sort */
+       if (!strcasecmp(sortby, "reverse")) {
+               bbs_reverse = 1;
+       }
+       else {
+               bbs_reverse = 0;
+       }
+
+       output_headers(1, 1, 1, 0, 0, 0);
+
+       /**
+        * When in summary mode, always show ALL messages instead of just
+        * new or old.  Otherwise, show what the user asked for.
+        */
+       if (!strcmp(oper, "readnew")) {
+               strcpy(cmd, "MSGS NEW");
+       }
+       else if (!strcmp(oper, "readold")) {
+               strcpy(cmd, "MSGS OLD");
+       }
+       else {
+               strcpy(cmd, "MSGS ALL");
+       }
+
+       if ((WC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1)) {
+               is_summary = 1;
+               strcpy(cmd, "MSGS ALL");
+       }
+
+       if ((WC->wc_view == VIEW_ADDRESSBOOK) && (maxmsgs > 1)) {
+               is_addressbook = 1;
+               strcpy(cmd, "MSGS ALL");
+               maxmsgs = 9999999;
+       }
+
+       if (is_summary) {
+               strcpy(cmd, "MSGS ALL|||1");    /**< fetch header summary */
+               startmsg = 1;
+               maxmsgs = 9999999;
+       }
+
+       /**
+        * Are we doing a summary view?  If so, we need to know old messages
+        * and new messages, so we can do that pretty boldface thing for the
+        * new messages.
+        */
+       strcpy(old_msgs, "");
+       if (is_summary) {
+               serv_puts("GTSN");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       strcpy(old_msgs, &buf[4]);
+               }
+       }
+
+       is_singlecard = atoi(bstr("is_singlecard"));
+
+       if (WC->wc_default_view == VIEW_CALENDAR) {             /**< calendar */
+               is_calendar = 1;
+               strcpy(cmd, "MSGS ALL");
+               maxmsgs = 32767;
+       }
+       if (WC->wc_default_view == VIEW_TASKS) {                /**< tasks */
+               is_tasks = 1;
+               strcpy(cmd, "MSGS ALL");
+               maxmsgs = 32767;
+       }
+       if (WC->wc_default_view == VIEW_NOTES) {                /**< notes */
+               is_notes = 1;
+               strcpy(cmd, "MSGS ALL");
+               maxmsgs = 32767;
+       }
+
+       nummsgs = load_msg_ptrs(cmd, is_summary);
+       if (nummsgs == 0) {
+
+               if ((!is_tasks) && (!is_calendar) && (!is_notes) && (!is_addressbook)) {
+                       wprintf("<em>");
+                       if (!strcmp(oper, "readnew")) {
+                               wprintf(_("No new messages."));
+                       } else if (!strcmp(oper, "readold")) {
+                               wprintf(_("No old messages."));
+                       } else {
+                               wprintf(_("No messages here."));
+                       }
+                       wprintf("</em>\n");
+               }
+
+               goto DONE;
+       }
+
+       if (is_summary) {
+               for (a = 0; a < nummsgs; ++a) {
+                       /** Are you a new message, or an old message? */
+                       if (is_summary) {
+                               if (is_msg_in_mset(old_msgs, WC->msgarr[a])) {
+                                       WC->summ[a].is_new = 0;
+                               }
+                               else {
+                                       WC->summ[a].is_new = 1;
+                               }
+                       }
+               }
+       }
+
+       if (startmsg == 0L) {
+               if (bbs_reverse) {
+                       startmsg = WC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0];
+               }
+               else {
+                       startmsg = WC->msgarr[0];
+               }
+       }
+
+       if (is_summary) {
+               if (!strcasecmp(sortby, "subject")) {
+                       qsort(WC->summ, WC->num_summ,
+                               sizeof(struct message_summary), summcmp_subj);
+               }
+               else if (!strcasecmp(sortby, "rsubject")) {
+                       qsort(WC->summ, WC->num_summ,
+                               sizeof(struct message_summary), summcmp_rsubj);
+               }
+               else if (!strcasecmp(sortby, "sender")) {
+                       qsort(WC->summ, WC->num_summ,
+                               sizeof(struct message_summary), summcmp_sender);
+               }
+               else if (!strcasecmp(sortby, "rsender")) {
+                       qsort(WC->summ, WC->num_summ,
+                               sizeof(struct message_summary), summcmp_rsender);
+               }
+               else if (!strcasecmp(sortby, "date")) {
+                       qsort(WC->summ, WC->num_summ,
+                               sizeof(struct message_summary), summcmp_date);
+               }
+               else if (!strcasecmp(sortby, "rdate")) {
+                       qsort(WC->summ, WC->num_summ,
+                               sizeof(struct message_summary), summcmp_rdate);
+               }
+       }
+
+       if (!strcasecmp(sortby, "subject")) {
+               subjsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rsubject\"><img border=\"0\" src=\"static/down_pointer.gif\" /></a>" ;
+       }
+       else if (!strcasecmp(sortby, "rsubject")) {
+               subjsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=subject\"><img border=\"0\" src=\"static/up_pointer.gif\" /></a>" ;
+       }
+       else {
+               subjsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=subject\"><img border=\"0\" src=\"static/sort_none.gif\" /></a>" ;
+       }
+
+       if (!strcasecmp(sortby, "sender")) {
+               sendsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rsender\"><img border=\"0\" src=\"static/down_pointer.gif\" /></a>" ;
+       }
+       else if (!strcasecmp(sortby, "rsender")) {
+               sendsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=sender\"><img border=\"0\" src=\"static/up_pointer.gif\" /></a>" ;
+       }
+       else {
+               sendsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=sender\"><img border=\"0\" src=\"static/sort_none.gif\" /></a>" ;
+       }
+
+       if (!strcasecmp(sortby, "date")) {
+               datesort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rdate\"><img border=\"0\" src=\"static/down_pointer.gif\" /></a>" ;
+       }
+       else if (!strcasecmp(sortby, "rdate")) {
+               datesort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=date\"><img border=\"0\" src=\"static/up_pointer.gif\" /></a>" ;
+       }
+       else {
+               datesort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rdate\"><img border=\"0\" src=\"static/sort_none.gif\" /></a>" ;
+       }
+
+       if (is_summary) {
+               wprintf("</div>\n");            /** end of 'content' div */
+
+               wprintf("<script language=\"javascript\" type=\"text/javascript\">"
+                       " document.onkeydown = CtdlMsgListKeyPress;     "
+                       " if (document.layers) {                        "
+                       "       document.captureEvents(Event.KEYPRESS); "
+                       " }                                             "
+                       "</script>\n"
+               );
+
+               /** note that Date and Delete are now in the same column */
+               wprintf("<div id=\"message_list_hdr\">"
+                       "<div class=\"fix_scrollbar_bug\">"
+                       "<table cellspacing=0 style=\"width:100%%\">"
+                       "<tr>"
+               );
+               wprintf("<td width=%d%%><b><i>%s</i></b> %s</td>"
+                       "<td width=%d%%><b><i>%s</i></b> %s</td>"
+                       "<td width=%d%%><b><i>%s</i></b> %s"
+                       "&nbsp;"
+                       "<input type=\"submit\" name=\"delete_button\" style=\"font-size:6pt\" "
+                       " onClick=\"CtdlDeleteSelectedMessages(event)\" "
+                       " value=\"%s\">"
+                       "</td>"
+                       "</tr>\n"
+                       ,
+                       SUBJ_COL_WIDTH_PCT,
+                       _("Subject"),   subjsort_button,
+                       SENDER_COL_WIDTH_PCT,
+                       _("Sender"),    sendsort_button,
+                       DATE_PLUS_BUTTONS_WIDTH_PCT,
+                       _("Date"),      datesort_button,
+                       _("Delete")
+               );
+               wprintf("</table></div></div>\n");
+
+               wprintf("<div id=\"message_list\">"
+
+                       "<div class=\"fix_scrollbar_bug\">\n"
+
+                       "<table class=\"mailbox_summary\" id=\"summary_headers\" rules=rows "
+                       "cellspacing=0 style=\"width:100%%;-moz-user-select:none;\">"
+               );
+       }
+
+       if (is_notes) {
+               wprintf("<div align=center>%s</div>\n", _("Click on any note to edit it."));
+               wprintf("<div id=\"new_notes_here\"></div>\n");
+       }
+
+       for (a = 0; a < nummsgs; ++a) {
+               if ((WC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) {
+
+                       /** Display the message */
+                       if (is_summary) {
+                               display_summarized(a);
+                       }
+                       else if (is_addressbook) {
+                               fetch_ab_name(WC->msgarr[a], buf);
+                               ++num_ab;
+                               addrbook = realloc(addrbook,
+                                       (sizeof(struct addrbookent) * num_ab) );
+                               safestrncpy(addrbook[num_ab-1].ab_name, buf,
+                                       sizeof(addrbook[num_ab-1].ab_name));
+                               addrbook[num_ab-1].ab_msgnum = WC->msgarr[a];
+                       }
+                       else if (is_calendar) {
+                               display_calendar(WC->msgarr[a]);
+                       }
+                       else if (is_tasks) {
+                               display_task(WC->msgarr[a]);
+                       }
+                       else if (is_notes) {
+                               display_note(WC->msgarr[a]);
+                       }
+                       else {
+                               if (displayed_msgs == NULL) {
+                                       displayed_msgs = malloc(sizeof(long) *
+                                                               (maxmsgs<nummsgs ? maxmsgs : nummsgs));
+                               }
+                               displayed_msgs[num_displayed] = WC->msgarr[a];
+                       }
+
+                       if (lowest_displayed < 0) lowest_displayed = a;
+                       highest_displayed = a;
+
+                       ++num_displayed;
+               }
+       }
+
+       /**
+        * Set the "is_bbview" variable if it appears that we are looking at
+        * a classic bulletin board view.
+        */
+       if ((!is_tasks) && (!is_calendar) && (!is_addressbook)
+             && (!is_notes) && (!is_singlecard) && (!is_summary)) {
+               is_bbview = 1;
+       }
+
+       /** Output loop */
+       if (displayed_msgs != NULL) {
+               if (bbs_reverse) {
+                       qsort(displayed_msgs, num_displayed, sizeof(long), longcmp_r);
+               }
+
+               /** if we do a split bbview in the future, begin messages div here */
+
+               for (a=0; a<num_displayed; ++a) {
+                       read_message(displayed_msgs[a], 0, "");
+               }
+
+               /** if we do a split bbview in the future, end messages div here */
+
+               free(displayed_msgs);
+               displayed_msgs = NULL;
+       }
+
+       if (is_summary) {
+               wprintf("</table>"
+                       "</div>\n");                    /**< end of 'fix_scrollbar_bug' div */
+               wprintf("</div>");                      /**< end of 'message_list' div */
+
+               /** Here's the grab-it-to-resize-the-message-list widget */
+               wprintf("<div id=\"resize_msglist\" "
+                       "onMouseDown=\"CtdlResizeMsgListMouseDown(event)\">"
+                       "<div class=\"fix_scrollbar_bug\">"
+                       "<table width=100%% border=3 cellspacing=0 "
+                       "bgcolor=\"#cccccc\" "
+                       "cellpadding=0><TR><TD> </td></tr></table>"
+                       "</div></div>\n"
+               );
+
+               wprintf("<div id=\"preview_pane\">");   /**< The preview pane will initially be empty */
+       }
+
+       /**
+        * Bump these because although we're thinking in zero base, the user
+        * is a drooling idiot and is thinking in one base.
+        */
+       ++lowest_displayed;
+       ++highest_displayed;
+
+       /**
+        * If we're not currently looking at ALL requested
+        * messages, then display the selector bar
+        */
+       if (is_bbview) {
+               /** begin bbview scroller */
+               wprintf("<form name=\"msgomatic\">");
+               wprintf(_("Reading #"), lowest_displayed, highest_displayed);
+
+               wprintf("<select name=\"whichones\" size=\"1\" "
+                       "OnChange=\"location.href=msgomatic.whichones.options"
+                       "[selectedIndex].value\">\n");
+
+               if (bbs_reverse) {
+                       for (b=nummsgs-1; b>=0; b = b - maxmsgs) {
+                               hi = b + 1;
+                               lo = b - maxmsgs + 2;
+                               if (lo < 1) lo = 1;
+                               wprintf("<option %s value="
+                                       "\"%s"
+                                       "?startmsg=%ld"
+                                       "?maxmsgs=%d"
+                                       "?summary=%d\">"
+                                       "%d-%d</option> \n",
+                                       ((WC->msgarr[lo-1] == startmsg) ? "selected" : ""),
+                                       oper,
+                                       WC->msgarr[lo-1],
+                                       maxmsgs,
+                                       is_summary,
+                                       hi, lo);
+                       }
+               }
+               else {
+                       for (b=0; b<nummsgs; b = b + maxmsgs) {
+                               lo = b + 1;
+                               hi = b + maxmsgs + 1;
+                               if (hi > nummsgs) hi = nummsgs;
+                               wprintf("<option %s value="
+                                       "\"%s"
+                                       "?startmsg=%ld"
+                                       "?maxmsgs=%d"
+                                       "?summary=%d\">"
+                                       "%d-%d</option> \n",
+                                       ((WC->msgarr[b] == startmsg) ? "selected" : ""),
+                                       oper,
+                                       WC->msgarr[lo-1],
+                                       maxmsgs,
+                                       is_summary,
+                                       lo, hi);
+                       }
+               }
+
+               wprintf("<option value=\"%s?startmsg=%ld"
+                       "?maxmsgs=9999999?summary=%d\">"
+                       "ALL"
+                       "</option> ",
+                       oper,
+                       WC->msgarr[0], is_summary);
+
+               wprintf("</select> ");
+               wprintf(_("of %d messages."), nummsgs);
+
+               /** forward/reverse */
+               wprintf("&nbsp;<select name=\"direction\" size=\"1\" "
+                       "OnChange=\"location.href=msgomatic.direction.options"
+                       "[selectedIndex].value\">\n"
+               );
+
+               wprintf("<option %s value=\"%s?sortby=forward\">oldest to newest</option>\n",
+                       (bbs_reverse ? "" : "selected"),
+                       oper
+               );
+       
+               wprintf("<option %s value=\"%s?sortby=reverse\">newest to oldest</option>\n",
+                       (bbs_reverse ? "selected" : ""),
+                       oper
+               );
+       
+               wprintf("</select></form>\n");
+               /** end bbview scroller */
+       }
+
+DONE:
+       if (is_tasks) {
+               do_tasks_view();        /** Render the task list */
+       }
+
+       if (is_calendar) {
+               do_calendar_view();     /** Render the calendar */
+       }
+
+       if (is_addressbook) {
+               do_addrbook_view(addrbook, num_ab);     /** Render the address book */
+       }
+
+       /** Note: wDumpContent() will output one additional </div> tag. */
+       wDumpContent(1);
+       if (addrbook != NULL) free(addrbook);
+
+       /** free the summary */
+       if (WC->summ != NULL) {
+               free(WC->summ);
+               WC->num_summ = 0;
+               WC->summ = NULL;
+       }
+}
+
+
+/**
+ * \brief Back end for post_message()
+ * ... this is where the actual message gets transmitted to the server.
+ */
+void post_mime_to_server(void) {
+       char boundary[SIZ];
+       int is_multipart = 0;
+       static int seq = 0;
+       struct wc_attachment *att;
+       char *encoded;
+       size_t encoded_length;
+
+       /** RFC2045 requires this, and some clients look for it... */
+       serv_puts("MIME-Version: 1.0");
+
+       /** If there are attachments, we have to do multipart/mixed */
+       if (WC->first_attachment != NULL) {
+               is_multipart = 1;
+       }
+
+       if (is_multipart) {
+               sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x",
+                       serv_info.serv_fqdn,
+                       getpid(),
+                       ++seq
+               );
+
+               /** Remember, serv_printf() appends an extra newline */
+               serv_printf("Content-type: multipart/mixed; "
+                       "boundary=\"%s\"\n", boundary);
+               serv_printf("This is a multipart message in MIME format.\n");
+               serv_printf("--%s", boundary);
+       }
+
+       serv_puts("Content-type: text/html; charset=utf-8");
+       serv_puts("Content-Transfer-Encoding: quoted-printable");
+       serv_puts("");
+       serv_puts("<html><body>\r\n");
+       text_to_server_qp(bstr("msgtext"));     /** Transmit message in quoted-printable encoding */
+       serv_puts("</body></html>\r\n");
+       
+       if (is_multipart) {
+
+               /** Add in the attachments */
+               for (att = WC->first_attachment; att!=NULL; att=att->next) {
+
+                       encoded_length = ((att->length * 150) / 100);
+                       encoded = malloc(encoded_length);
+                       if (encoded == NULL) break;
+                       CtdlEncodeBase64(encoded, att->data, att->length);
+
+                       serv_printf("--%s", boundary);
+                       serv_printf("Content-type: %s", att->content_type);
+                       serv_printf("Content-disposition: attachment; "
+                               "filename=\"%s\"", att->filename);
+                       serv_puts("Content-transfer-encoding: base64");
+                       serv_puts("");
+                       serv_write(encoded, strlen(encoded));
+                       serv_puts("");
+                       serv_puts("");
+                       free(encoded);
+               }
+               serv_printf("--%s--", boundary);
+       }
+
+       serv_puts("000");
+}
+
+
+/**
+ * \brief Post message (or don't post message)
+ *
+ * Note regarding the "dont_post" variable:
+ * A random value (actually, it's just a timestamp) is inserted as a hidden
+ * field called "postseq" when the display_enter page is generated.  This
+ * value is checked when posting, using the static variable dont_post.  If a
+ * user attempts to post twice using the same dont_post value, the message is
+ * discarded.  This prevents the accidental double-saving of the same message
+ * if the user happens to click the browser "back" button.
+ */
+void post_message(void)
+{
+       char buf[1024];
+       char encoded_subject[1024];
+       static long dont_post = (-1L);
+       struct wc_attachment *att, *aptr;
+       int is_anonymous = 0;
+
+       if (!strcasecmp(bstr("is_anonymous"), "yes")) {
+               is_anonymous = 1;
+       }
+
+       if (WC->upload_length > 0) {
+
+               /** There's an attachment.  Save it to this struct... */
+               att = malloc(sizeof(struct wc_attachment));
+               memset(att, 0, sizeof(struct wc_attachment));
+               att->length = WC->upload_length;
+               strcpy(att->content_type, WC->upload_content_type);
+               strcpy(att->filename, WC->upload_filename);
+               att->next = NULL;
+
+               /** And add it to the list. */
+               if (WC->first_attachment == NULL) {
+                       WC->first_attachment = att;
+               }
+               else {
+                       aptr = WC->first_attachment;
+                       while (aptr->next != NULL) aptr = aptr->next;
+                       aptr->next = att;
+               }
+
+               /**
+                * Mozilla sends a simple filename, which is what we want,
+                * but Satan's Browser sends an entire pathname.  Reduce
+                * the path to just a filename if we need to.
+                */
+               while (num_tokens(att->filename, '/') > 1) {
+                       remove_token(att->filename, 0, '/');
+               }
+               while (num_tokens(att->filename, '\\') > 1) {
+                       remove_token(att->filename, 0, '\\');
+               }
+
+               /**
+                * Transfer control of this memory from the upload struct
+                * to the attachment struct.
+                */
+               att->data = WC->upload;
+               WC->upload_length = 0;
+               WC->upload = NULL;
+               display_enter();
+               return;
+       }
+
+       if (strlen(bstr("cancel_button")) > 0) {
+               sprintf(WC->ImportantMessage, 
+                       _("Cancelled.  Message was not posted."));
+       } else if (strlen(bstr("attach_button")) > 0) {
+               display_enter();
+               return;
+       } else if (atol(bstr("postseq")) == dont_post) {
+               sprintf(WC->ImportantMessage, 
+                       _("Automatically cancelled because you have already "
+                       "saved this message."));
+       } else {
+               rfc2047encode(encoded_subject, sizeof encoded_subject, bstr("subject"));
+               sprintf(buf, "ENT0 1|%s|%d|4|%s|||%s|%s|%s",
+                       bstr("recp"),
+                       is_anonymous,
+                       encoded_subject,
+                       bstr("cc"),
+                       bstr("bcc"),
+                       bstr("wikipage")
+               );
+               serv_puts(buf);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '4') {
+                       post_mime_to_server();
+                       if ( (strlen(bstr("recp")) > 0)
+                          || (strlen(bstr("cc")) > 0)
+                          || (strlen(bstr("bcc")) > 0)
+                       ) {
+                               sprintf(WC->ImportantMessage, _("Message has been sent.\n"));
+                       }
+                       else {
+                               sprintf(WC->ImportantMessage, _("Message has been posted.\n"));
+                       }
+                       dont_post = atol(bstr("postseq"));
+               } else {
+                       sprintf(WC->ImportantMessage, "%s", &buf[4]);
+                       display_enter();
+                       return;
+               }
+       }
+
+       free_attachments(WC);
+
+       /**
+        *  We may have been supplied with instructions regarding the location
+        *  to which we must return after posting.  If found, go there.
+        */
+       if (strlen(bstr("return_to")) > 0) {
+               http_redirect(bstr("return_to"));
+       }
+       /**
+        *  If we were editing a page in a wiki room, go to that page now.
+        */
+       else if (strlen(bstr("wikipage")) > 0) {
+               snprintf(buf, sizeof buf, "wiki?page=%s", bstr("wikipage"));
+               http_redirect(buf);
+       }
+       /**
+        *  Otherwise, just go to the "read messages" loop.
+        */
+       else {
+               readloop("readnew");
+       }
+}
+
+
+
+
+/**
+ * \brief display the message entry screen
+ */
+void display_enter(void)
+{
+       char buf[SIZ];
+       char ebuf[SIZ];
+       long now;
+       struct wc_attachment *att;
+       int recipient_required = 0;
+       int recipient_bad = 0;
+       int i;
+       int is_anonymous = 0;
+       long existing_page = (-1L);
+
+       if (strlen(bstr("force_room")) > 0) {
+               gotoroom(bstr("force_room"));
+       }
+
+       if (!strcasecmp(bstr("is_anonymous"), "yes")) {
+               is_anonymous = 1;
+       }
+
+       /**
+        * Are we perhaps in an address book view?  If so, then an "enter
+        * message" command really means "add new entry."
+        */
+       if (WC->wc_default_view == VIEW_ADDRESSBOOK) {
+               do_edit_vcard(-1, "", "");
+               return;
+       }
+
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+       /**
+        * Are we perhaps in a calendar room?  If so, then an "enter
+        * message" command really means "add new calendar item."
+        */
+       if (WC->wc_default_view == VIEW_CALENDAR) {
+               display_edit_event();
+               return;
+       }
+
+       /**
+        * Are we perhaps in a tasks view?  If so, then an "enter
+        * message" command really means "add new task."
+        */
+       if (WC->wc_default_view == VIEW_TASKS) {
+               display_edit_task();
+               return;
+       }
+#endif
+
+       /**
+        * Otherwise proceed normally.
+        * Do a custom room banner with no navbar...
+        */
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       embed_room_banner(NULL, navbar_none);
+       wprintf("</div>\n");
+       wprintf("<div id=\"content\">\n"
+               "<div class=\"fix_scrollbar_bug\">"
+               "<table width=100%% border=0 bgcolor=\"#ffffff\"><tr><td>");
+
+       /** First test to see whether this is a room that requires recipients to be entered */
+       serv_puts("ENT0 0");
+       serv_getln(buf, sizeof buf);
+       if (!strncmp(buf, "570", 3)) {          /** 570 means that we need a recipient here */
+               recipient_required = 1;
+       }
+       else if (buf[0] != '2') {               /** Any other error means that we cannot continue */
+               wprintf("<em>%s</em><br />\n", &buf[4]);
+               goto DONE;
+       }
+
+       /** Now check our actual recipients if there are any */
+       if (recipient_required) {
+               sprintf(buf, "ENT0 0|%s|%d|0||||%s|%s|%s", bstr("recp"), is_anonymous,
+                       bstr("cc"), bstr("bcc"), bstr("wikipage"));
+               serv_puts(buf);
+               serv_getln(buf, sizeof buf);
+
+               if (!strncmp(buf, "570", 3)) {  /** 570 means we have an invalid recipient listed */
+                       if (strlen(bstr("recp")) + strlen(bstr("cc")) + strlen(bstr("bcc")) > 0) {
+                               recipient_bad = 1;
+                       }
+               }
+               else if (buf[0] != '2') {       /** Any other error means that we cannot continue */
+                       wprintf("<em>%s</em><br />\n", &buf[4]);
+                       goto DONE;
+               }
+       }
+
+       /** If we got this far, we can display the message entry screen. */
+
+       now = time(NULL);
+       fmt_date(buf, now, 0);
+       strcat(&buf[strlen(buf)], _(" <I>from</I> "));
+       stresc(&buf[strlen(buf)], WC->wc_fullname, 1, 1);
+
+       /* Don't need this anymore, it's in the input box below
+       if (strlen(bstr("recp")) > 0) {
+               strcat(&buf[strlen(buf)], _(" <I>to</I> "));
+               stresc(&buf[strlen(buf)], bstr("recp"), 1, 1);
+       }
+       */
+
+       strcat(&buf[strlen(buf)], _(" <I>in</I> "));
+       stresc(&buf[strlen(buf)], WC->wc_roomname, 1, 1);
+
+       /** begin message entry screen */
+       wprintf("<form "
+               "enctype=\"multipart/form-data\" "
+               "method=\"POST\" "
+               "accept-charset=\"UTF-8\" "
+               "action=\"post\" "
+               "name=\"enterform\""
+               ">\n");
+       wprintf("<input type=\"hidden\" name=\"postseq\" value=\"%ld\">\n", now);
+       if (WC->wc_view == VIEW_WIKI) {
+               wprintf("<input type=\"hidden\" name=\"wikipage\" value=\"%s\">\n", bstr("wikipage"));
+       }
+       wprintf("<input type=\"hidden\" name=\"return_to\" value=\"%s\">\n", bstr("return_to"));
+
+       wprintf("<img src=\"static/newmess3_24x.gif\" align=middle alt=\" \">");
+       wprintf("%s\n", buf);   /** header bar */
+       if (WC->room_flags & QR_ANONOPT) {
+               wprintf("&nbsp;"
+                       "<input type=\"checkbox\" name=\"is_anonymous\" value=\"yes\" %s>",
+                               (is_anonymous ? "checked" : "")
+               );
+               wprintf("Anonymous");
+       }
+       wprintf("<br>\n");      /** header bar */
+
+       wprintf("<table border=\"0\" width=\"100%%\">\n");
+       if (recipient_required) {
+
+               wprintf("<tr><td>");
+               wprintf("<font size=-1>");
+               wprintf(_("To:"));
+               wprintf("</font>");
+               wprintf("</td><td>"
+                       "<input autocomplete=\"off\" type=\"text\" name=\"recp\" id=\"recp_id\" value=\"");
+               escputs(bstr("recp"));
+               wprintf("\" size=50 maxlength=1000 />");
+               wprintf("<div class=\"auto_complete\" id=\"recp_name_choices\"></div>");
+               wprintf("</td><td></td></tr>\n");
+
+               wprintf("<tr><td>");
+               wprintf("<font size=-1>");
+               wprintf(_("CC:"));
+               wprintf("</font>");
+               wprintf("</td><td>"
+                       "<input autocomplete=\"off\" type=\"text\" name=\"cc\" id=\"cc_id\" value=\"");
+               escputs(bstr("cc"));
+               wprintf("\" size=50 maxlength=1000 />");
+               wprintf("<div class=\"auto_complete\" id=\"cc_name_choices\"></div>");
+               wprintf("</td><td></td></tr>\n");
+
+               wprintf("<tr><td>");
+               wprintf("<font size=-1>");
+               wprintf(_("BCC:"));
+               wprintf("</font>");
+               wprintf("</td><td>"
+                       "<input autocomplete=\"off\" type=\"text\" name=\"bcc\" id=\"bcc_id\" value=\"");
+               escputs(bstr("bcc"));
+               wprintf("\" size=50 maxlength=1000 />");
+               wprintf("<div class=\"auto_complete\" id=\"bcc_name_choices\"></div>");
+               wprintf("</td><td></td></tr>\n");
+
+               /** Initialize the autocomplete ajax helpers (found in wclib.js) */
+               wprintf("<script type=\"text/javascript\">      \n"
+                       " activate_entmsg_autocompleters();     \n"
+                       "</script>                              \n"
+               );
+       }
+
+       wprintf("<tr><td>");
+       wprintf("<font size=-1>");
+       wprintf(_("Subject (optional):"));
+       wprintf("</font>");
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"subject\" value=\"");
+       escputs(bstr("subject"));
+       wprintf("\" size=50 maxlength=70></td><td>\n");
+
+       wprintf("<input type=\"submit\" name=\"send_button\" value=\"");
+       if (recipient_required) {
+               wprintf(_("Send message"));
+       } else {
+               wprintf(_("Post message"));
+       }
+       wprintf("\">&nbsp;"
+               "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
+       wprintf("</td></tr></table>\n");
+
+       wprintf("<center>");
+
+       wprintf("<textarea name=\"msgtext\" cols=\"80\" rows=\"15\">");
+
+       /** If we're continuing from a previous edit, put our partially-composed message back... */
+       msgescputs(bstr("msgtext"));
+
+       /* If we're forwarding a message, insert it here... */
+       if (atol(bstr("fwdquote")) > 0L) {
+               wprintf("<br><div align=center><i>");
+               wprintf(_("--- forwarded message ---"));
+               wprintf("</i></div><br>");
+               pullquote_message(atol(bstr("fwdquote")), 1, 1);
+       }
+
+       /** If we're replying quoted, insert the quote here... */
+       else if (atol(bstr("replyquote")) > 0L) {
+               wprintf("<br>"
+                       "<blockquote>");
+               pullquote_message(atol(bstr("replyquote")), 0, 1);
+               wprintf("</blockquote><br>");
+       }
+
+       /** If we're editing a wiki page, insert the existing page here... */
+       else if (WC->wc_view == VIEW_WIKI) {
+               safestrncpy(buf, bstr("wikipage"), sizeof buf);
+               str_wiki_index(buf);
+               existing_page = locate_message_by_uid(buf);
+               if (existing_page >= 0L) {
+                       pullquote_message(existing_page, 1, 0);
+               }
+       }
+
+       /** Insert our signature if appropriate... */
+       if ( (WC->is_mailbox) && (strcmp(bstr("sig_inserted"), "yes")) ) {
+               get_preference("use_sig", buf, sizeof buf);
+               if (!strcasecmp(buf, "yes")) {
+                       get_preference("signature", ebuf, sizeof ebuf);
+                       euid_unescapize(buf, ebuf);
+                       wprintf("<br>--<br>");
+                       for (i=0; i<strlen(buf); ++i) {
+                               if (buf[i] == '\n') {
+                                       wprintf("<br>");
+                               }
+                               else if (buf[i] == '<') {
+                                       wprintf("&lt;");
+                               }
+                               else if (buf[i] == '>') {
+                                       wprintf("&gt;");
+                               }
+                               else if (buf[i] == '&') {
+                                       wprintf("&amp;");
+                               }
+                               else if (buf[i] == '\"') {
+                                       wprintf("&quot;");
+                               }
+                               else if (buf[i] == '\'') {
+                                       wprintf("&#39;");
+                               }
+                               else if (isprint(buf[i])) {
+                                       wprintf("%c", buf[i]);
+                               }
+                       }
+               }
+       }
+
+       wprintf("</textarea>");
+       wprintf("</center><br />\n");
+
+       /**
+        * The following script embeds the TinyMCE richedit control, and automatically
+        * transforms the textarea into a richedit textarea.
+        */
+       wprintf(
+               "<script language=\"javascript\" type=\"text/javascript\" src=\"tiny_mce/tiny_mce.js\"></script>\n"
+               "<script language=\"javascript\" type=\"text/javascript\">"
+               "tinyMCE.init({"
+               "       mode : \"textareas\", width : \"100%%\", browsers : \"msie,gecko\", "
+               "       theme : \"advanced\", plugins : \"iespell\", "
+               "       theme_advanced_buttons1 : \"bold, italic, underline, strikethrough, justifyleft, justifycenter, justifyright, justifyfull, bullist, numlist, cut, copy, paste, link, image, help, forecolor, iespell, code\", "
+               "       theme_advanced_buttons2 : \"\", "
+               "       theme_advanced_buttons3 : \"\" "
+               "});"
+               "</script>\n"
+       );
+
+
+       /** Enumerate any attachments which are already in place... */
+       wprintf("<img src=\"static/diskette_24x.gif\" border=0 "
+               "align=middle height=16 width=16> ");
+       wprintf(_("Attachments:"));
+       wprintf(" ");
+       wprintf("<select name=\"which_attachment\" size=1>");
+       for (att = WC->first_attachment; att != NULL; att = att->next) {
+               wprintf("<option value=\"");
+               urlescputs(att->filename);
+               wprintf("\">");
+               escputs(att->filename);
+               /* wprintf(" (%s, %d bytes)",att->content_type,att->length); */
+               wprintf("</option>\n");
+       }
+       wprintf("</select>");
+
+       /** Now offer the ability to attach additional files... */
+       wprintf("&nbsp;&nbsp;&nbsp;");
+       wprintf(_("Attach file:"));
+       wprintf(" <input NAME=\"attachfile\" "
+               "SIZE=16 TYPE=\"file\">\n&nbsp;&nbsp;"
+               "<input type=\"submit\" name=\"attach_button\" value=\"%s\">\n", _("Add"));
+
+       /** Seth asked for these to be at the top *and* bottom... */
+       wprintf("<input type=\"submit\" name=\"send_button\" value=\"");
+       if (recipient_required) {
+               wprintf(_("Send message"));
+       } else {
+               wprintf(_("Post message"));
+       }
+       wprintf("\">&nbsp;"
+               "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
+
+       /** Make sure we only insert our signature once */
+       if (strcmp(bstr("sig_inserted"), "yes")) {
+               wprintf("<INPUT TYPE=\"hidden\" NAME=\"sig_inserted\" VALUE=\"yes\">\n");
+       }
+
+       wprintf("</form>\n");
+
+       wprintf("</td></tr></table></div>\n");
+DONE:  wDumpContent(1);
+}
+
+
+
+/**
+ * \brief delete a message
+ */
+void delete_msg(void)
+{
+       long msgid;
+       char buf[SIZ];
+
+       msgid = atol(bstr("msgid"));
+
+       output_headers(1, 1, 1, 0, 0, 0);
+
+       if (WC->wc_is_trash) {  /** Delete from Trash is a real delete */
+               serv_printf("DELE %ld", msgid); 
+       }
+       else {                  /** Otherwise move it to Trash */
+               serv_printf("MOVE %ld|_TRASH_|0", msgid);
+       }
+
+       serv_getln(buf, sizeof buf);
+       wprintf("<em>%s</em><br />\n", &buf[4]);
+
+       wDumpContent(1);
+}
+
+
+
+
+/**
+ * \brief Confirm move of a message
+ */
+void confirm_move_msg(void)
+{
+       long msgid;
+       char buf[SIZ];
+       char targ[SIZ];
+
+       msgid = atol(bstr("msgid"));
+
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0><TR><TD>");
+       wprintf("<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Confirm move of message"));
+       wprintf("</SPAN>\n");
+       wprintf("</TD></TR></TABLE>\n");
+       wprintf("</div>\n<div id=\"content\">\n");
+
+       wprintf("<CENTER>");
+
+       wprintf(_("Move this message to:"));
+       wprintf("<br />\n");
+
+       wprintf("<form METHOD=\"POST\" action=\"move_msg\">\n");
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n", bstr("msgid"));
+
+       wprintf("<SELECT NAME=\"target_room\" SIZE=5>\n");
+       serv_puts("LKRA");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       extract_token(targ, buf, 0, '|', sizeof targ);
+                       wprintf("<OPTION>");
+                       escputs(targ);
+                       wprintf("\n");
+               }
+       }
+       wprintf("</SELECT>\n");
+       wprintf("<br />\n");
+
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"move_button\" VALUE=\"%s\">", _("Move"));
+       wprintf("&nbsp;");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
+       wprintf("</form></CENTER>\n");
+
+       wprintf("</CENTER>\n");
+       wDumpContent(1);
+}
+
+
+/**
+ * \brief move a message to another folder
+ */
+void move_msg(void)
+{
+       long msgid;
+       char buf[SIZ];
+
+       msgid = atol(bstr("msgid"));
+
+       if (strlen(bstr("move_button")) > 0) {
+               sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room"));
+               serv_puts(buf);
+               serv_getln(buf, sizeof buf);
+               sprintf(WC->ImportantMessage, "%s", &buf[4]);
+       } else {
+               sprintf(WC->ImportantMessage, (_("The message was not moved.")));
+       }
+
+       readloop("readnew");
+
+}
+
+
+/*@}*/
diff --git a/webcit/src/mime_parser.c b/webcit/src/mime_parser.c
new file mode 100644 (file)
index 0000000..02edd1c
--- /dev/null
@@ -0,0 +1,662 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup MIME This is the MIME parser for Citadel.
+ *
+ * Copyright (c) 1998-2005 by Art Cancro
+ * This code is distributed under the terms of the GNU General Public License.
+ * \ingroup WebcitHttpServer
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+#include "mime_parser.h"
+
+/**
+ * \brief get mime key
+ * \param target where to put the mime buffer at???
+ * \param source where to extract the mimetype from
+ * \param key what???
+ */
+void extract_key(char *target, char *source, char *key)
+{
+       int a, b;
+
+       strcpy(target, source);
+       for (a = 0; a < strlen(target); ++a) {
+               if ((!strncasecmp(&target[a], key, strlen(key)))
+                   && (target[a + strlen(key)] == '=')) {
+                       strcpy(target, &target[a + strlen(key) + 1]);
+                       if (target[0] == 34)
+                               strcpy(target, &target[1]);
+                       for (b = 0; b < strlen(target); ++b)
+                               if (target[b] == 34)
+                                       target[b] = 0;
+                       return;
+               }
+       }
+       strcpy(target, "");
+}
+
+
+/**
+ * \brief For non-multipart messages, we need to generate a quickie partnum of "1"
+ * to return to callback functions.  Some callbacks demand it.
+ * \param supplied_partnum partnum to convert
+ * \return the converted num
+ */
+char *fixed_partnum(char *supplied_partnum) {
+       if (supplied_partnum == NULL) return "1";
+       if (strlen(supplied_partnum)==0) return "1";
+       return supplied_partnum;
+}
+
+
+
+/**
+ * \brief Convert "quoted-printable" to binary.  Returns number of bytes decoded.
+ * \param decoded the buffer with the decoded output
+ * \param encoded the encoded string to decode
+ * \param sourcelen length of the decoded buffer
+ */
+int CtdlDecodeQuotedPrintable(char *decoded, char *encoded, int sourcelen) {
+       char buf[SIZ];
+       int buf_length = 0;
+       int soft_line_break = 0;
+       unsigned int ch;
+       int decoded_length = 0;
+       int i;
+
+       decoded[0] = 0;
+       decoded_length = 0;
+       buf[0] = 0;
+       buf_length = 0;
+
+       for (i = 0; i < sourcelen; ++i) {
+
+               buf[buf_length++] = encoded[i];
+
+               if ( (encoded[i] == '\n')
+                  || (encoded[i] == 0)
+                  || (i == (sourcelen-1)) ) {
+                       buf[buf_length++] = 0;
+
+                       /*** begin -- process one line ***/
+
+                       if (buf[strlen(buf)-1] == '\n') {
+                               buf[strlen(buf)-1] = 0;
+                       }
+                       if (buf[strlen(buf)-1] == '\r') {
+                               buf[strlen(buf)-1] = 0;
+                       }
+                       while (isspace(buf[strlen(buf)-1])) {
+                               buf[strlen(buf)-1] = 0;
+                       }
+                       soft_line_break = 0;
+
+                       while (strlen(buf) > 0) {
+                               if (!strcmp(buf, "=")) {
+                                       soft_line_break = 1;
+                                       strcpy(buf, "");
+                               } else if ((strlen(buf)>=3) && (buf[0]=='=')) {
+                                       sscanf(&buf[1], "%02x", &ch);
+                                       decoded[decoded_length++] = ch;
+                                       strcpy(buf, &buf[3]);
+                               } else {
+                                       decoded[decoded_length++] = buf[0];
+                                       strcpy(buf, &buf[1]);
+                               }
+                       }
+                       if (soft_line_break == 0) {
+                               decoded[decoded_length++] = '\r';
+                               decoded[decoded_length++] = '\n';
+                       }
+                       buf_length = 0;
+                       /*** end -- process one line ***/
+               }
+       }
+
+       decoded[decoded_length++] = 0;
+       return(decoded_length);
+}
+
+/**
+ * \brief fully decode a message
+ * Given a message or message-part body and a length, handle any necessary
+ * decoding and pass the request up the stack.
+ * \param partnum todo ?????
+ * \param part_start todo
+ * \param length todo
+ * \param content_type todo
+ * \param charset todo
+ * \param encoding todo
+ * \param disposition todo
+ * \param name todo
+ * \param filename todo
+ * \param CallBack todo
+ * \param PreMultiPartCallBack todo
+ * \param PostMultiPartCallBack todo
+ * \param userdata todo
+ * \param dont_decode todo
+ */
+void mime_decode(char *partnum,
+                char *part_start, size_t length,
+                char *content_type, char *charset, char *encoding,
+                char *disposition,
+                char *name, char *filename,
+                void (*CallBack)
+                 (char *cbname,
+                  char *cbfilename,
+                  char *cbpartnum,
+                  char *cbdisp,
+                  void *cbcontent,
+                  char *cbtype,
+                  char *cbcharset,
+                  size_t cblength,
+                  char *cbencoding,
+                  void *cbuserdata),
+                void (*PreMultiPartCallBack)
+                 (char *cbname,
+                  char *cbfilename,
+                  char *cbpartnum,
+                  char *cbdisp,
+                  void *cbcontent,
+                  char *cbtype,
+                  char *cbcharset,
+                  size_t cblength,
+                  char *cbencoding,
+                  void *cbuserdata),
+                void (*PostMultiPartCallBack)
+                 (char *cbname,
+                  char *cbfilename,
+                  char *cbpartnum,
+                  char *cbdisp,
+                  void *cbcontent,
+                  char *cbtype,
+                  char *cbcharset,
+                  size_t cblength,
+                  char *cbencoding,
+                  void *cbuserdata),
+                 void *userdata,
+                 int dont_decode
+)
+{
+
+       char *decoded;
+       size_t bytes_decoded = 0;
+
+       /* Some encodings aren't really encodings */
+       if (!strcasecmp(encoding, "7bit"))
+               strcpy(encoding, "");
+       if (!strcasecmp(encoding, "8bit"))
+               strcpy(encoding, "");
+       if (!strcasecmp(encoding, "binary"))
+               strcpy(encoding, "");
+
+       /* If this part is not encoded, send as-is */
+       if ( (strlen(encoding) == 0) || (dont_decode)) {
+               if (CallBack != NULL) {
+                       CallBack(name, filename, fixed_partnum(partnum),
+                               disposition, part_start,
+                               content_type, charset, length, encoding, userdata);
+                       }
+               return;
+       }
+       
+       if ((strcasecmp(encoding, "base64"))
+           && (strcasecmp(encoding, "quoted-printable"))) {
+               return;
+       }
+       /**
+        * Allocate a buffer for the decoded data.  The output buffer is the
+        * same size as the input buffer; this assumes that the decoded data
+        * will never be larger than the encoded data.  This is a safe
+        * assumption with base64, uuencode, and quoted-printable.
+        */
+       decoded = malloc(length+2048);
+       if (decoded == NULL) {
+               return;
+       }
+
+       if (!strcasecmp(encoding, "base64")) {
+               bytes_decoded = CtdlDecodeBase64(decoded, part_start, length);
+       }
+       else if (!strcasecmp(encoding, "quoted-printable")) {
+               bytes_decoded = CtdlDecodeQuotedPrintable(decoded,
+                                                       part_start, length);
+       }
+
+       if (bytes_decoded > 0) if (CallBack != NULL) {
+               CallBack(name, filename, fixed_partnum(partnum),
+                       disposition, decoded,
+                       content_type, charset, bytes_decoded, "binary", userdata);
+       }
+
+       free(decoded);
+}
+
+/**
+ * \brief Break out the components of a multipart message
+ * (This function expects to be fed HEADERS + CONTENT)
+ * Note: NULL can be supplied as content_end; in this case, the message is
+ * considered to have ended when the parser encounters a 0x00 byte.
+ * \param partnum todo
+ * \param content_start todo ?????
+ * \param content_end todo
+ * \param CallBack todo
+ * \param PreMultiPartCallBack
+ * \param PostMultiPartCallBack
+ * \param userdata todo
+ * \param dont_decode todo
+ */
+void the_mime_parser(char *partnum,
+                    char *content_start, char *content_end,
+                    void (*CallBack)
+                     (char *cbname,
+                      char *cbfilename,
+                      char *cbpartnum,
+                      char *cbdisp,
+                      void *cbcontent,
+                      char *cbtype,
+                      char *cbcharset,
+                      size_t cblength,
+                      char *cbencoding,
+                      void *cbuserdata),
+                    void (*PreMultiPartCallBack)
+                     (char *cbname,
+                      char *cbfilename,
+                      char *cbpartnum,
+                      char *cbdisp,
+                      void *cbcontent,
+                      char *cbtype,
+                      char *cbcharset,
+                      size_t cblength,
+                      char *cbencoding,
+                      void *cbuserdata),
+                    void (*PostMultiPartCallBack)
+                     (char *cbname,
+                      char *cbfilename,
+                      char *cbpartnum,
+                      char *cbdisp,
+                      void *cbcontent,
+                      char *cbtype,
+                      char *cbcharset,
+                      size_t cblength,
+                      char *cbencoding,
+                      void *cbuserdata),
+                     void *userdata,
+                     int dont_decode
+)
+{
+
+       char *ptr;
+       char *srch = NULL;
+       char *part_start, *part_end = NULL;
+       char buf[SIZ];
+       char *header;
+       char *boundary;
+       char *startary;
+       size_t startary_len = 0;
+       char *endary;
+       char *next_boundary;
+       char *content_type;
+       char *charset;
+       size_t content_length;
+       char *encoding;
+       char *disposition;
+       char *name = NULL;
+       char *content_type_name;
+       char *content_disposition_name;
+       char *filename;
+       int is_multipart;
+       int part_seq = 0;
+       int i;
+       size_t length;
+       char nested_partnum[SIZ];
+
+       ptr = content_start;
+       content_length = 0;
+
+       boundary = malloc(SIZ);
+       memset(boundary, 0, SIZ);
+
+       startary = malloc(SIZ);
+       memset(startary, 0, SIZ);
+
+       endary = malloc(SIZ);
+       memset(endary, 0, SIZ);
+
+       header = malloc(SIZ);
+       memset(header, 0, SIZ);
+
+       content_type = malloc(SIZ);
+       memset(content_type, 0, SIZ);
+
+       charset = malloc(SIZ);
+       memset(charset, 0, SIZ);
+
+       encoding = malloc(SIZ);
+       memset(encoding, 0, SIZ);
+
+       content_type_name = malloc(SIZ);
+       memset(content_type_name, 0, SIZ);
+
+       content_disposition_name = malloc(SIZ);
+       memset(content_disposition_name, 0, SIZ);
+
+       filename = malloc(SIZ);
+       memset(filename, 0, SIZ);
+
+       disposition = malloc(SIZ);
+       memset(disposition, 0, SIZ);
+
+       /** If the caller didn't supply an endpointer, generate one by measure */
+       if (content_end == NULL) {
+               content_end = &content_start[strlen(content_start)];
+       }
+
+       /** Learn interesting things from the headers */
+       strcpy(header, "");
+       do {
+               ptr = memreadline(ptr, buf, SIZ);
+               if (ptr >= content_end) {
+                       goto end_parser;
+               }
+
+               for (i = 0; i < strlen(buf); ++i) {
+                       if (isspace(buf[i])) {
+                               buf[i] = ' ';
+                       }
+               }
+
+               if (!isspace(buf[0])) {
+                       if (!strncasecmp(header, "Content-type: ", 14)) {
+                               strcpy(content_type, &header[14]);
+                               extract_key(content_type_name, content_type, "name");
+                               extract_key(charset, content_type, "charset");
+                               /** Deal with weird headers */
+                               if (strchr(content_type, ' '))
+                                       *(strchr(content_type, ' ')) = '\0';
+                               if (strchr(content_type, ';'))
+                                       *(strchr(content_type, ';')) = '\0';
+                       }
+                       if (!strncasecmp(header, "Content-Disposition: ", 21)) {
+                               strcpy(disposition, &header[21]);
+                               extract_key(content_disposition_name, disposition, "name");
+                               extract_key(filename, disposition, "filename");
+                       }
+                       if (!strncasecmp(header, "Content-length: ", 16)) {
+                               content_length = (size_t) atol(&header[16]);
+                       }
+                       if (!strncasecmp(header,
+                                     "Content-transfer-encoding: ", 27))
+                               strcpy(encoding, &header[27]);
+                       if (strlen(boundary) == 0)
+                               extract_key(boundary, header, "boundary");
+                       strcpy(header, "");
+               }
+               if ((strlen(header) + strlen(buf) + 2) < SIZ)
+                       strcat(header, buf);
+       } while ((strlen(buf) > 0) && (*ptr != 0));
+
+       if (strchr(disposition, ';'))
+               *(strchr(disposition, ';')) = '\0';
+       striplt(disposition);
+       if (strchr(content_type, ';'))
+               *(strchr(content_type, ';')) = '\0';
+       striplt(content_type);
+
+       if (strlen(boundary) > 0) {
+               is_multipart = 1;
+       } else {
+               is_multipart = 0;
+       }
+
+       /** If this is a multipart message, then recursively process it */
+       part_start = NULL;
+       if (is_multipart) {
+
+               /** Tell the client about this message's multipartedness */
+               if (PreMultiPartCallBack != NULL) {
+                       PreMultiPartCallBack("", "", partnum, "",
+                               NULL, content_type, charset,
+                               0, encoding, userdata);
+               }
+
+               /** Figure out where the boundaries are */
+               snprintf(startary, SIZ, "--%s", boundary);
+               snprintf(endary, SIZ, "--%s--", boundary);
+               startary_len = strlen(startary);
+
+               part_start = NULL;
+               do {
+                       next_boundary = NULL;
+                       for (srch=ptr; srch<content_end; ++srch) {
+                               if (!memcmp(srch, startary, startary_len)) {
+                                       next_boundary = srch;
+                                       srch = content_end;
+                               }
+                       }
+
+                       if ( (part_start != NULL) && (next_boundary != NULL) ) {
+                               part_end = next_boundary;
+                               --part_end;
+
+                               if (strlen(partnum) > 0) {
+                                       snprintf(nested_partnum,
+                                                sizeof nested_partnum,
+                                                "%s.%d", partnum,
+                                                ++part_seq);
+                               }
+                               else {
+                                       snprintf(nested_partnum,
+                                                sizeof nested_partnum,
+                                                "%d", ++part_seq);
+                               }
+                               the_mime_parser(nested_partnum,
+                                           part_start, part_end,
+                                               CallBack,
+                                               PreMultiPartCallBack,
+                                               PostMultiPartCallBack,
+                                               userdata,
+                                               dont_decode);
+                       }
+
+                       if (next_boundary != NULL) {
+                               /**
+                                * If we pass out of scope, don't attempt to
+                                * read past the end boundary. */
+                               if (!strcmp(next_boundary, endary)) {
+                                       ptr = content_end;
+                               }
+                               else {
+                                       /** Set up for the next part. */
+                                       part_start = strstr(next_boundary, "\n");
+                                       ++part_start;
+                                       ptr = part_start;
+                               }
+                       }
+                       else {
+                               /** Invalid end of multipart.  Bail out! */
+                               ptr = content_end;
+                       }
+               } while ( (ptr < content_end) && (next_boundary != NULL) );
+
+               if (PostMultiPartCallBack != NULL) {
+                       PostMultiPartCallBack("", "", partnum, "", NULL,
+                               content_type, charset, 0, encoding, userdata);
+               }
+               goto end_parser;
+       }
+
+       /** If it's not a multipart message, then do something with it */
+       if (!is_multipart) {
+               part_start = ptr;
+               length = 0;
+               while (ptr < content_end) {
+                       ++ptr;
+                       ++length;
+               }
+               part_end = content_end;
+               /** fix an off-by-one error */
+               --part_end;
+               --length;
+               
+               /** Truncate if the header told us to */
+               if ( (content_length > 0) && (length > content_length) ) {
+                       length = content_length;
+               }
+
+               /**
+                * Sometimes the "name" field is tacked on to Content-type,
+                * and sometimes it's tacked on to Content-disposition.  Use
+                * whichever one we have.
+                */
+               if (strlen(content_disposition_name) > strlen(content_type_name)) {
+                       name = content_disposition_name;
+               }
+               else {
+                       name = content_type_name;
+               }
+       
+               /*
+               lprintf(9, "mime_decode part=%s, len=%d, type=%s, charset=%s, encoding=%s\n",
+                       partnum, length, content_type, charset, encoding);
+               */
+
+               /**
+                * Ok, we've got a non-multipart part here, so do something with it.
+                */
+               mime_decode(partnum,
+                       part_start, length,
+                       content_type, charset, encoding, disposition,
+                       name, filename,
+                       CallBack, NULL, NULL,
+                       userdata, dont_decode
+               );
+
+               /**
+                * Now if it's an encapsulated message/rfc822 then we have to recurse into it
+                */
+               if (!strcasecmp(content_type, "message/rfc822")) {
+
+                       if (PreMultiPartCallBack != NULL) {
+                               PreMultiPartCallBack("", "", partnum, "",
+                                       NULL, content_type, charset,
+                                       0, encoding, userdata);
+                       }
+                       if (CallBack != NULL) {
+                               if (strlen(partnum) > 0) {
+                                       snprintf(nested_partnum,
+                                                sizeof nested_partnum,
+                                                "%s.%d", partnum,
+                                                ++part_seq);
+                               }
+                               else {
+                                       snprintf(nested_partnum,
+                                                sizeof nested_partnum,
+                                                "%d", ++part_seq);
+                               }
+                               the_mime_parser(nested_partnum,
+                                       part_start, part_end,
+                                       CallBack,
+                                       PreMultiPartCallBack,
+                                       PostMultiPartCallBack,
+                                       userdata,
+                                       dont_decode
+                               );
+                       }
+                       if (PostMultiPartCallBack != NULL) {
+                               PostMultiPartCallBack("", "", partnum, "", NULL,
+                                       content_type, charset, 0, encoding, userdata);
+                       }
+
+
+               }
+
+       }
+
+end_parser:    /** free the buffers!  end the oppression!! */
+       free(boundary);
+       free(startary);
+       free(endary);   
+       free(header);
+       free(content_type);
+       free(charset);
+       free(encoding);
+       free(content_type_name);
+       free(content_disposition_name);
+       free(filename);
+       free(disposition);
+}
+
+
+
+/**
+ * \brief Entry point for the MIME parser.
+ * (This function expects to be fed HEADERS + CONTENT)
+ * Note: NULL can be supplied as content_end; in this case, the message is
+ * considered to have ended when the parser encounters a 0x00 byte.
+ * \param content_start todo ?????????
+ * \param content_end todo 
+ * \param CallBack todo
+ * \param PreMultiPartCallBack todo
+ * \param PostMultiPartCallBack todo
+ * \param userdata todo
+ * \param dont_decode todo
+ */
+void mime_parser(char *content_start,
+               char *content_end,
+
+                void (*CallBack)
+                 (char *cbname,
+                  char *cbfilename,
+                  char *cbpartnum,
+                  char *cbdisp,
+                  void *cbcontent,
+                  char *cbtype,
+                  char *cbcharset,
+                  size_t cblength,
+                  char *cbencoding,
+                  void *cbuserdata),
+
+                void (*PreMultiPartCallBack)
+                 (char *cbname,
+                  char *cbfilename,
+                  char *cbpartnum,
+                  char *cbdisp,
+                  void *cbcontent,
+                  char *cbtype,
+                  char *cbcharset,
+                  size_t cblength,
+                  char *cbencoding,
+                  void *cbuserdata),
+
+                void (*PostMultiPartCallBack)
+                 (char *cbname,
+                  char *cbfilename,
+                  char *cbpartnum,
+                  char *cbdisp,
+                  void *cbcontent,
+                  char *cbtype,
+                  char *cbcharset,
+                  size_t cblength,
+                  char *cbencoding,
+                  void *cbuserdata),
+
+                 void *userdata,
+                 int dont_decode
+)
+{
+
+       the_mime_parser("", content_start, content_end,
+                       CallBack,
+                       PreMultiPartCallBack,
+                       PostMultiPartCallBack,
+                       userdata, dont_decode);
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/mime_parser.h b/webcit/src/mime_parser.h
new file mode 100644 (file)
index 0000000..b82cd68
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ */
+
+/*
+ * Here's a bunch of stupid magic to make the MIME parser portable between
+ * Citadel and WebCit.
+ */
+#ifndef SIZ
+#define SIZ    4096
+#endif
+
+
+/* 
+ * Declarations for functions in the parser
+ */
+
+void extract_key(char *target, char *source, char *key);
+
+void mime_parser(char *content_start, char *content_end,
+               void (*CallBack)
+                       (char *cbname,
+                       char *cbfilename,
+                       char *cbpartnum,
+                       char *cbdisp,
+                       void *cbcontent,
+                       char *cbtype,
+                       char *cbcharset,
+                       size_t cblength,
+                       char *cbencoding,
+                       void *cbuserdata),
+               void (*PreMultiPartCallBack)
+                       (char *cbname,
+                       char *cbfilename,
+                       char *cbpartnum,
+                       char *cbdisp,
+                       void *cbcontent,
+                       char *cbtype,
+                       char *cbcharset,
+                       size_t cblength,
+                       char *cbencoding,
+                       void *cbuserdata),
+               void (*PostMultiPartCallBack)
+                       (char *cbname,
+                       char *cbfilename,
+                       char *cbpartnum,
+                       char *cbdisp,
+                       void *cbcontent,
+                       char *cbtype,
+                       char *cbcharset,
+                       size_t cblength,
+                       char *cbencoding,
+                       void *cbuserdata),
+               void *userdata,
+               int dont_decode
+               );
diff --git a/webcit/src/netconf.c b/webcit/src/netconf.c
new file mode 100644 (file)
index 0000000..f380296
--- /dev/null
@@ -0,0 +1,320 @@
+/* 
+ * $Id$
+ */
+/**
+ * \defgroup NetShareConf Functions which handle network and sharing configuration.
+ *
+ * \ingroup CitadelConfig
+ */
+/*@{*/
+#include "webcit.h"
+
+/**
+ * \brief edit a network node
+ */
+void edit_node(void) {
+       char buf[SIZ];
+       char node[SIZ];
+       char cnode[SIZ];
+       FILE *fp;
+
+       if (strlen(bstr("ok_button")) > 0) {
+               strcpy(node, bstr("node") );
+               fp = tmpfile();
+               if (fp != NULL) {
+                       serv_puts("CONF getsys|application/x-citadel-ignet-config");
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] == '1') {
+                               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                                       extract_token(cnode, buf, 0, '|', sizeof cnode);
+                                       if (strcasecmp(node, cnode)) {
+                                               fprintf(fp, "%s\n", buf);
+                                       }
+                               }
+                       fprintf(fp, "%s|%s|%s|%s\n", 
+                               bstr("node"),
+                               bstr("secret"),
+                               bstr("host"),
+                               bstr("port") );
+                       }
+                       rewind(fp);
+
+                       serv_puts("CONF putsys|application/x-citadel-ignet-config");
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] == '4') {
+                               while (fgets(buf, sizeof buf, fp) != NULL) {
+                                       buf[strlen(buf)-1] = 0;
+                                       serv_puts(buf);
+                               }
+                               serv_puts("000");
+                       }
+                       fclose(fp);
+               }
+       }
+
+       display_netconf();
+}
+
+
+/**
+ * \brief add a node
+ */
+void display_add_node(void)
+{
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
+       wprintf("<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Add a new node"));
+       wprintf("</SPAN>");
+       wprintf("</TD></TR></TABLE>\n");
+       wprintf("</div>\n<div id=\"content\">\n");
+
+       wprintf("<FORM METHOD=\"POST\" action=\"edit_node\">\n");
+       wprintf("<CENTER><TABLE border=0>\n");
+       wprintf("<TR><TD>%s</TD>", _("Node name"));
+       wprintf("<TD><INPUT TYPE=\"text\" NAME=\"node\" MAXLENGTH=\"16\"></TD></TR>\n");
+       wprintf("<TR><TD>%s</TD>", _("Shared secret"));
+       wprintf("<TD><INPUT TYPE=\"password\" NAME=\"secret\" MAXLENGTH=\"16\"></TD></TR>\n");
+       wprintf("<TR><TD>%s</TD>", _("Host or IP address"));
+       wprintf("<TD><INPUT TYPE=\"text\" NAME=\"host\" MAXLENGTH=\"64\"></TD></TR>\n");
+       wprintf("<TR><TD>%s</TD>", _("Port number"));
+       wprintf("<TD><INPUT TYPE=\"text\" NAME=\"port\" MAXLENGTH=\"8\"></TD></TR>\n");
+       wprintf("</TABLE><br />");
+               wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Add node"));
+       wprintf("&nbsp;");
+               wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
+       wprintf("</CENTER></FORM>\n");
+
+       wDumpContent(1);
+}
+
+/**
+ * \brief modify an existing node
+ */
+void display_edit_node(void)
+{
+       char buf[512];
+       char node[256];
+       char cnode[256];
+       char csecret[256];
+       char chost[256];
+       char cport[256];
+
+       strcpy(node, bstr("node"));
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
+       wprintf("<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Edit node configuration for "));
+       escputs(node);
+       wprintf("</SPAN>\n");
+       wprintf("</TD></TR></TABLE>\n");
+       wprintf("</div>\n<div id=\"content\">\n");
+
+       serv_puts("CONF getsys|application/x-citadel-ignet-config");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       extract_token(cnode, buf, 0, '|', sizeof cnode);
+                       extract_token(csecret, buf, 1, '|', sizeof csecret);
+                       extract_token(chost, buf, 2, '|', sizeof chost);
+                       extract_token(cport, buf, 3, '|', sizeof cport);
+
+                       if (!strcasecmp(node, cnode)) {
+                               wprintf("<FORM METHOD=\"POST\" action=\"edit_node\">\n");
+                               wprintf("<CENTER><TABLE border=0>\n");
+                               wprintf("<TR><TD>");
+                               wprintf(_("Node name"));
+                               wprintf("</TD>");
+                               wprintf("<TD><INPUT TYPE=\"text\" NAME=\"node\" MAXLENGTH=\"16\" VALUE=\"%s\"></TD></TR>\n", cnode);
+                               wprintf("<TR><TD>");
+                               wprintf(_("Shared secret"));
+                               wprintf("</TD>");
+                               wprintf("<TD><INPUT TYPE=\"password\" NAME=\"secret\" MAXLENGTH=\"16\" VALUE=\"%s\"></TD></TR>\n", csecret);
+                               wprintf("<TR><TD>");
+                               wprintf(_("Host or IP address"));
+                               wprintf("</TD>");
+                               wprintf("<TD><INPUT TYPE=\"text\" NAME=\"host\" MAXLENGTH=\"64\" VALUE=\"%s\"></TD></TR>\n", chost);
+                               wprintf("<TR><TD>");
+                               wprintf(_("Port number"));
+                               wprintf("</TD>");
+                               wprintf("<TD><INPUT TYPE=\"text\" NAME=\"port\" MAXLENGTH=\"8\" VALUE=\"%s\"></TD></TR>\n", cport);
+                               wprintf("</TABLE><br />");
+                               wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">",
+                                       _("Save changes"));
+                               wprintf("&nbsp;");
+                               wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">",
+                                       _("Cancel"));
+                               wprintf("</CENTER></FORM>\n");
+                       }
+
+               }
+       }
+
+       else {          /** command error getting configuration */
+               wprintf("%s<br />\n", &buf[4]);
+       }
+
+       wDumpContent(1);
+}
+
+
+/**
+ * \brief display all configured nodes
+ */
+void display_netconf(void)
+{
+       char buf[SIZ];
+       char node[SIZ];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
+       wprintf("<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Network configuration"));
+       wprintf("</SPAN>\n");
+       wprintf("</TD></TR></TABLE>\n");
+       wprintf("</div>\n<div id=\"content\">\n");
+
+       wprintf("<CENTER>");
+       wprintf("<a href=\"display_add_node\">");
+       wprintf(_("Add a new node"));
+       wprintf("</A><br />\n");
+       wprintf("</CENTER>");
+
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
+       wprintf("<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Currently configured nodes"));
+       wprintf("</SPAN>\n");
+       wprintf("</TD></TR></TABLE>\n");
+       serv_puts("CONF getsys|application/x-citadel-ignet-config");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               wprintf("<CENTER><TABLE border=0>\n");
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       extract_token(node, buf, 0, '|', sizeof node);
+                       wprintf("<TR><TD><FONT SIZE=+1>");
+                       escputs(node);
+                       wprintf("</FONT></TD>");
+                       wprintf("<TD><a href=\"display_edit_node&node=");
+                       urlescputs(node);
+                       wprintf("\">");
+                       wprintf(_("(Edit)"));
+                       wprintf("</A></TD>");
+                       wprintf("<TD><a href=\"display_confirm_delete_node&node=");
+                       urlescputs(node);
+                       wprintf("\">");
+                       wprintf(_("(Delete)"));
+                       wprintf("</A></TD>");
+                       wprintf("</TR>\n");
+               }
+               wprintf("</TABLE></CENTER>\n");
+       }
+       wDumpContent(1);
+}
+
+/**
+ * \brief display the dialog to verify the deletion
+ */
+void display_confirm_delete_node(void)
+{
+       char node[SIZ];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
+       wprintf("<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Confirm delete"));
+       wprintf("</SPAN>\n");
+       wprintf("</TD></TR></TABLE>\n");
+       wprintf("</div>\n<div id=\"content\">\n");
+
+       strcpy(node, bstr("node"));
+       wprintf("<CENTER>");
+       wprintf(_("Are you sure you want to delete "));
+       wprintf("<FONT SIZE=+1>");
+       escputs(node);
+       wprintf("</FONT>?<br />\n");
+       wprintf("<a href=\"delete_node&node=");
+       urlescputs(node);
+       wprintf("\">");
+       wprintf(_("Yes"));
+       wprintf("</A>&nbsp;&nbsp;&nbsp;");
+       wprintf("<a href=\"display_netconf\">");
+       wprintf(_("No"));
+       wprintf("</A><br />\n");
+       wDumpContent(1);
+}
+
+/**
+ * \brief actually delete the node
+ */
+void delete_node(void)
+{
+       char buf[SIZ];
+       char node[SIZ];
+       char cnode[SIZ];
+       FILE *fp;
+
+       strcpy(node, bstr("node") );
+       fp = tmpfile();
+       if (fp != NULL) {
+               serv_puts("CONF getsys|application/x-citadel-ignet-config");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '1') {
+                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                               extract_token(cnode, buf, 0, '|', sizeof cnode);
+                               if (strcasecmp(node, cnode)) {
+                                       fprintf(fp, "%s\n", buf);
+                               }
+                       }
+               }
+               rewind(fp);
+
+               serv_puts("CONF putsys|application/x-citadel-ignet-config");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '4') {
+                       while (fgets(buf, sizeof buf, fp) != NULL) {
+                               buf[strlen(buf)-1] = 0;
+                               serv_puts(buf);
+                       }
+                       serv_puts("000");
+               }
+               fclose(fp);
+       }
+
+       display_netconf();
+}
+
+/**
+ * \brief add a new node
+ */
+void add_node(void)
+{
+       char node[SIZ];
+       char buf[SIZ];
+
+       strcpy(node, bstr("node"));
+
+       if (strlen(bstr("add_button")) > 0)  {
+               sprintf(buf, "NSET addnode|%s", node);
+               serv_puts(buf);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '1') {
+                       output_headers(1, 1, 0, 0, 0, 0);
+                       server_to_text();
+                       wprintf("<a href=\"display_netconf\">");
+                       wprintf(_("Back to menu"));
+                       wprintf("</A>\n");
+                       wDumpContent(1);
+               } else {
+                       strcpy(WC->ImportantMessage, &buf[4]);
+                       display_netconf();
+               }
+       }
+}
+
+
+/*@}*/
diff --git a/webcit/src/notes.c b/webcit/src/notes.c
new file mode 100644 (file)
index 0000000..137480a
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup StickyNotes Functions which handle "sticky notes"
+ * \ingroup WebcitDisplayItems
+ */
+/*@{*/
+#include "webcit.h"
+#include "groupdav.h"
+#include "webserver.h"
+
+/**
+ * \brief display sticky notes
+ * \param msgnum the citadel mesage number
+ */
+void display_note(long msgnum)
+{
+       char buf[SIZ];
+       char notetext[SIZ];
+       char display_notetext[SIZ];
+       char eid[128];
+       int in_text = 0;
+       int i;
+
+       wprintf("<IMG ALIGN=MIDDLE src=\"static/storenotes_48x.gif\">\n");
+
+       serv_printf("MSG0 %ld", msgnum);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               wprintf("%s<br />\n", &buf[4]);
+               return;
+       }
+
+       strcpy(notetext, "");
+       strcpy(eid, "");
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+
+               /** Fill the buffer */
+               if ( (in_text) && (strlen(notetext) < SIZ-256) ) {
+                       strcat(notetext, buf);
+               }
+
+               if ( (!in_text) && (!strncasecmp(buf, "exti=", 5)) ) {
+                       safestrncpy(eid, &buf[5], sizeof eid);
+               }
+
+               if ( (!in_text) && (!strcasecmp(buf, "text")) ) {
+                       in_text = 1;
+               }
+       }
+
+       /** Now sanitize the buffer */
+       for (i=0; i<strlen(notetext); ++i) {
+               if (isspace(notetext[i])) notetext[i] = ' ';
+       }
+
+       /** Make it HTML-happy and print it. */
+       stresc(display_notetext, notetext, 0, 0);
+       if (strlen(eid) > 0) {
+               wprintf("<span id=\"note%s\">%s</span><br />\n", eid, display_notetext);
+       }
+       else {
+               wprintf("<span id=\"note%ld\">%s</span><br />\n", msgnum, display_notetext);
+       }
+
+       /** Offer in-place editing. */
+       if (strlen(eid) > 0) {
+               wprintf("<script type=\"text/javascript\">"
+                       " new Ajax.InPlaceEditor('note%s', 'updatenote?eid=%s', {rows:5,cols:72}); "
+                       "</script>\n",
+                       eid,
+                       eid
+               );
+       }
+}
+
+
+/**
+ * \brief  This gets called by the Ajax.InPlaceEditor when we save a note.
+ */
+void updatenote(void)
+{
+       char buf[SIZ];
+       char notetext[SIZ];
+       char display_notetext[SIZ];
+       long msgnum;
+       int in_text = 0;
+       int i;
+
+       serv_printf("ENT0 1||0|0||||||%s", bstr("eid"));
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '4') {
+               text_to_server(bstr("value"));
+               serv_puts("000");
+       }
+
+       begin_ajax_response();
+       msgnum = locate_message_by_uid(bstr("eid"));
+       if (msgnum >= 0L) {
+               serv_printf("MSG0 %ld", msgnum);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '1') {
+                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               
+                               /** Fill the buffer */
+                               if ( (in_text) && (strlen(notetext) < SIZ-256) ) {
+                                       strcat(notetext, buf);
+                               }
+               
+                               if ( (!in_text) && (!strcasecmp(buf, "text")) ) {
+                                       in_text = 1;
+                               }
+                       }
+                       /** Now sanitize the buffer */
+                       for (i=0; i<strlen(notetext); ++i) {
+                               if (isspace(notetext[i])) notetext[i] = ' ';
+                       }
+               
+                       /** Make it HTML-happy and print it. */
+                       stresc(display_notetext, notetext, 0, 0);
+                       wprintf("%s\n", display_notetext);
+               }
+       }
+       else {
+               wprintf("%s", _("An error has occurred."));
+       }
+
+       end_ajax_response();
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/paging.c b/webcit/src/paging.c
new file mode 100644 (file)
index 0000000..9aee17f
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup PageFunc Functions which implement the chat and paging facilities.
+ * \ingroup ClientPower
+ */
+/*@{*/
+#include "webcit.h"
+
+/**
+ * \brief display the form for paging (x-messaging) another user
+ */
+void display_page(void)
+{
+       char recp[SIZ];
+
+       strcpy(recp, bstr("recp"));
+
+        output_headers(1, 1, 2, 0, 0, 0);
+        wprintf("<div id=\"banner\">\n"
+                "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+                "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Send instant message"));
+       wprintf("</SPAN>"
+                "</TD></TR></TABLE>\n"
+                "</div>\n<div id=\"content\">\n"
+        );
+                                                                                                                             
+        wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       wprintf(_("Send an instant message to: "));
+       escputs(recp);
+       wprintf("<br>\n");
+
+       wprintf("<FORM METHOD=\"POST\" action=\"page_user\">\n");
+
+       wprintf("<TABLE border=0 width=100%%><TR><TD>\n");
+
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"recp\" VALUE=\"");
+       escputs(recp);
+       wprintf("\">\n");
+
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"closewin\" VALUE=\"");
+       escputs(bstr("closewin"));
+       wprintf("\">\n");
+
+       wprintf(_("Enter message text:"));
+       wprintf("<br />");
+
+       wprintf("<TEXTAREA NAME=\"msgtext\" wrap=soft ROWS=5 COLS=40 "
+               "WIDTH=40></TEXTAREA>\n");
+
+       wprintf("</TD></TR></TABLE><br />\n");
+
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"send_button\" VALUE=\"%s\">", _("Send message"));
+       wprintf("<br /><a href=\"javascript:window.close();\"%s</A>\n", _("Cancel"));
+
+       wprintf("</FORM></CENTER>\n");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+/**
+ * \brief page another user
+ */
+void page_user(void)
+{
+       char recp[SIZ];
+       char buf[SIZ];
+       char closewin[SIZ];
+
+        output_headers(1, 1, 2, 0, 0, 0);
+        wprintf("<div id=\"banner\">\n"
+                "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+                "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Add or edit an event"));
+       wprintf("</SPAN>"
+                "</TD></TR></TABLE>\n"
+                "</div>\n<div id=\"content\">\n"
+        );
+                                                                                                                             
+       strcpy(recp, bstr("recp"));
+       strcpy(closewin, bstr("closewin"));
+
+       if (strlen(bstr("send_button")) == 0) {
+               wprintf("<EM>");
+               wprintf(_("Message was not sent."));
+               wprintf("</EM><br />\n");
+       } else {
+               serv_printf("SEXP %s|-", recp);
+               serv_getln(buf, sizeof buf);
+
+               if (buf[0] == '4') {
+                       text_to_server(bstr("msgtext"));
+                       serv_puts("000");
+                       wprintf("<EM>");
+                       wprintf(_("Message has been sent to "));
+                       escputs(recp);
+                       wprintf(".</EM><br />\n");
+               }
+               else {
+                       wprintf("<em>%s</em><br />\n", &buf[4]);
+               }
+       }
+       
+       if (!strcasecmp(closewin, "yes")) {
+               wprintf("<CENTER><a href=\"javascript:window.close();\">");
+               wprintf(_("[ close window ]"));
+               wprintf("</A></CENTER>\n");
+       }
+
+       wDumpContent(1);
+}
+
+
+
+/**
+ * \brief multiuser chat
+ */
+void do_chat(void)
+{
+       char buf[SIZ];
+
+       /** First, check to make sure we're still allowed in this room. */
+       serv_printf("GOTO %s", WC->wc_roomname);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               smart_goto("_BASEROOM_");
+               return;
+       }
+
+       /**
+        * If the chat socket is still open from a previous chat,
+        * close it -- because it might be stale or in the wrong room.
+        */
+       if (WC->chat_sock < 0) {
+               close(WC->chat_sock);
+               WC->chat_sock = (-1);
+       }
+
+       /**
+        * WebCit Chat works by having transmit, receive, and refresh
+        * frames.  Load the frameset.  (This isn't AJAX but the headers
+        * output by begin_ajax_response() happen to be the ones we need.)
+        */
+       begin_ajax_response();
+       do_template("chatframeset");
+       end_ajax_response();
+       return;
+}
+
+
+/**
+ * \brief display page popup
+ * If there are instant messages waiting, and we notice that we haven't checked them in
+ * a while, it probably means that we need to open the instant messenger window.
+ */
+void page_popup(void)
+{
+       char buf[SIZ];
+
+       /** JavaScript function to alert the user that popups are probably blocked */
+       wprintf("<script type=\"text/javascript\">      "
+               "function PopUpFailed() {       "
+               " alert(\"%s\");        "
+               "}      "
+               "</script>\n",
+               _("You have one or more instant messages waiting, but the Citadel Instant Messenger "
+                 "window failed to open.  This is probably because you have a popup blocker "
+                 "installed.  Please configure your popup blocker to allow popups from this site "
+                 "if you wish to receive instant messages.")
+       );
+
+       /** First, do the check as part of our page load. */
+       serv_puts("NOOP");
+       serv_getln(buf, sizeof buf);
+       if (buf[3] == '*') {
+               if ((time(NULL) - WC->last_pager_check) > 60) {
+                       wprintf("<script type=\"text/javascript\">"
+                               " var oWin = window.open('static/instant_messenger.html', "
+                               " 'CTDL_MESSENGER', 'width=700,height=400');    "
+                               " if (oWin==null || typeof(oWin)==\"undefined\") {      "
+                               "  PopUpFailed();       "
+                               " }     "
+                               "</script>"
+                       );      
+               }
+       }
+
+       /** Then schedule it to happen again a minute from now if the user is idle. */
+       wprintf("<script type=\"text/javascript\">      "
+               " function HandleSslp(sslg_xmlresponse) {       "
+               "  sslg_response = sslg_xmlresponse.responseText.substr(0, 1);  "
+               "  if (sslg_response == 'Y') {  "
+               "   var oWin = window.open('static/instant_messenger.html', 'CTDL_MESSENGER',   "
+               "    'width=700,height=400');   "
+               "   if (oWin==null || typeof(oWin)==\"undefined\") {    "
+               "    PopUpFailed();     "
+               "   }   "
+               "  }    "
+               " }     "
+               " function CheckPager() {       "
+               "  new Ajax.Request('sslg', { method: 'get', parameters: Math.random(), "
+               "   onSuccess: HandleSslp } );  "
+               " }     "
+               " new PeriodicalExecuter(CheckPager, 30);       "
+               "</script>      "
+       );
+}
+
+
+
+/**
+ * \brief Support function for chat
+ * make sure the chat socket is connected
+ * and in chat mode.
+ */
+int setup_chat_socket(void) {
+       char buf[SIZ];
+       int i;
+       int good_chatmode = 0;
+
+       if (WC->chat_sock < 0) {
+
+               if (!strcasecmp(ctdlhost, "uds")) {
+                       /** unix domain socket */
+                       sprintf(buf, "%s/citadel.socket", ctdlport);
+                       WC->chat_sock = uds_connectsock(buf);
+               }
+               else {
+                       /** tcp socket */
+                       WC->chat_sock = tcp_connectsock(ctdlhost, ctdlport);
+               }
+
+               if (WC->chat_sock < 0) {
+                       return(errno);
+               }
+
+               /** Temporarily swap the serv and chat sockets during chat talk */
+               i = WC->serv_sock;
+               WC->serv_sock = WC->chat_sock;
+               WC->chat_sock = i;
+
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       serv_printf("USER %s", WC->wc_username);
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] == '3') {
+                               serv_printf("PASS %s", WC->wc_password);
+                               serv_getln(buf, sizeof buf);
+                               if (buf[0] == '2') {
+                                       serv_printf("GOTO %s", WC->wc_roomname);
+                                       serv_getln(buf, sizeof buf);
+                                       if (buf[0] == '2') {
+                                               serv_puts("CHAT");
+                                               serv_getln(buf, sizeof buf);
+                                               if (buf[0] == '8') {
+                                                       good_chatmode = 1;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               /** Unswap the sockets. */
+               i = WC->serv_sock;
+               WC->serv_sock = WC->chat_sock;
+               WC->chat_sock = i;
+
+               if (!good_chatmode) close(WC->serv_sock);
+
+       }
+       return(0);
+}
+
+
+
+/**
+ * \brief Receiving side of the chat window.  
+ * This is implemented in a
+ * tiny hidden IFRAME that just does JavaScript writes to
+ * other frames whenever it refreshes and finds new data.
+ */
+void chat_recv(void) {
+       int i;
+       struct pollfd pf;
+       int got_data = 0;
+       int end_chat_now = 0;
+       char buf[SIZ];
+       char cl_user[SIZ];
+       char cl_text[SIZ];
+       char *output_data = NULL;
+
+       output_headers(0, 0, 0, 0, 0, 0);
+
+       wprintf("Content-type: text/html; charset=utf-8\n");
+       wprintf("\n");
+       wprintf("<html>\n"
+               "<head>\n"
+               "<meta http-equiv=\"refresh\" content=\"3\" />\n"
+               "</head>\n"
+
+               "<body bgcolor=\"#FFFFFF\">\n"
+       );
+
+       if (setup_chat_socket() != 0) {
+               wprintf(_("An error occurred while setting up the chat socket."));
+               wprintf("</BODY></HTML>\n");
+               wDumpContent(0);
+               return;
+       }
+
+       /**
+        * See if there is any chat data waiting.
+        */
+       output_data = strdup("");
+       do {
+               got_data = 0;
+               pf.fd = WC->chat_sock;
+               pf.events = POLLIN;
+               pf.revents = 0;
+               if (poll(&pf, 1, 1) > 0) if (pf.revents & POLLIN) {
+                       ++got_data;
+
+                       /** Temporarily swap the serv and chat sockets during chat talk */
+                       i = WC->serv_sock;
+                       WC->serv_sock = WC->chat_sock;
+                       WC->chat_sock = i;
+       
+                       serv_getln(buf, sizeof buf);
+
+                       if (!strcmp(buf, "000")) {
+                               strcpy(buf, ":|");
+                               strcat(buf, _("Now exiting chat mode."));
+                               end_chat_now = 1;
+                       }
+                       
+                       /** Unswap the sockets. */
+                       i = WC->serv_sock;
+                       WC->serv_sock = WC->chat_sock;
+                       WC->chat_sock = i;
+
+                       /** Append our output data */
+                       output_data = realloc(output_data, strlen(output_data) + strlen(buf) + 4);
+                       strcat(output_data, buf);
+                       strcat(output_data, "\n");
+               }
+
+       } while ( (got_data) && (!end_chat_now) );
+
+       if (end_chat_now) {
+               close(WC->chat_sock);
+               WC->chat_sock = (-1);
+               wprintf("<img src=\"static/blank.gif\" onLoad=\"parent.window.close();\">\n");
+       }
+
+       if (strlen(output_data) > 0) {
+
+               if (output_data[strlen(output_data)-1] == '\n') {
+                       output_data[strlen(output_data)-1] = 0;
+               }
+
+               /** Output our fun to the other frame. */
+               wprintf("<img src=\"static/blank.gif\" WIDTH=1 HEIGHT=1\n"
+                       "onLoad=\" \n"
+               );
+
+               for (i=0; i<num_tokens(output_data, '\n'); ++i) {
+                       extract_token(buf, output_data, i, '\n', sizeof buf);
+                       extract_token(cl_user, buf, 0, '|', sizeof cl_user);
+                       extract_token(cl_text, buf, 1, '|', sizeof cl_text);
+
+                       if (strcasecmp(cl_text, "NOOP")) {
+
+                               wprintf("parent.chat_transcript.document.write('");
+       
+                               if (strcasecmp(cl_user, WC->last_chat_user)) {
+                                       wprintf("<TABLE border=0 WIDTH=100%% "
+                                               "CELLSPACING=1 CELLPADDING=0 "
+                                               "BGCOLOR=&quot;#FFFFFF&quot;>"
+                                               "<TR><TD></TR></TD></TABLE>"
+                                       );
+       
+                               }
+
+                               wprintf("<TABLE border=0 WIDTH=100%% "
+                                       "CELLSPACING=0 CELLPADDING=0 "
+                                       "BGCOLOR=&quot;#EEEEEE&quot;>");
+       
+                               wprintf("<TR><TD>");
+       
+                               if (!strcasecmp(cl_user, ":")) {
+                                       wprintf("<I>");
+                               }
+
+                               if (strcasecmp(cl_user, WC->last_chat_user)) {
+                                       wprintf("<B>");
+       
+                                       if (!strcasecmp(cl_user, WC->wc_fullname)) {
+                                               wprintf("<FONT COLOR=&quot;#FF0000&quot;>");
+                                       }
+                                       else {
+                                               wprintf("<FONT COLOR=&quot;#0000FF&quot;>");
+                                       }
+                                       jsescputs(cl_user);
+       
+                                       wprintf("</FONT>: </B>");
+                               }
+                               else {
+                                       wprintf("&nbsp;&nbsp;&nbsp;");
+                               }
+                               jsescputs(cl_text);
+                               if (!strcasecmp(cl_user, ":")) {
+                                       wprintf("</I>");
+                               }
+
+                               wprintf("</TD></TR></TABLE>");
+                               wprintf("'); \n");
+
+                               strcpy(WC->last_chat_user, cl_user);
+                       }
+               }
+
+               wprintf("parent.chat_transcript.scrollTo(999999,999999);\">\n");
+       }
+
+       free(output_data);
+
+       wprintf("</BODY></HTML>\n");
+       wDumpContent(0);
+}
+
+
+/**
+ * \brief sending side of the chat window
+ */
+void chat_send(void) {
+       int i;
+       char send_this[SIZ];
+       char buf[SIZ];
+
+       output_headers(0, 0, 0, 0, 0, 0);
+       wprintf("Content-type: text/html; charset=utf-8\n");
+       wprintf("\n");
+       wprintf("<HTML>"
+               "<BODY onLoad=\"document.chatsendform.send_this.focus();\" >"
+       );
+
+       if (bstr("send_this") != NULL) {
+               strcpy(send_this, bstr("send_this"));
+       }
+       else {
+               strcpy(send_this, "");
+       }
+
+       if (strlen(bstr("help_button")) > 0) {
+               strcpy(send_this, "/help");
+       }
+
+       if (strlen(bstr("list_button")) > 0) {
+               strcpy(send_this, "/who");
+       }
+
+       if (strlen(bstr("exit_button")) > 0) {
+               strcpy(send_this, "/quit");
+       }
+
+       if (setup_chat_socket() != 0) {
+               wprintf(_("An error occurred while setting up the chat socket."));
+               wprintf("</BODY></HTML>\n");
+               wDumpContent(0);
+               return;
+       }
+
+       /** Temporarily swap the serv and chat sockets during chat talk */
+       i = WC->serv_sock;
+       WC->serv_sock = WC->chat_sock;
+       WC->chat_sock = i;
+
+       while (strlen(send_this) > 0) {
+               if (strlen(send_this) < 67) {
+                       serv_puts(send_this);
+                       strcpy(send_this, "");
+               }
+               else {
+                       for (i=55; i<67; ++i) {
+                               if (send_this[i] == ' ') break;
+                       }
+                       strncpy(buf, send_this, i);
+                       buf[i] = 0;
+                       strcpy(send_this, &send_this[i]);
+                       serv_puts(buf);
+               }
+       }
+
+       /** Unswap the sockets. */
+       i = WC->serv_sock;
+       WC->serv_sock = WC->chat_sock;
+       WC->chat_sock = i;
+
+       wprintf("<FORM METHOD=\"POST\" action=\"chat_send\" NAME=\"chatsendform\">\n");
+       wprintf("<INPUT TYPE=\"text\" SIZE=\"80\" MAXLENGTH=\"%d\" "
+               "NAME=\"send_this\">\n", SIZ-10);
+       wprintf("<br />");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"send_button\" VALUE=\"%s\">\n", _("Send"));
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"help_button\" VALUE=\"%s\">\n", _("Help"));
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"list_button\" VALUE=\"%s\">\n", _("List users"));
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"exit_button\" VALUE=\"%s\">\n", _("Exit"));
+       wprintf("</FORM>\n");
+
+       wprintf("</BODY></HTML>\n");
+       wDumpContent(0);
+}
+
+/*@}*/
diff --git a/webcit/src/preferences.c b/webcit/src/preferences.c
new file mode 100644 (file)
index 0000000..a21468c
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup ManagePrefs Manage user preferences with a little help from the Citadel server.
+ * \ingroup CitadelConfig
+ *
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+#include "groupdav.h"
+
+
+/**
+ * \brief display preferences dialog
+ */
+void load_preferences(void) {
+       char buf[SIZ];
+       long msgnum = 0L;
+
+       serv_printf("GOTO %s", USERCONFIGROOM);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') return;
+       
+       serv_puts("MSGS ALL|0|1");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '8') {
+               serv_puts("subj|__ WebCit Preferences __");
+               serv_puts("000");
+       }
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               msgnum = atol(buf);
+       }
+
+       if (msgnum > 0L) {
+               serv_printf("MSG0 %ld", msgnum);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '1') {
+                       while (serv_getln(buf, sizeof buf),
+                               (strcmp(buf, "text") && strcmp(buf, "000"))) {
+                       }
+                       if (!strcmp(buf, "text")) {
+                               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                                       if (WC->preferences == NULL) {
+                                               WC->preferences = malloc(SIZ);
+                                               strcpy(WC->preferences, "");
+                                       }
+                                       else {
+                                               WC->preferences = realloc(
+                                                       WC->preferences,
+                                                       strlen(WC->preferences)
+                                                       +SIZ
+                                               );
+                                       }
+                                       strcat(WC->preferences, buf);
+                                       strcat(WC->preferences, "\n");
+                               }
+                       }
+               }
+       }
+
+       /** Go back to the room we're supposed to be in */
+       serv_printf("GOTO %s", WC->wc_roomname);
+       serv_getln(buf, sizeof buf);
+}
+
+/**
+ * \brief Goto the user's configuration room, creating it if necessary.
+ * \return 0 on success or nonzero upon failure.
+ */
+int goto_config_room(void) {
+       char buf[SIZ];
+
+       serv_printf("GOTO %s", USERCONFIGROOM);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') { /* try to create the config room if not there */
+               serv_printf("CRE8 1|%s|4|0", USERCONFIGROOM);
+               serv_getln(buf, sizeof buf);
+               serv_printf("GOTO %s", USERCONFIGROOM);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '2') return(1);
+       }
+       return(0);
+}
+
+/**
+ * \brief save the modifications
+ */
+void save_preferences(void) {
+       char buf[SIZ];
+       long msgnum = 0L;
+
+       if (goto_config_room() != 0) return;    /* oh well. */
+       serv_puts("MSGS ALL|0|1");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '8') {
+               serv_puts("subj|__ WebCit Preferences __");
+               serv_puts("000");
+       }
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               msgnum = atol(buf);
+       }
+
+       if (msgnum > 0L) {
+               serv_printf("DELE %ld", msgnum);
+               serv_getln(buf, sizeof buf);
+       }
+
+       serv_printf("ENT0 1||0|1|__ WebCit Preferences __|");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '4') {
+               serv_puts(WC->preferences);
+               serv_puts("");
+               serv_puts("000");
+       }
+
+       /** Go back to the room we're supposed to be in */
+       serv_printf("GOTO %s", WC->wc_roomname);
+       serv_getln(buf, sizeof buf);
+}
+
+/**
+ * \brief query the actual setting of key in the citadel database
+ * \param key config key to query
+ * \param value value to the key to get
+ * \param value_len length of the value string
+ */
+void get_preference(char *key, char *value, size_t value_len) {
+       int num_prefs;
+       int i;
+       char buf[SIZ];
+       char thiskey[SIZ];
+
+       strcpy(value, "");
+
+       num_prefs = num_tokens(WC->preferences, '\n');
+       for (i=0; i<num_prefs; ++i) {
+               extract_token(buf, WC->preferences, i, '\n', sizeof buf);
+               extract_token(thiskey, buf, 0, '|', sizeof thiskey);
+               if (!strcasecmp(thiskey, key)) {
+                       extract_token(value, buf, 1, '|', value_len);
+               }
+       }
+}
+
+/**
+ * \brief      Write a key into the webcit preferences database for this user
+ *
+ * \params     key             key whichs value is to be modified
+ * \param      value           value to set
+ * \param      save_to_server  1 = flush all data to the server, 0 = cache it for now
+ */
+void set_preference(char *key, char *value, int save_to_server) {
+       int num_prefs;
+       int i;
+       char buf[SIZ];
+       char thiskey[SIZ];
+       char *newprefs = NULL;
+
+       num_prefs = num_tokens(WC->preferences, '\n');
+       for (i=0; i<num_prefs; ++i) {
+               extract_token(buf, WC->preferences, i, '\n', sizeof buf);
+               if (num_tokens(buf, '|') == 2) {
+                       extract_token(thiskey, buf, 0, '|', sizeof thiskey);
+                       if (strcasecmp(thiskey, key)) {
+                               if (newprefs == NULL) newprefs = strdup("");
+                               newprefs = realloc(newprefs,
+                                       strlen(newprefs) + SIZ );
+                               strcat(newprefs, buf);
+                               strcat(newprefs, "\n");
+                       }
+               }
+       }
+
+
+       if (newprefs == NULL) newprefs = strdup("");
+       newprefs = realloc(newprefs, strlen(newprefs) + SIZ);
+       sprintf(&newprefs[strlen(newprefs)], "%s|%s\n", key, value);
+
+       free(WC->preferences);
+       WC->preferences = newprefs;
+
+       if (save_to_server) save_preferences();
+}
+
+
+
+
+/** 
+ * \brief display form for changing your preferences and settings
+ */
+void display_preferences(void)
+{
+       output_headers(1, 1, 2, 0, 0, 0);
+       char ebuf[300];
+       char buf[256];
+       char calhourformat[16];
+       int i;
+
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
+       wprintf("<img src=\"static/advanpage2_48x.gif\" ALT=\" \" ALIGN=MIDDLE>");
+       wprintf("<SPAN CLASS=\"titlebar\">&nbsp;");
+       wprintf(_("Preferences and settings"));
+       wprintf("</SPAN></TD><TD ALIGN=RIGHT>");
+       offer_start_page();
+       wprintf("</TD></TR></TABLE>\n");
+       wprintf("</div>\n"
+               "<div id=\"content\">\n");
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       /** begin form */
+       wprintf("<center>\n"
+               "<form name=\"prefform\" action=\"set_preferences\" "
+               "method=\"post\">\n"
+               "<table border=0 cellspacing=5 cellpadding=5>\n");
+
+       /**
+        * Room list view
+        */
+       get_preference("roomlistview", buf, sizeof buf);
+       wprintf("<tr><td>");
+       wprintf(_("Room list view"));
+       wprintf("</td><td>");
+
+       wprintf("<input type=\"radio\" name=\"roomlistview\" VALUE=\"folders\"");
+       if (!strcasecmp(buf, "folders")) wprintf(" checked");
+       wprintf(">");
+       wprintf(_("Tree (folders) view"));
+       wprintf("<br></input>\n");
+
+       wprintf("<input type=\"radio\" name=\"roomlistview\" VALUE=\"rooms\"");
+       if (!strcasecmp(buf, "rooms")) wprintf(" checked");
+       wprintf(">");
+       wprintf(_("Table (rooms) view"));
+       wprintf("<br></input>\n");
+
+       wprintf("</td></tr>\n");
+
+       /**
+        * Calendar hour format
+        */
+       get_preference("calhourformat", calhourformat, sizeof calhourformat);
+       if (calhourformat[0] == 0) strcpy(calhourformat, "12");
+       wprintf("<tr><td>");
+       wprintf(_("Calendar hour format"));
+       wprintf("</td><td>");
+
+       wprintf("<input type=\"radio\" name=\"calhourformat\" VALUE=\"12\"");
+       if (!strcasecmp(calhourformat, "12")) wprintf(" checked");
+       wprintf(">");
+       wprintf(_("12 hour (am/pm)"));
+       wprintf("<br></input>\n");
+
+       wprintf("<input type=\"radio\" name=\"calhourformat\" VALUE=\"24\"");
+       if (!strcasecmp(calhourformat, "24")) wprintf(" checked");
+       wprintf(">");
+       wprintf(_("24 hour"));
+       wprintf("<br></input>\n");
+
+       wprintf("</td></tr>\n");
+
+       /**
+        * Calendar day view -- day start time
+        */
+       get_preference("daystart", buf, sizeof buf);
+       if (buf[0] == 0) strcpy(buf, "8");
+       wprintf("<tr><td>");
+       wprintf(_("Calendar day view begins at:"));
+       wprintf("</td><td>");
+
+       wprintf("<SELECT NAME=\"daystart\" SIZE=\"1\">\n");
+       for (i=0; i<=23; ++i) {
+
+               if (!strcasecmp(calhourformat, "24")) {
+                       wprintf("<OPTION %s VALUE=\"%d\">%d:00</OPTION>\n",
+                               ((atoi(buf) == i) ? "SELECTED" : ""),
+                               i, i
+                       );
+               }
+               else {
+                       wprintf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
+                               ((atoi(buf) == i) ? "SELECTED" : ""),
+                               i, hourname[i]
+                       );
+               }
+
+       }
+       wprintf("</SELECT>\n");
+       wprintf("</td></tr>\n");
+
+       /**
+        * Calendar day view -- day end time
+        */
+       get_preference("dayend", buf, sizeof buf);
+       if (buf[0] == 0) strcpy(buf, "17");
+       wprintf("<tr><td>");
+       wprintf(_("Calendar day view ends at:"));
+       wprintf("</td><td>");
+
+       wprintf("<SELECT NAME=\"dayend\" SIZE=\"1\">\n");
+       for (i=0; i<=23; ++i) {
+
+               if (!strcasecmp(calhourformat, "24")) {
+                       wprintf("<OPTION %s VALUE=\"%d\">%d:00</OPTION>\n",
+                               ((atoi(buf) == i) ? "SELECTED" : ""),
+                               i, i
+                       );
+               }
+               else {
+                       wprintf("<OPTION %s VALUE=\"%d\">%s</OPTION>\n",
+                               ((atoi(buf) == i) ? "SELECTED" : ""),
+                               i, hourname[i]
+                       );
+               }
+
+       }
+       wprintf("</SELECT>\n");
+       wprintf("</td></tr>\n");
+
+       /**
+        * Signature
+        */
+       get_preference("use_sig", buf, sizeof buf);
+       if (buf[0] == 0) strcpy(buf, "no");
+       wprintf("<tr><td>");
+       wprintf(_("Attach signature to email messages?"));
+       wprintf("</td><td>");
+
+       wprintf("       <script type=\"text/javascript\">                                       "
+               "       function show_or_hide_sigbox() {                                        "
+               "               if ( $F('yes_sig') ) {                                          "
+               "                       $('signature_box').style.display = 'inline';            "
+               "               }                                                               "
+               "               else {                                                          "
+               "                       $('signature_box').style.display = 'none';              "
+               "               }                                                               "
+               "       }                                                                       "
+               "       </script>                                                               "
+       );
+
+       wprintf("<input type=\"radio\" id=\"no_sig\" name=\"use_sig\" VALUE=\"no\"");
+       if (!strcasecmp(buf, "no")) wprintf(" checked");
+       wprintf(" onChange=\"show_or_hide_sigbox();\" >");
+       wprintf(_("No signature"));
+       wprintf("<br></input>\n");
+
+       wprintf("<input type=\"radio\" id=\"yes_sig\" name=\"use_sig\" VALUE=\"yes\"");
+       if (!strcasecmp(buf, "yes")) wprintf(" checked");
+       wprintf(" onChange=\"show_or_hide_sigbox();\" >");
+       wprintf(_("Use this signature:"));
+       wprintf("<div id=\"signature_box\">"
+               "<br><textarea name=\"signature\" cols=\"40\" rows=\"5\">"
+       );
+       get_preference("signature", ebuf, sizeof ebuf);
+       euid_unescapize(buf, ebuf);
+       escputs(buf);
+       wprintf("</textarea>"
+               "</div>"
+       );
+
+       wprintf("<br></input>\n");
+
+       wprintf("</td></tr>\n");
+
+       wprintf("       <script type=\"text/javascript\">       "
+               "       show_or_hide_sigbox();                  "
+               "       </script>                               "
+       );
+
+       /** Character set to assume is in use for improperly encoded headers */
+       get_preference("default_header_charset", buf, sizeof buf);
+       if (buf[0] == 0) strcpy(buf, "UTF-8");
+       wprintf("<tr><td>");
+       wprintf(_("Default character set for email headers:"));
+       wprintf("</td><td>");
+       wprintf("<input type=\"text\" NAME=\"default_header_charset\" MAXLENGTH=\"32\" VALUE=\"%s\">", buf);
+       wprintf("</td></tr>");
+
+       /** submit buttons */
+       wprintf("</table>\n"
+               "<input type=\"submit\" name=\"change_button\" value=\"%s\">"
+               "&nbsp;"
+               "<INPUT type=\"submit\" name=\"cancel_button\" value=\"%s\">\n",
+               _("Change"),
+               _("Cancel")
+       );
+
+       /** end form */
+       wprintf("</form></center>\n");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+/**
+ * \brief Commit new preferences and settings
+ */
+void set_preferences(void)
+{
+       char ebuf[300];
+
+       if (strlen(bstr("change_button")) == 0) {
+               safestrncpy(WC->ImportantMessage, 
+                       _("Cancelled.  No settings were changed."),
+                       sizeof WC->ImportantMessage);
+               display_main_menu();
+               return;
+       }
+
+       /**
+        * Set the last argument to 1 only for the final setting, so
+        * we don't send the prefs file to the server repeatedly
+        */
+       set_preference("roomlistview", bstr("roomlistview"), 0);
+       set_preference("calhourformat", bstr("calhourformat"), 0);
+       set_preference("use_sig", bstr("use_sig"), 0);
+       set_preference("daystart", bstr("daystart"), 0);
+       set_preference("dayend", bstr("dayend"), 0);
+       set_preference("default_header_charset", bstr("default_header_charset"), 0);
+
+       euid_escapize(ebuf, bstr("signature"));
+       set_preference("signature", ebuf, 1);
+
+       display_main_menu();
+}
+
+
+/*@}*/
diff --git a/webcit/src/roomops.c b/webcit/src/roomops.c
new file mode 100644 (file)
index 0000000..a7993a0
--- /dev/null
@@ -0,0 +1,3052 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup RoomOps Lots of different room-related operations.
+ * \ingroup CitadelCommunitacion
+ */
+/*@{*/
+#include "webcit.h"
+
+char floorlist[128][SIZ]; /**< list of our floor names */
+
+char *viewdefs[8]; /**< the different kinds of available views */
+
+/**
+ * \brief initialize the viewdefs with localized strings
+ */
+void initialize_viewdefs(void) {
+       viewdefs[0] = _("Bulletin Board");
+       viewdefs[1] = _("Mail Folder");
+       viewdefs[2] = _("Address Book");
+       viewdefs[3] = _("Calendar");
+       viewdefs[4] = _("Task List");
+       viewdefs[5] = _("Notes List");
+       viewdefs[6] = _("Wiki");
+       viewdefs[7] = _("Calendar List");
+}
+
+/**
+ * \brief      Determine which views are allowed as the default for creating a new room.
+ *
+ * \param      which_view      The view ID being queried.
+ */
+int is_view_allowed_as_default(int which_view)
+{
+       switch(which_view) {
+               case VIEW_BBS:          return(1);
+               case VIEW_MAILBOX:      return(1);
+               case VIEW_ADDRESSBOOK:  return(1);
+               case VIEW_CALENDAR:     return(1);
+               case VIEW_TASKS:        return(1);
+               case VIEW_NOTES:        return(1);
+               case VIEW_WIKI:         return(0);      /**< because it isn't finished yet */
+               case VIEW_CALBRIEF:     return(0);
+               default:                return(0);      /**< should never get here */
+       }
+}
+
+
+/**
+ * \brief load the list of floors
+ */
+void load_floorlist(void)
+{
+       int a;
+       char buf[SIZ];
+
+       for (a = 0; a < 128; ++a)
+               floorlist[a][0] = 0;
+
+       serv_puts("LFLR");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               strcpy(floorlist[0], "Main Floor");
+               return;
+       }
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               extract_token(floorlist[extract_int(buf, 0)], buf, 1, '|', sizeof floorlist[0]);
+       }
+}
+
+
+/**
+ * \brief      Free a session's march list
+ *
+ * \param      wcf             Pointer to session being cleared
+ */
+void free_march_list(struct wcsession *wcf)
+{
+       struct march *mptr;
+
+       while (wcf->march != NULL) {
+               mptr = wcf->march->next;
+               free(wcf->march);
+               wcf->march = mptr;
+       }
+
+}
+
+
+
+/**
+ * \brief remove a room from the march list
+ */
+void remove_march(char *aaa)
+{
+       struct march *mptr, *mptr2;
+
+       if (WC->march == NULL)
+               return;
+
+       if (!strcasecmp(WC->march->march_name, aaa)) {
+               mptr = WC->march->next;
+               free(WC->march);
+               WC->march = mptr;
+               return;
+       }
+       mptr2 = WC->march;
+       for (mptr = WC->march; mptr != NULL; mptr = mptr->next) {
+               if (!strcasecmp(mptr->march_name, aaa)) {
+                       mptr2->next = mptr->next;
+                       free(mptr);
+                       mptr = mptr2;
+               } else {
+                       mptr2 = mptr;
+               }
+       }
+}
+
+
+
+
+/**
+ * \brief display rooms in tree structure???
+ * \param rp the roomlist to build a tree from
+ */
+void room_tree_list(struct roomlisting *rp)
+{
+       char rmname[64];
+       int f;
+
+       if (rp == NULL) {
+               return;
+       }
+
+       room_tree_list(rp->lnext);
+
+       strcpy(rmname, rp->rlname);
+       f = rp->rlflags;
+
+       wprintf("<a href=\"dotgoto&room=");
+       urlescputs(rmname);
+       wprintf("\"");
+       wprintf(">");
+       escputs1(rmname, 1, 1);
+       if ((f & QR_DIRECTORY) && (f & QR_NETWORK))
+               wprintf("}");
+       else if (f & QR_DIRECTORY)
+               wprintf("]");
+       else if (f & QR_NETWORK)
+               wprintf(")");
+       else
+               wprintf("&gt;");
+       wprintf("</A><TT> </TT>\n");
+
+       room_tree_list(rp->rnext);
+       free(rp);
+}
+
+
+/** 
+ * \brief Room ordering stuff (compare first by floor, then by order)
+ * \param r1 first roomlist to compare
+ * \param r2 second roomlist co compare
+ * \return are they the same???
+ */
+int rordercmp(struct roomlisting *r1, struct roomlisting *r2)
+{
+       if ((r1 == NULL) && (r2 == NULL))
+               return (0);
+       if (r1 == NULL)
+               return (-1);
+       if (r2 == NULL)
+               return (1);
+       if (r1->rlfloor < r2->rlfloor)
+               return (-1);
+       if (r1->rlfloor > r2->rlfloor)
+               return (1);
+       if (r1->rlorder < r2->rlorder)
+               return (-1);
+       if (r1->rlorder > r2->rlorder)
+               return (1);
+       return (0);
+}
+
+
+/**
+ * \brief Common code for all room listings
+ * \param variety what???
+ */
+void listrms(char *variety)
+{
+       char buf[SIZ];
+       int num_rooms = 0;
+
+       struct roomlisting *rl = NULL;
+       struct roomlisting *rp;
+       struct roomlisting *rs;
+
+
+       /** Ask the server for a room list */
+       serv_puts(variety);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               wprintf("&nbsp;");
+               return;
+       }
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               ++num_rooms;
+               rp = malloc(sizeof(struct roomlisting));
+               extract_token(rp->rlname, buf, 0, '|', sizeof rp->rlname);
+               rp->rlflags = extract_int(buf, 1);
+               rp->rlfloor = extract_int(buf, 2);
+               rp->rlorder = extract_int(buf, 3);
+               rp->lnext = NULL;
+               rp->rnext = NULL;
+
+               rs = rl;
+               if (rl == NULL) {
+                       rl = rp;
+               } else
+                       while (rp != NULL) {
+                               if (rordercmp(rp, rs) < 0) {
+                                       if (rs->lnext == NULL) {
+                                               rs->lnext = rp;
+                                               rp = NULL;
+                                       } else {
+                                               rs = rs->lnext;
+                                       }
+                               } else {
+                                       if (rs->rnext == NULL) {
+                                               rs->rnext = rp;
+                                               rp = NULL;
+                                       } else {
+                                               rs = rs->rnext;
+                                       }
+                               }
+                       }
+       }
+
+       room_tree_list(rl);
+
+       /**
+        * If no rooms were listed, print an nbsp to make the cell
+        * borders show up anyway.
+        */
+       if (num_rooms == 0) wprintf("&nbsp;");
+}
+
+
+/**
+ * \brief list all forgotten rooms
+ */
+void zapped_list(void)
+{
+       output_headers(1, 1, 0, 0, 0, 0);
+
+       svprintf("BOXTITLE", WCS_STRING, _("Zapped (forgotten) rooms"));
+       do_template("beginbox");
+
+       listrms("LZRM -1");
+
+       wprintf("<br /><br />\n");
+       wprintf(_("Click on any room to un-zap it and goto that room.\n"));
+       do_template("endbox");
+       wDumpContent(1);
+}
+
+
+/**
+ * \brief read this room's info file (set v to 1 for verbose mode)
+ */
+void readinfo(void)
+{
+       char buf[SIZ];
+
+       serv_puts("RINF");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               fmout("CENTER");
+       }
+       else {
+               wprintf("&nbsp;");
+       }
+}
+
+
+
+
+/**
+ * \brief Display room banner icon.  
+ * The server doesn't actually
+ * need the room name, but we supply it in order to
+ * keep the browser from using a cached icon from 
+ * another room.
+ */
+void embed_room_graphic(void) {
+       char buf[SIZ];
+
+       serv_puts("OIMG _roompic_");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               wprintf("<IMG HEIGHT=64 src=\"image&name=_roompic_&room=");
+               urlescputs(WC->wc_roomname);
+               wprintf("\">");
+               serv_puts("CLOS");
+               serv_getln(buf, sizeof buf);
+       }
+       else if (WC->wc_view == VIEW_ADDRESSBOOK) {
+               wprintf("<img height=48 width=48 src=\""
+                       "static/viewcontacts_48x.gif"
+                       "\">"
+               );
+       }
+       else if ( (WC->wc_view == VIEW_CALENDAR) || (WC->wc_view == VIEW_CALBRIEF) ) {
+               wprintf("<img height=48 width=48 src=\""
+                       "static/calarea_48x.gif"
+                       "\">"
+               );
+       }
+       else if (WC->wc_view == VIEW_TASKS) {
+               wprintf("<img height=48 width=48 src=\""
+                       "static/taskmanag_48x.gif"
+                       "\">"
+               );
+       }
+       else if (WC->wc_view == VIEW_NOTES) {
+               wprintf("<img height=48 width=48 src=\""
+                       "static/storenotes_48x.gif"
+                       "\">"
+               );
+       }
+       else if (WC->wc_view == VIEW_MAILBOX) {
+               wprintf("<img height=48 width=48 src=\""
+                       "static/privatemess_48x.gif"
+                       "\">"
+               );
+       }
+       else {
+               wprintf("<img height=48 width=48 src=\""
+                       "static/chatrooms_48x.gif"
+                       "\">"
+               );
+       }
+
+}
+
+
+
+/**
+ * \brief Display the current view and offer an option to change it
+ */
+void embed_view_o_matic(void) {
+       int i;
+
+       wprintf("<form name=\"viewomatic\" action=\"changeview\">\n"
+               "<span class=\"room_banner_new_messages\">");
+       wprintf(_("View as:"));
+       wprintf(" "
+               "<SELECT NAME=\"newview\" SIZE=\"1\" "
+               "STYLE=\"font-size: 7pt; background: #444455; color: #ddddcc;\" "
+               "OnChange=\"location.href=viewomatic.newview.options"
+               "[selectedIndex].value\">\n");
+
+       for (i=0; i<(sizeof viewdefs / sizeof (char *)); ++i) {
+               /**
+                * Only offer the views that make sense, given the default
+                * view for the room.  For example, don't offer a Calendar
+                * view in a non-Calendar room.
+                */
+               if (
+                       (i == WC->wc_view)
+                       ||      (i == WC->wc_default_view)                      /**< default */
+                       ||      ( (i == 0) && (WC->wc_default_view == 1) )      /**< mail or bulletin */
+                       ||      ( (i == 1) && (WC->wc_default_view == 0) )      /**< mail or bulletin */
+                       /** ||  ( (i == 7) && (WC->wc_default_view == 3) )      (calendar list temporarily disabled) */
+               ) {
+
+                       wprintf("<OPTION %s VALUE=\"changeview?view=%d\">",
+                               ((i == WC->wc_view) ? "SELECTED" : ""),
+                               i );
+                       escputs(viewdefs[i]);
+                       wprintf("</OPTION>\n");
+               }
+       }
+       wprintf("</select></span></form>\n");
+}
+
+
+/**
+ * \brief view room banner
+ * \param got what???
+ * \param navbar_style
+ */
+void embed_room_banner(char *got, int navbar_style) {
+       char buf[256];
+
+       /**
+        * We need to have the information returned by a GOTO server command.
+        * If it isn't supplied, we fake it by issuing our own GOTO.
+        */
+       if (got == NULL) {
+               serv_printf("GOTO %s", WC->wc_roomname);
+               serv_getln(buf, sizeof buf);
+               got = buf;
+       }
+
+       /** The browser needs some information for its own use */
+       wprintf("<script type=\"text/javascript\">      \n"
+               "       room_is_trash = %d;             \n"
+               "</script>\n",
+               WC->wc_is_trash
+       );
+
+       /**
+        * If the user happens to select the "make this my start page" link,
+        * we want it to remember the URL as a "/dotskip" one instead of
+        * a "skip" or "gotonext" or something like that.
+        */
+       snprintf(WC->this_page, sizeof(WC->this_page), "dotskip&room=%s",
+               WC->wc_roomname);
+
+       /** Check for new mail. */
+       WC->new_mail = extract_int(&got[4], 9);
+       WC->wc_view = extract_int(&got[4], 11);
+
+       svprintf("ROOMNAME", WCS_STRING, "%s", WC->wc_roomname);
+       svprintf("NUMMSGS", WCS_STRING,
+               _("%d new of %d messages"),
+               extract_int(&got[4], 1),
+               extract_int(&got[4], 2)
+       );
+       svcallback("ROOMPIC", embed_room_graphic);
+       svcallback("ROOMINFO", readinfo);
+       svcallback("VIEWOMATIC", embed_view_o_matic);
+       svcallback("START", offer_start_page);
+
+       do_template("roombanner");
+       if (navbar_style != navbar_none) {
+
+               wprintf("<div style=\"position:absolute; bottom:0px; left:0px\">\n"
+                       "<table width=\"100%%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\"><tr>\n");
+
+
+               if (navbar_style == navbar_default) wprintf(
+                       "<td>"
+                       "<a href=\"ungoto\">"
+                       "<img align=\"middle\" src=\"static/ungoto2_24x.gif\" border=\"0\">"
+                       "<span class=\"navbar_link\">%s</span></A>"
+                       "</td>\n", _("Ungoto")
+               );
+
+               if ( (navbar_style == navbar_default) && (WC->wc_view == VIEW_BBS) ) {
+                       wprintf(
+                               "<td>"
+                               "<a href=\"readnew\">"
+                               "<img align=\"middle\" src=\"static/newmess2_24x.gif\" border=\"0\">"
+                               "<span class=\"navbar_link\">%s</span></A>"
+                               "</td>\n", _("Read new messages")
+                       );
+               }
+
+               if (navbar_style == navbar_default) {
+                       switch(WC->wc_view) {
+                               case VIEW_ADDRESSBOOK:
+                                       wprintf(
+                                               "<td>"
+                                               "<a href=\"readfwd\">"
+                                               "<img align=\"middle\" src=\"static/viewcontacts_24x.gif\" "
+                                               "border=\"0\">"
+                                               "<span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("View contacts")
+                                       );
+                                       break;
+                               case VIEW_CALENDAR:
+                                       wprintf(
+                                               "<td>"
+                                               "<a href=\"readfwd?calview=day\">"
+                                               "<img align=\"middle\" src=\"static/taskday2_24x.gif\" "
+                                               "border=\"0\">"
+                                               "<span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Day view")
+                                       );
+                                       wprintf(
+                                               "<td>"
+                                               "<a href=\"readfwd?calview=month\">"
+                                               "<img align=\"middle\" src=\"static/monthview2_24x.gif\" "
+                                               "border=\"0\">"
+                                               "<span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Month view")
+                                       );
+                                       break;
+                               case VIEW_CALBRIEF:
+                                       wprintf(
+                                               "<td>"
+                                               "<a href=\"readfwd?calview=month\">"
+                                               "<img align=\"middle\" src=\"static/monthview2_24x.gif\" "
+                                               "border=\"0\">"
+                                               "<span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Calendar list")
+                                       );
+                                       break;
+                               case VIEW_TASKS:
+                                       wprintf(
+                                               "<td>"
+                                               "<a href=\"readfwd\">"
+                                               "<img align=\"middle\" src=\"static/taskmanag_24x.gif\" "
+                                               "border=\"0\">"
+                                               "<span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("View tasks")
+                                       );
+                                       break;
+                               case VIEW_NOTES:
+                                       wprintf(
+                                               "<td>"
+                                               "<a href=\"readfwd\">"
+                                               "<img align=\"middle\" src=\"static/viewnotes_24x.gif\" "
+                                               "border=\"0\">"
+                                               "<span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("View notes")
+                                       );
+                                       break;
+                               case VIEW_MAILBOX:
+                                       wprintf(
+                                               "<td>"
+                                               "<a href=\"readfwd\">"
+                                               "<img align=\"middle\" src=\"static/readallmess3_24x.gif\" "
+                                               "border=\"0\">"
+                                               "<span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("View message list")
+                                       );
+                                       break;
+                               case VIEW_WIKI:
+                                       wprintf(
+                                               "<td>"
+                                               "<a href=\"readfwd\">"
+                                               "<img align=\"middle\" src=\"static/readallmess3_24x.gif\" "
+                                               "border=\"0\">"
+                                               "<span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Wiki home")
+                                       );
+                                       break;
+                               default:
+                                       wprintf(
+                                               "<td>"
+                                               "<a href=\"readfwd\">"
+                                               "<img align=\"middle\" src=\"static/readallmess3_24x.gif\" "
+                                               "border=\"0\">"
+                                               "<span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Read all messages")
+                                       );
+                                       break;
+                       }
+               }
+
+               if (navbar_style == navbar_default) {
+                       switch(WC->wc_view) {
+                               case VIEW_ADDRESSBOOK:
+                                       wprintf(
+                                               "<td><a href=\"display_enter\">"
+                                               "<img align=\"middle\" src=\"static/addnewcontact_24x.gif\" "
+                                               "border=\"0\"><span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Add new contact")
+                                       );
+                                       break;
+                               case VIEW_CALENDAR:
+                               case VIEW_CALBRIEF:
+                                       wprintf(
+                                               "<td><a href=\"display_enter\">"
+                                               "<img align=\"middle\" src=\"static/addevent_24x.gif\" "
+                                               "border=\"0\"><span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Add new event")
+                                       );
+                                       break;
+                               case VIEW_TASKS:
+                                       wprintf(
+                                               "<td><a href=\"display_enter\">"
+                                               "<img align=\"middle\" src=\"static/newmess3_24x.gif\" "
+                                               "border=\"0\"><span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Add new task")
+                                       );
+                                       break;
+                               case VIEW_NOTES:
+                                       wprintf(
+                                               "<td><a href=\"javascript:add_new_note();\">"
+                                               "<img align=\"middle\" src=\"static/enternewnote_24x.gif\" "
+                                               "border=\"0\"><span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Add new note")
+                                       );
+                                       break;
+                               case VIEW_WIKI:
+                                       safestrncpy(buf, bstr("page"), sizeof buf);
+                                       str_wiki_index(buf);
+                                       wprintf(
+                                               "<td><a href=\"display_enter?wikipage=%s\">"
+                                               "<img align=\"middle\" src=\"static/newmess3_24x.gif\" "
+                                               "border=\"0\"><span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", buf, _("Edit this page")
+                                       );
+                                       break;
+                               default:
+                                       wprintf(
+                                               "<td><a href=\"display_enter\">"
+                                               "<img align=\"middle\" src=\"static/newmess3_24x.gif\" "
+                                               "border=\"0\"><span class=\"navbar_link\">"
+                                               "%s"
+                                               "</span></a></td>\n", _("Enter a message")
+                                       );
+                                       break;
+                       }
+               }
+
+               if (navbar_style == navbar_default) wprintf(
+                       "<td>"
+                       "<a href=\"skip\" "
+                       "TITLE=\"%s\">"
+                       "<img align=\"middle\" src=\"static/skipthisroom_24x.gif\" border=\"0\">"
+                       "<span class=\"navbar_link\">%s</span></a>"
+                       "</td>\n",
+                       _("Leave all messages marked as unread, go to next room with unread messages"),
+                       _("Skip this room")
+               );
+
+               if (navbar_style == navbar_default) wprintf(
+                       "<td>"
+                       "<a href=\"gotonext\" "
+                       "TITLE=\"%s\">"
+                       "<img align=\"middle\" src=\"static/markngo_24x.gif\" border=\"0\">"
+                       "<span class=\"navbar_link\">%s</span></a>"
+                       "</td>\n",
+                       _("Mark all messages as read, go to next room with unread messages"),
+                       _("Goto next room")
+               );
+
+               wprintf("</tr></table></div>\n");
+       }
+
+}
+
+
+
+
+
+/**
+ * \brief back end routine to take the session to a new room
+ * \param gname room to go to
+ *
+ */
+int gotoroom(char *gname)
+{
+       char buf[SIZ];
+       static long ls = (-1L);
+       int err = 0;
+
+       /** store ungoto information */
+       strcpy(WC->ugname, WC->wc_roomname);
+       WC->uglsn = ls;
+
+       /** move to the new room */
+       serv_printf("GOTO %s", gname);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               buf[3] = 0;
+               err = atoi(buf);
+               serv_puts("GOTO _BASEROOM_");
+               serv_getln(buf, sizeof buf);
+       }
+       if (buf[0] != '2') {
+               buf[3] = 0;
+               err = atoi(buf);
+               return err;
+       }
+       extract_token(WC->wc_roomname, &buf[4], 0, '|', sizeof WC->wc_roomname);
+       WC->room_flags = extract_int(&buf[4], 4);
+       /* highest_msg_read = extract_int(&buf[4],6);
+          maxmsgnum = extract_int(&buf[4],5);
+        */
+       WC->is_mailbox = extract_int(&buf[4],7);
+       ls = extract_long(&buf[4], 6);
+       WC->wc_floor = extract_int(&buf[4], 10);
+       WC->wc_view = extract_int(&buf[4], 11);
+       WC->wc_default_view = extract_int(&buf[4], 12);
+       WC->wc_is_trash = extract_int(&buf[4], 13);
+
+       if (WC->is_aide)
+               WC->is_room_aide = WC->is_aide;
+       else
+               WC->is_room_aide = (char) extract_int(&buf[4], 8);
+
+       remove_march(WC->wc_roomname);
+       if (!strcasecmp(gname, "_BASEROOM_"))
+               remove_march(gname);
+
+       return err;
+}
+
+
+/**
+ * \brief Locate the room on the march list which we most want to go to.  
+ * Each room
+ * is measured given a "weight" of preference based on various factors.
+ * \param desired_floor the room number on the citadel server
+ * \return the roomname
+ */
+char *pop_march(int desired_floor)
+{
+       static char TheRoom[128];
+       int TheFloor = 0;
+       int TheOrder = 32767;
+       int TheWeight = 0;
+       int weight;
+       struct march *mptr = NULL;
+
+       strcpy(TheRoom, "_BASEROOM_");
+       if (WC->march == NULL)
+               return (TheRoom);
+
+       for (mptr = WC->march; mptr != NULL; mptr = mptr->next) {
+               weight = 0;
+               if ((strcasecmp(mptr->march_name, "_BASEROOM_")))
+                       weight = weight + 10000;
+               if (mptr->march_floor == desired_floor)
+                       weight = weight + 5000;
+
+               weight = weight + ((128 - (mptr->march_floor)) * 128);
+               weight = weight + (128 - (mptr->march_order));
+
+               if (weight > TheWeight) {
+                       TheWeight = weight;
+                       strcpy(TheRoom, mptr->march_name);
+                       TheFloor = mptr->march_floor;
+                       TheOrder = mptr->march_order;
+               }
+       }
+       return (TheRoom);
+}
+
+
+
+/**
+ *\brief Goto next room having unread messages.
+ * We want to skip over rooms that the user has already been to, and take the
+ * user back to the lobby when done.  The room we end up in is placed in
+ * newroom - which is set to 0 (the lobby) initially.
+ * We start the search in the current room rather than the beginning to prevent
+ * two or more concurrent users from dragging each other back to the same room.
+ */
+void gotonext(void)
+{
+       char buf[256];
+       struct march *mptr, *mptr2;
+       char next_room[128];
+
+       /**
+        * First check to see if the march-mode list is already allocated.
+        * If it is, pop the first room off the list and go there.
+        */
+
+       if (WC->march == NULL) {
+               serv_puts("LKRN");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '1')
+                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                               mptr = (struct march *) malloc(sizeof(struct march));
+                               mptr->next = NULL;
+                               extract_token(mptr->march_name, buf, 0, '|', sizeof mptr->march_name);
+                               mptr->march_floor = extract_int(buf, 2);
+                               mptr->march_order = extract_int(buf, 3);
+                               if (WC->march == NULL) {
+                                       WC->march = mptr;
+                               } else {
+                                       mptr2 = WC->march;
+                                       while (mptr2->next != NULL)
+                                               mptr2 = mptr2->next;
+                                       mptr2->next = mptr;
+                               }
+                       }
+               /**
+                * add _BASEROOM_ to the end of the march list, so the user will end up
+                * in the system base room (usually the Lobby>) at the end of the loop
+                */
+               mptr = (struct march *) malloc(sizeof(struct march));
+               mptr->next = NULL;
+               strcpy(mptr->march_name, "_BASEROOM_");
+               if (WC->march == NULL) {
+                       WC->march = mptr;
+               } else {
+                       mptr2 = WC->march;
+                       while (mptr2->next != NULL)
+                               mptr2 = mptr2->next;
+                       mptr2->next = mptr;
+               }
+               /**
+                * ...and remove the room we're currently in, so a <G>oto doesn't make us
+                * walk around in circles
+                */
+               remove_march(WC->wc_roomname);
+       }
+       if (WC->march != NULL) {
+               strcpy(next_room, pop_march(-1));
+       } else {
+               strcpy(next_room, "_BASEROOM_");
+       }
+
+
+       smart_goto(next_room);
+}
+
+
+/**
+ * \brief goto next room
+ * \param next_room next room to go to
+ */
+void smart_goto(char *next_room) {
+       gotoroom(next_room);
+       readloop("readnew");
+}
+
+
+
+/**
+ * \brief mark all messages in current room as having been read
+ */
+void slrp_highest(void)
+{
+       char buf[256];
+
+       serv_puts("SLRP HIGHEST");
+       serv_getln(buf, sizeof buf);
+}
+
+
+/**
+ * \brief un-goto the previous room
+ */
+void ungoto(void)
+{
+       char buf[SIZ];
+
+       if (!strcmp(WC->ugname, "")) {
+               smart_goto(WC->wc_roomname);
+               return;
+       }
+       serv_printf("GOTO %s", WC->ugname);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               smart_goto(WC->wc_roomname);
+               return;
+       }
+       if (WC->uglsn >= 0L) {
+               serv_printf("SLRP %ld", WC->uglsn);
+               serv_getln(buf, sizeof buf);
+       }
+       strcpy(buf, WC->ugname);
+       strcpy(WC->ugname, "");
+       smart_goto(buf);
+}
+
+
+
+
+
+/**
+ * \brief Set/clear/read the "self-service list subscribe" flag for a room
+ * 
+ * \param newval set to 0 to clear, 1 to set, any other value to leave unchanged.
+ * \return return the new value.
+ */
+
+int self_service(int newval) {
+       int current_value = 0;
+       char buf[SIZ];
+       
+       char name[SIZ];
+       char password[SIZ];
+       char dirname[SIZ];
+       int flags, floor, order, view, flags2;
+
+       serv_puts("GETR");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') return(0);
+
+       extract_token(name, &buf[4], 0, '|', sizeof name);
+       extract_token(password, &buf[4], 1, '|', sizeof password);
+       extract_token(dirname, &buf[4], 2, '|', sizeof dirname);
+       flags = extract_int(&buf[4], 3);
+       floor = extract_int(&buf[4], 4);
+       order = extract_int(&buf[4], 5);
+       view = extract_int(&buf[4], 6);
+       flags2 = extract_int(&buf[4], 7);
+
+       if (flags2 & QR2_SELFLIST) {
+               current_value = 1;
+       }
+       else {
+               current_value = 0;
+       }
+
+       if (newval == 1) {
+               flags2 = flags2 | QR2_SELFLIST;
+       }
+       else if (newval == 0) {
+               flags2 = flags2 & ~QR2_SELFLIST;
+       }
+       else {
+               return(current_value);
+       }
+
+       if (newval != current_value) {
+               serv_printf("SETR %s|%s|%s|%d|0|%d|%d|%d|%d",
+                       name, password, dirname, flags,
+                       floor, order, view, flags2);
+               serv_getln(buf, sizeof buf);
+       }
+
+       return(newval);
+
+}
+
+
+
+
+
+
+/**
+ * \brief display the form for editing a room
+ */
+void display_editroom(void)
+{
+       char buf[SIZ];
+       char cmd[SIZ];
+       char node[SIZ];
+       char remote_room[SIZ];
+       char recp[SIZ];
+       char er_name[128];
+       char er_password[10];
+       char er_dirname[15];
+       char er_roomaide[26];
+       unsigned er_flags;
+       int er_floor;
+       int i, j;
+       char *tab;
+       char *shared_with;
+       char *not_shared_with;
+       int roompolicy = 0;
+       int roomvalue = 0;
+       int floorpolicy = 0;
+       int floorvalue = 0;
+
+       tab = bstr("tab");
+       if (strlen(tab) == 0) tab = "admin";
+
+       load_floorlist();
+       serv_puts("GETR");
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] != '2') {
+               strcpy(WC->ImportantMessage, &buf[4]);
+               display_main_menu();
+               return;
+       }
+       extract_token(er_name, &buf[4], 0, '|', sizeof er_name);
+       extract_token(er_password, &buf[4], 1, '|', sizeof er_password);
+       extract_token(er_dirname, &buf[4], 2, '|', sizeof er_dirname);
+       er_flags = extract_int(&buf[4], 3);
+       er_floor = extract_int(&buf[4], 4);
+
+       output_headers(1, 1, 1, 0, 0, 0);
+
+       /** print the tabbed dialog */
+       wprintf("<br />"
+               "<div class=\"fix_scrollbar_bug\">"
+               "<TABLE border=0 cellspacing=0 cellpadding=0 width=100%%>"
+               "<TR ALIGN=CENTER>"
+               "<TD>&nbsp;</TD>\n");
+
+       if (!strcmp(tab, "admin")) {
+               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
+       }
+       else {
+               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=admin\">");
+       }
+       wprintf(_("Administration"));
+       if (!strcmp(tab, "admin")) {
+               wprintf("</SPAN></TD>\n");
+       }
+       else {
+               wprintf("</A></TD>\n");
+       }
+
+       wprintf("<TD>&nbsp;</TD>\n");
+
+       if (!strcmp(tab, "config")) {
+               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
+       }
+       else {
+               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=config\">");
+       }
+       wprintf(_("Configuration"));
+       if (!strcmp(tab, "config")) {
+               wprintf("</SPAN></TD>\n");
+       }
+       else {
+               wprintf("</A></TD>\n");
+       }
+
+       wprintf("<TD>&nbsp;</TD>\n");
+
+       if (!strcmp(tab, "expire")) {
+               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
+       }
+       else {
+               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=expire\">");
+       }
+       wprintf(_("Message expire policy"));
+       if (!strcmp(tab, "expire")) {
+               wprintf("</SPAN></TD>\n");
+       }
+       else {
+               wprintf("</A></TD>\n");
+       }
+
+       wprintf("<TD>&nbsp;</TD>\n");
+
+       if (!strcmp(tab, "access")) {
+               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
+       }
+       else {
+               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=access\">");
+       }
+       wprintf(_("Access controls"));
+       if (!strcmp(tab, "access")) {
+               wprintf("</SPAN></TD>\n");
+       }
+       else {
+               wprintf("</A></TD>\n");
+       }
+
+       wprintf("<TD>&nbsp;</TD>\n");
+
+       if (!strcmp(tab, "sharing")) {
+               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
+       }
+       else {
+               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=sharing\">");
+       }
+       wprintf(_("Sharing"));
+       if (!strcmp(tab, "sharing")) {
+               wprintf("</SPAN></TD>\n");
+       }
+       else {
+               wprintf("</A></TD>\n");
+       }
+
+       wprintf("<TD>&nbsp;</TD>\n");
+
+       if (!strcmp(tab, "listserv")) {
+               wprintf("<TD BGCOLOR=\"#FFFFFF\"><SPAN CLASS=\"tablabel\">");
+       }
+       else {
+               wprintf("<TD BGCOLOR=\"#CCCCCC\"><a href=\"display_editroom&tab=listserv\">");
+       }
+       wprintf(_("Mailing list service"));
+       if (!strcmp(tab, "listserv")) {
+               wprintf("</SPAN></TD>\n");
+       }
+       else {
+               wprintf("</A></TD>\n");
+       }
+
+       wprintf("<TD>&nbsp;</TD>\n");
+
+       wprintf("</TR></TABLE></div>\n");
+       /** end tabbed dialog */        
+
+       /** begin content of whatever tab is open now */
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<TABLE border=0 width=100%% bgcolor=\"#FFFFFF\">\n"
+               "<TR><TD>\n");
+
+       if (!strcmp(tab, "admin")) {
+               wprintf("<UL>"
+                       "<LI><a href=\"delete_room\" "
+                       "onClick=\"return confirm('");
+               wprintf(_("Are you sure you want to delete this room?"));
+               wprintf("');\">\n");
+               wprintf(_("Delete this room"));
+               wprintf("</A>\n"
+                       "<LI><a href=\"display_editroompic\">\n");
+               wprintf(_("Set or change the icon for this room's banner"));
+               wprintf("</A>\n"
+                       "<LI><a href=\"display_editinfo\">\n");
+               wprintf(_("Edit this room's Info file"));
+               wprintf("</A>\n"
+                       "</UL>");
+       }
+
+       if (!strcmp(tab, "config")) {
+               wprintf("<FORM METHOD=\"POST\" action=\"editroom\">\n");
+       
+               wprintf("<UL><LI>");
+               wprintf(_("Name of room: "));
+               wprintf("<INPUT TYPE=\"text\" NAME=\"er_name\" VALUE=\"%s\" MAXLENGTH=\"%d\">\n",
+                       er_name,
+                       (sizeof(er_name)-1)
+               );
+       
+               wprintf("<LI>");
+               wprintf(_("Resides on floor: "));
+               wprintf("<SELECT NAME=\"er_floor\" SIZE=\"1\">\n");
+               for (i = 0; i < 128; ++i)
+                       if (strlen(floorlist[i]) > 0) {
+                               wprintf("<OPTION ");
+                               if (i == er_floor)
+                                       wprintf("SELECTED ");
+                               wprintf("VALUE=\"%d\">", i);
+                               escputs(floorlist[i]);
+                               wprintf("</OPTION>\n");
+                       }
+               wprintf("</SELECT>\n");
+       
+               wprintf("<LI>");
+               wprintf(_("Type of room:"));
+               wprintf("<UL>\n");
+
+               wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"public\" ");
+               if ((er_flags & QR_PRIVATE) == 0)
+               wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Public room"));
+               wprintf("\n");
+
+               wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"hidden\" ");
+               if ((er_flags & QR_PRIVATE) &&
+                   (er_flags & QR_GUESSNAME))
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Private - guess name"));
+       
+               wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"passworded\" ");
+               if ((er_flags & QR_PRIVATE) &&
+                   (er_flags & QR_PASSWORDED))
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Private - require password:"));
+               wprintf("\n<INPUT TYPE=\"text\" NAME=\"er_password\" VALUE=\"%s\" MAXLENGTH=\"9\">\n",
+                       er_password);
+       
+               wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"invonly\" ");
+               if ((er_flags & QR_PRIVATE)
+                   && ((er_flags & QR_GUESSNAME) == 0)
+                   && ((er_flags & QR_PASSWORDED) == 0))
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Private - invitation only"));
+       
+               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"bump\" VALUE=\"yes\" ");
+               wprintf("> ");
+               wprintf(_("If private, cause current users to forget room"));
+       
+               wprintf("\n</UL>\n");
+       
+               wprintf("<LI><INPUT TYPE=\"checkbox\" NAME=\"prefonly\" VALUE=\"yes\" ");
+               if (er_flags & QR_PREFONLY)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Preferred users only"));
+       
+               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"readonly\" VALUE=\"yes\" ");
+               if (er_flags & QR_READONLY)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Read-only room"));
+       
+               /** directory stuff */
+               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"directory\" VALUE=\"yes\" ");
+               if (er_flags & QR_DIRECTORY)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("File directory room"));
+
+               wprintf("\n<UL><LI>");
+               wprintf(_("Directory name: "));
+               wprintf("<INPUT TYPE=\"text\" NAME=\"er_dirname\" VALUE=\"%s\" MAXLENGTH=\"14\">\n",
+                       er_dirname);
+
+               wprintf("<LI><INPUT TYPE=\"checkbox\" NAME=\"ulallowed\" VALUE=\"yes\" ");
+               if (er_flags & QR_UPLOAD)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Uploading allowed"));
+       
+               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"dlallowed\" VALUE=\"yes\" ");
+               if (er_flags & QR_DOWNLOAD)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Downloading allowed"));
+       
+               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"visdir\" VALUE=\"yes\" ");
+               if (er_flags & QR_VISDIR)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Visible directory"));
+               wprintf("</UL>\n");
+       
+               /** end of directory stuff */
+       
+               wprintf("<LI><INPUT TYPE=\"checkbox\" NAME=\"network\" VALUE=\"yes\" ");
+               if (er_flags & QR_NETWORK)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Network shared room"));
+
+               wprintf("\n<LI><INPUT TYPE=\"checkbox\" NAME=\"permanent\" VALUE=\"yes\" ");
+               if (er_flags & QR_PERMANENT)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Permanent (does not auto-purge)"));
+
+               /** start of anon options */
+       
+               wprintf("\n<LI>");
+               wprintf(_("Anonymous messages"));
+               wprintf("<UL>\n");
+       
+               wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"anon\" VALUE=\"no\" ");
+               if (((er_flags & QR_ANONONLY) == 0)
+                   && ((er_flags & QR_ANONOPT) == 0))
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("No anonymous messages"));
+       
+               wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"anon\" VALUE=\"anononly\" ");
+               if (er_flags & QR_ANONONLY)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("All messages are anonymous"));
+       
+               wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"anon\" VALUE=\"anon2\" ");
+               if (er_flags & QR_ANONOPT)
+                       wprintf("CHECKED ");
+               wprintf("> ");
+               wprintf(_("Prompt user when entering messages"));
+               wprintf("</UL>\n");
+       
+       /* end of anon options */
+       
+               wprintf("<LI>");
+               wprintf(_("Room aide: "));
+               serv_puts("GETA");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '2') {
+                       wprintf("<em>%s</em>\n", &buf[4]);
+               } else {
+                       extract_token(er_roomaide, &buf[4], 0, '|', sizeof er_roomaide);
+                       wprintf("<INPUT TYPE=\"text\" NAME=\"er_roomaide\" VALUE=\"%s\" MAXLENGTH=\"25\">\n", er_roomaide);
+               }
+       
+               wprintf("</UL><CENTER>\n");
+               wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"config\">\n"
+                       "<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">"
+                       "&nbsp;"
+                       "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">"
+                       "</CENTER>\n",
+                       _("Save changes"),
+                       _("Cancel")
+               );
+       }
+
+
+       /** Sharing the room with other Citadel nodes... */
+       if (!strcmp(tab, "sharing")) {
+
+               shared_with = strdup("");
+               not_shared_with = strdup("");
+
+               /** Learn the current configuration */
+               serv_puts("CONF getsys|application/x-citadel-ignet-config");
+               serv_getln(buf, sizeof buf);
+               if (buf[0]=='1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       extract_token(node, buf, 0, '|', sizeof node);
+                       not_shared_with = realloc(not_shared_with,
+                                       strlen(not_shared_with) + 32);
+                       strcat(not_shared_with, node);
+                       strcat(not_shared_with, "\n");
+               }
+
+               serv_puts("GNET");
+               serv_getln(buf, sizeof buf);
+               if (buf[0]=='1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       extract_token(cmd, buf, 0, '|', sizeof cmd);
+                       extract_token(node, buf, 1, '|', sizeof node);
+                       extract_token(remote_room, buf, 2, '|', sizeof remote_room);
+                       if (!strcasecmp(cmd, "ignet_push_share")) {
+                               shared_with = realloc(shared_with,
+                                               strlen(shared_with) + 32);
+                               strcat(shared_with, node);
+                               if (strlen(remote_room) > 0) {
+                                       strcat(shared_with, "|");
+                                       strcat(shared_with, remote_room);
+                               }
+                               strcat(shared_with, "\n");
+                       }
+               }
+
+               for (i=0; i<num_tokens(shared_with, '\n'); ++i) {
+                       extract_token(buf, shared_with, i, '\n', sizeof buf);
+                       extract_token(node, buf, 0, '|', sizeof node);
+                       for (j=0; j<num_tokens(not_shared_with, '\n'); ++j) {
+                               extract_token(cmd, not_shared_with, j, '\n', sizeof cmd);
+                               if (!strcasecmp(node, cmd)) {
+                                       remove_token(not_shared_with, j, '\n');
+                               }
+                       }
+               }
+
+               /** Display the stuff */
+               wprintf("<CENTER><br />"
+                       "<TABLE border=1 cellpadding=5><TR>"
+                       "<TD><B><I>");
+               wprintf(_("Shared with"));
+               wprintf("</I></B></TD>"
+                       "<TD><B><I>");
+               wprintf(_("Not shared with"));
+               wprintf("</I></B></TD></TR>\n"
+                       "<TR><TD VALIGN=TOP>\n");
+
+               wprintf("<TABLE border=0 cellpadding=5><TR BGCOLOR=\"#CCCCCC\"><TD>");
+               wprintf(_("Remote node name"));
+               wprintf("</TD><TD>");
+               wprintf(_("Remote room name"));
+               wprintf("</TD><TD>");
+               wprintf(_("Actions"));
+               wprintf("</TD></TR>\n");
+
+               for (i=0; i<num_tokens(shared_with, '\n'); ++i) {
+                       extract_token(buf, shared_with, i, '\n', sizeof buf);
+                       extract_token(node, buf, 0, '|', sizeof node);
+                       extract_token(remote_room, buf, 1, '|', sizeof remote_room);
+                       if (strlen(node) > 0) {
+                               wprintf("<FORM METHOD=\"POST\" "
+                                       "action=\"netedit\">"
+                                       "<TR><TD>%s</TD>\n", node);
+
+                               wprintf("<TD>");
+                               if (strlen(remote_room) > 0) {
+                                       escputs(remote_room);
+                               }
+                               wprintf("</TD>");
+
+                               wprintf("<TD>");
+               
+                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"line\" "
+                                       "VALUE=\"ignet_push_share|");
+                               urlescputs(node);
+                               if (strlen(remote_room) > 0) {
+                                       wprintf("|");
+                                       urlescputs(remote_room);
+                               }
+                               wprintf("\">");
+                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" "
+                                       "VALUE=\"sharing\">\n");
+                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"cmd\" "
+                                       "VALUE=\"remove\">\n");
+                               wprintf("<INPUT TYPE=\"submit\" "
+                                       "NAME=\"unshare_button\" VALUE=\"%s\">", _("Unshare"));
+                               wprintf("</TD></TR></FORM>\n");
+                       }
+               }
+
+               wprintf("</TABLE>\n");
+               wprintf("</TD><TD VALIGN=TOP>\n");
+               wprintf("<TABLE border=0 cellpadding=5><TR BGCOLOR=\"#CCCCCC\"><TD>");
+               wprintf(_("Remote node name"));
+               wprintf("</TD><TD>");
+               wprintf(_("Remote room name"));
+               wprintf("</TD><TD>");
+               wprintf(_("Actions"));
+               wprintf("</TD></TR>\n");
+
+               for (i=0; i<num_tokens(not_shared_with, '\n'); ++i) {
+                       extract_token(node, not_shared_with, i, '\n', sizeof node);
+                       if (strlen(node) > 0) {
+                               wprintf("<FORM METHOD=\"POST\" "
+                                       "action=\"netedit\">"
+                                       "<TR><TD>");
+                               escputs(node);
+                               wprintf("</TD><TD>"
+                                       "<INPUT TYPE=\"INPUT\" "
+                                       "NAME=\"suffix\" "
+                                       "MAXLENGTH=128>"
+                                       "</TD><TD>");
+                               wprintf("<INPUT TYPE=\"hidden\" "
+                                       "NAME=\"line\" "
+                                       "VALUE=\"ignet_push_share|");
+                               urlescputs(node);
+                               wprintf("|\">");
+                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" "
+                                       "VALUE=\"sharing\">\n");
+                               wprintf("<INPUT TYPE=\"hidden\" NAME=\"cmd\" "
+                                       "VALUE=\"add\">\n");
+                               wprintf("<INPUT TYPE=\"submit\" "
+                                       "NAME=\"add_button\" VALUE=\"%s\">", _("Share"));
+                               wprintf("</TD></TR></FORM>\n");
+                       }
+               }
+
+               wprintf("</TABLE>\n");
+               wprintf("</TD></TR>"
+                       "</TABLE></CENTER><br />\n"
+                       "<I><B>%s</B><UL><LI>", _("Notes:"));
+               wprintf(_("When sharing a room, "
+                       "it must be shared from both ends.  Adding a node to "
+                       "the 'shared' list sends messages out, but in order to"
+                       " receive messages, the other nodes must be configured"
+                       " to send messages out to your system as well. "
+                       "<LI>If the remote room name is blank, it is assumed "
+                       "that the room name is identical on the remote node."
+                       "<LI>If the remote room name is different, the remote "
+                       "node must also configure the name of the room here."
+                       "</UL></I><br />\n"
+               ));
+
+       }
+
+       /** Mailing list management */
+       if (!strcmp(tab, "listserv")) {
+
+               wprintf("<br /><center>"
+                       "<TABLE BORDER=0 WIDTH=100%% CELLPADDING=5>"
+                       "<TR><TD VALIGN=TOP>");
+
+               wprintf(_("<i>The contents of this room are being "
+                       "mailed <b>as individual messages</b> "
+                       "to the following list recipients:"
+                       "</i><br /><br />\n"));
+
+               serv_puts("GNET");
+               serv_getln(buf, sizeof buf);
+               if (buf[0]=='1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       extract_token(cmd, buf, 0, '|', sizeof cmd);
+                       if (!strcasecmp(cmd, "listrecp")) {
+                               extract_token(recp, buf, 1, '|', sizeof recp);
+                       
+                               escputs(recp);
+                               wprintf(" <a href=\"netedit&cmd=remove&line="
+                                       "listrecp|");
+                               urlescputs(recp);
+                               wprintf("&tab=listserv\">");
+                               wprintf(_("(remove)"));
+                               wprintf("</A><br />");
+                       }
+               }
+               wprintf("<br /><FORM METHOD=\"POST\" action=\"netedit\">\n"
+                       "<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"listserv\">\n"
+                       "<INPUT TYPE=\"hidden\" NAME=\"prefix\" VALUE=\"listrecp|\">\n");
+               wprintf("<INPUT TYPE=\"text\" NAME=\"line\">\n");
+               wprintf("<INPUT TYPE=\"submit\" NAME=\"add_button\" VALUE=\"%s\">", _("Add"));
+               wprintf("</FORM>\n");
+
+               wprintf("</TD><TD VALIGN=TOP>\n");
+               
+               wprintf(_("<i>The contents of this room are being "
+                       "mailed <b>in digest form</b> "
+                       "to the following list recipients:"
+                       "</i><br /><br />\n"));
+
+               serv_puts("GNET");
+               serv_getln(buf, sizeof buf);
+               if (buf[0]=='1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       extract_token(cmd, buf, 0, '|', sizeof cmd);
+                       if (!strcasecmp(cmd, "digestrecp")) {
+                               extract_token(recp, buf, 1, '|', sizeof recp);
+                       
+                               escputs(recp);
+                               wprintf(" <a href=\"netedit&cmd=remove&line="
+                                       "digestrecp|");
+                               urlescputs(recp);
+                               wprintf("&tab=listserv\">");
+                               wprintf(_("(remove)"));
+                               wprintf("</A><br />");
+                       }
+               }
+               wprintf("<br /><FORM METHOD=\"POST\" action=\"netedit\">\n"
+                       "<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"listserv\">\n"
+                       "<INPUT TYPE=\"hidden\" NAME=\"prefix\" VALUE=\"digestrecp|\">\n");
+               wprintf("<INPUT TYPE=\"text\" NAME=\"line\">\n");
+               wprintf("<INPUT TYPE=\"submit\" NAME=\"add_button\" VALUE=\"%s\">", _("Add"));
+               wprintf("</FORM>\n");
+               
+               wprintf("</TD></TR></TABLE><hr />\n");
+
+               if (self_service(999) == 1) {
+                       wprintf(_("This room is configured to allow "
+                               "self-service subscribe/unsubscribe requests."));
+                       wprintf("<a href=\"toggle_self_service?newval=0&tab=listserv\">");
+                       wprintf(_("Click to disable."));
+                       wprintf("</A><br />\n");
+                       wprintf(_("The URL for subscribe/unsubscribe is: "));
+                       wprintf("<TT>%s://%s/listsub</TT><br />\n",
+                               (is_https ? "https" : "http"),
+                               WC->http_host);
+               }
+               else {
+                       wprintf(_("This room is <i>not</i> configured to allow "
+                               "self-service subscribe/unsubscribe requests."));
+                       wprintf(" <a href=\"toggle_self_service?newval=1&"
+                               "tab=listserv\">");
+                       wprintf(_("Click to enable."));
+                       wprintf("</A><br />\n");
+               }
+
+
+               wprintf("</CENTER>\n");
+       }
+
+
+       /** Mailing list management */
+       if (!strcmp(tab, "expire")) {
+
+               serv_puts("GPEX room");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       roompolicy = extract_int(&buf[4], 0);
+                       roomvalue = extract_int(&buf[4], 1);
+               }
+               
+               serv_puts("GPEX floor");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       floorpolicy = extract_int(&buf[4], 0);
+                       floorvalue = extract_int(&buf[4], 1);
+               }
+               
+               wprintf("<br /><FORM METHOD=\"POST\" action=\"set_room_policy\">\n");
+               wprintf("<TABLE border=0 cellspacing=5>\n");
+               wprintf("<TR><TD>");
+               wprintf(_("Message expire policy for this room"));
+               wprintf("<br />(");
+               escputs(WC->wc_roomname);
+               wprintf(")</TD><TD>");
+               wprintf("<INPUT TYPE=\"radio\" NAME=\"roompolicy\" VALUE=\"0\" %s>",
+                       ((roompolicy == 0) ? "CHECKED" : "") );
+               wprintf(_("Use the default policy for this floor"));
+               wprintf("<br />\n");
+               wprintf("<INPUT TYPE=\"radio\" NAME=\"roompolicy\" VALUE=\"1\" %s>",
+                       ((roompolicy == 1) ? "CHECKED" : "") );
+               wprintf(_("Never automatically expire messages"));
+               wprintf("<br />\n");
+               wprintf("<INPUT TYPE=\"radio\" NAME=\"roompolicy\" VALUE=\"2\" %s>",
+                       ((roompolicy == 2) ? "CHECKED" : "") );
+               wprintf(_("Expire by message count"));
+               wprintf("<br />\n");
+               wprintf("<INPUT TYPE=\"radio\" NAME=\"roompolicy\" VALUE=\"3\" %s>",
+                       ((roompolicy == 3) ? "CHECKED" : "") );
+               wprintf(_("Expire by message age"));
+               wprintf("<br />");
+               wprintf(_("Number of messages or days: "));
+               wprintf("<INPUT TYPE=\"text\" NAME=\"roomvalue\" MAXLENGTH=\"5\" VALUE=\"%d\">", roomvalue);
+               wprintf("</TD></TR>\n");
+
+               if (WC->axlevel >= 6) {
+                       wprintf("<TR><TD COLSPAN=2><hr /></TD></TR>\n");
+                       wprintf("<TR><TD>");
+                       wprintf(_("Message expire policy for this floor"));
+                       wprintf("<br />(");
+                       escputs(floorlist[WC->wc_floor]);
+                       wprintf(")</TD><TD>");
+                       wprintf("<INPUT TYPE=\"radio\" NAME=\"floorpolicy\" VALUE=\"0\" %s>",
+                               ((floorpolicy == 0) ? "CHECKED" : "") );
+                       wprintf(_("Use the system default"));
+                       wprintf("<br />\n");
+                       wprintf("<INPUT TYPE=\"radio\" NAME=\"floorpolicy\" VALUE=\"1\" %s>",
+                               ((floorpolicy == 1) ? "CHECKED" : "") );
+                       wprintf(_("Never automatically expire messages"));
+                       wprintf("<br />\n");
+                       wprintf("<INPUT TYPE=\"radio\" NAME=\"floorpolicy\" VALUE=\"2\" %s>",
+                               ((floorpolicy == 2) ? "CHECKED" : "") );
+                       wprintf(_("Expire by message count"));
+                       wprintf("<br />\n");
+                       wprintf("<INPUT TYPE=\"radio\" NAME=\"floorpolicy\" VALUE=\"3\" %s>",
+                               ((floorpolicy == 3) ? "CHECKED" : "") );
+                       wprintf(_("Expire by message age"));
+                       wprintf("<br />");
+                       wprintf(_("Number of messages or days: "));
+                       wprintf("<INPUT TYPE=\"text\" NAME=\"floorvalue\" MAXLENGTH=\"5\" VALUE=\"%d\">",
+                               floorvalue);
+               }
+
+               wprintf("<CENTER>\n");
+               wprintf("<TR><TD COLSPAN=2><hr /><CENTER>\n");
+               wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Save changes"));
+               wprintf("&nbsp;");
+               wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
+               wprintf("</CENTER></TD><TR>\n");
+
+               wprintf("</TABLE>\n"
+                       "<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"expire\">\n"
+                       "</FORM>\n"
+               );
+
+       }
+
+       /** Mailing list management */
+       if (!strcmp(tab, "access")) {
+               display_whok();
+       }
+
+       /** end content of whatever tab is open now */
+       wprintf("</TD></TR></TABLE></div>\n");
+
+       wDumpContent(1);
+}
+
+
+/** 
+ * \brief Toggle self-service list subscription
+ */
+void toggle_self_service(void) {
+       int newval = 0;
+
+       newval = atoi(bstr("newval"));
+       self_service(newval);
+       display_editroom();
+}
+
+
+
+/**
+ * \brief save new parameters for a room
+ */
+void editroom(void)
+{
+       char buf[SIZ];
+       char er_name[128];
+       char er_password[10];
+       char er_dirname[15];
+       char er_roomaide[26];
+       int er_floor;
+       unsigned er_flags;
+       int bump;
+
+
+       if (strlen(bstr("ok_button")) == 0) {
+               strcpy(WC->ImportantMessage,
+                       _("Cancelled.  Changes were not saved."));
+               display_editroom();
+               return;
+       }
+       serv_puts("GETR");
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] != '2') {
+               strcpy(WC->ImportantMessage, &buf[4]);
+               display_editroom();
+               return;
+       }
+       extract_token(er_name, &buf[4], 0, '|', sizeof er_name);
+       extract_token(er_password, &buf[4], 1, '|', sizeof er_password);
+       extract_token(er_dirname, &buf[4], 2, '|', sizeof er_dirname);
+       er_flags = extract_int(&buf[4], 3);
+
+       strcpy(er_roomaide, bstr("er_roomaide"));
+       if (strlen(er_roomaide) == 0) {
+               serv_puts("GETA");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '2') {
+                       strcpy(er_roomaide, "");
+               } else {
+                       extract_token(er_roomaide, &buf[4], 0, '|', sizeof er_roomaide);
+               }
+       }
+       strcpy(buf, bstr("er_name"));
+       buf[128] = 0;
+       if (strlen(buf) > 0) {
+               strcpy(er_name, buf);
+       }
+
+       strcpy(buf, bstr("er_password"));
+       buf[10] = 0;
+       if (strlen(buf) > 0)
+               strcpy(er_password, buf);
+
+       strcpy(buf, bstr("er_dirname"));
+       buf[15] = 0;
+       if (strlen(buf) > 0)
+               strcpy(er_dirname, buf);
+
+       strcpy(buf, bstr("type"));
+       er_flags &= !(QR_PRIVATE | QR_PASSWORDED | QR_GUESSNAME);
+
+       if (!strcmp(buf, "invonly")) {
+               er_flags |= (QR_PRIVATE);
+       }
+       if (!strcmp(buf, "hidden")) {
+               er_flags |= (QR_PRIVATE | QR_GUESSNAME);
+       }
+       if (!strcmp(buf, "passworded")) {
+               er_flags |= (QR_PRIVATE | QR_PASSWORDED);
+       }
+       if (!strcmp(bstr("prefonly"), "yes")) {
+               er_flags |= QR_PREFONLY;
+       } else {
+               er_flags &= ~QR_PREFONLY;
+       }
+
+       if (!strcmp(bstr("readonly"), "yes")) {
+               er_flags |= QR_READONLY;
+       } else {
+               er_flags &= ~QR_READONLY;
+       }
+
+       if (!strcmp(bstr("permanent"), "yes")) {
+               er_flags |= QR_PERMANENT;
+       } else {
+               er_flags &= ~QR_PERMANENT;
+       }
+
+       if (!strcmp(bstr("network"), "yes")) {
+               er_flags |= QR_NETWORK;
+       } else {
+               er_flags &= ~QR_NETWORK;
+       }
+
+       if (!strcmp(bstr("directory"), "yes")) {
+               er_flags |= QR_DIRECTORY;
+       } else {
+               er_flags &= ~QR_DIRECTORY;
+       }
+
+       if (!strcmp(bstr("ulallowed"), "yes")) {
+               er_flags |= QR_UPLOAD;
+       } else {
+               er_flags &= ~QR_UPLOAD;
+       }
+
+       if (!strcmp(bstr("dlallowed"), "yes")) {
+               er_flags |= QR_DOWNLOAD;
+       } else {
+               er_flags &= ~QR_DOWNLOAD;
+       }
+
+       if (!strcmp(bstr("visdir"), "yes")) {
+               er_flags |= QR_VISDIR;
+       } else {
+               er_flags &= ~QR_VISDIR;
+       }
+
+       strcpy(buf, bstr("anon"));
+
+       er_flags &= ~(QR_ANONONLY | QR_ANONOPT);
+       if (!strcmp(buf, "anononly"))
+               er_flags |= QR_ANONONLY;
+       if (!strcmp(buf, "anon2"))
+               er_flags |= QR_ANONOPT;
+
+       bump = 0;
+       if (!strcmp(bstr("bump"), "yes"))
+               bump = 1;
+
+       er_floor = atoi(bstr("er_floor"));
+
+       sprintf(buf, "SETR %s|%s|%s|%u|%d|%d",
+            er_name, er_password, er_dirname, er_flags, bump, er_floor);
+       serv_puts(buf);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               strcpy(WC->ImportantMessage, &buf[4]);
+               display_editroom();
+               return;
+       }
+       gotoroom(er_name);
+
+       if (strlen(er_roomaide) > 0) {
+               sprintf(buf, "SETA %s", er_roomaide);
+               serv_puts(buf);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '2') {
+                       strcpy(WC->ImportantMessage, &buf[4]);
+                       display_main_menu();
+                       return;
+               }
+       }
+       gotoroom(er_name);
+       strcpy(WC->ImportantMessage, _("Your changes have been saved."));
+       display_editroom();
+       return;
+}
+
+
+/**
+ * \brief Display form for Invite, Kick, and show Who Knows a room
+ */
+void do_invt_kick(void) {
+        char buf[SIZ], room[SIZ], username[SIZ];
+
+        serv_puts("GETR");
+        serv_getln(buf, sizeof buf);
+
+        if (buf[0] != '2') {
+               escputs(&buf[4]);
+               return;
+        }
+        extract_token(room, &buf[4], 0, '|', sizeof room);
+
+        strcpy(username, bstr("username"));
+
+        if (strlen(bstr("kick_button")) > 0) {
+                sprintf(buf, "KICK %s", username);
+                serv_puts(buf);
+                serv_getln(buf, sizeof buf);
+
+                if (buf[0] != '2') {
+                        strcpy(WC->ImportantMessage, &buf[4]);
+                } else {
+                        sprintf(WC->ImportantMessage,
+                               _("<B><I>User %s kicked out of room %s.</I></B>\n"), 
+                                username, room);
+                }
+        }
+
+       if (strlen(bstr("invite_button")) > 0) {
+                sprintf(buf, "INVT %s", username);
+                serv_puts(buf);
+                serv_getln(buf, sizeof buf);
+
+                if (buf[0] != '2') {
+                        strcpy(WC->ImportantMessage, &buf[4]);
+                } else {
+                        sprintf(WC->ImportantMessage,
+                               _("<B><I>User %s invited to room %s.</I></B>\n"), 
+                                username, room);
+                }
+        }
+
+       display_editroom();
+}
+
+
+
+/**
+ * \brief Display form for Invite, Kick, and show Who Knows a room
+ */
+void display_whok(void)
+{
+        char buf[SIZ], room[SIZ], username[SIZ];
+
+        serv_puts("GETR");
+        serv_getln(buf, sizeof buf);
+
+        if (buf[0] != '2') {
+               escputs(&buf[4]);
+               return;
+        }
+        extract_token(room, &buf[4], 0, '|', sizeof room);
+
+        
+       wprintf("<TABLE border=0 CELLSPACING=10><TR VALIGN=TOP><TD>");
+       wprintf(_("The users listed below have access to this room.  "
+               "To remove a user from the access list, select the user "
+               "name from the list and click 'Kick'."));
+       wprintf("<br /><br />");
+       
+        wprintf("<CENTER><FORM METHOD=\"POST\" action=\"do_invt_kick\">\n");
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"access\">\n");
+        wprintf("<SELECT NAME=\"username\" SIZE=\"10\" style=\"width:100%%\">\n");
+        serv_puts("WHOK");
+        serv_getln(buf, sizeof buf);
+        if (buf[0] == '1') {
+                while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                        extract_token(username, buf, 0, '|', sizeof username);
+                        wprintf("<OPTION>");
+                        escputs(username);
+                        wprintf("\n");
+                }
+        }
+        wprintf("</SELECT><br />\n");
+
+        wprintf("<input type=\"submit\" name=\"kick_button\" value=\"%s\">", _("Kick"));
+        wprintf("</FORM></CENTER>\n");
+
+       wprintf("</TD><TD>");
+       wprintf(_("To grant another user access to this room, enter the "
+               "user name in the box below and click 'Invite'."));
+       wprintf("<br /><br />");
+
+        wprintf("<CENTER><FORM METHOD=\"POST\" action=\"do_invt_kick\">\n");
+       wprintf("<INPUT TYPE=\"hidden\" NAME=\"tab\" VALUE=\"access\">\n");
+        wprintf(_("Invite:"));
+       wprintf(" ");
+        wprintf("<input type=\"text\" name=\"username\" style=\"width:100%%\"><br />\n"
+               "<input type=\"hidden\" name=\"invite_button\" value=\"Invite\">"
+               "<input type=\"submit\" value=\"%s\">"
+               "</FORM></CENTER>\n", _("Invite"));
+
+       wprintf("</TD></TR></TABLE>\n");
+        wDumpContent(1);
+}
+
+
+
+/**
+ * \brief display the form for entering a new room
+ */
+void display_entroom(void)
+{
+       int i;
+       char buf[SIZ];
+
+       serv_puts("CRE8 0");
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] != '2') {
+               strcpy(WC->ImportantMessage, &buf[4]);
+               display_main_menu();
+               return;
+       }
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Create a new room"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       wprintf("<form name=\"create_room_form\" method=\"POST\" action=\"entroom\">\n");
+
+       wprintf("<UL><LI>");
+       wprintf(_("Name of room: "));
+       wprintf("<INPUT TYPE=\"text\" NAME=\"er_name\" MAXLENGTH=\"127\">\n");
+
+        wprintf("<LI>");
+       wprintf(_("Resides on floor: "));
+        load_floorlist(); 
+        wprintf("<SELECT NAME=\"er_floor\" SIZE=\"1\">\n");
+        for (i = 0; i < 128; ++i)
+                if (strlen(floorlist[i]) > 0) {
+                        wprintf("<OPTION ");
+                        wprintf("VALUE=\"%d\">", i);
+                        escputs(floorlist[i]);
+                        wprintf("</OPTION>\n");
+                }
+        wprintf("</SELECT>\n");
+
+               /**
+                * Our clever little snippet of JavaScript automatically selects
+                * a public room if the view is set to Bulletin Board or wiki, and
+                * it selects a mailbox room otherwise.  The user can override this,
+                * of course.  We also disable the floor selector for mailboxes.
+                */
+               wprintf("<LI>");
+               wprintf(_("Default view for room: "));
+        wprintf("<SELECT NAME=\"er_view\" SIZE=\"1\" OnChange=\""
+               "       if ( (this.form.er_view.value == 0)             "
+               "          || (this.form.er_view.value == 6) ) {        "
+               "               this.form.type[0].checked=true;         "
+               "               this.form.er_floor.disabled = false;    "
+               "       }                                               "
+               "       else {                                          "
+               "               this.form.type[4].checked=true;         "
+               "               this.form.er_floor.disabled = true;     "
+               "       }                                               "
+               "\">\n");
+       for (i=0; i<(sizeof viewdefs / sizeof (char *)); ++i) {
+               if (is_view_allowed_as_default(i)) {
+                       wprintf("<OPTION %s VALUE=\"%d\">",
+                               ((i == 0) ? "SELECTED" : ""), i );
+                       escputs(viewdefs[i]);
+                       wprintf("</OPTION>\n");
+               }
+       }
+       wprintf("</SELECT>\n");
+
+       wprintf("<LI>");
+       wprintf(_("Type of room:"));
+       wprintf("<UL>\n");
+
+       wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"public\" ");
+       wprintf("CHECKED OnChange=\""
+               "       if (this.form.type[0].checked == true) {        "
+               "               this.form.er_floor.disabled = false;    "
+               "       }                                               "
+               "\"> ");
+       wprintf(_("Public (automatically appears to everyone)"));
+
+       wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"hidden\" OnChange=\""
+               "       if (this.form.type[1].checked == true) {        "
+               "               this.form.er_floor.disabled = false;    "
+               "       }                                               "
+               "\"> ");
+       wprintf(_("Private - hidden (accessible to anyone who knows its name)"));
+
+       wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"passworded\" OnChange=\""
+               "       if (this.form.type[2].checked == true) {        "
+               "               this.form.er_floor.disabled = false;    "
+               "       }                                               "
+               "\"> ");
+       wprintf(_("Private - require password: "));
+       wprintf("<INPUT TYPE=\"text\" NAME=\"er_password\" MAXLENGTH=\"9\">\n");
+
+       wprintf("<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"invonly\" OnChange=\""
+               "       if (this.form.type[3].checked == true) {        "
+               "               this.form.er_floor.disabled = false;    "
+               "       }                                               "
+               "\"> ");
+       wprintf(_("Private - invitation only"));
+
+       wprintf("\n<LI><INPUT TYPE=\"radio\" NAME=\"type\" VALUE=\"personal\" "
+               "OnChange=\""
+               "       if (this.form.type[4].checked == true) {        "
+               "               this.form.er_floor.disabled = true;     "
+               "       }                                               "
+               "\"> ");
+       wprintf(_("Personal (mailbox for you only)"));
+
+       wprintf("\n</UL>\n");
+
+       wprintf("<CENTER>\n");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Create new room"));
+       wprintf("&nbsp;");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
+       wprintf("</CENTER>\n");
+       wprintf("</FORM>\n<hr />");
+       serv_printf("MESG roomaccess");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               fmout("CENTER");
+       }
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+
+
+
+/**
+ * \brief support function for entroom() -- sets the default view 
+ */
+void er_set_default_view(int newview) {
+
+       char buf[SIZ];
+
+       char rm_name[SIZ];
+       char rm_pass[SIZ];
+       char rm_dir[SIZ];
+       int rm_bits1;
+       int rm_floor;
+       int rm_listorder;
+       int rm_bits2;
+
+       serv_puts("GETR");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') return;
+
+       extract_token(rm_name, &buf[4], 0, '|', sizeof rm_name);
+       extract_token(rm_pass, &buf[4], 1, '|', sizeof rm_pass);
+       extract_token(rm_dir, &buf[4], 2, '|', sizeof rm_dir);
+       rm_bits1 = extract_int(&buf[4], 3);
+       rm_floor = extract_int(&buf[4], 4);
+       rm_listorder = extract_int(&buf[4], 5);
+       rm_bits2 = extract_int(&buf[4], 7);
+
+       serv_printf("SETR %s|%s|%s|%d|0|%d|%d|%d|%d",
+               rm_name, rm_pass, rm_dir, rm_bits1, rm_floor,
+               rm_listorder, newview, rm_bits2
+       );
+       serv_getln(buf, sizeof buf);
+}
+
+
+
+/**
+ * \brief enter a new room
+ */
+void entroom(void)
+{
+       char buf[SIZ];
+       char er_name[SIZ];
+       char er_type[SIZ];
+       char er_password[SIZ];
+       int er_floor;
+       int er_num_type;
+       int er_view;
+
+       if (strlen(bstr("ok_button")) == 0) {
+               strcpy(WC->ImportantMessage,
+                       _("Cancelled.  No new room was created."));
+               display_main_menu();
+               return;
+       }
+       strcpy(er_name, bstr("er_name"));
+       strcpy(er_type, bstr("type"));
+       strcpy(er_password, bstr("er_password"));
+       er_floor = atoi(bstr("er_floor"));
+       er_view = atoi(bstr("er_view"));
+
+       er_num_type = 0;
+       if (!strcmp(er_type, "hidden"))
+               er_num_type = 1;
+       if (!strcmp(er_type, "passworded"))
+               er_num_type = 2;
+       if (!strcmp(er_type, "invonly"))
+               er_num_type = 3;
+       if (!strcmp(er_type, "personal"))
+               er_num_type = 4;
+
+       sprintf(buf, "CRE8 1|%s|%d|%s|%d|%d|%d", 
+               er_name, er_num_type, er_password, er_floor, 0, er_view);
+       serv_puts(buf);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               strcpy(WC->ImportantMessage, &buf[4]);
+               display_main_menu();
+               return;
+       }
+       gotoroom(er_name);
+       do_change_view(er_view);                /* Now go there */
+}
+
+
+/**
+ * \brief display the screen to enter a private room
+ */
+void display_private(char *rname, int req_pass)
+{
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Go to a hidden room"));
+       wprintf("</SPAN>"
+               "</TD></TR></TABLE>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       wprintf("<CENTER>\n");
+       wprintf("<br />");
+       wprintf(_("If you know the name of a hidden (guess-name) or "
+               "passworded room, you can enter that room by typing "
+               "its name below.  Once you gain access to a private "
+               "room, it will appear in your regular room listings "
+               "so you don't have to keep returning here."));
+       wprintf("\n<br /><br />");
+
+       wprintf("<FORM METHOD=\"POST\" action=\"goto_private\">\n");
+
+       wprintf("<table border=\"0\" cellspacing=\"5\" "
+               "cellpadding=\"5\" BGCOLOR=\"#EEEEEE\">\n"
+               "<TR><TD>");
+       wprintf(_("Enter room name:"));
+       wprintf("</TD><TD>"
+               "<INPUT TYPE=\"text\" NAME=\"gr_name\" "
+               "VALUE=\"%s\" MAXLENGTH=\"128\">\n", rname);
+
+       if (req_pass) {
+               wprintf("</TD></TR><TR><TD>");
+               wprintf(_("Enter room password:"));
+               wprintf("</TD><TD>");
+               wprintf("<INPUT TYPE=\"password\" NAME=\"gr_pass\" MAXLENGTH=\"9\">\n");
+       }
+       wprintf("</TD></TR></TABLE><br />\n");
+
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">"
+               "&nbsp;"
+               "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">",
+               _("Go there"),
+               _("Cancel")
+       );
+       wprintf("</FORM>\n");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+/**
+ * \brief goto a private room
+ */
+void goto_private(void)
+{
+       char hold_rm[SIZ];
+       char buf[SIZ];
+
+       if (strlen(bstr("ok_button")) == 0) {
+               display_main_menu();
+               return;
+       }
+       strcpy(hold_rm, WC->wc_roomname);
+       strcpy(buf, "GOTO ");
+       strcat(buf, bstr("gr_name"));
+       strcat(buf, "|");
+       strcat(buf, bstr("gr_pass"));
+       serv_puts(buf);
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] == '2') {
+               smart_goto(bstr("gr_name"));
+               return;
+       }
+       if (!strncmp(buf, "540", 3)) {
+               display_private(bstr("gr_name"), 1);
+               return;
+       }
+       output_headers(1, 1, 1, 0, 0, 0);
+       wprintf("%s\n", &buf[4]);
+       wDumpContent(1);
+       return;
+}
+
+
+/**
+ * \brief display the screen to zap a room
+ */
+void display_zap(void)
+{
+       output_headers(1, 1, 2, 0, 0, 0);
+
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#770000\"><TR><TD>");
+       wprintf("<SPAN CLASS=\"titlebar\">");
+       wprintf(_("Zap (forget/unsubscribe) the current room"));
+       wprintf("</SPAN>\n");
+       wprintf("</TD></TR></TABLE>\n");
+       wprintf("</div>\n<div id=\"content\">\n");
+
+       wprintf(_("If you select this option, <em>%s</em> will "
+               "disappear from your room list.  Is this what you wish "
+               "to do?<br />\n"), WC->wc_roomname);
+
+       wprintf("<FORM METHOD=\"POST\" action=\"zap\">\n");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Zap this room"));
+       wprintf("&nbsp;");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
+       wprintf("</FORM>\n");
+       wDumpContent(1);
+}
+
+
+/**
+ * \brief zap a room
+ */
+void zap(void)
+{
+       char buf[SIZ];
+       char final_destination[SIZ];
+
+       /**
+        * If the forget-room routine fails for any reason, we fall back
+        * to the current room; otherwise, we go to the Lobby
+        */
+       strcpy(final_destination, WC->wc_roomname);
+
+       if (strlen(bstr("ok_button")) > 0) {
+               serv_printf("GOTO %s", WC->wc_roomname);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       serv_puts("FORG");
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] == '2') {
+                               strcpy(final_destination, "_BASEROOM_");
+                       }
+               }
+       }
+       smart_goto(final_destination);
+}
+
+
+
+/**
+ * \brief Delete the current room
+ */
+void delete_room(void)
+{
+       char buf[SIZ];
+
+       serv_puts("KILL 1");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               strcpy(WC->ImportantMessage, &buf[4]);
+               display_main_menu();
+               return;
+       } else {
+               smart_goto("_BASEROOM_");
+       }
+}
+
+
+
+/**
+ * \brief Perform changes to a room's network configuration
+ */
+void netedit(void) {
+       FILE *fp;
+       char buf[SIZ];
+       char line[SIZ];
+       char cmpa0[SIZ];
+       char cmpa1[SIZ];
+       char cmpb0[SIZ];
+       char cmpb1[SIZ];
+
+       if (strlen(bstr("line"))==0) {
+               display_editroom();
+               return;
+       }
+
+       strcpy(line, bstr("prefix"));
+       strcat(line, bstr("line"));
+       strcat(line, bstr("suffix"));
+
+       fp = tmpfile();
+       if (fp == NULL) {
+               display_editroom();
+               return;
+       }
+
+       serv_puts("GNET");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               fclose(fp);
+               display_editroom();
+               return;
+       }
+
+       /** This loop works for add *or* remove.  Spiffy, eh? */
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               extract_token(cmpa0, buf, 0, '|', sizeof cmpa0);
+               extract_token(cmpa1, buf, 1, '|', sizeof cmpa1);
+               extract_token(cmpb0, line, 0, '|', sizeof cmpb0);
+               extract_token(cmpb1, line, 1, '|', sizeof cmpb1);
+               if ( (strcasecmp(cmpa0, cmpb0)) 
+                  || (strcasecmp(cmpa1, cmpb1)) ) {
+                       fprintf(fp, "%s\n", buf);
+               }
+       }
+
+       rewind(fp);
+       serv_puts("SNET");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '4') {
+               fclose(fp);
+               display_editroom();
+               return;
+       }
+
+       while (fgets(buf, sizeof buf, fp) != NULL) {
+               buf[strlen(buf)-1] = 0;
+               serv_puts(buf);
+       }
+
+       if (strlen(bstr("add_button")) > 0) {
+               serv_puts(line);
+       }
+
+       serv_puts("000");
+       fclose(fp);
+       display_editroom();
+}
+
+
+
+/**
+ * \brief Convert a room name to a folder-ish-looking name.
+ * \param folder the folderish name
+ * \param room the room name
+ * \param floor the floor name
+ * \param is_mailbox is it a mailbox?
+ */
+void room_to_folder(char *folder, char *room, int floor, int is_mailbox)
+{
+       int i;
+
+       /**
+        * For mailboxes, just do it straight...
+        */
+       if (is_mailbox) {
+               sprintf(folder, "My folders|%s", room);
+       }
+
+       /**
+        * Otherwise, prefix the floor name as a "public folders" moniker
+        */
+       else {
+               sprintf(folder, "%s|%s", floorlist[floor], room);
+       }
+
+       /**
+        * Replace "\" characters with "|" for pseudo-folder-delimiting
+        */
+       for (i=0; i<strlen(folder); ++i) {
+               if (folder[i] == '\\') folder[i] = '|';
+       }
+}
+
+
+
+
+/**
+ * \brief Back end for change_view()
+ * \param newview set newview???
+ */
+void do_change_view(int newview) {
+       char buf[SIZ];
+
+       serv_printf("VIEW %d", newview);
+       serv_getln(buf, sizeof buf);
+       WC->wc_view = newview;
+       smart_goto(WC->wc_roomname);
+}
+
+
+
+/**
+ * \brief Change the view for this room
+ */
+void change_view(void) {
+       int view;
+
+       view = atol(bstr("view"));
+       do_change_view(view);
+}
+
+
+/**
+ * \brief One big expanded tree list view --- like a folder list
+ * \param fold the folder to view
+ * \param max_folders how many folders???
+ * \param num_floors hom many floors???
+ */
+void do_folder_view(struct folder *fold, int max_folders, int num_floors) {
+       char buf[SIZ];
+       int levels;
+       int i;
+       int has_subfolders = 0;
+       int *parents;
+
+       parents = malloc(max_folders * sizeof(int));
+
+       /** BEGIN TREE MENU */
+       wprintf("<div id=\"roomlist_div\">Loading folder list...</div>\n");
+
+       /** include NanoTree */
+       wprintf("<script type=\"text/javascript\" src=\"static/nanotree.js\"></script>\n");
+
+       /** initialize NanoTree */
+       wprintf("<script type=\"text/javascript\">                      \n"
+               "       showRootNode = false;                           \n"
+               "       sortNodes = false;                              \n"
+               "       dragable = false;                               \n"
+               "                                                       \n"
+               "       function standardClick(treeNode) {              \n"
+               "       }                                               \n"
+               "                                                       \n"
+               "       var closedGif = 'static/folder_closed.gif';     \n"
+               "       var openGif = 'static/folder_open.gif';         \n"
+               "                                                       \n"
+               "       rootNode = new TreeNode(1, 'root node - hide'); \n"
+       );
+
+       levels = 0;
+       for (i=0; i<max_folders; ++i) {
+
+               has_subfolders = 0;
+               if ((i+1) < max_folders) {
+                       if ( (!strncasecmp(fold[i].name, fold[i+1].name, strlen(fold[i].name)))
+                          && (fold[i+1].name[strlen(fold[i].name)] == '|') ) {
+                               has_subfolders = 1;
+                       }
+               }
+
+               levels = num_tokens(fold[i].name, '|');
+               parents[levels] = i;
+
+               wprintf("var node%d = new TreeNode(%d, '", i, i);
+
+               if (fold[i].selectable) {
+                       wprintf("<a href=\"dotgoto?room=");
+                       urlescputs(fold[i].room);
+                       wprintf("\">");
+               }
+
+               if (levels == 1) {
+                       wprintf("<SPAN CLASS=\"roomlist_floor\">");
+               }
+               else if (fold[i].hasnewmsgs) {
+                       wprintf("<SPAN CLASS=\"roomlist_new\">");
+               }
+               else {
+                       wprintf("<SPAN CLASS=\"roomlist_old\">");
+               }
+               extract_token(buf, fold[i].name, levels-1, '|', sizeof buf);
+               escputs(buf);
+               wprintf("</SPAN>");
+
+               wprintf("</a>', ");
+               if (has_subfolders) {
+                       wprintf("new Array(closedGif, openGif)");
+               }
+               else if (fold[i].view == VIEW_ADDRESSBOOK) {
+                       wprintf("'static/viewcontacts_16x.gif'");
+               }
+               else if (fold[i].view == VIEW_CALENDAR) {
+                       wprintf("'static/calarea_16x.gif'");
+               }
+               else if (fold[i].view == VIEW_CALBRIEF) {
+                       wprintf("'static/calarea_16x.gif'");
+               }
+               else if (fold[i].view == VIEW_TASKS) {
+                       wprintf("'static/taskmanag_16x.gif'");
+               }
+               else if (fold[i].view == VIEW_NOTES) {
+                       wprintf("'static/storenotes_16x.gif'");
+               }
+               else if (fold[i].view == VIEW_MAILBOX) {
+                       wprintf("'static/privatemess_16x.gif'");
+               }
+               else {
+                       wprintf("'static/chatrooms_16x.gif'");
+               }
+               wprintf(", '");
+               urlescputs(fold[i].name);
+               wprintf("');\n");
+
+               if (levels < 2) {
+                       wprintf("rootNode.addChild(node%d);\n", i);
+               }
+               else {
+                       wprintf("node%d.addChild(node%d);\n", parents[levels-1], i);
+               }
+       }
+
+       wprintf("container = document.getElementById('roomlist_div');   \n"
+               "showTree('');  \n"
+               "</script>\n"
+       );
+
+       free(parents);
+       /** END TREE MENU */
+}
+
+/**
+ * \brief Boxes and rooms and lists ... oh my!
+ * \param fold the folder to view
+ * \param max_folders how many folders???
+ * \param num_floors hom many floors???
+ */
+void do_rooms_view(struct folder *fold, int max_folders, int num_floors) {
+       char buf[256];
+       char floor_name[256];
+       char old_floor_name[256];
+       char boxtitle[256];
+       int levels, oldlevels;
+       int i, t;
+       int num_boxes = 0;
+       static int columns = 3;
+       int boxes_per_column = 0;
+       int current_column = 0;
+       int nf;
+
+       strcpy(floor_name, "");
+       strcpy(old_floor_name, "");
+
+       nf = num_floors;
+       while (nf % columns != 0) ++nf;
+       boxes_per_column = (nf / columns);
+       if (boxes_per_column < 1) boxes_per_column = 1;
+
+       /** Outer table (for columnization) */
+       wprintf("<TABLE BORDER=0 WIDTH=96%% CELLPADDING=5>"
+               "<tr><td valign=top>");
+
+       levels = 0;
+       oldlevels = 0;
+       for (i=0; i<max_folders; ++i) {
+
+               levels = num_tokens(fold[i].name, '|');
+               extract_token(floor_name, fold[i].name, 0,
+                       '|', sizeof floor_name);
+
+               if ( (strcasecmp(floor_name, old_floor_name))
+                  && (strlen(old_floor_name) > 0) ) {
+                       /* End inner box */
+                       do_template("endbox");
+
+                       ++num_boxes;
+                       if ((num_boxes % boxes_per_column) == 0) {
+                               ++current_column;
+                               if (current_column < columns) {
+                                       wprintf("</td><td valign=top>\n");
+                               }
+                       }
+               }
+               strcpy(old_floor_name, floor_name);
+
+               if (levels == 1) {
+                       /** Begin inner box */
+                       stresc(boxtitle, floor_name, 1, 0);
+                       svprintf("BOXTITLE", WCS_STRING, boxtitle);
+                       do_template("beginbox");
+               }
+
+               oldlevels = levels;
+
+               if (levels > 1) {
+                       wprintf("&nbsp;");
+                       if (levels>2) for (t=0; t<(levels-2); ++t) wprintf("&nbsp;&nbsp;&nbsp;");
+                       if (fold[i].selectable) {
+                               wprintf("<a href=\"dotgoto?room=");
+                               urlescputs(fold[i].room);
+                               wprintf("\">");
+                       }
+                       else {
+                               wprintf("<i>");
+                       }
+                       if (fold[i].hasnewmsgs) {
+                               wprintf("<SPAN CLASS=\"roomlist_new\">");
+                       }
+                       else {
+                               wprintf("<SPAN CLASS=\"roomlist_old\">");
+                       }
+                       extract_token(buf, fold[i].name, levels-1, '|', sizeof buf);
+                       escputs(buf);
+                       wprintf("</SPAN>");
+                       if (fold[i].selectable) {
+                               wprintf("</A>");
+                       }
+                       else {
+                               wprintf("</i>");
+                       }
+                       if (!strcasecmp(fold[i].name, "My Folders|Mail")) {
+                               wprintf(" (INBOX)");
+                       }
+                       wprintf("<br />\n");
+               }
+       }
+       /** End the final inner box */
+       do_template("endbox");
+
+       wprintf("</TD></TR></TABLE>\n");
+}
+
+/**
+ * \brief print a floor div???
+ * \param which_floordiv name of the floordiv???
+ */
+void set_floordiv_expanded(char *which_floordiv) {
+       begin_ajax_response();
+       safestrncpy(WC->floordiv_expanded, which_floordiv, sizeof WC->floordiv_expanded);
+       end_ajax_response();
+}
+
+/**
+ * \brief view the iconbar
+ * \param fold the folder to view
+ * \param max_folders how many folders???
+ * \param num_floors hom many floors???
+ */
+void do_iconbar_view(struct folder *fold, int max_folders, int num_floors) {
+       char buf[256];
+       char floor_name[256];
+       char old_floor_name[256];
+       char floordivtitle[256];
+       char floordiv_id[32];
+       int levels, oldlevels;
+       int i, t;
+       int num_drop_targets = 0;
+       char *icon = NULL;
+
+       strcpy(floor_name, "");
+       strcpy(old_floor_name, "");
+
+       levels = 0;
+       oldlevels = 0;
+       for (i=0; i<max_folders; ++i) {
+
+               levels = num_tokens(fold[i].name, '|');
+               extract_token(floor_name, fold[i].name, 0,
+                       '|', sizeof floor_name);
+
+               if ( (strcasecmp(floor_name, old_floor_name))
+                  && (strlen(old_floor_name) > 0) ) {
+                       /** End inner box */
+                       wprintf("<br>\n");
+                       wprintf("</div>\n");    /** floordiv */
+               }
+               strcpy(old_floor_name, floor_name);
+
+               if (levels == 1) {
+                       /** Begin floor */
+                       stresc(floordivtitle, floor_name, 0, 0);
+                       sprintf(floordiv_id, "floordiv%d", i);
+                       wprintf("<span class=\"ib_roomlist_floor\" "
+                               "onClick=\"expand_floor('%s')\">"
+                               "%s</span><br>\n", floordiv_id, floordivtitle);
+                       wprintf("<div id=\"%s\" style=\"display:%s\">",
+                               floordiv_id,
+                               (!strcasecmp(floordiv_id, WC->floordiv_expanded) ? "block" : "none")
+                       );
+               }
+
+               oldlevels = levels;
+
+               if (levels > 1) {
+                       wprintf("<div id=\"roomdiv%d\">", i);
+                       wprintf("&nbsp;");
+                       if (levels>2) for (t=0; t<(levels-2); ++t) wprintf("&nbsp;");
+
+                       /** choose the icon */
+                       if (fold[i].view == VIEW_ADDRESSBOOK) {
+                               icon = "viewcontacts_16x.gif" ;
+                       }
+                       else if (fold[i].view == VIEW_CALENDAR) {
+                               icon = "calarea_16x.gif" ;
+                       }
+                       else if (fold[i].view == VIEW_CALBRIEF) {
+                               icon = "calarea_16x.gif" ;
+                       }
+                       else if (fold[i].view == VIEW_TASKS) {
+                               icon = "taskmanag_16x.gif" ;
+                       }
+                       else if (fold[i].view == VIEW_NOTES) {
+                               icon = "storenotes_16x.gif" ;
+                       }
+                       else if (fold[i].view == VIEW_MAILBOX) {
+                               icon = "privatemess_16x.gif" ;
+                       }
+                       else {
+                               icon = "chatrooms_16x.gif" ;
+                       }
+
+                       if (fold[i].selectable) {
+                               wprintf("<a href=\"dotgoto?room=");
+                               urlescputs(fold[i].room);
+                               wprintf("\">");
+                               wprintf("<img align=\"middle\" border=0 src=\"static/%s\" alt=\"\"> ", icon);
+                       }
+                       else {
+                               wprintf("<i>");
+                       }
+                       if (fold[i].hasnewmsgs) {
+                               wprintf("<SPAN CLASS=\"ib_roomlist_new\">");
+                       }
+                       else {
+                               wprintf("<SPAN CLASS=\"ib_roomlist_old\">");
+                       }
+                       extract_token(buf, fold[i].name, levels-1, '|', sizeof buf);
+                       escputs(buf);
+                       if (!strcasecmp(fold[i].name, "My Folders|Mail")) {
+                               wprintf(" (INBOX)");
+                       }
+                       wprintf("</SPAN>");
+                       if (fold[i].selectable) {
+                               wprintf("</A>");
+                       }
+                       else {
+                               wprintf("</i>");
+                       }
+                       wprintf("<br />");
+                       wprintf("</div>\n");    /** roomdiv */
+               }
+       }
+       wprintf("</div>\n");    /** floordiv */
+
+
+       /** BEGIN: The old invisible pixel trick, to get our JavaScript to initialize */
+       wprintf("<img src=\"static/blank.gif\" onLoad=\"\n");
+
+       num_drop_targets = 0;
+
+       for (i=0; i<max_folders; ++i) {
+               levels = num_tokens(fold[i].name, '|');
+               if (levels > 1) {
+                       wprintf("drop_targets_elements[%d]=$('roomdiv%d');\n", num_drop_targets, i);
+                       wprintf("drop_targets_roomnames[%d]='", num_drop_targets);
+                       jsescputs(fold[i].room);
+                       wprintf("';\n");
+                       ++num_drop_targets;
+               }
+       }
+
+       wprintf("num_drop_targets = %d;\n", num_drop_targets);
+       if (strlen(WC->floordiv_expanded) > 1) {
+               wprintf("which_div_expanded = '%s';\n", WC->floordiv_expanded);
+       }
+
+       wprintf("\">\n");
+       /** END: The old invisible pixel trick, to get our JavaScript to initialize */
+}
+
+
+
+/**
+ * \brief Show the room list.  
+ * (only should get called by
+ * knrooms() because that's where output_headers() is called from)
+ * \param viewpref the view preferences???
+ */
+
+void list_all_rooms_by_floor(char *viewpref) {
+       char buf[SIZ];
+       int swap = 0;
+       struct folder *fold = NULL;
+       struct folder ftmp;
+       int max_folders = 0;
+       int alloc_folders = 0;
+       int i, j;
+       int ra_flags = 0;
+       int flags = 0;
+       int num_floors = 1;     /** add an extra one for private folders */
+
+       /** If our cached folder list is very old, burn it. */
+       if (WC->cache_fold != NULL) {
+               if ((time(NULL) - WC->cache_timestamp) > 300) {
+                       free(WC->cache_fold);
+                       WC->cache_fold = NULL;
+               }
+       }
+
+       /** Can we do the iconbar roomlist from cache? */
+       if ((WC->cache_fold != NULL) && (!strcasecmp(viewpref, "iconbar"))) {
+               do_iconbar_view(WC->cache_fold, WC->cache_max_folders, WC->cache_num_floors);
+               return;
+       }
+
+       /** Grab the floor table so we know how to build the list... */
+       load_floorlist();
+
+       /** Start with the mailboxes */
+       max_folders = 1;
+       alloc_folders = 1;
+       fold = malloc(sizeof(struct folder));
+       memset(fold, 0, sizeof(struct folder));
+       strcpy(fold[0].name, "My folders");
+       fold[0].is_mailbox = 1;
+
+       /** Then add floors */
+       serv_puts("LFLR");
+       serv_getln(buf, sizeof buf);
+       if (buf[0]=='1') while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               if (max_folders >= alloc_folders) {
+                       alloc_folders = max_folders + 100;
+                       fold = realloc(fold,
+                               alloc_folders * sizeof(struct folder));
+               }
+               memset(&fold[max_folders], 0, sizeof(struct folder));
+               extract_token(fold[max_folders].name, buf, 1, '|', sizeof fold[max_folders].name);
+               ++max_folders;
+               ++num_floors;
+       }
+
+       /** Now add rooms */
+       serv_puts("LKRA");
+       serv_getln(buf, sizeof buf);
+       if (buf[0]=='1') while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               if (max_folders >= alloc_folders) {
+                       alloc_folders = max_folders + 100;
+                       fold = realloc(fold,
+                               alloc_folders * sizeof(struct folder));
+               }
+               memset(&fold[max_folders], 0, sizeof(struct folder));
+               extract_token(fold[max_folders].room, buf, 0, '|', sizeof fold[max_folders].room);
+               ra_flags = extract_int(buf, 5);
+               flags = extract_int(buf, 1);
+               fold[max_folders].floor = extract_int(buf, 2);
+               fold[max_folders].hasnewmsgs =
+                       ((ra_flags & UA_HASNEWMSGS) ? 1 : 0 );
+               if (flags & QR_MAILBOX) {
+                       fold[max_folders].is_mailbox = 1;
+               }
+               fold[max_folders].view = extract_int(buf, 6);
+               room_to_folder(fold[max_folders].name,
+                               fold[max_folders].room,
+                               fold[max_folders].floor,
+                               fold[max_folders].is_mailbox);
+               fold[max_folders].selectable = 1;
+               ++max_folders;
+       }
+
+       /** Bubble-sort the folder list */
+       for (i=0; i<max_folders; ++i) {
+               for (j=0; j<(max_folders-1)-i; ++j) {
+                       if (fold[j].is_mailbox == fold[j+1].is_mailbox) {
+                               swap = strcasecmp(fold[j].name, fold[j+1].name);
+                       }
+                       else {
+                               if ( (fold[j+1].is_mailbox)
+                                  && (!fold[j].is_mailbox)) {
+                                       swap = 1;
+                               }
+                               else {
+                                       swap = 0;
+                               }
+                       }
+                       if (swap > 0) {
+                               memcpy(&ftmp, &fold[j], sizeof(struct folder));
+                               memcpy(&fold[j], &fold[j+1],
+                                                       sizeof(struct folder));
+                               memcpy(&fold[j+1], &ftmp,
+                                                       sizeof(struct folder));
+                       }
+               }
+       }
+
+
+       if (!strcasecmp(viewpref, "folders")) {
+               do_folder_view(fold, max_folders, num_floors);
+       }
+       else if (!strcasecmp(viewpref, "hackish_view")) {
+               for (i=0; i<max_folders; ++i) {
+                       escputs(fold[i].name);
+                       wprintf("<br />\n");
+               }
+       }
+       else if (!strcasecmp(viewpref, "iconbar")) {
+               do_iconbar_view(fold, max_folders, num_floors);
+       }
+       else {
+               do_rooms_view(fold, max_folders, num_floors);
+       }
+
+       /* Don't free the folder list ... cache it for future use! */
+       if (WC->cache_fold != NULL) {
+               free(WC->cache_fold);
+       }
+       WC->cache_fold = fold;
+       WC->cache_max_folders = max_folders;
+       WC->cache_num_floors = num_floors;
+       WC->cache_timestamp = time(NULL);
+}
+
+
+/**
+ * \brief Do either a known rooms list or a folders list, depending on the
+ * user's preference
+ */
+void knrooms(void)
+{
+       char listviewpref[SIZ];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+
+       /** Determine whether the user is trying to change views */
+       if (bstr("view") != NULL) {
+               if (strlen(bstr("view")) > 0) {
+                       set_preference("roomlistview", bstr("view"), 1);
+               }
+       }
+
+       get_preference("roomlistview", listviewpref, sizeof listviewpref);
+
+       if ( (strcasecmp(listviewpref, "folders"))
+          && (strcasecmp(listviewpref, "table")) ) {
+               strcpy(listviewpref, "rooms");
+       }
+
+       /** title bar */
+       wprintf("<div id=\"banner\">\n"
+               "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
+               "<SPAN CLASS=\"titlebar\">"
+       );
+       if (!strcasecmp(listviewpref, "rooms")) {
+               wprintf(_("Room list"));
+       }
+       if (!strcasecmp(listviewpref, "folders")) {
+               wprintf(_("Folder list"));
+       }
+       if (!strcasecmp(listviewpref, "table")) {
+               wprintf(_("Room list"));
+       }
+       wprintf("</SPAN></TD>\n");
+
+       /** offer the ability to switch views */
+       wprintf("<TD ALIGN=RIGHT><FORM NAME=\"roomlistomatic\">\n"
+               "<SELECT NAME=\"newview\" SIZE=\"1\" "
+               "OnChange=\"location.href=roomlistomatic.newview.options"
+               "[selectedIndex].value\">\n");
+
+       wprintf("<OPTION %s VALUE=\"knrooms&view=rooms\">"
+               "View as room list"
+               "</OPTION>\n",
+               ( !strcasecmp(listviewpref, "rooms") ? "SELECTED" : "" )
+       );
+
+       wprintf("<OPTION %s VALUE=\"knrooms&view=folders\">"
+               "View as folder list"
+               "</OPTION>\n",
+               ( !strcasecmp(listviewpref, "folders") ? "SELECTED" : "" )
+       );
+
+       wprintf("</SELECT><br />");
+       offer_start_page();
+       wprintf("</FORM></TD></TR></TABLE>\n");
+       wprintf("</div>\n"
+               "</div>\n"
+               "<div id=\"content\">\n");
+
+       /** Display the room list in the user's preferred format */
+       list_all_rooms_by_floor(listviewpref);
+       wDumpContent(1);
+}
+
+
+
+/**
+ * \brief Set the message expire policy for this room and/or floor
+ */
+void set_room_policy(void) {
+       char buf[SIZ];
+
+       if (strlen(bstr("ok_button")) == 0) {
+               strcpy(WC->ImportantMessage,
+                       _("Cancelled.  Changes were not saved."));
+               display_editroom();
+               return;
+       }
+
+       serv_printf("SPEX room|%d|%d", atoi(bstr("roompolicy")), atoi(bstr("roomvalue")));
+       serv_getln(buf, sizeof buf);
+       strcpy(WC->ImportantMessage, &buf[4]);
+
+       if (WC->axlevel >= 6) {
+               strcat(WC->ImportantMessage, "<br />\n");
+               serv_printf("SPEX floor|%d|%d", atoi(bstr("floorpolicy")), atoi(bstr("floorvalue")));
+               serv_getln(buf, sizeof buf);
+               strcat(WC->ImportantMessage, &buf[4]);
+       }
+
+       display_editroom();
+}
+
+
+/*@}*/
diff --git a/webcit/src/rss.c b/webcit/src/rss.c
new file mode 100644 (file)
index 0000000..6a96255
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup RssRooms Generate some RSS for our rooms.
+ * \ingroup WebcitHttpServerRSS
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+
+
+time_t if_modified_since;    /**< the last modified stamp */
+
+/**
+ * \brief view rss Config menu
+ * \param reply_to the original author
+ * \param subject the subject of the feed
+ */
+void display_rss_control(char *reply_to, char *subject)
+{
+       wprintf("<div style=\"align: right;\"><p>\n");
+       wprintf("<a href=\"display_enter?recp=");
+       urlescputs(reply_to);
+       wprintf("&subject=");
+       if (strncasecmp(subject, "Re: ", 3)) wprintf("Re:%20");
+       urlescputs(subject);
+       wprintf("\">[%s]</a> \n", _("Reply"));
+       wprintf("<a href=\"display_enter?recp=");
+       urlescputs(reply_to);
+       wprintf("&force_room=_MAIL_&subject=");
+       if (strncasecmp(subject, "Re: ", 3)) wprintf("Re:%20");
+       urlescputs(subject);
+       wprintf("\">[%s]</a>\n", _("Email"));
+       wprintf("</p></div>\n");
+}
+
+
+/**
+ * \brief print the feed to the subscriber
+ * \param roomname the room we sould print out as rss 
+ * \param request_method the way the rss is requested????
+ */
+void display_rss(char *roomname, char *request_method)
+{
+       int nummsgs;
+       int a, b;
+       int bq = 0;
+       time_t now = 0L;
+       struct tm now_tm;
+#ifdef HAVE_ICONV
+       iconv_t ic = (iconv_t)(-1) ;
+       char *ibuf;                   /**< Buffer of characters to be converted */
+       char *obuf;                   /**< Buffer for converted characters      */
+       size_t ibuflen;               /**< Length of input buffer               */
+       size_t obuflen;               /**< Length of output buffer              */
+       char *osav;                   /**< Saved pointer to output buffer       */
+#endif
+       char buf[SIZ];
+       char date[30];
+       char from[256];
+       char subj[256];
+       char node[256];
+       char hnod[256];
+       char room[256];
+       char rfca[256];
+       char rcpt[256];
+       char msgn[256];
+       char content_type[256];
+       char charset[256];
+
+       if (!WC->logged_in) {
+               authorization_required(_("Not logged in"));
+               return;
+       }
+
+       if (gotoroom((char *)roomname)) {
+               lprintf(3, "RSS: Can't goto requested room\n");
+               wprintf("HTTP/1.1 404 Not Found\r\n");
+               wprintf("Content-Type: text/html\r\n");
+               wprintf("\r\n");
+               wprintf("Error retrieving RSS feed: couldn't find room\n");
+               return;
+       }
+
+       nummsgs = load_msg_ptrs("MSGS LAST|15", 0);
+       if (nummsgs == 0) {
+               lprintf(3, "RSS: No messages found\n");
+               wprintf("HTTP/1.1 404 Not Found\r\n");
+               wprintf("Content-Type: text/html\r\n");
+               wprintf("\r\n");
+               wprintf(_("Error retrieving RSS feed: couldn't find messages\n"));
+               return;
+       }
+
+       /** Read time of last message immediately */
+       serv_printf("MSG4 %ld", WC->msgarr[nummsgs - 1]);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               while (serv_getln(buf, sizeof buf), strcasecmp(buf, "000")) {
+                       if (!strncasecmp(buf, "msgn=", 5)) {
+                               strcpy(msgn, &buf[5]);
+                       }
+                       if (!strncasecmp(buf, "time=", 5)) {
+                               now = atol(&buf[5]);
+                               gmtime_r(&now, &now_tm);
+                               strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
+                       }
+               }
+       }
+
+       if (if_modified_since > 0 && if_modified_since > now) {
+               lprintf(3, "RSS: Feed not updated since the last time you looked\n");
+               wprintf("HTTP/1.1 304 Not Modified\r\n");
+               wprintf("Last-Modified: %s\r\n", date);
+               now = time(NULL);
+               gmtime_r(&now, &now_tm);
+               strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
+               wprintf("Date: %s\r\n", date);
+/*             if (*msgn) wprintf("ETag: %s\r\n\r\n", msgn); */
+               wDumpContent(0);
+               return;
+       }
+
+       /* Do RSS header */
+       lprintf(3, "RSS: Yum yum! This feed is tasty!\n");
+       wprintf("HTTP/1.1 200 OK\r\n");
+       wprintf("Last-Modified: %s\r\n", date);
+/*     if (*msgn) wprintf("ETag: %s\r\n\r\n", msgn); */
+       wprintf("Content-Type: application/rss+xml\r\n");
+       wprintf("$erver: %s\r\n", SERVER);
+       wprintf("Connection: close\r\n");
+       wprintf("\r\n");
+       if (!strcasecmp(request_method, "HEAD"))
+               return;
+
+       wprintf("<?xml version=\"1.0\"?>\n");
+       wprintf("<rss version=\"2.0\">\n");
+       wprintf("   <channel>\n");
+       wprintf("   <title>%s - %s</title>\n", WC->wc_roomname, serv_info.serv_humannode);
+       wprintf("   <link>%s://%s:%d/dotgoto?room=", (is_https ? "https" : "http"), WC->http_host, PORT_NUM);
+       escputs(roomname);
+       wprintf("</link>\n");
+       wprintf("   <description>");
+       /** Get room info for description */
+       serv_puts("RINF");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               while (1) {
+                       serv_getln(buf, sizeof buf);
+                       if (!strcmp(buf, "000"))
+                               break;
+                       wprintf("%s\n", buf);
+               }
+       }
+       wprintf("</description>\n");
+       if (now) {
+               wprintf("   <pubDate>%s</pubDate>\n", date);
+       }
+       wprintf("   <generator>%s</generator>\n", SERVER);
+       wprintf("   <docs>http://blogs.law.harvard.edu/tech/rss</docs>\n");
+       wprintf("   <ttl>30</ttl>\n");
+
+       /** Read all messages and output as RSS items */
+       for (a = 0; a < nummsgs; ++a) {
+               /** Read message and output each as RSS item */
+               serv_printf("MSG4 %ld", WC->msgarr[a]);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '1') continue;
+
+               now = 0L;
+               strcpy(subj, "");
+               strcpy(hnod, "");
+               strcpy(node, "");
+               strcpy(room, "");
+               strcpy(rfca, "");
+               strcpy(rcpt, "");
+               strcpy(msgn, "");
+
+               while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
+                       if (!strcmp(buf, "000")) {
+                               goto ENDITEM;   /** screw it */
+                       } else if (!strncasecmp(buf, "from=", 5)) {
+                               strcpy(from, &buf[5]);
+#ifdef HAVE_ICONV
+                               utf8ify_rfc822_string(from);
+#endif
+                       } else if (!strncasecmp(buf, "subj=", 5)) {
+                               strcpy(subj, &buf[5]);
+#ifdef HAVE_ICONV
+                               utf8ify_rfc822_string(subj);
+#endif
+                       } else if (!strncasecmp(buf, "hnod=", 5)) {
+                               strcpy(node, &buf[5]);
+                       } else if (!strncasecmp(buf, "room=", 5)) {
+                               strcpy(room, &buf[5]);
+                       } else if (!strncasecmp(buf, "rfca=", 5)) {
+                               strcpy(rfca, &buf[5]);
+                       } else if (!strncasecmp(buf, "rcpt=", 5)) {
+                               strcpy(rcpt, &buf[5]);
+                       } else if (!strncasecmp(buf, "msgn=", 5)) {
+                               strcpy(msgn, &buf[5]);
+                       } else if (!strncasecmp(buf, "time=", 5)) {
+                               now = atol(&buf[5]);
+                               gmtime_r(&now, &now_tm);
+                               strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
+                       }
+               }
+               wprintf("   <item>\n");
+               if (subj[0]) {
+                       wprintf("      <title>%s from", subj);
+               } else {
+                       wprintf("      <title>From");
+               }
+               wprintf(" %s", from);
+               wprintf(" in %s", room);
+               if (strcmp(hnod, serv_info.serv_humannode) && strlen(hnod) > 0) {
+                       wprintf(" on %s", hnod);
+               }
+               wprintf("</title>\n");
+               if (now) {
+                       wprintf("      <pubDate>%s</pubDate>\n", date);
+               }
+               wprintf("      <guid isPermaLink=\"false\">%s</guid>\n", msgn);
+               /** Now the hard part, the message itself */
+               strcpy(content_type, "text/plain");
+               while (serv_getln(buf, sizeof buf), strlen(buf) > 0) {
+                       if (!strcmp(buf, "000")) {
+                               goto ENDBODY;
+                       }
+                       if (!strncasecmp(buf, "Content-type: ", 14)) {
+                               safestrncpy(content_type, &buf[14], sizeof content_type);
+                               for (b = 0; b < strlen(content_type); ++b) {
+                                       if (!strncasecmp(&content_type[b], "charset=", 8)) {
+                                               safestrncpy(charset, &content_type[b + 8], sizeof charset);
+                                       }
+                               }
+                               for (b = 0; b < strlen(content_type); ++b) {
+                                       if (content_type[b] == ';') {
+                                               content_type[b] = 0;
+                                       }
+                               }
+                       }
+               }
+
+               /** Set up a character set conversion if we need to */
+#ifdef HAVE_ICONV
+               if (strcasecmp(charset, "us-ascii") && strcasecmp(charset, "utf-8") && strcasecmp(charset, "") ) {
+                       ic = ctdl_iconv_open("UTF-8", charset);
+                       if (ic == (iconv_t)(-1)) {
+                               lprintf(5, "%s:%d iconv_open() failed: %s\n",
+                                       __FILE__, __LINE__, strerror(errno));
+                               goto ENDBODY;
+                       }
+               }
+#endif
+
+               /** Messages in legacy Citadel variformat get handled thusly... */
+               if (!strcasecmp(content_type, "text/x-citadel-variformat")) {
+                       int intext = 0;
+
+                       wprintf("      <description><![CDATA[");
+                       while (1) {
+                               serv_getln(buf, sizeof buf);
+                               if (!strcmp(buf, "000")) {
+                                       if (bq == 1)
+                                               wprintf("</blockquote>");
+                                       wprintf("\n");
+                                       break;
+                               }
+                               if (intext == 1 && isspace(buf[0])) {
+                                       wprintf("<br/>");
+                               }
+                               intext = 1;
+                               if (bq == 0 && !strncmp(buf, " >", 2)) {
+                                       wprintf("<blockquote>");
+                                       bq = 1;
+                               } else if (bq == 1 && strncmp(buf, " >", 2)) {
+                                       wprintf("</blockquote>");
+                                       bq = 0;
+                               }
+                               url(buf);
+                               escputs(buf);
+                               wprintf("\n");
+                       }
+                       display_rss_control(from, subj);
+                       wprintf("]]></description>\n");
+               }
+               /** Boring old 80-column fixed format text gets handled this way... */
+               else if (!strcasecmp(content_type, "text/plain")) {
+                       wprintf("      <description><![CDATA[");
+                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                               if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
+                               if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
+       
+#ifdef HAVE_ICONV
+                               if (ic != (iconv_t)(-1) ) {
+                                       ibuf = buf;
+                                       ibuflen = strlen(ibuf);
+                                       obuflen = SIZ;
+                                       obuf = (char *) malloc(obuflen);
+                                       osav = obuf;
+                                       iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
+                                       osav[SIZ-obuflen] = 0;
+                                       safestrncpy(buf, osav, sizeof buf);
+                                       free(osav);
+                               }
+#endif
+
+                               while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
+                                       buf[strlen(buf) - 1] = 0;
+                               if ((bq == 0) &&
+                               ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) {
+                                       wprintf("<blockquote>");
+                                       bq = 1;
+                               } else if ((bq == 1) &&
+                                       (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) {
+                                       wprintf("</blockquote>");
+                                       bq = 0;
+                               }
+                               wprintf("<tt>");
+                               url(buf);
+                               escputs(buf);
+                               wprintf("</tt><br />\n");
+                       }
+                       display_rss_control(from, subj);
+                       wprintf("]]></description>\n");
+               }
+               /** HTML is fun, but we've got to strip it first */
+               else if (!strcasecmp(content_type, "text/html")) {
+                       wprintf("      <description><![CDATA[");
+                       output_html(charset, 0);
+                       wprintf("]]></description>\n");
+               }
+
+ENDBODY:
+               wprintf("   </item>\n");
+ENDITEM:
+               now = 0L;
+       }
+
+       /** Do RSS footer */
+       wprintf("   </channel>\n");
+       wprintf("</rss>\n");
+       wDumpContent(0);
+}
+
+
+/*@}*/
diff --git a/webcit/src/serv_func.c b/webcit/src/serv_func.c
new file mode 100644 (file)
index 0000000..05574c2
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup ServFuncs Handles various types of data transfer operations with the Citadel service.
+ * \ingroup CitadelCommunitacion
+ */
+
+/*@{*/ 
+#include "webcit.h"
+#include "webserver.h"
+
+struct serv_info serv_info; /**< our connection data to the server */
+
+/**
+ * \brief get info about the server we've connected to
+ * \param browser_host the citadell we want to connect to
+ * \param user_agent which browser uses our client?
+ */
+void get_serv_info(char *browser_host, char *user_agent)
+{
+       char buf[SIZ];
+       int a;
+
+       /** Tell the server what kind of client is connecting */
+       serv_printf("IDEN %d|%d|%d|%s|%s",
+               DEVELOPER_ID,
+               CLIENT_ID,
+               CLIENT_VERSION,
+               user_agent,
+               browser_host
+       );
+       serv_getln(buf, sizeof buf);
+
+       /** Tell the server what kind of richtext we prefer */
+       serv_puts("MSGP text/html|text/plain");
+       serv_getln(buf, sizeof buf);
+
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+       /**
+        * Tell the server that when we save a calendar event, we
+        * want invitations to be generated by the Citadel server
+        * instead of by the client.
+        */
+       serv_puts("ICAL sgi|1");
+       serv_getln(buf, sizeof buf);
+#endif
+
+       /** Now ask the server to tell us a little bit about itself... */
+       serv_puts("INFO");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1')
+               return;
+
+       a = 0;
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               switch (a) {
+               case 0:
+                       serv_info.serv_pid = atoi(buf);
+                       WC->ctdl_pid = serv_info.serv_pid;
+                       break;
+               case 1:
+                       safestrncpy(serv_info.serv_nodename, buf, sizeof serv_info.serv_nodename);
+                       break;
+               case 2:
+                       safestrncpy(serv_info.serv_humannode, buf, sizeof serv_info.serv_humannode);
+                       break;
+               case 3:
+                       safestrncpy(serv_info.serv_fqdn, buf, sizeof serv_info.serv_fqdn);
+                       break;
+               case 4:
+                       safestrncpy(serv_info.serv_software, buf, sizeof serv_info.serv_software);
+                       break;
+               case 5:
+                       serv_info.serv_rev_level = atoi(buf);
+                       break;
+               case 6:
+                       safestrncpy(serv_info.serv_bbs_city, buf, sizeof serv_info.serv_bbs_city);
+                       break;
+               case 7:
+                       safestrncpy(serv_info.serv_sysadm, buf, sizeof serv_info.serv_sysadm);
+                       break;
+               case 9:
+                       safestrncpy(serv_info.serv_moreprompt, buf, sizeof serv_info.serv_moreprompt);
+                       break;
+               case 14:
+                       serv_info.serv_supports_ldap = atoi(buf);
+                       break;
+               case 15:
+                       serv_info.serv_newuser_disabled = atoi(buf);
+                       break;
+               }
+               ++a;
+       }
+}
+
+
+
+/**
+ * \brief Read Citadel variformat text and spit it out as HTML.
+ * \param align html align string
+ */
+void fmout(char *align)
+{
+       int intext = 0;
+       int bq = 0;
+       char buf[SIZ];
+
+       wprintf("<div align=%s>\n", align);
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+
+               if ((intext == 1) && (isspace(buf[0]))) {
+                       wprintf("<br />");
+               }
+               intext = 1;
+
+               /**
+                * Quoted text should be displayed in italics and in a
+                * different colour.  This code understands Citadel-style
+                * " >" quotes and will convert to <BLOCKQUOTE> tags.
+                */
+               if ((bq == 0) && (!strncmp(buf, " >", 2))) {
+                       wprintf("<BLOCKQUOTE>");
+                       bq = 1;
+               } else if ((bq == 1) && (strncmp(buf, " >", 2))) {
+                       wprintf("</BLOCKQUOTE>");
+                       bq = 0;
+               }
+               if ((bq == 1) && (!strncmp(buf, " >", 2))) {
+                       strcpy(buf, &buf[2]);
+               }
+               /** Activate embedded URL's */
+               url(buf);
+
+               escputs(buf);
+               wprintf("\n");
+       }
+       if (bq == 1) {
+               wprintf("</I>");
+       }
+       wprintf("</div><br />\n");
+}
+
+
+
+
+/**
+ * \brief Read Citadel variformat text and spit it out as HTML in a form
+ * suitable for embedding in another message (forward/quote).
+ * (NO LINEBREAKS ALLOWED HERE!)
+ */
+void pullquote_fmout(void) {
+       int intext = 0;
+       int bq = 0;
+       char buf[SIZ];
+
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+
+               if ((intext == 1) && (isspace(buf[0]))) {
+                       wprintf("<br />");
+               }
+               intext = 1;
+
+               /**
+                * Quoted text should be displayed in italics and in a
+                * different colour.  This code understands Citadel-style
+                * " >" quotes and will convert to <BLOCKQUOTE> tags.
+                */
+               if ((bq == 0) && (!strncmp(buf, " >", 2))) {
+                       wprintf("<BLOCKQUOTE>");
+                       bq = 1;
+               } else if ((bq == 1) && (strncmp(buf, " >", 2))) {
+                       wprintf("</BLOCKQUOTE>");
+                       bq = 0;
+               }
+               if ((bq == 1) && (!strncmp(buf, " >", 2))) {
+                       strcpy(buf, &buf[2]);
+               }
+
+               msgescputs(buf);
+       }
+       if (bq == 1) {
+               wprintf("</I>");
+       }
+}
+
+
+
+
+/**
+ * \brief Transmit message text (in memory) to the server.
+ *
+ * \param ptr Pointer to the message being transmitted
+ */
+void text_to_server(char *ptr)
+{
+       char buf[256];
+       int ch, a, pos;
+
+       pos = 0;
+       buf[0] = 0;
+
+       while (ptr[pos] != 0) {
+               ch = ptr[pos++];
+               if (ch == 10) {
+                       while ( (isspace(buf[strlen(buf) - 1]))
+                         && (strlen(buf) > 1) )
+                               buf[strlen(buf) - 1] = 0;
+                       serv_puts(buf);
+                       buf[0] = 0;
+                       if (ptr[pos] != 0) strcat(buf, " ");
+               } else {
+                       a = strlen(buf);
+                       buf[a + 1] = 0;
+                       buf[a] = ch;
+                       if ((ch == 32) && (strlen(buf) > 200)) {
+                               buf[a] = 0;
+                               serv_puts(buf);
+                               buf[0] = 0;
+                       }
+                       if (strlen(buf) > 250) {
+                               serv_puts(buf);
+                               buf[0] = 0;
+                       }
+               }
+       }
+       serv_puts(buf);
+}
+
+
+/**
+ * \brief Transmit message text (in memory) to the server,
+ *        converting to Quoted-Printable encoding as we go.
+ *
+ * \param ptr Pointer to the message being transmitted
+ */
+void text_to_server_qp(char *ptr)
+{
+       char buf[256];
+       int ch, pos;
+       int output_len = 0;
+
+       pos = 0;
+       buf[0] = 0;
+       output_len = 0;
+
+       while (ptr[pos] != 0) {
+               ch = ptr[pos++];
+
+               if (ch == 13) {
+                       /* ignore carriage returns */
+               }
+               else if (ch == 10) {
+                       /* hard line break */
+                       if (output_len > 0) {
+                               if (isspace(buf[output_len-1])) {
+                                       sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]);
+                                       output_len += 2;
+                               }
+                       }
+                       buf[output_len++] = 0;
+                       serv_puts(buf);
+                       output_len = 0;
+               }
+               else if (ch == 9) {
+                       buf[output_len++] = ch;
+               }
+               else if ( (ch >= 32) && (ch <= 60) ) {
+                       buf[output_len++] = ch;
+               }
+               else if ( (ch >= 62) && (ch <= 126) ) {
+                       buf[output_len++] = ch;
+               }
+               else {
+                       sprintf(&buf[output_len], "=%02X", ch);
+                       output_len += 3;
+               }
+               
+               if (output_len > 72) {
+                       /* soft line break */
+                       if (isspace(buf[output_len-1])) {
+                               sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]);
+                               output_len += 2;
+                       }
+                       buf[output_len++] = '=';
+                       buf[output_len++] = 0;
+                       serv_puts(buf);
+                       output_len = 0;
+               }
+       }
+
+       /* end of data - transmit anything that's left */
+       if (output_len > 0) {
+               if (isspace(buf[output_len-1])) {
+                       sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]);
+                       output_len += 2;
+               }
+               buf[output_len++] = 0;
+               serv_puts(buf);
+               output_len = 0;
+       }
+}
+
+
+
+
+/**
+ * \brief translate server message output to text
+ * (used for editing room info files and such)
+ */
+void server_to_text()
+{
+       char buf[SIZ];
+
+       int count = 0;
+
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               if ((buf[0] == 32) && (count > 0)) {
+                       wprintf("\n");
+               }
+               wprintf("%s", buf);
+               ++count;
+       }
+}
+
+
+
+/**
+ * Read binary data from server into memory using a series of
+ * server READ commands.
+ * \param buffer the output buffer
+ * \param total_len the maximal length of buffer
+ */
+void read_server_binary(char *buffer, size_t total_len) {
+       char buf[SIZ];
+       size_t bytes = 0;
+       size_t thisblock = 0;
+
+       memset(buffer, 0, total_len);
+       while (bytes < total_len) {
+               thisblock = 4095;
+               if ((total_len - bytes) < thisblock) {
+                       thisblock = total_len - bytes;
+                       if (thisblock == 0) return;
+               }
+               serv_printf("READ %d|%d", (int)bytes, (int)thisblock);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '6') {
+                       thisblock = (size_t)atoi(&buf[4]);
+                       if (!WC->connected) return;
+                       serv_read(&buffer[bytes], thisblock);
+                       bytes += thisblock;
+               }
+               else {
+                       lprintf(3, "Error: %s\n", &buf[4]);
+                       return;
+               }
+       }
+}
+
+
+/**
+ * \brief Read text from server, appending to a string buffer until the
+ * usual 000 terminator is found.  Caller is responsible for freeing
+ * the returned pointer.
+ */
+char *read_server_text(void) {
+       char *text = NULL;
+       size_t bytes_allocated = 0;
+       size_t bytes_read = 0;
+       int linelen;
+       char buf[SIZ];
+
+       text = malloc(SIZ);
+       if (text == NULL) {
+               return(NULL);
+       }
+       text[0] = 0;
+       bytes_allocated = SIZ;
+
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               linelen = strlen(buf);
+               buf[linelen] = '\n';
+               buf[linelen+1] = 0;
+               ++linelen;
+
+               if ((bytes_read + linelen) >= (bytes_allocated - 2)) {
+                       bytes_allocated = 2 * bytes_allocated;
+                       text = realloc(text, bytes_allocated);
+               }
+
+               strcpy(&text[bytes_read], buf);
+               bytes_read += linelen;
+       }
+
+       return(text);
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/setup.c b/webcit/src/setup.c
new file mode 100644 (file)
index 0000000..f17e7ac
--- /dev/null
@@ -0,0 +1,683 @@
+/*
+ * $Id$
+ *
+ * WebCit setup utility
+ * 
+ * (This is basically just an install wizard.  It's not required.)
+ *
+ */
+
+
+#include "webcit.h"
+#include "webserver.h"
+
+
+#ifdef HAVE_NEWT
+#include <newt.h>
+#endif
+
+
+#define UI_TEXT                0       /* Default setup type -- text only */
+#define UI_DIALOG      2       /* Use the 'dialog' program */
+#define UI_SILENT      3       /* Silent running, for use in scripts */
+#define UI_NEWT                4       /* Use the "newt" window library */
+
+int setup_type;
+char setup_directory[SIZ];
+char init_entry[SIZ];
+int using_web_installer = 0;
+char suggested_url[SIZ];
+
+/*
+ * Set an entry in inittab to the desired state
+ */
+void set_init_entry(char *which_entry, char *new_state) {
+       char *inittab = NULL;
+       FILE *fp;
+       char buf[SIZ];
+       char entry[SIZ];
+       char levels[SIZ];
+       char state[SIZ];
+       char prog[SIZ];
+
+       inittab = strdup("");
+       if (inittab == NULL) return;
+
+       fp = fopen("/etc/inittab", "r");
+       if (fp == NULL) return;
+
+       while(fgets(buf, sizeof buf, fp) != NULL) {
+
+               if (num_tokens(buf, ':') == 4) {
+                       extract_token(entry, buf, 0, ':', sizeof entry);
+                       extract_token(levels, buf, 1, ':', sizeof levels);
+                       extract_token(state, buf, 2, ':', sizeof state);
+                       extract_token(prog, buf, 3, ':', sizeof prog); /* includes 0x0a LF */
+
+                       if (!strcmp(entry, which_entry)) {
+                               strcpy(state, new_state);
+                               sprintf(buf, "%s:%s:%s:%s",
+                                       entry, levels, state, prog);
+                       }
+               }
+
+               inittab = realloc(inittab, strlen(inittab) + strlen(buf) + 2);
+               if (inittab == NULL) {
+                       fclose(fp);
+                       return;
+               }
+               
+               strcat(inittab, buf);
+       }
+       fclose(fp);
+       fp = fopen("/etc/inittab", "w");
+       if (fp != NULL) {
+               fwrite(inittab, strlen(inittab), 1, fp);
+               fclose(fp);
+               kill(1, SIGHUP);        /* Tell init to re-read /etc/inittab */
+       }
+       free(inittab);
+}
+
+
+
+
+/* 
+ * Shut down the Citadel service if necessary, during setup.
+ */
+void shutdown_service(void) {
+       FILE *infp;
+       char buf[SIZ];
+       char looking_for[SIZ];
+       int have_entry = 0;
+       char entry[SIZ];
+       char prog[SIZ];
+
+       strcpy(init_entry, "");
+
+       /* Determine the fully qualified path name of webserver */
+       snprintf(looking_for, sizeof looking_for, "%s/webserver ", setup_directory);
+
+       /* Pound through /etc/inittab line by line.  Set have_entry to 1 if
+        * an entry is found which we believe starts webserver.
+        */
+       infp = fopen("/etc/inittab", "r");
+       if (infp == NULL) {
+               return;
+       } else {
+               while (fgets(buf, sizeof buf, infp) != NULL) {
+                       buf[strlen(buf) - 1] = 0;
+                       extract_token(entry, buf, 0, ':', sizeof entry);
+                       extract_token(prog, buf, 3, ':', sizeof prog);
+                       if (!strncasecmp(prog, looking_for,
+                          strlen(looking_for))) {
+                               ++have_entry;
+                               strcpy(init_entry, entry);
+                       }
+               }
+               fclose(infp);
+       }
+
+       /* Bail out if there's nothing to do. */
+       if (!have_entry) return;
+
+       set_init_entry(init_entry, "off");
+}
+
+
+/*
+ * Start the Citadel service.
+ */
+void start_the_service(void) {
+       if (strlen(init_entry) > 0) {
+               set_init_entry(init_entry, "respawn");
+       }
+}
+
+
+
+void cleanup(int exitcode)
+{
+#ifdef HAVE_NEWT
+       newtCls();
+       newtRefresh();
+       newtFinished();
+#endif
+       exit(exitcode);
+}
+
+
+
+void title(char *text)
+{
+       if (setup_type == UI_TEXT) {
+               printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
+       }
+}
+
+
+
+int yesno(char *question)
+{
+#ifdef HAVE_NEWT
+       newtComponent form = NULL;
+       newtComponent yesbutton = NULL;
+       newtComponent nobutton = NULL;
+#endif
+       int i = 0;
+       int answer = 0;
+       char buf[SIZ];
+
+       switch (setup_type) {
+
+       case UI_TEXT:
+               do {
+                       printf("%s\nYes/No --> ", question);
+                       fgets(buf, sizeof buf, stdin);
+                       answer = tolower(buf[0]);
+                       if (answer == 'y')
+                               answer = 1;
+                       else if (answer == 'n')
+                               answer = 0;
+               } while ((answer < 0) || (answer > 1));
+               break;
+
+       case UI_DIALOG:
+               sprintf(buf, "exec %s --yesno '%s' 10 72",
+                       getenv("CTDL_DIALOG"),
+                       question);
+               i = system(buf);
+               if (i == 0) {
+                       answer = 1;
+               }
+               else {
+                       answer = 0;
+               }
+               break;
+
+#ifdef HAVE_NEWT
+       case UI_NEWT:
+               newtCenteredWindow(76, 10, "Question");
+               form = newtForm(NULL, NULL, 0);
+               for (i=0; i<num_tokens(question, '\n'); ++i) {
+                       extract_token(buf, question, i, '\n', sizeof buf);
+                       newtFormAddComponent(form, newtLabel(1, 1+i, buf));
+               }
+               yesbutton = newtButton(10, 5, "Yes");
+               nobutton = newtButton(60, 5, "No");
+               newtFormAddComponent(form, yesbutton);
+               newtFormAddComponent(form, nobutton);
+               if (newtRunForm(form) == yesbutton) {
+                       answer = 1;
+               }
+               else {
+                       answer = 0;
+               }
+               newtPopWindow();
+               newtFormDestroy(form);  
+
+               break;
+#endif
+
+       }
+       return (answer);
+}
+
+void set_value(char *prompt, char str[])
+{
+#ifdef HAVE_NEWT
+       newtComponent form;
+       char *result;
+       int i;
+#endif
+       char buf[SIZ];
+       char dialog_result[PATH_MAX];
+       char setupmsg[SIZ];
+       FILE *fp;
+
+       strcpy(setupmsg, "");
+
+       switch (setup_type) {
+       case UI_TEXT:
+               title("WebCit setup");
+               printf("\n%s\n", prompt);
+               printf("This is currently set to:\n%s\n", str);
+               printf("Enter new value or press return to leave unchanged:\n");
+               fgets(buf, sizeof buf, stdin);
+               buf[strlen(buf) - 1] = 0;
+               if (strlen(buf) != 0)
+                       strcpy(str, buf);
+               break;
+
+       case UI_DIALOG:
+               CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
+               sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
+                       getenv("CTDL_DIALOG"),
+                       prompt,
+                       str,
+                       dialog_result);
+               system(buf);
+               fp = fopen(dialog_result, "r");
+               if (fp != NULL) {
+                       fgets(str, sizeof buf, fp);
+                       if (str[strlen(str)-1] == 10) {
+                               str[strlen(str)-1] = 0;
+                       }
+                       fclose(fp);
+                       unlink(dialog_result);
+               }
+               break;
+
+#ifdef HAVE_NEWT
+       case UI_NEWT:
+
+               newtCenteredWindow(76, 10, "WebCit setup");
+               form = newtForm(NULL, NULL, 0);
+               for (i=0; i<num_tokens(prompt, '\n'); ++i) {
+                       extract_token(buf, prompt, i, '\n', sizeof buf);
+                       newtFormAddComponent(form, newtLabel(1, 1+i, buf));
+               }
+               newtFormAddComponent(form, newtEntry(1, 8, str, 74, (const char **) &result,
+                                       NEWT_FLAG_RETURNEXIT));
+               newtRunForm(form);
+               strcpy(str, result);
+
+               newtPopWindow();
+               newtFormDestroy(form);  
+
+#endif
+       }
+}
+
+
+void important_message(char *title, char *msgtext)
+{
+#ifdef HAVE_NEWT
+       newtComponent form = NULL;
+       int i = 0;
+#endif
+       char buf[SIZ];
+
+       switch (setup_type) {
+
+       case UI_TEXT:
+               printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
+               printf("       %s \n\n%s\n\n", title, msgtext);
+               printf("Press return to continue...");
+               fgets(buf, sizeof buf, stdin);
+               break;
+
+       case UI_DIALOG:
+               sprintf(buf, "exec %s --msgbox '%s' 19 72",
+                       getenv("CTDL_DIALOG"),
+                       msgtext);
+               system(buf);
+               break;
+
+#ifdef HAVE_NEWT
+       case UI_NEWT:
+               newtCenteredWindow(76, 10, title);
+               form = newtForm(NULL, NULL, 0);
+               for (i=0; i<num_tokens(msgtext, '\n'); ++i) {
+                       extract_token(buf, msgtext, i, '\n', sizeof buf);
+                       newtFormAddComponent(form, newtLabel(1, 1+i, buf));
+               }
+               newtFormAddComponent(form, newtButton(35, 5, "OK"));
+               newtRunForm(form);
+               newtPopWindow();
+               newtFormDestroy(form);  
+               break;
+#endif
+
+       }
+}
+
+
+void display_error(char *error_message)
+{
+       important_message("Error", error_message);
+}
+
+void progress(char *text, long int curr, long int cmax)
+{
+#ifdef HAVE_NEWT
+
+       /* These variables are static because progress() gets called
+        * multiple times during the course of whatever operation is
+        * being performed.  This makes setup non-threadsafe, but who
+        * cares?
+        */
+       static newtComponent form = NULL;
+       static newtComponent scale = NULL;
+#endif
+       static long dots_printed = 0L;
+       long a = 0;
+       char buf[SIZ];
+       static FILE *fp = NULL;
+
+       switch (setup_type) {
+
+       case UI_TEXT:
+               if (curr == 0) {
+                       printf("%s\n", text);
+                       printf("..........................");
+                       printf("..........................");
+                       printf("..........................\r");
+                       fflush(stdout);
+                       dots_printed = 0;
+               } else if (curr == cmax) {
+                       printf("\r%79s\n", "");
+               } else {
+                       a = (curr * 100) / cmax;
+                       a = a * 78;
+                       a = a / 100;
+                       while (dots_printed < a) {
+                               printf("*");
+                               ++dots_printed;
+                               fflush(stdout);
+                       }
+               }
+               break;
+
+       case UI_DIALOG:
+               if (curr == 0) {
+                       sprintf(buf, "exec %s --gauge '%s' 7 72 0",
+                               getenv("CTDL_DIALOG"),
+                               text);
+                       fp = popen(buf, "w");
+                       if (fp != NULL) {
+                               fprintf(fp, "0\n");
+                               fflush(fp);
+                       }
+               } 
+               else if (curr == cmax) {
+                       if (fp != NULL) {
+                               fprintf(fp, "100\n");
+                               pclose(fp);
+                               fp = NULL;
+                       }
+               }
+               else {
+                       a = (curr * 100) / cmax;
+                       if (fp != NULL) {
+                               fprintf(fp, "%ld\n", a);
+                               fflush(fp);
+                       }
+               }
+               break;
+
+#ifdef HAVE_NEWT
+       case UI_NEWT:
+               if (curr == 0) {
+                       newtCenteredWindow(76, 8, text);
+                       form = newtForm(NULL, NULL, 0);
+                       scale = newtScale(1, 3, 74, cmax);
+                       newtFormAddComponent(form, scale);
+                       newtDrawForm(form);
+                       newtRefresh();
+               }
+               if ((curr > 0) && (curr <= cmax)) {
+                       newtScaleSet(scale, curr);
+                       newtRefresh();
+               }
+               if (curr == cmax) {
+                       newtFormDestroy(form);  
+                       newtPopWindow();
+                       newtRefresh();
+               }
+               break;
+#endif
+
+       }
+}
+
+
+
+
+/*
+ * check_inittab_entry()  -- Make sure "webserver" is in /etc/inittab
+ *
+ */
+void check_inittab_entry(void)
+{
+       FILE *infp;
+       char buf[SIZ];
+       char looking_for[SIZ];
+       char question[SIZ];
+       char entryname[5];
+       char http_port[128];
+#ifdef HAVE_OPENSSL
+       char https_port[128];
+#endif
+       char hostname[128];
+       char portname[128];
+       struct utsname my_utsname;
+
+       /* Determine the fully qualified path name of webserver */
+       snprintf(looking_for, sizeof looking_for, "%s/webserver", setup_directory);
+
+       /* If there's already an entry, then we have nothing left to do. */
+       if (strlen(init_entry) > 0) {
+               return;
+       }
+
+       /* Otherwise, prompt the user to create an entry. */
+       snprintf(question, sizeof question,
+               "There is no '%s' entry in /etc/inittab.\n"
+               "Would you like to add one?",
+               looking_for);
+       if (yesno(question) == 0)
+               return;
+
+       snprintf(question, sizeof question,
+               "On which port do you want WebCit to listen for HTTP "
+               "requests?\n\nYou can use the standard port (80) if you are "
+               "not running another\nweb server (such as Apache), otherwise "
+               "select another port.");
+       sprintf(http_port, "2000");
+       set_value(question, http_port);
+       uname(&my_utsname);
+       sprintf(suggested_url, "http://%s:%s/", my_utsname.nodename, http_port);
+
+#ifdef HAVE_OPENSSL
+       snprintf(question, sizeof question,
+               "On which port do you want WebCit to listen for HTTPS "
+               "requests?\n\nYou can use the standard port (443) if you are "
+               "not running another\nweb server (such as Apache), otherwise "
+               "select another port.");
+       sprintf(https_port, "443");
+       set_value(question, https_port);
+#endif
+
+       /* Find out where Citadel is. */
+       if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
+               strcpy(hostname, "uds");
+               strcpy(portname, getenv("CITADEL"));
+       }
+       else {
+               snprintf(question, sizeof question,
+                       "Is the Citadel service running on the same host as WebCit?");
+               if (yesno(question)) {
+                       sprintf(hostname, "uds");
+                       sprintf(portname, "/usr/local/citadel");
+                       set_value("In what directory is Citadel installed?", portname);
+               }
+               else {
+                       sprintf(hostname, "127.0.0.1");
+                       sprintf(portname, "504");
+                       set_value("Enter the host name or IP address of your "
+                               "Citadel server.", hostname);
+                       set_value("Enter the port number on which Citadel is "
+                               "running (usually 504)", portname);
+               }
+       }
+
+       /* Generate unique entry names for /etc/inittab */
+       snprintf(entryname, sizeof entryname, "c0");
+       do {
+               ++entryname[1];
+               if (entryname[1] > '9') {
+                       entryname[1] = 0;
+                       ++entryname[0];
+                       if (entryname[0] > 'z') {
+                               display_error(
+                                  "Can't generate a unique entry name");
+                               return;
+                       }
+               }
+               snprintf(buf, sizeof buf,
+                    "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
+       } while (system(buf) == 0);
+       
+
+       /* Now write it out to /etc/inittab */
+       infp = fopen("/etc/inittab", "a");
+       if (infp == NULL) {
+               display_error(strerror(errno));
+       } else {
+               fprintf(infp, "# Start the WebCit server...\n");
+               fprintf(infp, "h%s:2345:respawn:%s -p%s %s %s\n",
+                       entryname, looking_for,
+                       http_port, hostname, portname);
+#ifdef HAVE_OPENSSL
+               fprintf(infp, "s%s:2345:respawn:%s -p%s -s %s %s\n",
+                       entryname, looking_for,
+                       https_port, hostname, portname);
+#endif
+               fclose(infp);
+               strcpy(init_entry, entryname);
+       }
+}
+
+
+
+
+/*
+ * Figure out what type of user interface we're going to use
+ */
+int discover_ui(void)
+{
+
+       /* Use "dialog" if we have it */
+       if (getenv("CTDL_DIALOG") != NULL) {
+               return UI_DIALOG;
+       }
+               
+#ifdef HAVE_NEWT
+       newtInit();
+       newtCls();
+       newtDrawRootText(0, 0, "WebCit Setup");
+       return UI_NEWT;
+#endif
+       return UI_TEXT;
+}
+
+
+
+
+
+int main(int argc, char *argv[])
+{
+       int a;
+       char aaa[256];
+       int info_only = 0;
+       strcpy(suggested_url, "http://<your_host_name>:<port>/");
+
+       /* set an invalid setup type */
+       setup_type = (-1);
+
+       /* Check to see if we're running the web installer */
+       if (getenv("CITADEL_INSTALLER") != NULL) {
+               using_web_installer = 1;
+       }
+
+       /* parse command line args */
+       for (a = 0; a < argc; ++a) {
+               if (!strncmp(argv[a], "-u", 2)) {
+                       strcpy(aaa, argv[a]);
+                       strcpy(aaa, &aaa[2]);
+                       setup_type = atoi(aaa);
+               }
+               if (!strcmp(argv[a], "-i")) {
+                       info_only = 1;
+               }
+               if (!strcmp(argv[a], "-q")) {
+                       setup_type = UI_SILENT;
+               }
+       }
+
+
+       /* If a setup type was not specified, try to determine automatically
+        * the best one to use out of all available types.
+        */
+       if (setup_type < 0) {
+               setup_type = discover_ui();
+       }
+       if (info_only == 1) {
+               important_message("WebCit Setup", "Welcome to WebCit setup");
+               cleanup(0);
+       }
+
+       /* If we're on something BSDish then we don't have inittab */
+       if (access("/etc/inittab", F_OK)) {
+               important_message("Not running SysV style init",
+                               "WebCit Setup can only run on systems that use /etc/inittab.\n"
+                               "Please manually configure your startup scripts to run WebCit\n"
+                               "when the system is booted.\n");
+               cleanup(0);
+       }
+
+       /* Get started in a valid setup directory. */
+       strcpy(setup_directory, WEBCITDIR);
+       if ( (using_web_installer) && (getenv("WEBCIT") != NULL) ) {
+               strcpy(setup_directory, getenv("WEBCIT"));
+       }
+       else {
+               set_value("In what directory is WebCit installed?",
+                       setup_directory);
+       }
+       if (chdir(setup_directory) != 0) {
+               important_message("WebCit Setup",
+                         "The directory you specified does not exist.");
+               cleanup(errno);
+       }
+
+       /* See if we need to shut down the WebCit service. */
+       for (a=0; a<=3; ++a) {
+               progress("Shutting down the WebCit service...", a, 3);
+               if (a == 0) shutdown_service();
+               sleep(1);
+       }
+
+       /* Now begin. */
+       switch (setup_type) {
+
+       case UI_TEXT:
+               printf("\n\n\n"
+                       "               *** WebCit setup program ***\n\n");
+               break;
+
+       }
+
+       check_inittab_entry();  /* Check /etc/inittab */
+
+       /* See if we can start the WebCit service. */
+       if (strlen(init_entry) > 0) {
+               for (a=0; a<=3; ++a) {
+                       progress("Starting the WebCit service...", a, 3);
+                       if (a == 0) start_the_service();
+                       sleep(1);
+               }
+               sprintf(aaa,
+                       "Setup is finished.  You may now log in.\n"
+                       "Point your web browser at %s\n", suggested_url);
+               important_message("Setup finished", aaa);
+       }
+       else {
+               important_message("Setup finished",
+                       "Setup is finished.  You may now start the server.");
+       }
+
+       cleanup(0);
+       return 0;
+}
diff --git a/webcit/src/setup_wizard.c b/webcit/src/setup_wizard.c
new file mode 100644 (file)
index 0000000..bc2b227
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * $Id$
+ *
+ * First-time setup wizard
+ */
+
+#include "webcit.h"
+
+
+/*
+ */
+void do_setup_wizard(void)
+{
+       char *step;
+       FILE *fp;
+
+       step = bstr("step");
+
+       if (!strcasecmp(step, "Finish")) {
+               fp = fopen(wizard_filename, "w");
+               if (fp != NULL) {
+                       fprintf(fp, "%d\n", serv_info.serv_rev_level);
+                       fclose(fp);
+               }
+               do_welcome();
+               return;
+       }
+
+       output_headers(1, 1, 2, 0, 0, 0);
+
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
+       wprintf("<img src=\"static/citadel-logo.gif\" WIDTH=64 HEIGHT=64 ALT=\" \" ALIGN=MIDDLE>");
+       wprintf("<SPAN CLASS=\"titlebar\">&nbsp;First time setup");
+       wprintf("</SPAN></TD><TD ALIGN=RIGHT>");
+       wprintf("</TD></TR></TABLE>\n");
+       wprintf("</div>\n"
+               "<div id=\"content\">\n");
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<form method=\"post\" action=\"setup_wizard\">\n"
+       );
+
+       wprintf("<div align=center>"
+               "This is where the setup wizard will be placed.<br>\n"
+               "For now, just click Finish.<br><br>\n"
+       );
+
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"step\" VALUE=\"Next\">\n");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"step\" VALUE=\"Finish\">\n");
+
+       wprintf("</form></div></div>\n");
+       wDumpContent(1);
+}
+
+
diff --git a/webcit/src/siteconfig.c b/webcit/src/siteconfig.c
new file mode 100644 (file)
index 0000000..17a7c3c
--- /dev/null
@@ -0,0 +1,650 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup AdminConfig Administrative screen for site-wide configuration
+ * \ingroup CitadelConfig
+ */
+/*@{*/
+
+#include "webcit.h"
+
+/**
+ * \brief display all configuration items
+ */
+void display_siteconfig(void)
+{
+       char buf[SIZ];
+       int i, j;
+
+       char general[SIZ];
+       char access[SIZ];
+       char network[SIZ];
+       char tuning[SIZ];
+       char directory[SIZ];
+       char purger[SIZ];
+       char idxjnl[SIZ];
+
+       /** expire policy settings */
+       int sitepolicy = 0;
+       int sitevalue = 0;
+       int mboxpolicy = 0;
+       int mboxvalue = 0;
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>"
+               "<span class=\"titlebar\">");
+       wprintf(_("Site configuration"));
+       wprintf("</span>"
+               "</td></tr></table>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       serv_printf("CONF get");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               wprintf("<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>");
+               wprintf("<span class=\"titlebar\">");
+               wprintf(_("Error"));
+               wprintf("</span>\n");
+               wprintf("</td></tr></table><br />\n");
+               wprintf("%s<br />\n", &buf[4]);
+               wDumpContent(1);
+               return;
+       }
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>");
+
+       char *tabnames[] = {
+               _("General"),
+               _("Access"),
+               _("Network"),
+               _("Tuning"),
+               _("Directory"),
+               _("Auto-purger"),
+               _("Indexing/Journaling")
+       };
+
+       sprintf(general, "<center><h1>%s</h1><table border=\"0\">",
+                       _("General site configuration items")
+       );
+
+       sprintf(access, "<center><h1>%s</h1><table border=\"0\">",
+                       _("Access controls and site policy settings")
+       );
+
+       sprintf(network, "<center><h1>%s</h1><h2>%s</h2><table border=\"0\">",
+                       _("Network services"),
+                       _("Changes made on this screen will not take effect "
+                       "until you restart the Citadel server.")
+       );
+
+       sprintf(tuning, "<center><h1>%s</h1><table border=\"0\">",
+                       _("Advanced server fine-tuning controls")
+       );
+
+       sprintf(directory, "<center><h1>%s</h1><h2>%s</h2><table border=\"0\">",
+                       _("Configure the LDAP connector for Citadel"),
+                       _("Changes made on this screen will not take effect "
+                       "until you restart the Citadel server.")
+       );
+
+       sprintf(purger, "<center><h1>%s</h1><h2>%s</h2><table border=\"0\">",
+                       _("Configure automatic expiry of old messages"),
+                       _("These settings may be overridden on a per-floor or per-room basis.")
+       );
+
+       sprintf(idxjnl, "<center><h1>%s</h1><h2>%s</h2><table border=\"0\">",
+                       _("Indexing and Journaling"),
+                       _("Warning: these facilities are resource intensive.")
+       );
+
+
+       wprintf("<form method=\"post\" action=\"siteconfig\">\n");
+
+       i = 0;
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               switch (i++) {
+               case 0:
+                       sprintf(&general[strlen(general)], "<tr><td>");
+                       sprintf(&general[strlen(general)], _("Node name"));
+                       sprintf(&general[strlen(general)], "</td><td>");
+                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_nodename\" maxlength=\"15\" value=\"%s\">", buf);
+                       sprintf(&general[strlen(general)], "</td></tr>\n");
+                       break;
+               case 1:
+                       sprintf(&general[strlen(general)], "<tr><td>");
+                       sprintf(&general[strlen(general)], _("Fully qualified domain name"));
+                       sprintf(&general[strlen(general)], "</td><td>");
+                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_fqdn\" maxlength=\"63\" value=\"%s\">", buf);
+                       sprintf(&general[strlen(general)], "</td></tr>\n");
+                       break;
+               case 2:
+                       sprintf(&general[strlen(general)], "<tr><td>");
+                       sprintf(&general[strlen(general)], _("Human-readable node name"));
+                       sprintf(&general[strlen(general)], "</td><td>");
+                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_humannode\" maxlength=\"20\" value=\"%s\">", buf);
+                       sprintf(&general[strlen(general)], "</td></tr>\n");
+                       break;
+               case 3:
+                       sprintf(&general[strlen(general)], "<tr><td>");
+                       sprintf(&general[strlen(general)], _("Telephone number"));
+                       sprintf(&general[strlen(general)], "</td><td>");
+                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_phonenum\" maxlength=\"15\" value=\"%s\">", buf);
+                       sprintf(&general[strlen(general)], "</td></tr>\n");
+                       break;
+               case 4:
+                       sprintf(&access[strlen(access)], "<tr><td>");
+                       sprintf(&access[strlen(access)], _("Automatically grant room-aide status to users who create private rooms"));
+                       sprintf(&access[strlen(access)], "</td><td>");
+                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" name=\"c_creataide\" value=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "checked" : ""));
+                       sprintf(&access[strlen(access)], "</td></tr>\n");
+                       break;
+               case 5:
+                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
+                       sprintf(&tuning[strlen(tuning)], _("Server connection idle timeout (in seconds)"));
+                       sprintf(&tuning[strlen(tuning)], "</td><td>");
+                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_sleeping\" maxlength=\"15\" value=\"%s\">", buf);
+                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
+                       break;
+               case 6:
+                       sprintf(&access[strlen(access)], "<tr><td>");
+                       sprintf(&access[strlen(access)], _("Initial access level for new users"));
+                       sprintf(&access[strlen(access)], "</td><td>");
+                       sprintf(&access[strlen(access)], "<select name=\"c_initax\" size=\"1\">\n");
+                       for (j=0; j<=6; ++j) {
+                               sprintf(&access[strlen(access)], "<option %s value=\"%d\">%d - %s</option>\n",
+                                       ((atoi(buf) == j) ? "selected" : ""),
+                                       j, j, axdefs[j]
+                               );
+                       }
+                       sprintf(&access[strlen(access)], "</select>");
+                       sprintf(&access[strlen(access)], "</td></tr>\n");
+                       break;
+               case 7:
+                       sprintf(&access[strlen(access)], "<tr><td>");
+                       sprintf(&access[strlen(access)], _("Require registration for new users"));
+                       sprintf(&access[strlen(access)], "</td><td>");
+                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" name=\"c_regiscall\" value=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "checked" : ""));
+                       sprintf(&access[strlen(access)], "</td></tr>\n");
+                       break;
+               case 8:
+                       sprintf(&access[strlen(access)], "<tr><td>");
+                       sprintf(&access[strlen(access)], _("Quarantine messages from problem users"));
+                       sprintf(&access[strlen(access)], "</td><td>");
+                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" name=\"c_twitdetect\" value=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "checked" : ""));
+                       sprintf(&access[strlen(access)], "</td></tr>\n");
+                       break;
+               case 9:
+                       sprintf(&access[strlen(access)], "<tr><td>");
+                       sprintf(&access[strlen(access)], _("Name of quarantine room"));
+                       sprintf(&access[strlen(access)], "</td><td>");
+                       sprintf(&access[strlen(access)], "<input type=\"text\" name=\"c_twitroom\" maxlength=\"63\" value=\"%s\">", buf);
+                       sprintf(&access[strlen(access)], "</td></tr>\n");
+                       break;
+               case 10:
+                       sprintf(&general[strlen(general)], "<tr><td>");
+                       sprintf(&general[strlen(general)], _("Paginator prompt (for text mode clients)"));
+                       sprintf(&general[strlen(general)], "</td><td>");
+                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_moreprompt\" maxlength=\"79\" value=\"%s\">", buf);
+                       sprintf(&general[strlen(general)], "</td></tr>\n");
+                       break;
+               case 11:
+                       sprintf(&access[strlen(access)], "<tr><td>");
+                       sprintf(&access[strlen(access)], _("Restrict access to Internet mail"));
+                       sprintf(&access[strlen(access)], "</td><td>");
+                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" name=\"c_restrict\" value=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "checked" : ""));
+                       sprintf(&access[strlen(access)], "</td></tr>\n");
+                       break;
+               case 12:
+                       sprintf(&general[strlen(general)], "<tr><td>");
+                       sprintf(&general[strlen(general)], _("Geographic location of this system"));
+                       sprintf(&general[strlen(general)], "</td><td>");
+                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_bbs_city\" maxlength=\"31\" value=\"%s\">", buf);
+                       sprintf(&general[strlen(general)], "</td></tr>\n");
+                       break;
+               case 13:
+                       sprintf(&general[strlen(general)], "<tr><td>");
+                       sprintf(&general[strlen(general)], _("Name of system administrator"));
+                       sprintf(&general[strlen(general)], "</td><td>");
+                       sprintf(&general[strlen(general)], "<input type=\"text\" name=\"c_sysadm\" MAXLENGTH=\"25\" VALUE=\"%s\">", buf);
+                       sprintf(&general[strlen(general)], "</td></tr>\n");
+                       break;
+               case 14:
+                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
+                       sprintf(&tuning[strlen(tuning)], _("Maximum concurrent sessions (0 = no limit)"));
+                       sprintf(&tuning[strlen(tuning)], "</td><td>");
+                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_maxsessions\" maxlength=\"5\" value=\"%s\">", buf);
+                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
+                       break;
+               case 16:
+                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
+                       sprintf(&tuning[strlen(tuning)], _("Default user purge time (days)"));
+                       sprintf(&tuning[strlen(tuning)], "</td><td>");
+                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_userpurge\" maxlength=\"5\" value=\"%s\">", buf);
+                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
+                       break;
+               case 17:
+                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
+                       sprintf(&tuning[strlen(tuning)], _("Default room purge time (days)"));
+                       sprintf(&tuning[strlen(tuning)], "</td><td>");
+                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_roompurge\" maxlength=\"5\" value=\"%s\">", buf);
+                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
+                       break;
+               case 18:
+                       sprintf(&access[strlen(access)], "<tr><td>");
+                       sprintf(&access[strlen(access)], _("Name of room to log pages"));
+                       sprintf(&access[strlen(access)], "</td><td>");
+                       sprintf(&access[strlen(access)], "<input type=\"text\" name=\"c_logpages\" maxlength=\"63\" value=\"%s\">", buf);
+                       sprintf(&access[strlen(access)], "</td></tr>\n");
+                       break;
+               case 19:
+                       sprintf(&access[strlen(access)], "<tr><td>");
+                       sprintf(&access[strlen(access)], _("Access level required to create rooms"));
+                       sprintf(&access[strlen(access)], "</td><td>");
+                       sprintf(&access[strlen(access)], "<select name=\"c_createax\" size=\"1\">\n");
+                       for (j=0; j<=6; ++j) {
+                               sprintf(&access[strlen(access)], "<option %s value=\"%d\">%d - %s</option>\n",
+                                       ((atoi(buf) == j) ? "selected" : ""),
+                                       j, j, axdefs[j]
+                               );
+                       }
+                       sprintf(&access[strlen(access)], "</select>");
+                       sprintf(&access[strlen(access)], "</td></tr>\n");
+                       break;
+               case 20:
+                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
+                       sprintf(&tuning[strlen(tuning)], _("Maximum message length"));
+                       sprintf(&tuning[strlen(tuning)], "</td><td>");
+                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_maxmsglen\" maxlength=\"20\" value=\"%s\">", buf);
+                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
+                       break;
+               case 21:
+                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
+                       sprintf(&tuning[strlen(tuning)], _("Minimum number of worker threads"));
+                       sprintf(&tuning[strlen(tuning)], "</td><td>");
+                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_min_workers\" maxlength=\"5\" value=\"%s\">", buf);
+                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
+                       break;
+               case 22:
+                       sprintf(&tuning[strlen(tuning)], "<tr><td>");
+                       sprintf(&tuning[strlen(tuning)], _("Maximum number of worker threads"));
+                       sprintf(&tuning[strlen(tuning)], "</td><td>");
+                       sprintf(&tuning[strlen(tuning)], "<input type=\"text\" name=\"c_max_workers\" maxlength=\"5\" value=\"%s\">", buf);
+                       sprintf(&tuning[strlen(tuning)], "</td></tr>\n");
+                       break;
+               case 23:
+                       sprintf(&network[strlen(network)], "<tr><td>");
+                       sprintf(&network[strlen(network)], _("POP3 listener port (-1 to disable)"));
+                       sprintf(&network[strlen(network)], "</td><td>");
+                       sprintf(&network[strlen(network)], "<input type=\"text\" name=\"c_pop3_port\" maxlength=\"5\" value=\"%s\">", buf);
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 24:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("SMTP MTA port (-1 to disable)"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_smtp_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 25:        /* note: reverse bool */
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("Correct forged From: lines during authenticated SMTP"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"checkbox\" NAME=\"c_rfc822_strict_from\" VALUE=\"yes\" %s>",
+                               ((atoi(buf) == 0) ? "CHECKED" : ""));
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 26:
+                       sprintf(&access[strlen(access)], "<TR><TD>");
+                       sprintf(&access[strlen(access)], _("Allow aides to zap (forget) rooms"));
+                       sprintf(&access[strlen(access)], "</TD><TD>");
+                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" NAME=\"c_aide_zap\" VALUE=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "CHECKED" : ""));
+                       sprintf(&access[strlen(access)], "</TD></TR>\n");
+                       break;
+               case 27:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("IMAP listener port (-1 to disable)"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_imap_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 28:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("Network run frequency (in seconds)"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_net_freq\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 29:
+                       sprintf(&access[strlen(access)], "<TR><TD>");
+                       sprintf(&access[strlen(access)], _("Disable self-service user account creation"));
+                       sprintf(&access[strlen(access)], "</TD><TD>");
+                       sprintf(&access[strlen(access)], "<input type=\"checkbox\" NAME=\"c_disable_newu\" VALUE=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "CHECKED" : ""));
+                       sprintf(&access[strlen(access)], "</TD></TR>\n");
+                       break;
+               case 31:
+                       sprintf(&purger[strlen(purger)], "<TR><TD>");
+                       sprintf(&purger[strlen(purger)], _("Hour to run database auto-purge"));
+                       sprintf(&purger[strlen(purger)], "</TD><TD>");
+                       sprintf(&purger[strlen(purger)], "<SELECT NAME=\"c_purge_hour\" SIZE=\"1\">\n");
+                       for (j=0; j<=23; ++j) {
+                               sprintf(&purger[strlen(purger)], "<OPTION %s VALUE=\"%d\">%d:00%s</OPTION>\n",
+                                       ((atoi(buf) == j) ? "SELECTED" : ""),
+                                       j,
+                                       ((j == 0) ? 12 : ((j>12) ? j-12 : j)),
+                                       ((j >= 12) ? "pm" : "am")
+                               );
+                       }
+                       sprintf(&purger[strlen(purger)], "</SELECT>");
+                       sprintf(&purger[strlen(purger)], "</TD></TR>\n");
+                       break;
+               case 32:
+                       sprintf(&directory[strlen(directory)], "<TR><TD>");
+                       sprintf(&directory[strlen(directory)], _("Host name of LDAP server (blank to disable)"));
+                       sprintf(&directory[strlen(directory)], "</TD><TD>");
+                       sprintf(&directory[strlen(directory)], "<input type=\"text\" NAME=\"c_ldap_host\" MAXLENGTH=\"127\" VALUE=\"%s\">", buf);
+                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
+                       break;
+               case 33:
+                       sprintf(&directory[strlen(directory)], "<TR><TD>");
+                       sprintf(&directory[strlen(directory)], _("Port number of LDAP server (blank to disable)"));
+                       sprintf(&directory[strlen(directory)], "</TD><TD>");
+                       sprintf(&directory[strlen(directory)], "<input type=\"text\" NAME=\"c_ldap_port\" MAXLENGTH=\"127\" VALUE=\"%d\">", atoi(buf));
+                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
+                       break;
+               case 34:
+                       sprintf(&directory[strlen(directory)], "<TR><TD>");
+                       sprintf(&directory[strlen(directory)], _("Base DN"));
+                       sprintf(&directory[strlen(directory)], "</TD><TD>");
+                       sprintf(&directory[strlen(directory)], "<input type=\"text\" NAME=\"c_ldap_base_dn\" MAXLENGTH=\"255\" VALUE=\"%s\">", buf);
+                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
+                       break;
+               case 35:
+                       sprintf(&directory[strlen(directory)], "<TR><TD>");
+                       sprintf(&directory[strlen(directory)], _("Bind DN"));
+                       sprintf(&directory[strlen(directory)], "</TD><TD>");
+                       sprintf(&directory[strlen(directory)], "<input type=\"text\" NAME=\"c_ldap_bind_dn\" MAXLENGTH=\"255\" VALUE=\"%s\">", buf);
+                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
+                       break;
+               case 36:
+                       sprintf(&directory[strlen(directory)], "<TR><TD>");
+                       sprintf(&directory[strlen(directory)], _("Password for bind DN"));
+                       sprintf(&directory[strlen(directory)], "</TD><TD>");
+                       sprintf(&directory[strlen(directory)], "<input type=\"password\" NAME=\"c_ldap_bind_pw\" MAXLENGTH=\"255\" VALUE=\"%s\">",
+                               buf);
+                       sprintf(&directory[strlen(directory)], "</TD></TR>\n");
+                       break;
+               case 37:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("Server IP address (0.0.0.0 for 'any')"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_ip_addr\" MAXLENGTH=\"15\" VALUE=\"%s\">", buf);
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 38:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("SMTP MSA port (-1 to disable)"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_msa_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 39:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("IMAP over SSL port (-1 to disable)"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_imaps_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 40:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("POP3 over SSL port (-1 to disable)"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_pop3s_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 41:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("SMTP over SSL port (-1 to disable)"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"text\" NAME=\"c_smtps_port\" MAXLENGTH=\"5\" VALUE=\"%s\">", buf);
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 42:
+                               sprintf(&idxjnl[strlen(idxjnl)], "<TR><TD>");
+                               sprintf(&idxjnl[strlen(idxjnl)], _("Enable full text index"));
+                               sprintf(&idxjnl[strlen(idxjnl)], "</TD><TD>");
+                               sprintf(&idxjnl[strlen(idxjnl)], "<input type=\"checkbox\" NAME=\"c_enable_fulltext\" VALUE=\"yes\" %s>",
+                                       ((atoi(buf) != 0) ? "CHECKED" : ""));
+                               sprintf(&idxjnl[strlen(idxjnl)], "</TD></TR>\n");
+                       break;
+               case 43:
+                       sprintf(&tuning[strlen(tuning)], "<TR><TD>");
+                       sprintf(&tuning[strlen(tuning)], _("Automatically delete committed database logs"));
+                       sprintf(&tuning[strlen(tuning)], "</TD><TD>");
+                       sprintf(&tuning[strlen(tuning)], "<input type=\"checkbox\" NAME=\"c_auto_cull\" VALUE=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "CHECKED" : ""));
+                       sprintf(&tuning[strlen(tuning)], "</TD></TR>\n");
+                       break;
+               case 44:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("Instantly expunge deleted messages in IMAP"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"checkbox\" NAME=\"c_instant_expunge\" VALUE=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "CHECKED" : ""));
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 45:
+                       sprintf(&network[strlen(network)], "<TR><TD>");
+                       sprintf(&network[strlen(network)], _("Allow unauthenticated SMTP clients to spoof this site's domains"));
+                       sprintf(&network[strlen(network)], "</TD><TD>");
+                       sprintf(&network[strlen(network)], "<input type=\"checkbox\" NAME=\"c_allow_spoofing\" VALUE=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "CHECKED" : ""));
+                       sprintf(&network[strlen(network)], "</TD></TR>\n");
+                       break;
+               case 46:
+                       sprintf(&idxjnl[strlen(idxjnl)], "<TR><TD>");
+                       sprintf(&idxjnl[strlen(idxjnl)], _("Perform journaling of email messages"));
+                       sprintf(&idxjnl[strlen(idxjnl)], "</TD><TD>");
+                       sprintf(&idxjnl[strlen(idxjnl)], "<input type=\"checkbox\" NAME=\"c_journal_email\" VALUE=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "CHECKED" : ""));
+                       sprintf(&idxjnl[strlen(idxjnl)], "</TD></TR>\n");
+                       break;
+               case 47:
+                       sprintf(&idxjnl[strlen(idxjnl)], "<TR><TD>");
+                       sprintf(&idxjnl[strlen(idxjnl)], _("Perform journaling of non-email messages"));
+                       sprintf(&idxjnl[strlen(idxjnl)], "</TD><TD>");
+                       sprintf(&idxjnl[strlen(idxjnl)], "<input type=\"checkbox\" NAME=\"c_journal_pubmsgs\" VALUE=\"yes\" %s>",
+                               ((atoi(buf) != 0) ? "CHECKED" : ""));
+                       sprintf(&idxjnl[strlen(idxjnl)], "</TD></TR>\n");
+                       break;
+               case 48:
+                       sprintf(&idxjnl[strlen(idxjnl)], "<TR><TD>");
+                       sprintf(&idxjnl[strlen(idxjnl)], _("Email destination of journalized messages"));
+                       sprintf(&idxjnl[strlen(idxjnl)], "</TD><TD>");
+                       sprintf(&idxjnl[strlen(idxjnl)], "<input type=\"text\" NAME=\"c_journal_dest\" MAXLENGTH=\"127\" VALUE=\"%s\">", buf);
+                       sprintf(&idxjnl[strlen(idxjnl)], "</TD></TR>\n");
+                       break;
+               }
+       }
+
+       serv_puts("GPEX site");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               sitepolicy = extract_int(&buf[4], 0);
+               sitevalue = extract_int(&buf[4], 1);
+       }
+
+       serv_puts("GPEX mailboxes");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               mboxpolicy = extract_int(&buf[4], 0);
+               mboxvalue = extract_int(&buf[4], 1);
+       }
+
+
+       sprintf(&purger[strlen(purger)], "<TR><TD COLSPAN=2><hr /></TD></TR>\n");
+
+       sprintf(&purger[strlen(purger)], "<TR><TD>");
+       sprintf(&purger[strlen(purger)], _("Default message expire policy for public rooms"));
+       sprintf(&purger[strlen(purger)], "</TD><TD>");
+       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"sitepolicy\" VALUE=\"1\" %s>",
+               ((sitepolicy == 1) ? "CHECKED" : "") );
+       sprintf(&purger[strlen(purger)], _("Never automatically expire messages"));
+       sprintf(&purger[strlen(purger)], "<br />\n");
+       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"sitepolicy\" VALUE=\"2\" %s>",
+               ((sitepolicy == 2) ? "CHECKED" : "") );
+       sprintf(&purger[strlen(purger)], _("Expire by message count"));
+       sprintf(&purger[strlen(purger)], "<br />\n");
+       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"sitepolicy\" VALUE=\"3\" %s>",
+               ((sitepolicy == 3) ? "CHECKED" : "") );
+       sprintf(&purger[strlen(purger)], _("Expire by message age"));
+       sprintf(&purger[strlen(purger)], "<br />");
+       sprintf(&purger[strlen(purger)], _("Number of messages or days: "));
+       sprintf(&purger[strlen(purger)], "<input type=\"text\" NAME=\"sitevalue\" MAXLENGTH=\"5\" VALUE=\"%d\">", sitevalue);
+       sprintf(&purger[strlen(purger)], "</TD></TR>\n");
+
+       sprintf(&purger[strlen(purger)], "<TR><TD COLSPAN=2><hr /></TD></TR>\n");
+
+       sprintf(&purger[strlen(purger)], "<TR><TD>");
+       sprintf(&purger[strlen(purger)], _("Default message expire policy for private mailboxes"));
+       sprintf(&purger[strlen(purger)], "</TD><TD>");
+       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"mboxpolicy\" VALUE=\"0\" %s>",
+               ((mboxpolicy == 0) ? "CHECKED" : "") );
+       sprintf(&purger[strlen(purger)], _("Same policy as public rooms"));
+       sprintf(&purger[strlen(purger)], "<br />\n");
+       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"mboxpolicy\" VALUE=\"1\" %s>",
+                       ((mboxpolicy == 1) ? "CHECKED" : "") );
+       sprintf(&purger[strlen(purger)], _("Never automatically expire messages"));
+       sprintf(&purger[strlen(purger)], "<br />\n");
+       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"mboxpolicy\" VALUE=\"2\" %s>",
+               ((mboxpolicy == 2) ? "CHECKED" : "") );
+       sprintf(&purger[strlen(purger)], _("Expire by message count"));
+       sprintf(&purger[strlen(purger)], "<br />\n");
+       sprintf(&purger[strlen(purger)], "<input type=\"radio\" NAME=\"mboxpolicy\" VALUE=\"3\" %s>",
+               ((mboxpolicy == 3) ? "CHECKED" : "") );
+       sprintf(&purger[strlen(purger)], _("Expire by message age"));
+       sprintf(&purger[strlen(purger)], "<br />");
+       sprintf(&purger[strlen(purger)], _("Number of messages or days: "));
+       sprintf(&purger[strlen(purger)], "<input type=\"text\" NAME=\"mboxvalue\" MAXLENGTH=\"5\" VALUE=\"%d\">", mboxvalue);
+       sprintf(&purger[strlen(purger)], "</TD></TR>\n");
+
+       sprintf(&purger[strlen(purger)], "<TR><TD COLSPAN=2><hr /></TD></TR>\n");
+
+
+       sprintf(&general[strlen(general)], "</table>");
+       sprintf(&access[strlen(access)], "</table>");
+       sprintf(&network[strlen(network)], "</table>");
+       sprintf(&tuning[strlen(tuning)], "</table>");
+       sprintf(&directory[strlen(directory)], "</table>");
+       sprintf(&purger[strlen(purger)], "</table>");
+       sprintf(&idxjnl[strlen(idxjnl)], "</table>");
+
+       tabbed_dialog(7, tabnames);
+
+       begin_tab(0, 7);        wprintf("%s", general);          end_tab(0, 7);
+       begin_tab(1, 7);        wprintf("%s", access);           end_tab(1, 7);
+       begin_tab(2, 7);        wprintf("%s", network);          end_tab(2, 7);
+       begin_tab(3, 7);        wprintf("%s", tuning);           end_tab(3, 7);
+       begin_tab(4, 7);        wprintf("%s", directory);        end_tab(4, 7);
+       begin_tab(5, 7);        wprintf("%s", purger);           end_tab(5, 7);
+       begin_tab(6, 7);        wprintf("%s", idxjnl);   end_tab(6, 7);
+
+       wprintf("<div align=\"center\"><br>");
+       wprintf("<input type=\"submit\" NAME=\"ok_button\" VALUE=\"%s\">", _("Save changes"));
+       wprintf("&nbsp;");
+       wprintf("<input type=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">\n", _("Cancel"));
+       wprintf("</div></FORM>\n");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+/**
+ * parse siteconfig changes 
+ */
+void siteconfig(void)
+{
+       char buf[256];
+
+       if (strlen(bstr("ok_button")) == 0) {
+               display_aide_menu();
+               return;
+       }
+       serv_printf("CONF set");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '4') {
+               safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage);
+               display_aide_menu();
+               return;
+       }
+       serv_printf("%s", bstr("c_nodename"));
+       serv_printf("%s", bstr("c_fqdn"));
+       serv_printf("%s", bstr("c_humannode"));
+       serv_printf("%s", bstr("c_phonenum"));
+       serv_printf("%s", ((!strcasecmp(bstr("c_creataide"), "yes") ? "1" : "0")));
+       serv_printf("%s", bstr("c_sleeping"));
+       serv_printf("%s", bstr("c_initax"));
+       serv_printf("%s", ((!strcasecmp(bstr("c_regiscall"), "yes") ? "1" : "0")));
+       serv_printf("%s", ((!strcasecmp(bstr("c_twitdetect"), "yes") ? "1" : "0")));
+       serv_printf("%s", bstr("c_twitroom"));
+       serv_printf("%s", bstr("c_moreprompt"));
+       serv_printf("%s", ((!strcasecmp(bstr("c_restrict"), "yes") ? "1" : "0")));
+       serv_printf("%s", bstr("c_bbs_city"));
+       serv_printf("%s", bstr("c_sysadm"));
+       serv_printf("%s", bstr("c_maxsessions"));
+       serv_printf("");  /* placeholder - this field is not in use */
+       serv_printf("%s", bstr("c_userpurge"));
+       serv_printf("%s", bstr("c_roompurge"));
+       serv_printf("%s", bstr("c_logpages"));
+       serv_printf("%s", bstr("c_createax"));
+       serv_printf("%s", bstr("c_maxmsglen"));
+       serv_printf("%s", bstr("c_min_workers"));
+       serv_printf("%s", bstr("c_max_workers"));
+       serv_printf("%s", bstr("c_pop3_port"));
+       serv_printf("%s", bstr("c_smtp_port"));
+       serv_printf("%s", ((!strcasecmp(bstr("c_rfc822_strict_from"), "yes") ? "0" : "1"))); /* note: reverse bool */
+       serv_printf("%s", ((!strcasecmp(bstr("c_aide_zap"), "yes") ? "1" : "0")));
+       serv_printf("%s", bstr("c_imap_port"));
+       serv_printf("%s", bstr("c_net_freq"));
+       serv_printf("%s", ((!strcasecmp(bstr("c_disable_newu"), "yes") ? "1" : "0")));
+       serv_printf("1"); /* placeholder - this field is not in use */
+       serv_printf("%s", bstr("c_purge_hour"));
+       serv_printf("%s", bstr("c_ldap_host"));
+       serv_printf("%s", bstr("c_ldap_port"));
+       serv_printf("%s", bstr("c_ldap_base_dn"));
+       serv_printf("%s", bstr("c_ldap_bind_dn"));
+       serv_printf("%s", bstr("c_ldap_bind_pw"));
+       serv_printf("%s", bstr("c_ip_addr"));
+       serv_printf("%s", bstr("c_msa_port"));
+       serv_printf("%s", bstr("c_imaps_port"));
+       serv_printf("%s", bstr("c_pop3s_port"));
+       serv_printf("%s", bstr("c_smtps_port"));
+       serv_printf("%s", ((!strcasecmp(bstr("c_enable_fulltext"), "yes") ? "1" : "0")));
+       serv_printf("%s", ((!strcasecmp(bstr("c_auto_cull"), "yes") ? "1" : "0")));
+       serv_printf("%s", ((!strcasecmp(bstr("c_instant_expunge"), "yes") ? "1" : "0")));
+       serv_printf("%s", ((!strcasecmp(bstr("c_allow_spoofing"), "yes") ? "1" : "0")));
+       serv_printf("%s", ((!strcasecmp(bstr("c_journal_email"), "yes") ? "1" : "0")));
+       serv_printf("%s", ((!strcasecmp(bstr("c_journal_pubmsgs"), "yes") ? "1" : "0")));
+       serv_printf("%s", bstr("c_journal_dest"));
+       serv_printf("000");
+
+       serv_printf("SPEX site|%d|%d", atoi(bstr("sitepolicy")), atoi(bstr("sitevalue")));
+       serv_getln(buf, sizeof buf);
+       serv_printf("SPEX mailboxes|%d|%d", atoi(bstr("mboxpolicy")), atoi(bstr("mboxvalue")));
+       serv_getln(buf, sizeof buf);
+
+       safestrncpy(WC->ImportantMessage, _("Your system configuration has been updated."),
+               sizeof WC->ImportantMessage);
+       display_aide_menu();
+}
+
+
+/*@}*/
diff --git a/webcit/src/snprintf.c b/webcit/src/snprintf.c
new file mode 100644 (file)
index 0000000..66e9701
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * $Id$
+ */
+/** 
+ * \defgroup SnprintfReplacement modified from Sten Gunterberg's BUGTRAQ post of 22 Jul 1997
+ * --nathan bryant <bryant@cs.usm.maine.edu>
+ * \ingroup tools
+ */
+/*@{*/
+/**
+ * \brief Replacements for snprintf() and vsnprintf()
+ *
+ * Use it only if you have the "spare" cycles needed to effectively
+ * do every snprintf operation twice! Why is that? Because everything
+ * is first vfprintf()'d to /dev/null to determine the number of bytes.
+ * Perhaps a bit slow for demanding applications on slow machines,
+ * no problem for a fast machine with some spare cycles.
+ *
+ * You don't have a /dev/null? Every Linux contains one for free!
+ *
+ * Because the format string is never even looked at, all current and
+ * possible future printf-conversions should be handled just fine.
+ *
+ * Written July 1997 by Sten Gunterberg (gunterberg@ergon.ch)
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+
+/**
+ * \brief is it needed????
+ * \param fmt the formatstring?
+ * \param argp how many params?
+ */
+static int needed(const char *fmt, va_list argp)
+{
+       static FILE *sink = NULL;
+
+       /**
+        * ok, there's a small race here that could result in the sink being
+        * opened more than once if we're threaded, but I'd rather ignore it than
+        * spend cycles synchronizing :-) */
+
+       if (sink == NULL) {
+               if ((sink = fopen("/dev/null", "w")) == NULL) {
+                       perror("/dev/null");
+                       exit(1);
+               }
+       }
+       return vfprintf(sink, fmt, argp);
+}
+
+/**
+ * \brief vsnprintf wrapper
+ * \param buf the output charbuffer
+ * \param max maximal size of the buffer
+ * \param fmt the formatstring (see man printf)
+ * \param argp the variable argument list 
+ */
+int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp)
+{
+       char *p;
+       int size;
+
+       if ((p = malloc(needed(fmt, argp) + 1)) == NULL) {
+               lprintf(1, "vsnprintf: malloc failed, aborting\n");
+               abort();
+       }
+       if ((size = vsprintf(p, fmt, argp)) >= max)
+               size = -1;
+
+       strncpy(buf, p, max);
+       buf[max - 1] = 0;
+       free(p);
+       return size;
+}
+
+/**
+ * \brief snprintf wrapper
+ * \param buf the output charbuffer
+ * \param max maximal size of the buffer
+ * \param fmt the formatstring (see man printf)
+ * \param ... the variable argument list 
+ */
+int snprintf(char *buf, size_t max, const char *fmt,...)
+{
+       va_list argp;
+       int bytes;
+
+       va_start(argp, fmt);
+       bytes = vsnprintf(buf, max, fmt, argp);
+       va_end(argp);
+
+       return bytes;
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/subst.c b/webcit/src/subst.c
new file mode 100644 (file)
index 0000000..4359306
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup Subst Variable substitution type stuff
+ * \ingroup CitadelConfig
+ */
+
+/*@{*/
+
+#include "webcit.h"
+
+
+/**
+ * \brief Clear out the list of substitution variables local to this session
+ */
+void clear_local_substs(void) {
+       struct wcsubst *ptr;
+
+       while (WC->vars != NULL) {
+               ptr = WC->vars->next;
+
+               if ((WC->vars->wcs_type == WCS_STRING)
+                  || (WC->vars->wcs_type == WCS_SERVCMD)) {
+                       free(WC->vars->wcs_value);
+               }
+
+               free(WC->vars);
+               WC->vars = ptr;
+       }
+
+       WC->vars = NULL;
+}
+
+
+/*
+ * \brief Add a substitution variable (local to this session)
+ * \param keyname the replacementstring to substitute
+ * \param keytype the kind of the key
+ * \param format the format string ala printf
+ * \param ... the arguments to substitute in the formatstring
+ */
+void svprintf(char *keyname, int keytype, const char *format,...)
+{
+       va_list arg_ptr;
+       char wbuf[SIZ];
+       struct wcsubst *ptr = NULL;
+       struct wcsubst *scan;
+
+       /**
+        * First scan through to see if we're doing a replacement of
+        * an existing key
+        */
+       for (scan=WC->vars; scan!=NULL; scan=scan->next) {
+               if (!strcasecmp(scan->wcs_key, keyname)) {
+                       ptr = scan;
+                       free(ptr->wcs_value);
+               }
+       }
+
+       /** Otherwise allocate a new one */
+       if (ptr == NULL) {
+               ptr = (struct wcsubst *) malloc(sizeof(struct wcsubst));
+               ptr->next = WC->vars;
+               safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
+               WC->vars = ptr;
+       }
+
+       /** Format the string and save it */
+
+       va_start(arg_ptr, format);
+       vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
+       va_end(arg_ptr);
+
+       ptr->wcs_type = keytype;
+       ptr->wcs_value = strdup(wbuf);
+}
+
+/**
+ * \brief Add a substitution variable (local to this session) that does a callback
+ * \param keyname the keystring to substitute
+ * \param fcn_ptr the function callback to give the substitution string
+ */
+void svcallback(char *keyname, void (*fcn_ptr)() )
+{
+       struct wcsubst *ptr;
+
+       ptr = (struct wcsubst *) malloc(sizeof(struct wcsubst));
+       ptr->next = WC->vars;
+       ptr->wcs_type = WCS_FUNCTION;
+       strcpy(ptr->wcs_key, keyname);
+       ptr->wcs_function = fcn_ptr;
+       WC->vars = ptr;
+}
+
+
+
+/**
+ * \brief back end for print_value_of() ... does a server command
+ * \param servcmd server command to execute on the citadel server
+ */
+void pvo_do_cmd(char *servcmd) {
+       char buf[SIZ];
+
+       serv_puts(servcmd);
+       serv_getln(buf, sizeof buf);
+
+       switch(buf[0]) {
+               case '2':
+               case '3':
+               case '5':
+                       wprintf("%s\n", &buf[4]);
+                       break;
+               case '1':
+                       fmout("CENTER");
+                       break;
+               case '4':
+                       wprintf("%s\n", &buf[4]);
+                       serv_puts("000");
+                       break;
+       }
+}
+
+
+
+/**
+ * \brief Print the value of a variable
+ * \param keyname get a key to print
+ */
+void print_value_of(char *keyname) {
+       struct wcsubst *ptr;
+       void *fcn();
+
+       if (keyname[0] == '=') {
+               do_template(&keyname[1]);
+       }
+
+       if (!strcasecmp(keyname, "SERV_PID")) {
+               wprintf("%d", WC->ctdl_pid);
+       }
+
+       else if (!strcasecmp(keyname, "SERV_NODENAME")) {
+               escputs(serv_info.serv_nodename);
+       }
+
+       else if (!strcasecmp(keyname, "SERV_HUMANNODE")) {
+               escputs(serv_info.serv_humannode);
+       }
+
+       else if (!strcasecmp(keyname, "SERV_FQDN")) {
+               escputs(serv_info.serv_fqdn);
+       }
+
+       else if (!strcasecmp(keyname, "SERV_SOFTWARE")) {
+               escputs(serv_info.serv_software);
+       }
+
+       else if (!strcasecmp(keyname, "SERV_REV_LEVEL")) {
+               wprintf("%d.%02d",
+                       serv_info.serv_rev_level / 100,
+                       serv_info.serv_rev_level % 100
+               );
+       }
+
+       else if (!strcasecmp(keyname, "SERV_BBS_CITY")) {
+               escputs(serv_info.serv_bbs_city);
+       }
+
+       else if (!strcasecmp(keyname, "CURRENT_USER")) {
+               escputs(WC->wc_fullname);
+       }
+
+       else if (!strcasecmp(keyname, "CURRENT_ROOM")) {
+               escputs(WC->wc_roomname);
+       }
+
+       /** Page-local variables */
+       else for (ptr = WC->vars; ptr != NULL; ptr = ptr->next) {
+               if (!strcasecmp(ptr->wcs_key, keyname)) {
+                       if (ptr->wcs_type == WCS_STRING) {
+                               wprintf("%s", ptr->wcs_value);
+                       }
+                       else if (ptr->wcs_type == WCS_SERVCMD) {
+                               pvo_do_cmd(ptr->wcs_value);
+                       }
+                       else if (ptr->wcs_type == WCS_FUNCTION) {
+                               (*ptr->wcs_function) ();
+                       }
+               }
+       }
+}
+
+
+
+/**
+ * \brief Display a variable-substituted template
+ * \param templatename template file to load
+ */
+void do_template(void *templatename) {
+       char filename[PATH_MAX];
+       FILE *fp;
+       char inbuf[1024];
+       char outbuf[sizeof inbuf];
+       char key[sizeof inbuf];
+       int i, pos;
+
+       strcpy(filename, "static/");
+       strcat(filename, templatename);
+       if (WC->is_wap)
+               strcat(filename, ".wml");
+       else
+               strcat(filename, ".html");
+       
+       fp = fopen(filename, "r");
+       if (fp == NULL) {
+               wprintf(_("ERROR: could not open template "));
+               wprintf("'%s' - %s<br />\n",
+                       templatename, strerror(errno));
+               return;
+       }
+
+       strcpy(inbuf, "");
+
+       while (fgets(inbuf, sizeof inbuf, fp) != NULL) {
+               strcpy(outbuf, "");
+
+               while (strlen(inbuf) > 0) {
+                       pos = (-1);
+                       for (i=strlen(inbuf); i>=0; --i) {
+                               if ((inbuf[i]=='<')&&(inbuf[i+1]=='?')) pos = i;
+                       }
+                       if (pos < 0) {
+                               wprintf("%s", inbuf);
+                               strcpy(inbuf, "");
+                       }
+                       else {
+                               strncpy(outbuf, inbuf, pos);
+                               outbuf[pos] = 0;
+                               wprintf("%s", outbuf);
+                               strcpy(inbuf, &inbuf[pos]);
+                               pos = 1;
+                               for (i=strlen(inbuf); i>=0; --i) {
+                                       if (inbuf[i]=='>') pos = i;
+                               }
+                               strncpy(key, &inbuf[2], pos-2);
+                               key[pos-2] = 0;
+                               print_value_of(key);
+                               strcpy(inbuf, &inbuf[pos+1]);
+                       }
+               }
+       }
+
+       fclose(fp);
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/summary.c b/webcit/src/summary.c
new file mode 100644 (file)
index 0000000..3d99403
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup SymaryFuncs Displays the "Summary Page"
+ * \ingroup WebcitDisplayItems
+ */
+/*@{*/
+#include "webcit.h"
+
+/**
+ * \brief Display today's date in a friendly format
+ */
+void output_date(void) {
+       struct tm tm;
+       time_t now;
+       char buf[128];
+
+       time(&now);
+       localtime_r(&now, &tm);
+
+       wc_strftime(buf, 32, "%A, %x", &tm);
+       wprintf("%s", buf);
+}
+
+
+
+
+/**
+ * \brief Dummy section
+ */
+void dummy_section(void) {
+       svprintf("BOXTITLE", WCS_STRING, "(dummy&nbsp;section)");
+       do_template("beginbox");
+       wprintf(_("(nothing)"));
+       do_template("endbox");
+}
+
+
+/**
+ * \brief New messages section
+ */
+void new_messages_section(void) {
+       char buf[SIZ];
+       char room[SIZ];
+       int i;
+       int number_of_rooms_to_check;
+       char *rooms_to_check = "Mail|Lobby";
+
+       svprintf("BOXTITLE", WCS_STRING, _("Messages"));
+       do_template("beginbox");
+
+       number_of_rooms_to_check = num_tokens(rooms_to_check, '|');
+       if (number_of_rooms_to_check == 0) return;
+
+       wprintf("<table border=0 width=100%%>\n");
+       for (i=0; i<number_of_rooms_to_check; ++i) {
+               extract_token(room, rooms_to_check, i, '|', sizeof room);
+
+               serv_printf("GOTO %s", room);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       extract_token(room, &buf[4], 0, '|', sizeof room);
+                       wprintf("<tr><td><a href=\"dotgoto?room=");
+                       urlescputs(room);
+                       wprintf("\">");
+                       escputs(room);
+                       wprintf("</a></td><td>%d/%d</td></tr>\n",
+                               extract_int(&buf[4], 1),
+                               extract_int(&buf[4], 2)
+                       );
+               }
+       }
+       wprintf("</table>\n");
+       do_template("endbox");
+
+}
+
+
+/**
+ * \brief Wholist section
+ */
+void wholist_section(void) {
+       char buf[SIZ];
+       char user[SIZ];
+
+       svprintf("BOXTITLE", WCS_STRING, _("Who's&nbsp;online&nbsp;now"));
+       do_template("beginbox");
+       serv_puts("RWHO");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               extract_token(user, buf, 1, '|', sizeof user);
+               escputs(user);
+               wprintf("<br />\n");
+       }
+       do_template("endbox");
+}
+
+
+/**
+ * \brief Task list section
+ */
+void tasks_section(void) {
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+       int num_msgs = 0;
+       int i;
+#endif
+
+       svprintf("BOXTITLE", WCS_STRING, _("Tasks"));
+       do_template("beginbox");
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+       gotoroom("_TASKS_");
+       if (WC->wc_view != VIEW_TASKS) {
+               num_msgs = 0;
+       }
+       else {
+               num_msgs = load_msg_ptrs("MSGS ALL", 0);
+       }
+
+       if (num_msgs < 1) {
+               wprintf("<i>");
+               wprintf(_("(None)"));
+               wprintf("</i><br />\n");
+       }
+       else {
+               for (i=0; i<num_msgs; ++i) {
+                       display_task(WC->msgarr[i]);
+               }
+       }
+
+       calendar_summary_view();
+
+#else /* WEBCIT_WITH_CALENDAR_SERVICE */
+       wprintf("<i>");
+       wprintf(_("(This server does not support task lists)"));
+       wprintf("</i>\n");
+#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
+       do_template("endbox");
+}
+
+
+/**
+ * \brief Calendar section
+ */
+void calendar_section(void) {
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+       int num_msgs = 0;
+       int i;
+#endif
+
+       svprintf("BOXTITLE", WCS_STRING, _("Today&nbsp;on&nbsp;your&nbsp;calendar"));
+       do_template("beginbox");
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+       gotoroom("_CALENDAR_");
+       if ( (WC->wc_view != VIEW_CALENDAR) && (WC->wc_view != VIEW_CALBRIEF) ) {
+               num_msgs = 0;
+       }
+       else {
+               num_msgs = load_msg_ptrs("MSGS ALL", 0);
+       }
+
+       if (num_msgs < 1) {
+               wprintf("<i>");
+               wprintf(_("(Nothing)"));
+               wprintf("</i><br />\n");
+       }
+       else {
+               for (i=0; i<num_msgs; ++i) {
+                       display_calendar(WC->msgarr[i]);
+               }
+               calendar_summary_view();
+       }
+
+#else /* WEBCIT_WITH_CALENDAR_SERVICE */
+       wprintf("<i>");
+       wprintf(_("(This server does not support calendars)"));
+       wprintf("</i>\n");
+#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
+       do_template("endbox");
+}
+
+/**
+ * \brief Server info section (fluff, really)
+ */
+void server_info_section(void) {
+       char message[512];
+
+       svprintf("BOXTITLE", WCS_STRING, _("About&nbsp;this&nbsp;server"));
+       do_template("beginbox");
+
+       snprintf(message, sizeof message,
+               _("You are connected to %s, running %s with %s, and located in %s.  Your system administrator is %s."),
+               serv_info.serv_humannode,
+               serv_info.serv_software,
+               SERVER,
+               serv_info.serv_bbs_city,
+               serv_info.serv_sysadm);
+       escputs(message);
+       do_template("endbox");
+}
+
+/**
+ * \brief summary of inner div????
+ */
+void summary_inner_div(void) {
+       /**
+        * Now let's do three columns of crap.  All portals and all groupware
+        * clients seem to want to do three columns, so we'll do three
+        * columns too.  Conformity is not inherently a virtue, but there are
+        * a lot of really shallow people out there, and even though they're
+        * not people I consider worthwhile, I still want them to use WebCit.
+        */
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%%><tr valign=top>");
+
+       /**
+        * Column One
+        */
+       wprintf("<td width=33%%>");
+       wholist_section();
+
+       /**
+        * Column Two
+        */
+       wprintf("</td><td width=33%%>");
+       server_info_section();
+       wprintf("<br />");
+       tasks_section();
+
+       /**
+        * Column Three
+        */
+       wprintf("</td><td width=33%%>");
+       new_messages_section();
+       wprintf("<br />");
+       calendar_section();
+
+       /**
+        * End of columns
+        */
+       wprintf("</td></tr></table>");
+}
+
+
+/**
+ * \brief Display this user's summary page
+ */
+void summary(void) {
+       char title[256];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<table width=100%% border=0 bgcolor=#444455><tr>"
+               "<td><img src=\"static/summscreen_48x.gif\"></td><td>"
+               "<span class=\"titlebar\">"
+       );
+
+       snprintf(title, sizeof title, _("Summary page for %s"), WC->wc_fullname);
+       escputs(title);
+       wprintf("</span></td><td>\n");
+       wprintf("</td><td aling=right><span class=\"titlebar\">");
+       output_date();
+       wprintf("</span><br />");
+       offer_start_page();
+       wprintf("</td></tr></table>\n");
+
+       /**
+        * You guessed it ... we're going to refresh using ajax.
+        * In the future we might consider updating individual sections of the summary
+        * instead of the whole thing.
+        */
+       wprintf("</div>\n<div id=\"content\">\n");
+       summary_inner_div();
+       wprintf("</div>\n");
+
+       wprintf(
+               "<script type=\"text/javascript\">                                      "
+               " new Ajax.PeriodicalUpdater('content', 'summary_inner_div',            "
+               "                            { method: 'get', frequency: 60 }  );       "
+               "</script>                                                              \n"
+       );
+
+       wDumpContent(1);
+}
+
+
+/*@}*/
diff --git a/webcit/src/sysmsgs.c b/webcit/src/sysmsgs.c
new file mode 100644 (file)
index 0000000..7de3998
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup ShowSysMsgs Editing of various text files on the Citadel server.
+ * \ingroup WebcitDisplayItems
+ */
+/*@{*/
+#include "webcit.h"
+
+
+/**
+ * \brief display the form for editing something (room info, bio, etc)
+ * \param description the descriptive text for the box
+ * \param check_cmd command to check????
+ * \param read_cmd read answer from citadel server???
+ * \param save_cmd save comand to the citadel server??
+ * \param with_room_banner should we bisplay a room banner?
+ */
+void display_edit(char *description, char *check_cmd,
+                 char *read_cmd, char *save_cmd, int with_room_banner)
+{
+       char buf[SIZ];
+
+       serv_puts(check_cmd);
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] != '2') {
+               safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage);
+               display_main_menu();
+               return;
+       }
+       if (with_room_banner) {
+               output_headers(1, 1, 1, 0, 0, 0);
+       }
+       else {
+               output_headers(1, 1, 0, 0, 0, 0);
+       }
+
+       svprintf("BOXTITLE", WCS_STRING, _("Edit %s"), description);
+       do_template("beginbox");
+
+       wprintf("<div align=\"center\">");
+       wprintf(_("Enter %s below.  Text is formatted to "
+               "the reader's screen width.  To defeat the "
+               "formatting, indent a line at least one space."), description);
+       wprintf("<br />");
+
+       wprintf("<FORM METHOD=\"POST\" action=\"%s\">\n", save_cmd);
+       wprintf("<TEXTAREA NAME=\"msgtext\" wrap=soft "
+               "ROWS=10 COLS=80 WIDTH=80>\n");
+       serv_puts(read_cmd);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1')
+               server_to_text();
+       wprintf("</TEXTAREA><br /><br />\n");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"save_button\" VALUE=\"%s\">", _("Save changes"));
+       wprintf("&nbsp;");
+       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\"><br />\n", _("Cancel"));
+
+       wprintf("</FORM></div>\n");
+       do_template("endbox");
+       wDumpContent(1);
+}
+
+
+/**
+ * \brief save a screen which was displayed with display_edit()
+ * \param description the window title???
+ * \param enter_cmd which command to enter at the citadel server???
+ * \param regoto should we go to that room again after executing that command?
+ */
+void save_edit(char *description, char *enter_cmd, int regoto)
+{
+       char buf[SIZ];
+
+       if (strlen(bstr("save_button")) == 0) {
+               sprintf(WC->ImportantMessage,
+                       _("Cancelled.  %s was not saved."),
+                       description);
+               display_main_menu();
+               return;
+       }
+       serv_puts(enter_cmd);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '4') {
+               safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage);
+               display_main_menu();
+               return;
+       }
+       text_to_server(bstr("msgtext"));
+       serv_puts("000");
+
+       if (regoto) {
+               smart_goto(WC->wc_roomname);
+       } else {
+               sprintf(WC->ImportantMessage,
+                       _("%s has been saved."),
+                       description);
+               display_main_menu();
+               return;
+       }
+}
+
+
+/*@}*/
diff --git a/webcit/src/tabs.c b/webcit/src/tabs.c
new file mode 100644 (file)
index 0000000..5ecd53a
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * $Id:  $
+ */
+/**
+ * \defgroup TabUtils Utility functions for creating tabbed dialogs
+ * \ingroup WebcitDisplayItems
+ */
+/*@{*/
+#include "webcit.h"
+
+/**
+ * \brief print tabbed dialog
+ * \param num_tabs how many tabs do we have?
+ * \param tabnames the headers of the tables
+ */
+void tabbed_dialog(int num_tabs, char *tabnames[]) {
+       int i;
+
+       wprintf("<script type=\"text/javascript\">                                              "
+               "var previously_selected_tab = '0';                                             "
+               "function tabsel(which_tab) {                                                   "
+               "       if (which_tab == previously_selected_tab) {                             "
+               "               return;                                                         "
+               "       }                                                                       "
+               "       $('tabtd'+previously_selected_tab).style.backgroundColor = '#cccccc';   "
+               "       $('tabdiv'+previously_selected_tab).style.display = 'none';             "
+               "       $('tabtd'+which_tab).style.backgroundColor = '#ffffff';                 "
+               "       $('tabdiv'+which_tab).style.display = 'block';                          "
+               "       previously_selected_tab = which_tab;                                    "
+               "}                                                                              "
+               "</script>                                                                      \n"
+       );
+
+       wprintf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%%\">"
+               "<tr align=\"center\" style=\"cursor:pointer\"><td>&nbsp;</td>"
+       );
+
+       for (i=0; i<num_tabs; ++i) {
+               wprintf("<td id=\"tabtd%d\" bgcolor=\"#%s\" onClick='tabsel(\"%d\");'>"
+                       "<span class=\"tablabel\">",
+                       i,
+                       ( (i==0) ? "ffffff" : "cccccc" ),
+                       i
+               );
+               wprintf("%s", tabnames[i]);
+               wprintf("</td>");
+
+               wprintf("<td>&nbsp;</td>\n");
+       }
+
+       wprintf("</tr></table>\n");
+       wprintf("<table border=\"0\" width=\"100%%\" bgcolor=\"#ffffff\"><tr><td>");
+}
+
+/**
+ * \brief print the tab-header
+ * \param tabnum number of the tab to print
+ * \param num_tabs total number oftabs to be printed
+ */
+void begin_tab(int tabnum, int num_tabs) {
+       wprintf("<div id=\"tabdiv%d\" style=\"display:%s\">",
+               tabnum,
+               ( (tabnum == 0) ? "block" : "none" )
+       );
+}
+
+/**
+ * \brief print the tab-footer
+ * \param tabnum number of the tab to print
+ * \param num_tabs total number oftabs to be printed
+ */
+void end_tab(int tabnum, int num_tabs) {
+       wprintf("</div>\n");
+
+       if (tabnum == num_tabs-1) {
+               wprintf("</td></tr></table>\n");
+       }
+}
+
+
+/*@}*/
diff --git a/webcit/src/tcp_sockets.c b/webcit/src/tcp_sockets.c
new file mode 100644 (file)
index 0000000..2d5d985
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * $Id$
+ */
+/** 
+ * \defgroup TcpSockets TCP client socket module for WebCit
+ * \ingroup CitadelCommunitacion
+ */
+/*@{*/
+
+/*
+ * Uncomment this to log all communications with the Citadel server
+#define SERV_TRACE 1
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+
+/**
+ * \brief register the timeout
+ * \param signum signalhandler number
+ * \return signals
+ */
+RETSIGTYPE timeout(int signum)
+{
+       lprintf(1, "Connection timed out.\n");
+       exit(3);
+}
+
+
+/**
+ * \brief Connect a unix domain socket
+ * \param sockpath where to open a unix domain socket
+ */
+int uds_connectsock(char *sockpath)
+{
+       struct sockaddr_un addr;
+       int s;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
+
+       s = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (s < 0) {
+               lprintf(1, "Can't create socket: %s\n",
+                       strerror(errno));
+               return(-1);
+       }
+
+       if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               lprintf(1, "Can't connect: %s\n",
+                       strerror(errno));
+               close(s);
+               return(-1);
+       }
+
+       return s;
+}
+
+
+/**
+ * \brief Connect a TCP/IP socket
+ * \param host the host to connect to
+ * \param service the service on the host to call
+ */
+int tcp_connectsock(char *host, char *service)
+{
+       struct hostent *phe;
+       struct servent *pse;
+       struct protoent *ppe;
+       struct sockaddr_in sin;
+       int s;
+
+       memset(&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+
+       pse = getservbyname(service, "tcp");
+       if (pse) {
+               sin.sin_port = pse->s_port;
+       } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) {
+               lprintf(1, "Can't get %s service entry\n", service);
+               return (-1);
+       }
+       phe = gethostbyname(host);
+       if (phe) {
+               memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
+       } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
+               lprintf(1, "Can't get %s host entry: %s\n",
+                       host, strerror(errno));
+               return (-1);
+       }
+       if ((ppe = getprotobyname("tcp")) == 0) {
+               lprintf(1, "Can't get TCP protocol entry: %s\n",
+                       strerror(errno));
+               return (-1);
+       }
+
+       s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
+       if (s < 0) {
+               lprintf(1, "Can't create socket: %s\n", strerror(errno));
+               return (-1);
+       }
+       signal(SIGALRM, timeout);
+       alarm(30);
+
+       if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+               lprintf(1, "Can't connect to %s.%s: %s\n",
+                       host, service, strerror(errno));
+               close(s);
+               return (-1);
+       }
+       alarm(0);
+       signal(SIGALRM, SIG_IGN);
+
+       return (s);
+}
+
+
+
+
+/**
+ * \brief Input binary data from socket
+ * \param buf the buffer to get the input to
+ * \param bytes the maximal number of bytes to read
+ */
+void serv_read(char *buf, int bytes)
+{
+       int len, rlen;
+
+       len = 0;
+       while (len < bytes) {
+               rlen = read(WC->serv_sock, &buf[len], bytes - len);
+               if (rlen < 1) {
+                       lprintf(1, "Server connection broken: %s\n",
+                               strerror(errno));
+                       close(WC->serv_sock);
+                       WC->serv_sock = (-1);
+                       WC->connected = 0;
+                       WC->logged_in = 0;
+                       memset(buf, 0, bytes);
+                       return;
+               }
+               len = len + rlen;
+       }
+}
+
+
+/**
+ * \brief input string from pipe
+ */
+void serv_getln(char *strbuf, int bufsize)
+{
+       int ch, len;
+       char buf[2];
+
+       len = 0;
+       strbuf[0] = 0;
+       do {
+               serv_read(&buf[0], 1);
+               ch = buf[0];
+               if ((ch != 13) && (ch != 10)) {
+                       strbuf[len++] = ch;
+               }
+       } while ((ch != 10) && (ch != 0) && (len < (bufsize-1)));
+       strbuf[len] = 0;
+#ifdef SERV_TRACE
+       lprintf(9, "%3d>%s\n", WC->serv_sock, strbuf);
+#endif
+}
+
+
+
+/**
+ * \brief send binary to server
+ * \param buf the buffer to write to citadel server
+ * \param nbytes how many bytes to send to citadel server
+ */
+void serv_write(char *buf, int nbytes)
+{
+       int bytes_written = 0;
+       int retval;
+       while (bytes_written < nbytes) {
+               retval = write(WC->serv_sock, &buf[bytes_written],
+                              nbytes - bytes_written);
+               if (retval < 1) {
+                       lprintf(1, "Server connection broken: %s\n",
+                               strerror(errno));
+                       close(WC->serv_sock);
+                       WC->serv_sock = (-1);
+                       WC->connected = 0;
+                       WC->logged_in = 0;
+                       return;
+               }
+               bytes_written = bytes_written + retval;
+       }
+}
+
+
+/**
+ * \brief send line to server
+ * \param string the line to send to the citadel server
+ */
+void serv_puts(char *string)
+{
+       char buf[SIZ];
+
+#ifdef SERV_TRACE
+       lprintf(9, "%3d<%s\n", WC->serv_sock, string);
+#endif
+       sprintf(buf, "%s\n", string);
+       serv_write(buf, strlen(buf));
+}
+
+
+/**
+ * \brief convenience function to send stuff to the server
+ * \param format the formatstring
+ * \param ... the entities to insert into format 
+ */
+void serv_printf(const char *format,...)
+{
+       va_list arg_ptr;
+       char buf[SIZ];
+
+       va_start(arg_ptr, format);
+       vsnprintf(buf, sizeof buf, format, arg_ptr);
+       va_end(arg_ptr);
+
+       strcat(buf, "\n");
+       serv_write(buf, strlen(buf));
+#ifdef SERV_TRACE
+       lprintf(9, "<%s", buf);
+#endif
+}
+
+
+/*@}*/
diff --git a/webcit/src/tools.c b/webcit/src/tools.c
new file mode 100644 (file)
index 0000000..0a07183
--- /dev/null
@@ -0,0 +1,618 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup MiscRout Miscellaneous routines 
+ * \ingroup tools
+ */
+
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+
+
+typedef unsigned char byte; /**< byte data type */
+
+#define FALSE 0 /**< no. */
+#define TRUE 1  /**< yes. */
+
+static byte dtable[256];       /**< base64 encode / decode table */
+
+/**
+ * \brief sanitize strncopy.
+ * \param dest destination string
+ * \param src source string
+ * \param n length of source to copy 
+ * \return result string
+ */
+char *safestrncpy(char *dest, const char *src, size_t n)
+{
+       if (dest == NULL || src == NULL) {
+               abort();
+       }
+       strncpy(dest, src, n);
+       dest[n - 1] = 0;
+       return dest;
+}
+
+
+
+/**
+ * \brief discover number of parameters/tokens in a string
+ * \param source string to inspect
+ * \param tok seperation token
+ * \return number of tokenized parts found
+ */
+int num_tokens(char *source, char tok)
+{
+       int a = 0;
+       int count = 1;
+
+       if (source == NULL)
+               return (0);
+       for (a = 0; a < strlen(source); ++a) {
+               if (source[a] == tok)
+                       ++count;
+       }
+       return (count);
+}
+
+/**
+ * brief a string tokenizer
+ * \param dest destination string 
+ * \param source the string to grab tokens from
+ * \param parmnum the n'th token to grab
+ * \param separator the tokenizer string
+ * \param maxlen the length of dest
+ */
+void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
+{
+       char *d;                /* dest */
+       const char *s;          /* source */
+       int count = 0;
+       int len = 0;
+
+       dest[0] = 0;
+
+       /* Locate desired parameter */
+       s = source;
+       while (count < parmnum) {
+               /* End of string, bail! */
+               if (!*s) {
+                       s = NULL;
+                       break;
+               }
+               if (*s == separator) {
+                       count++;
+               }
+               s++;
+       }
+       if (!s) return;         /* Parameter not found */
+
+       for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
+               *d = *s;
+       }
+       *d = 0;
+}
+
+
+
+/**
+ * \brief a tokenizer that kills, maims, and destroys
+ * \param source the string to process
+ * \param parmnum which token to kill
+ * \param separator the tokenizer string
+ */
+void remove_token(char *source, int parmnum, char separator)
+{
+       int i;
+       int len;
+       int curr_parm;
+       int start, end;
+
+       len = 0;
+       curr_parm = 0;
+       start = (-1);
+       end = (-1);
+
+       if (strlen(source) == 0) {
+               return;
+       }
+
+       for (i = 0; i < strlen(source); ++i) {
+               if ((start < 0) && (curr_parm == parmnum)) {
+                       start = i;
+               }
+
+               if ((end < 0) && (curr_parm == (parmnum + 1))) {
+                       end = i;
+               }
+
+               if (source[i] == separator) {
+                       ++curr_parm;
+               }
+       }
+
+       if (end < 0)
+               end = strlen(source);
+
+       strcpy(&source[start], &source[end]);
+}
+
+
+
+
+/**
+ * \brief extract an int parm w/o supplying a buffer
+ * \param source the string to locate the int in
+ * \param parmnum the n'th token to grab the int from
+ * \return the integer
+ */
+int extract_int(const char *source, int parmnum)
+{
+       char buf[32];
+       
+       extract_token(buf, source, parmnum, '|', sizeof buf);
+       return(atoi(buf));
+}
+
+/**
+ * \brief extract an long parm w/o supplying a buffer
+ * \param source string to examine
+ * \param parmnum n'th token to search long in
+ * \return the found long value
+ */
+long extract_long(const char *source, int parmnum)
+{
+       char buf[32];
+       
+       extract_token(buf, source, parmnum, '|', sizeof buf);
+       return(atol(buf));
+}
+
+
+
+
+
+
+/**
+ * \brief check for the presence of a character within a string (returns count)
+ * \param st the string to examine
+ * \param ch the char to search
+ * \return the position inside of st
+ */
+int haschar(char *st,char ch)
+{
+       int a, b;
+       b = 0;
+       for (a = 0; a < strlen(st); ++a)
+               if (st[a] == ch)
+                       ++b;
+       return (b);
+}
+
+
+/** 
+ * \brief Utility function to "readline" from memory
+ * \param start Location in memory from which we are reading.
+ * \param buf the buffer to place the string in.
+ * \param maxlen Size of string buffer
+ * \return Pointer to the source memory right after we stopped reading.
+ */
+char *memreadline(char *start, char *buf, int maxlen)
+{
+       char ch;
+       char *ptr;
+       int len = 0;            /**< tally our own length to avoid strlen() delays */
+
+       ptr = start;
+       memset(buf, 0, maxlen);
+
+       while (1) {
+               ch = *ptr++;
+               if ((len < (maxlen - 1)) && (ch != 13) && (ch != 10)) {
+                       buf[strlen(buf) + 1] = 0;
+                       buf[strlen(buf)] = ch;
+                       ++len;
+               }
+               if ((ch == 10) || (ch == 0)) {
+                       return ptr;
+               }
+       }
+}
+
+
+
+/**
+ * \brief searches for a  paternn within asearch string
+ * \param search the string to search 
+ * \param patn the pattern to find in string
+ * \returns position in string
+ */
+int pattern2(char *search, char *patn)
+{
+       int a;
+       for (a = 0; a < strlen(search); ++a) {
+               if (!strncasecmp(&search[a], patn, strlen(patn)))
+                       return (a);
+       }
+       return (-1);
+}
+
+
+/**
+ * \brief Strip leading and trailing spaces from a string
+ * \param buf the string to modify
+ */
+void striplt(char *buf)
+{
+       if (strlen(buf) == 0) return;
+       while ((strlen(buf) > 0) && (isspace(buf[0])))
+               strcpy(buf, &buf[1]);
+       if (strlen(buf) == 0) return;
+       while (isspace(buf[strlen(buf) - 1]))
+               buf[strlen(buf) - 1] = 0;
+}
+
+
+/**
+ * \brief Determine whether the specified message number is contained within the
+ * specified set.
+ *
+ * \param mset Message set string
+ * \param msgnum Message number we are looking for
+ *
+ * \return Nonzero if the specified message number is in the specified message set string.
+ */
+int is_msg_in_mset(char *mset, long msgnum) {
+       int num_sets;
+       int s;
+       char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
+       long lo, hi;
+
+       /*
+        * Now set it for all specified messages.
+        */
+       num_sets = num_tokens(mset, ',');
+       for (s=0; s<num_sets; ++s) {
+               extract_token(setstr, mset, s, ',', sizeof setstr);
+
+               extract_token(lostr, setstr, 0, ':', sizeof lostr);
+               if (num_tokens(setstr, ':') >= 2) {
+                       extract_token(histr, setstr, 1, ':', sizeof histr);
+                       if (!strcmp(histr, "*")) {
+                               snprintf(histr, sizeof histr, "%ld", LONG_MAX);
+                       }
+               } 
+               else {
+                       strcpy(histr, lostr);
+               }
+               lo = atol(lostr);
+               hi = atol(histr);
+
+               if ((msgnum >= lo) && (msgnum <= hi)) return(1);
+       }
+
+       return(0);
+}
+
+
+
+/**
+ * \brief Strip a boundarized substring out of a string
+ * (for example, remove
+ * parentheses and anything inside them).
+ *
+ * This improved version can strip out *multiple* boundarized substrings.
+ * \param str the string to process
+ * \param leftboundary the boundary character on the left side of the target string 
+ * \param rightboundary the boundary character on the right side of the target string
+ */
+void stripout(char *str, char leftboundary, char rightboundary)
+{
+       int a;
+       int lb = (-1);
+       int rb = (-1);
+
+       do {
+               lb = (-1);
+               rb = (-1);
+
+               for (a = 0; a < strlen(str); ++a) {
+                       if (str[a] == leftboundary)
+                               lb = a;
+                       if (str[a] == rightboundary)
+                               rb = a;
+               }
+
+               if ((lb > 0) && (rb > lb)) {
+                       strcpy(&str[lb - 1], &str[rb + 1]);
+               }
+
+       } while ((lb > 0) && (rb > lb));
+
+}
+
+
+
+/**
+ * \brief Replacement for sleep() that uses select() in order to avoid SIGALRM
+ * \param seconds how many seconds should we sleep?
+ */
+void sleeeeeeeeeep(int seconds)
+{
+       struct timeval tv;
+
+       tv.tv_sec = seconds;
+       tv.tv_usec = 0;
+       select(0, NULL, NULL, NULL, &tv);
+}
+
+
+
+/**
+ * \brief encode a string into base64 to for example tunnel it through mail transport
+ * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
+ * John Walker, copied over from the Citadel server.
+ * \param dest encrypted string
+ * \param source the string to encrypt
+ * \param sourcelen the length of the source data (may contain string terminators)
+ */
+
+void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen)
+{
+       int i, hiteof = FALSE;
+       int spos = 0;
+       int dpos = 0;
+       int thisline = 0;
+
+       /**  Fill dtable with character encodings.  */
+
+       for (i = 0; i < 26; i++) {
+               dtable[i] = 'A' + i;
+               dtable[26 + i] = 'a' + i;
+       }
+       for (i = 0; i < 10; i++) {
+               dtable[52 + i] = '0' + i;
+       }
+       dtable[62] = '+';
+       dtable[63] = '/';
+
+       while (!hiteof) {
+               byte igroup[3], ogroup[4];
+               int c, n;
+
+               igroup[0] = igroup[1] = igroup[2] = 0;
+               for (n = 0; n < 3; n++) {
+                       if (spos >= sourcelen) {
+                               hiteof = TRUE;
+                               break;
+                       }
+                       c = source[spos++];
+                       igroup[n] = (byte) c;
+               }
+               if (n > 0) {
+                       ogroup[0] = dtable[igroup[0] >> 2];
+                       ogroup[1] =
+                           dtable[((igroup[0] & 3) << 4) |
+                                  (igroup[1] >> 4)];
+                       ogroup[2] =
+                           dtable[((igroup[1] & 0xF) << 2) |
+                                  (igroup[2] >> 6)];
+                       ogroup[3] = dtable[igroup[2] & 0x3F];
+
+                       /**
+                        * Replace characters in output stream with "=" pad
+                        * characters if fewer than three characters were
+                        * read from the end of the input stream. 
+                        */
+
+                       if (n < 3) {
+                               ogroup[3] = '=';
+                               if (n < 2) {
+                                       ogroup[2] = '=';
+                               }
+                       }
+                       for (i = 0; i < 4; i++) {
+                               dest[dpos++] = ogroup[i];
+                               dest[dpos] = 0;
+                       }
+                       thisline += 4;
+                       if (thisline > 70) {
+                               dest[dpos++] = '\r';
+                               dest[dpos++] = '\n';
+                               dest[dpos] = 0;
+                               thisline = 0;
+                       }
+               }
+       }
+       if (thisline > 70) {
+               dest[dpos++] = '\r';
+               dest[dpos++] = '\n';
+               dest[dpos] = 0;
+               thisline = 0;
+       }
+}
+
+
+/**
+ * \brief Convert base64-encoded to binary.  
+ * It will stop after reading 'length' bytes.
+ *
+ * \param dest The destination buffer 
+ * \param source The base64 data to be decoded.
+ * \param length The number of bytes to decode.
+ * \return The actual length of the decoded data.
+ */
+int CtdlDecodeBase64(char *dest, const char *source, size_t length)
+{
+       int i, c;
+       int dpos = 0;
+       int spos = 0;
+
+       for (i = 0; i < 255; i++) {
+               dtable[i] = 0x80;
+       }
+       for (i = 'A'; i <= 'Z'; i++) {
+               dtable[i] = 0 + (i - 'A');
+       }
+       for (i = 'a'; i <= 'z'; i++) {
+               dtable[i] = 26 + (i - 'a');
+       }
+       for (i = '0'; i <= '9'; i++) {
+               dtable[i] = 52 + (i - '0');
+       }
+       dtable['+'] = 62;
+       dtable['/'] = 63;
+       dtable['='] = 0;
+
+       /**CONSTANTCONDITION*/ while (TRUE) {
+               byte a[4], b[4], o[3];
+
+               for (i = 0; i < 4; i++) {
+                       if (spos >= length) {
+                               return (dpos);
+                       }
+                       c = source[spos++];
+
+                       if (c == 0) {
+                               if (i > 0) {
+                                       return (dpos);
+                               }
+                               return (dpos);
+                       }
+                       if (dtable[c] & 0x80) {
+                               /** Ignoring errors: discard invalid character */
+                               i--;
+                               continue;
+                       }
+                       a[i] = (byte) c;
+                       b[i] = (byte) dtable[c];
+               }
+               o[0] = (b[0] << 2) | (b[1] >> 4);
+               o[1] = (b[1] << 4) | (b[2] >> 2);
+               o[2] = (b[2] << 6) | b[3];
+               i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
+               if (i >= 1)
+                       dest[dpos++] = o[0];
+               if (i >= 2)
+                       dest[dpos++] = o[1];
+               if (i >= 3)
+                       dest[dpos++] = o[2];
+               dest[dpos] = 0;
+               if (i < 3) {
+                       return (dpos);
+               }
+       }
+}
+
+
+
+/**
+ * \brief Generate a new, globally unique UID parameter for a calendar etc. object
+ *
+ * \param buf String buffer into which our newly created UUID should be placed
+ */
+void generate_uuid(char *buf) {
+       static int seq = 0;
+
+       sprintf(buf, "%s-%lx-%x-%x",
+               serv_info.serv_nodename,
+               (long)time(NULL),
+               getpid(),
+               (seq++)
+       );
+}
+
+
+/**
+ * \brief Local replacement for controversial C library function that generates
+ * names for temporary files.  Included to shut up compiler warnings.
+ * \todo return a fd to the file instead of the name for security reasons
+ * \param name the created filename
+ * \param len the length of the filename
+ */
+void CtdlMakeTempFileName(char *name, int len) {
+       int i = 0;
+
+       while (i++, i < 100) {
+               snprintf(name, len, "/tmp/ctdl.%04x.%04x",
+                       getpid(),
+                       rand()
+               );
+               if (!access(name, F_OK)) {
+                       return;
+               }
+       }
+}
+
+
+
+/*
+ * \brief      case-insensitive substring search
+ *
+ *             This uses the Boyer-Moore search algorithm and is therefore quite fast.
+ *             The code is roughly based on the strstr() replacement from 'tin' written
+ *             by Urs Jannsen.
+ *
+ * \param      text    String to be searched
+ * \param      pattern String to search for
+ */
+char *bmstrcasestr(char *text, char *pattern) {
+
+       register unsigned char *p, *t;
+       register int i, j, *delta;
+       register size_t p1;
+       int deltaspace[256];
+       size_t textlen;
+       size_t patlen;
+
+       textlen = strlen (text);
+       patlen = strlen (pattern);
+
+       /* algorithm fails if pattern is empty */
+       if ((p1 = patlen) == 0)
+               return (text);
+
+       /* code below fails (whenever i is unsigned) if pattern too long */
+       if (p1 > textlen)
+               return (NULL);
+
+       /* set up deltas */
+       delta = deltaspace;
+       for (i = 0; i <= 255; i++)
+               delta[i] = p1;
+       for (p = (unsigned char *) pattern, i = p1; --i > 0;)
+               delta[tolower(*p++)] = i;
+
+       /*
+        * From now on, we want patlen - 1.
+        * In the loop below, p points to the end of the pattern,
+        * t points to the end of the text to be tested against the
+        * pattern, and i counts the amount of text remaining, not
+        * including the part to be tested.
+        */
+       p1--;
+       p = (unsigned char *) pattern + p1;
+       t = (unsigned char *) text + p1;
+       i = textlen - patlen;
+       while(1) {
+               if (tolower(p[0]) == tolower(t[0])) {
+                       if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
+                               return ((char *)t - p1);
+                       }
+               }
+               j = delta[tolower(t[0])];
+               if (i < j)
+                       break;
+               i -= j;
+               t += j;
+       }
+       return (NULL);
+}
+
+
+
+
+
+/*@}*/
diff --git a/webcit/src/useredit.c b/webcit/src/useredit.c
new file mode 100644 (file)
index 0000000..265f915
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup AdminTasks Administrative screen to add/change/delete user accounts
+ * \ingroup CitadelConfig
+ *
+ */
+/*@{*/
+
+#include "webcit.h"
+#include "webserver.h"
+
+
+/**
+ * \brief show a list of available users to edit them
+ * \param message the header message???
+ * \param preselect which user should be selected in the browser
+ */
+void select_user_to_edit(char *message, char *preselect)
+{
+       char buf[SIZ];
+       char username[SIZ];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<table width=100%% border=0 bgcolor=#444455><tr>"
+               "<td>"
+               "<span class=\"titlebar\">"
+               "<img src=\"static/usermanag_48x.gif\">");
+       wprintf(_("Edit or delete users"));
+       wprintf("</span></td></tr></table>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       if (message != NULL) wprintf(message);
+
+       wprintf("<table border=0 cellspacing=10><tr valign=top><td>\n");
+
+       svprintf("BOXTITLE", WCS_STRING, _("Add users"));
+       do_template("beginbox");
+
+       wprintf(_("To create a new user account, enter the desired "
+               "user name in the box below and click 'Create'."));
+       wprintf("<br /><br />");
+
+        wprintf("<center><form method=\"POST\" action=\"create_user\">\n");
+        wprintf(_("New user: "));
+        wprintf("<input type=\"text\" name=\"username\"><br />\n"
+               "<input type=\"submit\" name=\"create_button\" value=\"%s\">"
+               "</form></center>\n", _("Create"));
+
+       do_template("endbox");
+
+       wprintf("</td><td>");
+
+       svprintf("BOXTITLE", WCS_STRING, _("Edit or Delete users"));
+       do_template("beginbox");
+
+       wprintf(_("To edit an existing user account, select the user "
+               "name from the list and click 'Edit'."));
+       wprintf("<br /><br />");
+       
+        wprintf("<center>"
+               "<form method=\"POST\" action=\"display_edituser\">\n");
+        wprintf("<select name=\"username\" size=10 style=\"width:100%%\">\n");
+        serv_puts("LIST");
+        serv_getln(buf, sizeof buf);
+        if (buf[0] == '1') {
+                while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                        extract_token(username, buf, 0, '|', sizeof username);
+                        wprintf("<option");
+                       if (preselect != NULL)
+                          if (!strcasecmp(username, preselect))
+                             wprintf(" selected");
+                       wprintf(">");
+                        escputs(username);
+                        wprintf("\n");
+                }
+        }
+        wprintf("</select><br />\n");
+
+        wprintf("<input type=\"submit\" name=\"edit_config_button\" value=\"%s\">", _("Edit configuration"));
+        wprintf("<input type=\"submit\" name=\"edit_abe_button\" value=\"%s\">", _("Edit address book entry"));
+        wprintf("<input type=\"submit\" name=\"delete_button\" value=\"%s\" "
+               "onClick=\"return confirm('%s');\">", _("Delete user"), _("Delete this user?"));
+        wprintf("</form></center>\n");
+       do_template("endbox");
+
+       wprintf("</td></tr></table>\n");
+
+       wDumpContent(1);
+}
+
+
+
+/**
+ * \brief Locate the message number of a user's vCard in the current room
+ * \param username the plaintext name of the user
+ * \param usernum the number of the user on the citadel server
+ * \return the message id of his vcard
+ */
+long locate_user_vcard(char *username, long usernum) {
+       char buf[SIZ];
+       long vcard_msgnum = (-1L);
+       char content_type[SIZ];
+       char partnum[SIZ];
+       int already_tried_creating_one = 0;
+
+       struct stuff_t {
+               struct stuff_t *next;
+               long msgnum;
+       };
+
+       struct stuff_t *stuff = NULL;
+       struct stuff_t *ptr;
+
+TRYAGAIN:
+       /** Search for the user's vCard */
+       serv_puts("MSGS ALL");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               ptr = malloc(sizeof(struct stuff_t));
+               ptr->msgnum = atol(buf);
+               ptr->next = stuff;
+               stuff = ptr;
+       }
+
+       /** Iterate through the message list looking for vCards */
+       while (stuff != NULL) {
+               serv_printf("MSG0 %ld|2", stuff->msgnum);
+               serv_getln(buf, sizeof buf);
+               if (buf[0]=='1') {
+                       while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                               if (!strncasecmp(buf, "part=", 5)) {
+                                       extract_token(partnum, &buf[5], 2, '|', sizeof partnum);
+                                       extract_token(content_type, &buf[5], 4, '|', sizeof content_type);
+                                       if (!strcasecmp(content_type,
+                                          "text/x-vcard")) {
+                                               vcard_msgnum = stuff->msgnum;
+                                       }
+                               }
+                       }
+               }
+
+               ptr = stuff->next;
+               free(stuff);
+               stuff = ptr;
+       }
+
+       /** If there's no vcard, create one */
+       if (vcard_msgnum < 0) if (already_tried_creating_one == 0) {
+               already_tried_creating_one = 1;
+               serv_puts("ENT0 1|||4");
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '4') {
+                       serv_puts("Content-type: text/x-vcard");
+                       serv_puts("");
+                       serv_puts("begin:vcard");
+                       serv_puts("end:vcard");
+                       serv_puts("000");
+               }
+               goto TRYAGAIN;
+       }
+
+       return(vcard_msgnum);
+}
+
+
+/**
+ * \brief Display the form for editing a user's address book entry
+ * \param username the name of the user
+ * \param usernum the citadel-uid of the user
+ */
+void display_edit_address_book_entry(char *username, long usernum) {
+       char roomname[SIZ];
+       char buf[SIZ];
+       char error_message[SIZ];
+       long vcard_msgnum = (-1L);
+
+       /** Locate the user's config room, creating it if necessary */
+       sprintf(roomname, "%010ld.%s", usernum, USERCONFIGROOM);
+       serv_printf("GOTO %s||1", roomname);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               serv_printf("CRE8 1|%s|5|||1|", roomname);
+               serv_getln(buf, sizeof buf);
+               serv_printf("GOTO %s||1", roomname);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '2') {
+                       sprintf(error_message,
+                               "<img src=\"static/error.gif\" align=center>"
+                               "%s<br /><br />\n", &buf[4]);
+                       select_user_to_edit(error_message, username);
+                       return;
+               }
+       }
+
+       vcard_msgnum = locate_user_vcard(username, usernum);
+
+       if (vcard_msgnum < 0) {
+               sprintf(error_message,
+                       "<img src=\"static/error.gif\" align=center>%s<br /><br />\n",
+                       _("An error occurred while trying to create or edit this address book entry.")
+               );
+               select_user_to_edit(error_message, username);
+               return;
+       }
+
+       do_edit_vcard(vcard_msgnum, "1", "select_user_to_edit");
+}
+
+
+
+
+/**
+ * \brief Edit a user.  
+ * If supplied_username is null, look in the "username"
+ * web variable for the name of the user to edit.
+ * 
+ * If "is_new" is set to nonzero, this screen will set the web variables
+ * to send the user to the vCard editor next.
+ * \param supplied_username user to look up or NULL if to search in the environment
+ * \param is_new should we create the user?
+ */
+void display_edituser(char *supplied_username, int is_new) {
+       char buf[1024];
+       char error_message[1024];
+       time_t now;
+
+       char username[256];
+       char password[256];
+       unsigned int flags;
+       int timescalled;
+       int msgsposted;
+       int axlevel;
+       long usernum;
+       time_t lastcall;
+       int purgedays;
+       int i;
+
+       if (supplied_username != NULL) {
+               safestrncpy(username, supplied_username, sizeof username);
+       }
+       else {
+               safestrncpy(username, bstr("username"), sizeof username);
+       }
+
+       serv_printf("AGUP %s", username);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               sprintf(error_message,
+                       "<img src=\"static/error.gif\" align=center>"
+                       "%s<br /><br />\n", &buf[4]);
+               select_user_to_edit(error_message, username);
+               return;
+       }
+
+       extract_token(username, &buf[4], 0, '|', sizeof username);
+       extract_token(password, &buf[4], 1, '|', sizeof password);
+       flags = extract_int(&buf[4], 2);
+       timescalled = extract_int(&buf[4], 3);
+       msgsposted = extract_int(&buf[4], 4);
+       axlevel = extract_int(&buf[4], 5);
+       usernum = extract_long(&buf[4], 6);
+       lastcall = extract_long(&buf[4], 7);
+       purgedays = extract_long(&buf[4], 8);
+
+       if (strlen(bstr("edit_abe_button")) > 0) {
+               display_edit_address_book_entry(username, usernum);
+               return;
+       }
+
+       if (strlen(bstr("delete_button")) > 0) {
+               delete_user(username);
+               return;
+       }
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>");
+       wprintf("<span class=\"titlebar\">");
+       wprintf(_("Edit user account: "));
+       escputs(username);
+       wprintf("</span></td></tr></table>\n");
+       wprintf("</div>\n<div id=\"content\">\n");
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+       wprintf("<form method=\"POST\" action=\"edituser\">\n"
+               "<input type=\"hidden\" name=\"username\" value=\"");
+       escputs(username);
+       wprintf("\">\n");
+       wprintf("<input type=\"hidden\" name=\"is_new\" value=\"%d\">\n"
+               "<input type=\"hidden\" name=\"usernum\" value=\"%ld\">\n",
+               is_new, usernum);
+
+       wprintf("<input type=\"hidden\" name=\"flags\" value=\"%d\">\n", flags);
+
+       wprintf("<center><table>");
+
+       wprintf("<tr><td>");
+       wprintf(_("Password"));
+       wprintf("</td><td>"
+               "<input type=\"password\" name=\"password\" value=\"");
+       escputs(password);
+       wprintf("\" maxlength=\"20\"></td></tr>\n");
+
+       wprintf("<tr><td>");
+       wprintf(_("Permission to send Internet mail"));
+       wprintf("</td><td>");
+       wprintf("<input type=\"checkbox\" name=\"inetmail\" value=\"yes\" ");
+       if (flags & US_INTERNET) {
+               wprintf("checked ");
+       }
+       wprintf("></td></tr>\n");
+
+       wprintf("<tr><td>");
+       wprintf(_("Number of logins"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"timescalled\" value=\"");
+       wprintf("%d", timescalled);
+       wprintf("\" maxlength=\"6\"></td></tr>\n");
+
+       wprintf("<tr><td>");
+       wprintf(_("Messages submitted"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"msgsposted\" value=\"");
+       wprintf("%d", msgsposted);
+       wprintf("\" maxlength=\"6\"></td></tr>\n");
+
+       wprintf("<tr><td>");
+       wprintf(_("Access level"));
+       wprintf("</td><td>"
+               "<select name=\"axlevel\">\n");
+       for (i=0; i<7; ++i) {
+               wprintf("<option ");
+               if (axlevel == i) {
+                       wprintf("selected ");
+               }
+               wprintf("value=\"%d\">%d - %s</option>\n",
+                       i, i, axdefs[i]);
+       }
+       wprintf("</select></td></tr>\n");
+
+       wprintf("<tr><td>");
+       wprintf(_("User ID number"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"usernum\" value=\"");
+       wprintf("%ld", usernum);
+       wprintf("\" maxlength=\"7\"></td></tr>\n");
+
+       now = time(NULL);
+       wprintf("<tr><td>");
+       wprintf(_("Date and time of last login"));
+       wprintf("</td><td>"
+               "<select name=\"lastcall\">\n");
+
+       wprintf("<option selected value=\"%ld\">", lastcall);
+       escputs(asctime(localtime(&lastcall)));
+       wprintf("</option>\n");
+
+       wprintf("<option value=\"%ld\">", now);
+       escputs(asctime(localtime(&now)));
+       wprintf("</option>\n");
+
+       wprintf("</select></td></tr>");
+
+       wprintf("<tr><td>");
+       wprintf(_("Auto-purge after this many days"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"purgedays\" value=\"");
+       wprintf("%d", purgedays);
+       wprintf("\" maxlength=\"5\"></td></tr>\n");
+
+       wprintf("</table>\n");
+
+       wprintf("<input type=\"submit\" name=\"ok_button\" value=\"%s\">\n"
+               "&nbsp;"
+               "<input type=\"submit\" name=\"cancel\" value=\"%s\">\n"
+               "<br /><br /></form>\n", _("Save changes"), _("Cancel"));
+
+       wprintf("</center>\n");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+
+}
+
+
+/**
+ * \brief do the backend operation of the user edit on the server
+ */
+void edituser(void) {
+       char message[SIZ];
+       char buf[SIZ];
+       int is_new = 0;
+       unsigned int flags = 0;
+
+       is_new = atoi(bstr("is_new"));
+
+       if (strlen(bstr("ok_button")) == 0) {
+               safestrncpy(message, _("Changes were not saved."), sizeof message);
+       }
+       else {
+               flags = atoi(bstr("flags"));
+               if (!strcasecmp(bstr("inetmail"), "yes")) {
+                       flags |= US_INTERNET;
+               }
+               else {
+                       flags &= ~US_INTERNET ;
+               }
+
+               serv_printf("ASUP %s|%s|%d|%s|%s|%s|%s|%s|%s|",
+                       bstr("username"),
+                       bstr("password"),
+                       flags,
+                       bstr("timescalled"),
+                       bstr("msgsposted"),
+                       bstr("axlevel"),
+                       bstr("usernum"),
+                       bstr("lastcall"),
+                       bstr("purgedays")
+               );
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '2') {
+                       sprintf(message,
+                               "<img src=\"static/error.gif\" align=center>"
+                               "%s<br /><br />\n", &buf[4]);
+               }
+               else {
+                       safestrncpy(message, "", sizeof message);
+               }
+       }
+
+       /**
+        * If we are in the middle of creating a new user, move on to
+        * the vCard edit screen.
+        */
+       if (is_new) {
+               display_edit_address_book_entry( bstr("username"), atol(bstr("usernum")) );
+       }
+       else {
+               select_user_to_edit(message, bstr("username"));
+       }
+}
+
+/**
+ * \brief burge a user 
+ * \param username the name of the user to remove
+ */
+void delete_user(char *username) {
+       char buf[SIZ];
+       char message[SIZ];
+
+       serv_printf("ASUP %s|0|0|0|0|0|", username);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '2') {
+               sprintf(message,
+                       "<img src=\"static/error.gif\" align=center>"
+                       "%s<br /><br />\n", &buf[4]);
+       }
+       else {
+               safestrncpy(message, "", sizeof message);
+       }
+       select_user_to_edit(message, bstr("username"));
+}
+               
+
+
+/**
+ * \brief create a new user
+ * take the web environment username and create it on the citadel server
+ */
+void create_user(void) {
+       char buf[SIZ];
+       char error_message[SIZ];
+       char username[SIZ];
+
+       safestrncpy(username, bstr("username"), sizeof username);
+
+       serv_printf("CREU %s", username);
+       serv_getln(buf, sizeof buf);
+
+       if (buf[0] == '2') {
+               sprintf(WC->ImportantMessage,
+                       _("A new user has been created."));
+               display_edituser(username, 1);
+       }
+       else {
+               sprintf(error_message,
+                       "<img src=\"static/error.gif\" align=center>"
+                       "%s<br /><br />\n", &buf[4]);
+               select_user_to_edit(error_message, NULL);
+       }
+
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/userlist.c b/webcit/src/userlist.c
new file mode 100644 (file)
index 0000000..cbd6c66
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup AccDisplay Display a list of all accounts on a Citadel system.
+ * \ingroup CitadelConfig
+ */
+
+/*@{*/
+#include "webcit.h"
+
+/** 
+ * \brief structure to keep namelists in
+ */
+struct namelist {
+       struct namelist *next; /**< next item of the linked list */
+       char name[32];         /**< name of the userentry */
+};
+
+/**
+ * \brief display the userlist
+ */
+void userlist(void)
+{
+       char buf[256];
+       char fl[256];
+       char title[256];
+       struct tm tmbuf;
+       time_t lc;
+       struct namelist *bio = NULL;
+       struct namelist *bptr;
+       int has_bio;
+       int bg = 0;
+
+       serv_puts("LBIO");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1')
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       bptr = (struct namelist *) malloc(sizeof(struct namelist));
+                       bptr->next = bio;
+                       strcpy(bptr->name, buf);
+                       bio = bptr;
+               }
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>"
+               "<span class=\"titlebar\">");
+       snprintf(title, sizeof title, _("User list for %s"), serv_info.serv_humannode);
+       escputs(title);
+       wprintf("</span>"
+               "</td></tr></table>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       serv_puts("LIST");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '1') {
+               wprintf("<em>%s</em><br />\n", &buf[4]);
+               goto DONE;
+       }
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+       wprintf("<tr><th>%s</th><th>%s</th><th>%s</th>"
+                       "<th>%s</th><th>%s</th><th>%s</th></tr>",
+                       _("User Name"),
+                       _("Number"),
+                       _("Access Level"),
+                       _("Last Login"),
+                       _("Total Logins"),
+                       _("Total Posts"));
+
+       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+               extract_token(fl, buf, 0, '|', sizeof fl);
+               has_bio = 0;
+               for (bptr = bio; bptr != NULL; bptr = bptr->next) {
+                       if (!strcasecmp(fl, bptr->name))
+                               has_bio = 1;
+               }
+               bg = 1 - bg;
+               wprintf("<tr bgcolor=\"#%s\"><td>",
+                       (bg ? "DDDDDD" : "FFFFFF")
+               );
+               if (has_bio) {
+                       wprintf("<a href=\"showuser&who=");
+                       urlescputs(fl);
+                       wprintf("\">");
+                       escputs(fl);
+                       wprintf("</A>");
+               } else {
+                       escputs(fl);
+               }
+               wprintf("</td><td>%ld</td><td>%d</td><td>",
+                       extract_long(buf, 2),
+                       extract_int(buf, 1));
+               lc = extract_long(buf, 3);
+               localtime_r(&lc, &tmbuf);
+               wprintf("%02d/%02d/%04d ",
+                       (tmbuf.tm_mon + 1),
+                       tmbuf.tm_mday,
+                       (tmbuf.tm_year + 1900));
+
+
+               wprintf("</td><td>%ld</td><td>%5ld</td></tr>\n",
+                       extract_long(buf, 4), extract_long(buf, 5));
+
+       }
+       wprintf("</table></div>\n");
+DONE:  wDumpContent(1);
+}
+
+
+/**
+ * \brief Display (non confidential) information about a particular user
+ */
+void showuser(void)
+{
+       char who[256];
+       char buf[256];
+       int have_pic;
+
+       strcpy(who, bstr("who"));
+
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<table width=100%% border=0 bgcolor=\"#444455\"><tr>"
+               "<td><img src=\"static/usermanag_48x.gif\"></td>"
+               "<td align=left><span class=\"titlebar\">");
+       wprintf(_("User profile"));
+       wprintf("</span>"
+               "</td></tr></table>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       serv_printf("OIMG _userpic_|%s", who);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               have_pic = 1;
+               serv_puts("CLOS");
+               serv_getln(buf, sizeof buf);
+       } else {
+               have_pic = 0;
+       }
+
+       wprintf("<center><table><tr><td>");
+       if (have_pic == 1) {
+               wprintf("<img src=\"image&name=_userpic_&parm=");
+               urlescputs(who);
+               wprintf("\">");
+       }
+       wprintf("</td><td><h1>%s</h1></td></tr></table></center>\n", who);
+       serv_printf("RBIO %s", who);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               fmout("JUSTIFY");
+       }
+       wprintf("<br /><a href=\"display_page?recp=");
+       urlescputs(who);
+       wprintf("\">"
+               "<img src=\"static/citadelchat_24x.gif\" "
+               "align=middle border=0>&nbsp;&nbsp;");
+       snprintf(buf, sizeof buf, _("Click here to send an instant message to %s"), who);
+       escputs(buf);
+       wprintf("</a>\n");
+
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+
+/*@}*/
diff --git a/webcit/src/vcard.c b/webcit/src/vcard.c
new file mode 100644 (file)
index 0000000..d236b00
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * $Id$
+ * Copyright (C) 1999-2006 by Art Cancro
+ * This code is freely redistributable under the terms of the GNU General
+ * Public License.  All other rights reserved.
+ */
+/**
+ * \defgroup VCardMain vCard data type implementation for the Citadel system.
+ * \ingroup VCards
+ */
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+#include "vcard.h"
+
+/** 
+ * \brief Constructor (empty vCard)
+ * \return an empty vcard
+ */
+struct vCard *vcard_new() {
+       struct vCard *v;
+
+       v = (struct vCard *) malloc(sizeof(struct vCard));
+       if (v == NULL) return v;
+
+       v->magic = CTDL_VCARD_MAGIC;
+       v->numprops = 0;
+       v->prop = NULL;
+
+       return v;
+}
+
+/**
+ * \brief      Remove the "charset=" attribute from a vCard property name
+ *
+ * \param      strbuf          The property name string to be stripped
+ */
+void remove_charset_attribute(char *strbuf)
+{
+       int i, t;
+       char compare[256];
+
+       t = num_tokens(strbuf, ';');
+       for (i=0; i<t; ++i) {
+               extract_token(compare, strbuf, i, ';', sizeof compare);
+               striplt(compare);
+               if (!strncasecmp(compare, "charset=", 8)) {
+                       remove_token(strbuf, i, ';');
+               }
+       }
+       if (strlen(strbuf) > 0) {
+               if (strbuf[strlen(strbuf)-1] == ';') {
+                       strbuf[strlen(strbuf)-1] = 0;
+               }
+       }
+}
+
+
+/*
+ * \brief      Add a property to a vCard
+ *
+ * \param      v               vCard structure to which we are adding
+ * \param      propname        name of new property
+ * \param      propvalue       value of new property
+ */
+void vcard_add_prop(struct vCard *v, char *propname, char *propvalue) {
+       ++v->numprops;
+       v->prop = realloc(v->prop,
+               (v->numprops * sizeof(struct vCardProp)) );
+       v->prop[v->numprops-1].name = strdup(propname);
+       v->prop[v->numprops-1].value = strdup(propvalue);
+}
+
+
+
+/**
+ * \brief Constructor (supply serialized vCard)
+ * \param vtext the text to parse into the new vcard
+ * \return the parsed VCard
+ */
+struct vCard *vcard_load(char *vtext) {
+       struct vCard *v;
+       int valid = 0;
+       char *mycopy, *ptr;
+       char *namebuf, *valuebuf;
+       int i;
+       int colonpos, nlpos;
+
+       if (vtext == NULL) return vcard_new();
+       mycopy = strdup(vtext);
+       if (mycopy == NULL) return NULL;
+
+       /**
+        * First, fix this big pile o' vCard to make it more parseable.
+        * To make it easier to parse, we convert CRLF to LF, and unfold any
+        * multi-line fields into single lines.
+        */
+       for (i=0; i<strlen(mycopy); ++i) {
+               if (!strncmp(&mycopy[i], "\r\n", 2)) {
+                       strcpy(&mycopy[i], &mycopy[i+1]);
+               }
+               if ( (mycopy[i]=='\n') && (isspace(mycopy[i+1])) ) {
+                       strcpy(&mycopy[i], &mycopy[i+1]);
+               }
+       }
+
+       v = vcard_new();
+       if (v == NULL) return v;
+
+       ptr = mycopy;
+       while (strlen(ptr)>0) {
+               colonpos = (-1);
+               nlpos = (-1);
+               colonpos = pattern2(ptr, ":");
+               nlpos = pattern2(ptr, "\n");
+
+               if ((nlpos > colonpos) && (colonpos > 0)) {
+                       namebuf = malloc(colonpos + 1);
+                       valuebuf = malloc(nlpos - colonpos + 1);
+                       strncpy(namebuf, ptr, colonpos);
+                       namebuf[colonpos] = 0;
+                       strncpy(valuebuf, &ptr[colonpos+1], nlpos-colonpos-1);
+                       valuebuf[nlpos-colonpos-1] = 0;
+
+                       if (!strcasecmp(namebuf, "end")) {
+                               valid = 0;
+                       }
+                       if (    (!strcasecmp(namebuf, "begin"))
+                               && (!strcasecmp(valuebuf, "vcard"))
+                       ) {
+                               valid = 1;
+                       }
+
+                       if ( (valid) && (strcasecmp(namebuf, "begin")) ) {
+                               remove_charset_attribute(namebuf);
+                               ++v->numprops;
+                               v->prop = realloc(v->prop,
+                                       (v->numprops * sizeof(struct vCardProp))
+                               );
+                               v->prop[v->numprops-1].name = namebuf;
+                               v->prop[v->numprops-1].value = valuebuf;
+                       } 
+                       else {
+                               free(namebuf);
+                               free(valuebuf);
+                       }
+
+               }
+
+               while ( (*ptr != '\n') && (strlen(ptr)>0) ) {
+                       ++ptr;
+               }
+               if (*ptr == '\n') ++ptr;
+       }
+
+       free(mycopy);
+       return v;
+}
+
+
+/**
+ * \brief Fetch the value of a particular key.
+ * If is_partial is set to 1, a partial match is ok (for example,
+ * a key of "tel;home" will satisfy a search for "tel").
+ * Set "instance" to a value higher than 0 to return subsequent instances
+ * of the same key.
+ * Set "get_propname" to nonzero to fetch the property name instead of value.
+ * \param v vCard to get keyvalue from
+ * \param propname key to retrieve
+ * \param is_partial dunno???
+ * \param instance if >0 return a later token of the value
+ * \param get_propname if nonzero get the real property name???
+ * \return the requested value / token / propertyname
+ */
+char *vcard_get_prop(struct vCard *v, char *propname,
+                       int is_partial, int instance, int get_propname) {
+       int i;
+       int found_instance = 0;
+
+       if (v->numprops) for (i=0; i<(v->numprops); ++i) {
+               if ( (!strcasecmp(v->prop[i].name, propname))
+                  || (propname[0] == 0)
+                  || (  (!strncasecmp(v->prop[i].name,
+                                       propname, strlen(propname)))
+                        && (v->prop[i].name[strlen(propname)] == ';')
+                        && (is_partial) ) ) {
+                       if (instance == found_instance++) {
+                               if (get_propname) {
+                                       return(v->prop[i].name);
+                               }
+                               else {
+                                       return(v->prop[i].value);
+                               }
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+
+
+
+/**
+ * \brief Destructor
+ * kill a vCard
+ * \param v the vCard to purge from memory
+ */
+void vcard_free(struct vCard *v) {
+       int i;
+       
+       if (v->magic != CTDL_VCARD_MAGIC) return;       /* Self-check */
+       
+       if (v->numprops) for (i=0; i<(v->numprops); ++i) {
+               free(v->prop[i].name);
+               free(v->prop[i].value);
+       }
+
+       if (v->prop != NULL) free(v->prop);
+       
+       memset(v, 0, sizeof(struct vCard));
+       free(v);
+}
+
+
+
+
+/**
+ * \brief Set a name/value pair in the card
+ * \param v vCard to inspect
+ * \param name key to set
+ * \param value the value to assign to key
+ * \param append should we append the value to an existing one?
+ */
+void vcard_set_prop(struct vCard *v, char *name, char *value, int append) {
+       int i;
+
+       if (v->magic != CTDL_VCARD_MAGIC) return;       /** Self-check */
+
+       /** If this key is already present, replace it */
+       if (!append) if (v->numprops) for (i=0; i<(v->numprops); ++i) {
+               if (!strcasecmp(v->prop[i].name, name)) {
+                       free(v->prop[i].name);
+                       free(v->prop[i].value);
+                       v->prop[i].name = strdup(name);
+                       v->prop[i].value = strdup(value);
+                       return;
+               }
+       }
+
+       /** Otherwise, append it */
+       ++v->numprops;
+       v->prop = realloc(v->prop,
+               (v->numprops * sizeof(struct vCardProp)) );
+       v->prop[v->numprops-1].name = strdup(name);
+       v->prop[v->numprops-1].value = strdup(value);
+}
+
+
+
+
+/**
+ * \brief Serialize a struct vcard into a standard text/x-vcard MIME type.
+ * \param v vCard to serialize
+ * \return the serialized vCard
+ */
+char *vcard_serialize(struct vCard *v)
+{
+       char *ser;
+       int i, j;
+       size_t len;
+       int is_utf8 = 0;
+
+       if (v->magic != CTDL_VCARD_MAGIC) return NULL;  /** self check */
+
+       /** Figure out how big a buffer we need to allocate */
+       len = 64;       /** for begin, end, and a little padding for safety */
+       if (v->numprops) for (i=0; i<(v->numprops); ++i) {
+               len = len +
+                       strlen(v->prop[i].name) +
+                       strlen(v->prop[i].value) + 16;
+       }
+
+       ser = malloc(len);
+       if (ser == NULL) return NULL;
+
+       safestrncpy(ser, "begin:vcard\r\n", len);
+       if (v->numprops) for (i=0; i<(v->numprops); ++i) {
+               if (strcasecmp(v->prop[i].name, "end")) {
+                       is_utf8 = 0;
+                       for (j=0; i<strlen(v->prop[i].value); ++i) {
+                               if ( (v->prop[i].value[j] < 32) || (v->prop[i].value[j] > 126) ) {
+                                       is_utf8 = 1;
+                               }
+                       }
+                       strcat(ser, v->prop[i].name);
+                       if (is_utf8) {
+                               strcat(ser, ";charset=UTF-8");
+                       }
+                       strcat(ser, ":");
+                       strcat(ser, v->prop[i].value);
+                       strcat(ser, "\r\n");
+               }
+       }
+       strcat(ser, "end:vcard\r\n");
+
+       return ser;
+}
+
+
+
+/*
+ * \brief      Convert FN (Friendly Name) into N (Name)
+ *
+ * \param      vname           Supplied friendly-name
+ * \param      n               Target buffer to store Name
+ * \param      vname_size      Size of buffer
+ */
+void vcard_fn_to_n(char *vname, char *n, size_t vname_size) {
+       char lastname[256];
+       char firstname[256];
+       char middlename[256];
+       char honorific_prefixes[256];
+       char honorific_suffixes[256];
+       char buf[256];
+
+       safestrncpy(buf, n, sizeof buf);
+
+       /* Try to intelligently convert the screen name to a
+        * fully expanded vCard name based on the number of
+        * words in the name
+        */
+       safestrncpy(lastname, "", sizeof lastname);
+       safestrncpy(firstname, "", sizeof firstname);
+       safestrncpy(middlename, "", sizeof middlename);
+       safestrncpy(honorific_prefixes, "", sizeof honorific_prefixes);
+       safestrncpy(honorific_suffixes, "", sizeof honorific_suffixes);
+
+       /* Honorific suffixes */
+       if (num_tokens(buf, ',') > 1) {
+               extract_token(honorific_suffixes, buf, (num_tokens(buf, ' ') - 1), ',',
+                       sizeof honorific_suffixes);
+               remove_token(buf, (num_tokens(buf, ',') - 1), ',');
+       }
+
+       /* Find a last name */
+       extract_token(lastname, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof lastname);
+       remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
+
+       /* Find honorific prefixes */
+       if (num_tokens(buf, ' ') > 2) {
+               extract_token(honorific_prefixes, buf, 0, ' ', sizeof honorific_prefixes);
+               remove_token(buf, 0, ' ');
+       }
+
+       /* Find a middle name */
+       if (num_tokens(buf, ' ') > 1) {
+               extract_token(middlename, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof middlename);
+               remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
+       }
+
+       /* Anything left is probably the first name */
+       safestrncpy(firstname, buf, sizeof firstname);
+       striplt(firstname);
+
+       /* Compose the structured name */
+       snprintf(vname, vname_size, "%s;%s;%s;%s;%s", lastname, firstname, middlename,
+               honorific_prefixes, honorific_suffixes);
+}
+
+
+
+
+
+
+/*@}*/
diff --git a/webcit/src/vcard.h b/webcit/src/vcard.h
new file mode 100644 (file)
index 0000000..ac30d42
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * $Id$
+ * Copyright (C) 1999 by Art Cancro
+ * This code is freely redistributable under the terms of the GNU General
+ * Public License.  All other rights reserved.
+ */
+/**
+ * \defgroup VcardHeader vCard implementation for Citadel
+ * \ingroup VCards
+ *
+ */
+
+/*@{ */
+#define CTDL_VCARD_MAGIC       0xa1f9 /**< magic byte vcards start with??? */
+
+/**
+ * \brief This data structure represents a vCard object currently in memory.
+ */
+struct vCard {
+       int magic;          /**< the Magic Byte */
+       int numprops;       /**< number of properties this vcard will have */
+       struct vCardProp {  
+               char *name;         /**< Keyname of the property */
+               char *value;        /**< value of the property */
+       } *prop;            /**< Vcard Property. Linked list??? */
+};
+
+
+struct vCard *vcard_new(void);
+struct vCard *vcard_load(char *);
+void vcard_free(struct vCard *);
+void vcard_set_prop(struct vCard *v, char *name, char *value, int append);
+char *vcard_get_prop(struct vCard *v, char *propname, int is_partial,
+                       int instance, int return_propname);
+char *vcard_serialize(struct vCard *);
+
+
+/*@}*/
diff --git a/webcit/src/vcard_edit.c b/webcit/src/vcard_edit.c
new file mode 100644 (file)
index 0000000..2c78477
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup vCardEdit Handles on-screen editing of vCard objects.
+ * \ingroup VCards
+ */
+/*@{*/
+#include "webcit.h"
+#include "vcard.h"
+
+/**
+ * \brief Edit the vCard component of a MIME message.  
+ * Supply the message number
+ * and MIME part number to fetch.  Or, specify -1 for the message number
+ * to start with a blank card.
+ * \param msgnum number of the item on the citadel server
+ * \param partnum what???
+ * \param return_to where to go back in the browser after edit ????
+ */
+void do_edit_vcard(long msgnum, char *partnum, char *return_to) {
+       char buf[SIZ];
+       char *serialized_vcard = NULL;
+       size_t total_len = 0;
+       struct vCard *v;
+       int i;
+       char *key, *value;
+       char whatuser[256];
+
+       char lastname[256];
+       char firstname[256];
+       char middlename[256];
+       char prefix[256];
+       char suffix[256];
+       char pobox[256];
+       char extadr[256];
+       char street[256];
+       char city[256];
+       char state[256];
+       char zipcode[256];
+       char country[256];
+       char hometel[256];
+       char worktel[256];
+       char primary_inetemail[256];
+       char other_inetemail[SIZ];
+       char extrafields[SIZ];
+       char fullname[256];
+       char title[256];
+       char org[256];
+
+       lastname[0] = 0;
+       firstname[0] = 0;
+       middlename[0] = 0;
+       prefix[0] = 0;
+       suffix[0] = 0;
+       pobox[0] = 0;
+       extadr[0] = 0;
+       street[0] = 0;
+       city[0] = 0;
+       state[0] = 0;
+       zipcode[0] = 0;
+       country[0] = 0;
+       hometel[0] = 0;
+       worktel[0] = 0;
+       primary_inetemail[0] = 0;
+       other_inetemail[0] = 0;
+       title[0] = 0;
+       org[0] = 0;
+       extrafields[0] = 0;
+
+       safestrncpy(whatuser, "", sizeof whatuser);
+
+       if (msgnum >= 0) {
+               sprintf(buf, "MSG0 %ld|1", msgnum);
+               serv_puts(buf);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '1') {
+                       convenience_page("770000", _("Error"), &buf[4]);
+                       return;
+               }
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       if (!strncasecmp(buf, "from=", 5)) {
+                               safestrncpy(whatuser, &buf[5], sizeof whatuser);
+                       }
+                       else if (!strncasecmp(buf, "node=", 5)) {
+                               strcat(whatuser, " @ ");
+                               strcat(whatuser, &buf[5]);
+                       }
+               }
+       
+               sprintf(buf, "OPNA %ld|%s", msgnum, partnum);
+               serv_puts(buf);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] != '2') {
+                       convenience_page("770000", "Error", &buf[4]);
+                       return;
+               }
+       
+               total_len = atoi(&buf[4]);
+               serialized_vcard = malloc(total_len + 2);
+       
+               read_server_binary(serialized_vcard, total_len);
+       
+               serv_puts("CLOS");
+               serv_getln(buf, sizeof buf);
+               serialized_vcard[total_len] = 0;
+       
+               v = vcard_load(serialized_vcard);
+               free(serialized_vcard);
+       
+               /* Populate the variables for our form */
+               i = 0;
+               while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) {
+                       value = vcard_get_prop(v, "", 0, i++, 0);
+       
+                       if (!strcasecmp(key, "n")) {
+                               extract_token(lastname, value, 0, ';', sizeof lastname);
+                               extract_token(firstname, value, 1, ';', sizeof firstname);
+                               extract_token(middlename, value, 2, ';', sizeof middlename);
+                               extract_token(prefix, value, 3, ';', sizeof prefix);
+                               extract_token(suffix, value, 4, ';', sizeof suffix);
+                       }
+
+                       else if (!strcasecmp(key, "fn")) {
+                               safestrncpy(fullname, value, sizeof fullname);
+                       }
+
+                       else if (!strcasecmp(key, "title")) {
+                               safestrncpy(title, value, sizeof title);
+                       }
+       
+                       else if (!strcasecmp(key, "org")) {
+                               safestrncpy(org, value, sizeof org);
+                       }
+       
+                       else if (!strcasecmp(key, "adr")) {
+                               extract_token(pobox, value, 0, ';', sizeof pobox);
+                               extract_token(extadr, value, 1, ';', sizeof extadr);
+                               extract_token(street, value, 2, ';', sizeof street);
+                               extract_token(city, value, 3, ';', sizeof city);
+                               extract_token(state, value, 4, ';', sizeof state);
+                               extract_token(zipcode, value, 5, ';', sizeof zipcode);
+                               extract_token(country, value, 6, ';', sizeof country);
+                       }
+       
+                       else if (!strcasecmp(key, "tel;home")) {
+                               extract_token(hometel, value, 0, ';', sizeof hometel);
+                       }
+       
+                       else if (!strcasecmp(key, "tel;work")) {
+                               extract_token(worktel, value, 0, ';', sizeof worktel);
+                       }
+       
+                       else if (!strcasecmp(key, "email;internet")) {
+                               if (primary_inetemail[0] == 0) {
+                                       safestrncpy(primary_inetemail, value, sizeof primary_inetemail);
+                               }
+                               else {
+                                       if (other_inetemail[0] != 0) {
+                                               strcat(other_inetemail, "\n");
+                                       }
+                                       strcat(other_inetemail, value);
+                               }
+                       }
+       
+                       else {
+                               strcat(extrafields, key);
+                               strcat(extrafields, ":");
+                               strcat(extrafields, value);
+                               strcat(extrafields, "\n");
+                       }
+       
+               }
+       
+               vcard_free(v);
+       }
+
+       /** Display the form */
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n"
+               "<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>"
+               "<span class=\"titlebar\">"
+               "<img src=\"static/savecontact_48x.gif\">");
+       wprintf(_("Edit contact information"));
+       wprintf("</span>"
+               "</td></tr></table>\n"
+               "</div>\n<div id=\"content\">\n"
+       );
+
+       wprintf("<form method=\"POST\" action=\"submit_vcard\">\n");
+       wprintf("<div class=\"fix_scrollbar_bug\">"
+               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
+
+       wprintf("<table border=0><tr>"
+               "<td>%s</td>"
+               "<td>%s</td>"
+               "<td>%s</td>"
+               "<td>%s</td>"
+               "<td>%s</td></tr>\n",
+               _("Prefix"), _("First"), _("Middle"), _("Last"), _("Suffix")
+       );
+       wprintf("<tr><td><input type=\"text\" name=\"prefix\" "
+               "value=\"%s\" maxlength=\"5\" size=\"5\"></td>",
+               prefix);
+       wprintf("<td><input type=\"text\" name=\"firstname\" "
+               "value=\"%s\" maxlength=\"29\"></td>",
+               firstname);
+       wprintf("<td><input type=\"text\" name=\"middlename\" "
+               "value=\"%s\" maxlength=\"29\"></td>",
+               middlename);
+       wprintf("<td><input type=\"text\" name=\"lastname\" "
+               "value=\"%s\" maxlength=\"29\"></td>",
+               lastname);
+       wprintf("<td><input type=\"text\" name=\"suffix\" "
+               "value=\"%s\" maxlength=\"10\" size=\"10\"></td></tr></table>\n",
+               suffix);
+
+       wprintf("<table border=0 width=100%% bgcolor=\"#dddddd\">");
+       wprintf("<tr><td>");
+
+       wprintf(_("Display name:"));
+       wprintf("<br>"
+               "<input type=\"text\" name=\"fullname\" "
+               "value=\"%s\" maxlength=\"40\"><br><br>\n",
+               fullname
+       );
+
+       wprintf(_("Title:"));
+       wprintf("<br>"
+               "<input type=\"text\" name=\"title\" "
+               "value=\"%s\" maxlength=\"40\"><br><br>\n",
+               title
+       );
+
+       wprintf(_("Organization:"));
+       wprintf("<br>"
+               "<input type=\"text\" name=\"org\" "
+               "value=\"%s\" maxlength=\"40\"><br><br>\n",
+               org
+       );
+
+       wprintf("</td><td>");
+
+       wprintf("<table border=0>");
+       wprintf("<tr><td>");
+       wprintf(_("PO box:"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"pobox\" "
+               "value=\"%s\" maxlength=\"29\"></td></tr>\n",
+               pobox);
+       wprintf("<tr><td>");
+       wprintf(_("Address:"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"extadr\" "
+               "value=\"%s\" maxlength=\"29\"></td></tr>\n",
+               extadr);
+       wprintf("<tr><td> </td><td>"
+               "<input type=\"text\" name=\"street\" "
+               "value=\"%s\" maxlength=\"29\"></td></tr>\n",
+               street);
+       wprintf("<tr><td>");
+       wprintf(_("City:"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"city\" "
+               "value=\"%s\" maxlength=\"29\"></td></tr>\n",
+               city);
+       wprintf("<tr><td>");
+       wprintf(_("State:"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"state\" "
+               "value=\"%s\" maxlength=\"2\"></td></tr>\n",
+               state);
+       wprintf("<tr><td>");
+       wprintf(_("ZIP code:"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"zipcode\" "
+               "value=\"%s\" maxlength=\"10\"></td></tr>\n",
+               zipcode);
+       wprintf("<tr><td>");
+       wprintf(_("Country:"));
+       wprintf("</td><td>"
+               "<input type=\"text\" name=\"country\" "
+               "value=\"%s\" maxlength=\"29\" width=\"5\"></td></tr>\n",
+               country);
+       wprintf("</table>\n");
+
+       wprintf("</table>\n");
+
+       wprintf("<table border=0><tr><td>");
+       wprintf(_("Home telephone:"));
+       wprintf("</td>"
+               "<td><input type=\"text\" name=\"hometel\" "
+               "value=\"%s\" maxlength=\"29\"></td>\n",
+               hometel);
+       wprintf("<td>");
+       wprintf(_("Work telephone:"));
+       wprintf("</td>"
+               "<td><input type=\"text\" name=\"worktel\" "
+               "value=\"%s\" maxlength=\"29\"></td></tr></table>\n",
+               worktel);
+
+       wprintf("<table border=0 width=100%% bgcolor=\"#dddddd\">");
+       wprintf("<tr><td>");
+
+       wprintf("<table border=0><TR>"
+               "<td valign=top>");
+       wprintf(_("Primary Internet e-mail address"));
+       wprintf("<br />"
+               "<input type=\"text\" name=\"primary_inetemail\" "
+               "size=40 maxlength=40 value=\"");
+       escputs(primary_inetemail);
+       wprintf("\"><br />"
+               "</td><td valign=top>");
+       wprintf(_("Internet e-mail aliases"));
+       wprintf("<br />"
+               "<textarea name=\"other_inetemail\" rows=5 cols=40 width=40>");
+       escputs(other_inetemail);
+       wprintf("</textarea></td></tr></table>\n");
+
+       wprintf("</td></tr></table>\n");
+
+       wprintf("<input type=\"hidden\" name=\"extrafields\" value=\"");
+       escputs(extrafields);
+       wprintf("\">\n");
+
+       wprintf("<input type=\"hidden\" name=\"return_to\" value=\"");
+       urlescputs(return_to);
+       wprintf("\">\n");
+
+       wprintf("<center>\n"
+               "<input type=\"submit\" name=\"ok_button\" value=\"%s\">"
+               "&nbsp;"
+               "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">"
+               "</center></form>\n",
+               _("Save changes"),
+               _("Cancel")
+       );
+       
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+
+/**
+ * \brief commit the edits to the citadel server
+ */
+void edit_vcard(void) {
+       long msgnum;
+       char *partnum;
+
+       msgnum = atol(bstr("msgnum"));
+       partnum = bstr("partnum");
+       do_edit_vcard(msgnum, partnum, "");
+}
+
+
+
+/**
+ * \brief parse edited vcard from the browser
+ */
+void submit_vcard(void) {
+       char buf[SIZ];
+       int i;
+
+       if (strlen(bstr("ok_button")) == 0) { 
+               readloop("readnew");
+               return;
+       }
+
+       sprintf(buf, "ENT0 1|||4||");
+       serv_puts(buf);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] != '4') {
+               edit_vcard();
+               return;
+       }
+
+       serv_puts("Content-type: text/x-vcard; charset=UTF-8");
+       serv_puts("");
+       serv_puts("begin:vcard");
+       serv_printf("n:%s;%s;%s;%s;%s",
+               bstr("lastname"),
+               bstr("firstname"),
+               bstr("middlename"),
+               bstr("prefix"),
+               bstr("suffix") );
+       serv_printf("title:%s", bstr("title") );
+       serv_printf("fn:%s", bstr("fullname") );
+       serv_printf("org:%s", bstr("org") );
+       serv_printf("adr:%s;%s;%s;%s;%s;%s;%s",
+               bstr("pobox"),
+               bstr("extadr"),
+               bstr("street"),
+               bstr("city"),
+               bstr("state"),
+               bstr("zipcode"),
+               bstr("country") );
+       serv_printf("tel;home:%s", bstr("hometel") );
+       serv_printf("tel;work:%s", bstr("worktel") );
+
+       serv_printf("email;internet:%s\n", bstr("primary_inetemail"));  
+       for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
+               extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
+               if (strlen(buf) > 0) {
+                       serv_printf("email;internet:%s", buf);
+               }
+       }
+
+       serv_printf("%s", bstr("extrafields") );
+       serv_puts("end:vcard");
+       serv_puts("000");
+
+       if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
+               select_user_to_edit(NULL, NULL);
+       }
+       else if (!strcmp(bstr("return_to"), "do_welcome")) {
+               do_welcome();
+       }
+       else {
+               readloop("readnew");
+       }
+}
+
+
+
+/*@}*/
diff --git a/webcit/src/webcit.c b/webcit/src/webcit.c
new file mode 100644 (file)
index 0000000..85ee1ca
--- /dev/null
@@ -0,0 +1,1676 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup MainServer This is the main transaction loop of the web service.  It maintains a
+ * persistent session to the Citadel server, handling HTTP WebCit requests as
+ * they arrive and presenting a user interface.
+ * \ingroup WebcitHttpServer
+ */
+/*@{*/
+#include "webcit.h"
+#include "groupdav.h"
+#include "webserver.h"
+#include "mime_parser.h"
+
+/**
+ * Subdirectories from which the client may request static content
+ */
+char *static_content_dirs[] = {
+       "static",                     /** static templates */
+       "tiny_mce"                    /** the JS editor */
+};
+
+/**
+ * String to unset the cookie.
+ * Any date "in the past" will work, so I chose my birthday, right down to
+ * the exact minute.  :)
+ */
+static char *unset = "; expires=28-May-1971 18:10:00 GMT";
+
+/**
+ * \brief remove escaped strings from i.e. the url string (like %20 for blanks)
+ * \param buf the buffer to examine
+ */
+void unescape_input(char *buf)
+{
+       int a, b;
+       char hex[3];
+
+       while ((isspace(buf[strlen(buf) - 1])) && (strlen(buf) > 0))
+               buf[strlen(buf) - 1] = 0;
+
+       for (a = 0; a < strlen(buf); ++a) {
+               if (buf[a] == '+')
+                       buf[a] = ' ';
+               if (buf[a] == '%') {
+                       hex[0] = buf[a + 1];
+                       hex[1] = buf[a + 2];
+                       hex[2] = 0;
+                       b = 0;
+                       sscanf(hex, "%02x", &b);
+                       buf[a] = (char) b;
+                       strcpy(&buf[a + 1], &buf[a + 3]);
+               }
+       }
+
+}
+
+/**
+ * \brief Extract variables from the URL.
+ * \param url URL supplied by the HTTP parser
+ */
+void addurls(char *url)
+{
+       char *up, *ptr;
+       char buf[SIZ];
+       int a, b;
+       struct urlcontent *u;
+
+       up = url;
+       while (strlen(up) > 0) {
+
+               /** locate the = sign */
+               safestrncpy(buf, up, sizeof buf);
+               b = (-1);
+               for (a = 255; a >= 0; --a)
+                       if (buf[a] == '=')
+                               b = a;
+               if (b < 0)
+                       return;
+               buf[b] = 0;
+
+               u = (struct urlcontent *) malloc(sizeof(struct urlcontent));
+               u->next = WC->urlstrings;
+               WC->urlstrings = u;
+               safestrncpy(u->url_key, buf, sizeof u->url_key);
+
+               /** now chop that part off */
+               for (a = 0; a <= b; ++a)
+                       ++up;
+
+               /** locate "&" and "?" delimiters */
+               ptr = up;
+               b = strlen(up);
+               for (a = 0; a < strlen(up); ++a) {
+                       if ( (ptr[0] == '&') || (ptr[0] == '?') ) {
+                               b = a;
+                               break;
+                       }
+                       ++ptr;
+               }
+               ptr = up;
+               for (a = 0; a < b; ++a)
+                       ++ptr;
+               strcpy(ptr, "");
+
+               u->url_data = malloc(strlen(up) + 2);
+               safestrncpy(u->url_data, up, strlen(up) + 1);
+               u->url_data[b] = 0;
+               unescape_input(u->url_data);
+               up = ptr;
+               ++up;
+       }
+}
+
+/**
+ * \brief free urlstring memory
+ */
+void free_urls(void)
+{
+       struct urlcontent *u;
+
+       while (WC->urlstrings != NULL) {
+               free(WC->urlstrings->url_data);
+               u = WC->urlstrings->next;
+               free(WC->urlstrings);
+               WC->urlstrings = u;
+       }
+}
+
+/**
+ * \brief Diagnostic function to display the contents of all variables
+ */
+void dump_vars(void)
+{
+       struct urlcontent *u;
+
+       for (u = WC->urlstrings; u != NULL; u = u->next) {
+               wprintf("%38s = %s\n", u->url_key, u->url_data);
+       }
+}
+
+/**
+ * \brief Return the value of a variable supplied to the current web page (from the url or a form)
+ * \param key The name of the variable we want
+ */
+char *bstr(char *key)
+{
+       struct urlcontent *u;
+
+       for (u = WC->urlstrings; u != NULL; u = u->next) {
+               if (!strcasecmp(u->url_key, key))
+                       return (u->url_data);
+       }
+       return ("");
+}
+
+/**
+ * \brief web-printing funcion. uses our vsnprintf wrapper
+ * \param format printf format string 
+ * \param ... the varargs to put into formatstring
+ */
+void wprintf(const char *format,...)
+{
+       va_list arg_ptr;
+       char wbuf[4096];
+
+       va_start(arg_ptr, format);
+       vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
+       va_end(arg_ptr);
+
+       client_write(wbuf, strlen(wbuf));
+}
+
+
+/**
+ * \brief wrap up an HTTP session, closes tags, etc.
+ * \todo multiline params?
+ * \param print_standard_html_footer should be set to 0 to transmit only, 1 to
+ * append the main menu and closing tags, or 2 to
+ * append the closing tags only.
+ */
+void wDumpContent(int print_standard_html_footer)
+{
+       if (print_standard_html_footer) {
+               wprintf("</div>\n");    /* end of "text" div */
+               do_template("trailing");
+       }
+
+       /* If we've been saving it all up for one big output burst,
+        * go ahead and do that now.
+        */
+       end_burst();
+}
+
+
+/**
+ * \brief Copy a string, escaping characters which have meaning in HTML.  
+ * \param target target buffer
+ * \param strbuf source buffer
+ * \param nbsp If nonzero, spaces are converted to non-breaking spaces.
+ * \param nolinebreaks if set, linebreaks are removed from the string.
+ */
+void stresc(char *target, char *strbuf, int nbsp, int nolinebreaks)
+{
+       int a;
+       strcpy(target, "");
+
+       for (a = 0; a < strlen(strbuf); ++a) {
+               if (strbuf[a] == '<')
+                       strcat(target, "&lt;");
+               else if (strbuf[a] == '>')
+                       strcat(target, "&gt;");
+               else if (strbuf[a] == '&')
+                       strcat(target, "&amp;");
+               else if (strbuf[a] == '\"')
+                       strcat(target, "&quot;");
+               else if (strbuf[a] == '\'') 
+                       strcat(target, "&#39;");
+               else if (strbuf[a] == LB)
+                       strcat(target, "<");
+               else if (strbuf[a] == RB)
+                       strcat(target, ">");
+               else if (strbuf[a] == QU)
+                       strcat(target, "\"");
+               else if ((strbuf[a] == 32) && (nbsp == 1))
+                       strcat(target, "&nbsp;");
+               else if ((strbuf[a] == '\n') && (nolinebreaks))
+                       strcat(target, "");     /* nothing */
+               else if ((strbuf[a] == '\r') && (nolinebreaks))
+                       strcat(target, "");     /* nothing */
+               else
+                       strncat(target, &strbuf[a], 1);
+       }
+}
+
+/**
+ * \brief WHAT???
+ * \param strbuf what???
+ * \param nbsp If nonzero, spaces are converted to non-breaking spaces.
+ * \param nolinebreaks if set, linebreaks are removed from the string.
+ */ 
+void escputs1(char *strbuf, int nbsp, int nolinebreaks)
+{
+       char *buf;
+
+       if (strbuf == NULL) return;
+       buf = malloc( (3 * strlen(strbuf)) + SIZ );
+       stresc(buf, strbuf, nbsp, nolinebreaks);
+       wprintf("%s", buf);
+       free(buf);
+}
+
+/** 
+ * \brief static wrapper for ecsputs1
+ * \param strbuf buffer to print escaped to client
+ */
+void escputs(char *strbuf)
+{
+       escputs1(strbuf, 0, 0);
+}
+
+/** 
+ * \brief Escape a string for feeding out as a URL.
+ * Returns a pointer to a buffer that must be freed by the caller!
+ * \param outbuf the output buffer
+ * \param strbuf the input buffer
+ */
+void urlesc(char *outbuf, char *strbuf)
+{
+       int a, b, c;
+       char *ec = " #&;`'|*?-~<>^()[]{}$\"\\";
+
+       strcpy(outbuf, "");
+
+       for (a = 0; a < strlen(strbuf); ++a) {
+               c = 0;
+               for (b = 0; b < strlen(ec); ++b) {
+                       if (strbuf[a] == ec[b])
+                               c = 1;
+               }
+               b = strlen(outbuf);
+               if (c == 1)
+                       sprintf(&outbuf[b], "%%%02x", strbuf[a]);
+               else
+                       sprintf(&outbuf[b], "%c", strbuf[a]);
+       }
+}
+
+/**
+ * \brief urlescape buffer and print it to the client
+ * \param strbuf buffer to urlescape
+ */
+void urlescputs(char *strbuf)
+{
+       char outbuf[SIZ];
+       
+       urlesc(outbuf, strbuf);
+       wprintf("%s", outbuf);
+}
+
+
+/**
+ * \brief Copy a string, escaping characters for JavaScript strings.
+ * \param target output string
+ * \param strbuf input string
+ */
+void jsesc(char *target, char *strbuf)
+{
+       int a;
+       strcpy(target, "");
+
+       for (a = 0; a < strlen(strbuf); ++a) {
+               if (strbuf[a] == '<')
+                       strcat(target, "[");
+               else if (strbuf[a] == '>')
+                       strcat(target, "]");
+               else if (strbuf[a] == '\"')
+                       strcat(target, "&quot;");
+               else if (strbuf[a] == '&')
+                       strcat(target, "&amp;;");
+               else if (strbuf[a] == '\'') 
+                       strcat(target, "\\'");
+               else {
+                       strncat(target, &strbuf[a], 1);
+               }
+       }
+}
+
+/**
+ * \brief escape and print java script
+ * \param strbuf the js code
+ */
+void jsescputs(char *strbuf)
+{
+       char outbuf[SIZ];
+       
+       jsesc(outbuf, strbuf);
+       wprintf("%s", outbuf);
+}
+
+/**
+ * \brief Copy a string, escaping characters for message text hold
+ * \param target target buffer
+ * \param strbuf source buffer
+ */
+void msgesc(char *target, char *strbuf)
+{
+       int a;
+       strcpy(target, "");
+
+       for (a = 0; a < strlen(strbuf); ++a) {
+               if (strbuf[a] == '\n')
+                       strcat(target, " ");
+               else if (strbuf[a] == '\r')
+                       strcat(target, " ");
+               else if (strbuf[a] == '\'')
+                       strcat(target, "&#39;");
+               else {
+                       strncat(target, &strbuf[a], 1);
+               }
+       }
+}
+
+/**
+ * \brief print a string to the client after cleaning it with msgesc()
+ * \param strbuf string to be printed
+ */
+void msgescputs(char *strbuf) {
+       char *outbuf;
+
+       if (strbuf == NULL) return;
+       outbuf = malloc( (3 * strlen(strbuf)) + SIZ);
+       msgesc(outbuf, strbuf);
+       wprintf("%s", outbuf);
+       free(outbuf);
+}
+
+
+
+
+/**
+ * \brief Output all that important stuff that the browser will want to see
+ */
+void output_headers(   int do_httpheaders,     /**< 1 = output HTTP headers                          */
+                       int do_htmlhead,        /**< 1 = output HTML <head> section and <body> opener */
+
+                       int do_room_banner,     /**< 0=no, 1=yes,                                     
+                                                                * 2 = I'm going to embed my own, so don't open the 
+                                                                *     <div id="content"> either.                   
+                                                                */
+
+                       int unset_cookies,      /**< 1 = session is terminating, so unset the cookies */
+                       int suppress_check,     /**< 1 = suppress check for instant messages          */
+                       int cache               /**< 1 = allow browser to cache this page             */
+) {
+       char cookie[1024];
+       char httpnow[128];
+
+       wprintf("HTTP/1.1 200 OK\n");
+       http_datestring(httpnow, sizeof httpnow, time(NULL));
+
+       if (do_httpheaders) {
+               wprintf("Content-type: text/html; charset=utf-8\r\n"
+                       "Server: %s / %s\n"
+                       "Connection: close\r\n",
+                       SERVER, serv_info.serv_software
+               );
+       }
+
+       if (cache) {
+               wprintf("Pragma: public\r\n"
+                       "Cache-Control: max-age=3600, must-revalidate\r\n"
+                       "Last-modified: %s\r\n",
+                       httpnow
+               );
+       }
+       else {
+               wprintf("Pragma: no-cache\r\n"
+                       "Cache-Control: no-store\r\n"
+               );
+       }
+
+       stuff_to_cookie(cookie, WC->wc_session, WC->wc_username,
+                       WC->wc_password, WC->wc_roomname);
+
+       if (unset_cookies) {
+               wprintf("Set-cookie: webcit=%s; path=/\r\n", unset);
+       } else {
+               wprintf("Set-cookie: webcit=%s; path=/\r\n", cookie);
+               if (server_cookie != NULL) {
+                       wprintf("%s\n", server_cookie);
+               }
+       }
+
+       if (do_htmlhead) {
+               begin_burst();
+               do_template("head");
+       }
+
+       /** ICONBAR */
+       if (do_htmlhead) {
+
+
+               /** check for ImportantMessages (these display in a div overlaying the main screen) */
+               if (strlen(WC->ImportantMessage) > 0) {
+                       wprintf("<div id=\"important_message\">\n");
+                       wprintf("<span class=\"imsg\">"
+                               "%s</span><br />\n", WC->ImportantMessage);
+                       wprintf("</div>\n");
+                       wprintf("<script type=\"text/javascript\">\n"
+                               "        setTimeout('hide_imsg_popup()', 3000); \n"
+                               "</script>\n");
+                       safestrncpy(WC->ImportantMessage, "", sizeof WC->ImportantMessage);
+               }
+
+               if ( (WC->logged_in) && (!unset_cookies) ) {
+                       wprintf("<div id=\"iconbar\">");
+                       do_selected_iconbar();
+                       /** check for instant messages (these display in a new window) */
+                       page_popup();
+                       wprintf("</div>");
+               }
+
+               if (do_room_banner == 1) {
+                       wprintf("<div id=\"banner\">\n");
+                       embed_room_banner(NULL, navbar_default);
+                       wprintf("</div>\n");
+               }
+       }
+
+       if (do_room_banner == 1) {
+               wprintf("<div id=\"content\">\n");
+       }
+}
+
+
+/**
+ * \brief Generic function to do an HTTP redirect.  Easy and fun.
+ * \param whichpage target url to 302 to
+ */
+void http_redirect(char *whichpage) {
+       wprintf("HTTP/1.1 302 Moved Temporarily\n");
+       wprintf("Location: %s\r\n", whichpage);
+       wprintf("URI: %s\r\n", whichpage);
+       wprintf("Content-type: text/html; charset=utf-8\r\n\r\n");
+       wprintf("<html><body>");
+       wprintf("Go <a href=\"%s\">here</A>.", whichpage);
+       wprintf("</body></html>\n");
+}
+
+
+
+/**
+ * \brief Output a piece of content to the web browser
+ */
+void http_transmit_thing(char *thing, size_t length, char *content_type,
+                        int is_static) {
+
+       output_headers(0, 0, 0, 0, 0, is_static);
+
+       wprintf("Content-type: %s\r\n"
+               "Server: %s\r\n"
+               "Connection: close\r\n",
+               content_type,
+               SERVER);
+
+#ifdef HAVE_ZLIB
+       /** If we can send the data out compressed, please do so. */
+       if (WC->gzip_ok) {
+               char *compressed_data = NULL;
+               uLongf compressed_len;
+
+               compressed_len = (uLongf) ((length * 101) / 100) + 100;
+               compressed_data = malloc(compressed_len);
+
+               if (compress_gzip((Bytef *) compressed_data,
+                                 &compressed_len,
+                                 (Bytef *) thing,
+                                 (uLongf) length, Z_BEST_SPEED) == Z_OK) {
+                       wprintf("Content-encoding: gzip\r\n"
+                               "Content-length: %ld\r\n"
+                               "\r\n",
+                               (long) compressed_len
+                       );
+                       client_write(compressed_data, (size_t)compressed_len);
+                       free(compressed_data);
+                       return;
+               }
+       }
+#endif
+
+       /** No compression ... just send it out as-is */
+       wprintf("Content-length: %ld\r\n"
+               "\r\n",
+               (long) length
+       );
+       client_write(thing, (size_t)length);
+}
+
+
+
+/**
+ * \brief dump out static pages from disk
+ * \param what the file urs to print
+ */
+void output_static(char *what)
+{
+       FILE *fp;
+       struct stat statbuf;
+       off_t bytes;
+       char *bigbuffer;
+       char content_type[128];
+
+       fp = fopen(what, "rb");
+       if (fp == NULL) {
+               lprintf(9, "output_static('%s')  -- NOT FOUND --\n", what);
+               wprintf("HTTP/1.1 404 %s\n", strerror(errno));
+               wprintf("Content-Type: text/plain\r\n");
+               wprintf("\r\n");
+               wprintf("Cannot open %s: %s\n", what, strerror(errno));
+       } else {
+               if (!strncasecmp(&what[strlen(what) - 4], ".gif", 4))
+                       safestrncpy(content_type, "image/gif", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 4], ".txt", 4))
+                       safestrncpy(content_type, "text/plain", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 4], ".css", 4))
+                       safestrncpy(content_type, "text/css", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 4], ".jpg", 4))
+                       safestrncpy(content_type, "image/jpeg", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 4], ".png", 4))
+                       safestrncpy(content_type, "image/png", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 4], ".ico", 4))
+                       safestrncpy(content_type, "image/x-icon", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 5], ".html", 5))
+                       safestrncpy(content_type, "text/html", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 4], ".htm", 4))
+                       safestrncpy(content_type, "text/html", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 4], ".wml", 4))
+                       safestrncpy(content_type, "text/vnd.wap.wml", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 5], ".wmls", 5))
+                       safestrncpy(content_type, "text/vnd.wap.wmlscript", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 5], ".wmlc", 5))
+                       safestrncpy(content_type, "application/vnd.wap.wmlc", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 6], ".wmlsc", 6))
+                       safestrncpy(content_type, "application/vnd.wap.wmlscriptc", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 5], ".wbmp", 5))
+                       safestrncpy(content_type, "image/vnd.wap.wbmp", sizeof content_type);
+               else if (!strncasecmp(&what[strlen(what) - 3], ".js", 3))
+                       safestrncpy(content_type, "text/javascript", sizeof content_type);
+               else
+                       safestrncpy(content_type, "application/octet-stream", sizeof content_type);
+
+               fstat(fileno(fp), &statbuf);
+               bytes = statbuf.st_size;
+               bigbuffer = malloc(bytes + 2);
+               fread(bigbuffer, bytes, 1, fp);
+               fclose(fp);
+
+               lprintf(9, "output_static('%s')  %s\n", what, content_type);
+               http_transmit_thing(bigbuffer, (size_t)bytes, content_type, 1);
+               free(bigbuffer);
+       }
+       if (!strcasecmp(bstr("force_close_session"), "yes")) {
+               end_webcit_session();
+       }
+}
+
+
+/**
+ * \brief When the browser requests an image file from the Citadel server,
+ * this function is called to transmit it.
+ */
+void output_image()
+{
+       char buf[SIZ];
+       char *xferbuf = NULL;
+       off_t bytes;
+
+       serv_printf("OIMG %s|%s", bstr("name"), bstr("parm"));
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               bytes = extract_long(&buf[4], 0);
+               xferbuf = malloc(bytes + 2);
+
+               /** Read it from the server */
+               read_server_binary(xferbuf, bytes);
+               serv_puts("CLOS");
+               serv_getln(buf, sizeof buf);
+
+               /** Write it to the browser */
+               http_transmit_thing(xferbuf, (size_t)bytes, "image/gif", 0);
+               free(xferbuf);
+
+       } else {
+               /**
+                * Instead of an ugly 404, send a 1x1 transparent GIF
+                * when there's no such image on the server.
+                */
+               output_static("static/blank.gif");
+       }
+
+
+
+}
+
+/**
+ * \brief Generic function to output an arbitrary MIME part from an arbitrary
+ *        message number on the server.
+ *
+ * \param msgnum               Number of the item on the citadel server
+ * \param partnum              The MIME part to be output
+ * \param force_download       Nonzero to force set the Content-Type: header
+ *                              to "application/octet-stream"
+ */
+void mimepart(char *msgnum, char *partnum, int force_download)
+{
+       char buf[256];
+       off_t bytes;
+       char content_type[256];
+       char *content = NULL;
+       
+       serv_printf("OPNA %s|%s", msgnum, partnum);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               bytes = extract_long(&buf[4], 0);
+               content = malloc(bytes + 2);
+               if (force_download) {
+                       strcpy(content_type, "application/octet-stream");
+               }
+               else {
+                       extract_token(content_type, &buf[4], 3, '|', sizeof content_type);
+               }
+               output_headers(0, 0, 0, 0, 0, 0);
+               read_server_binary(content, bytes);
+               serv_puts("CLOS");
+               serv_getln(buf, sizeof buf);
+               http_transmit_thing(content, bytes, content_type, 0);
+               free(content);
+       } else {
+               wprintf("HTTP/1.1 404 %s\n", &buf[4]);
+               output_headers(0, 0, 0, 0, 0, 0);
+               wprintf("Content-Type: text/plain\r\n");
+               wprintf("\r\n");
+               wprintf(_("An error occurred while retrieving this part: %s\n"), &buf[4]);
+       }
+
+}
+
+
+/**
+ * \brief Read any MIME part of a message, from the server, into memory.
+ * \param msgnum number of the message on the citadel server
+ * \param partnum the MIME part to be loaded
+ */
+char *load_mimepart(long msgnum, char *partnum)
+{
+       char buf[SIZ];
+       off_t bytes;
+       char content_type[SIZ];
+       char *content;
+       
+       serv_printf("OPNA %ld|%s", msgnum, partnum);
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               bytes = extract_long(&buf[4], 0);
+               extract_token(content_type, &buf[4], 3, '|', sizeof content_type);
+
+               content = malloc(bytes + 2);
+               read_server_binary(content, bytes);
+
+               serv_puts("CLOS");
+               serv_getln(buf, sizeof buf);
+               content[bytes] = 0;     /* null terminate for good measure */
+               return(content);
+       }
+       else {
+               return(NULL);
+       }
+
+}
+
+
+/**
+ * \brief Convenience functions to display a page containing only a string
+ * \param titlebarcolor color of the titlebar of the frame
+ * \param titlebarmsg text to display in the title bar
+ * \param messagetext body of the box
+ */
+void convenience_page(char *titlebarcolor, char *titlebarmsg, char *messagetext)
+{
+       wprintf("HTTP/1.1 200 OK\n");
+       output_headers(1, 1, 2, 0, 0, 0);
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<table width=100%% border=0 bgcolor=\"#%s\"><tr><td>", titlebarcolor);
+       wprintf("<span class=\"titlebar\">%s</span>\n", titlebarmsg);
+       wprintf("</td></tr></table>\n");
+       wprintf("</div>\n<div id=\"content\">\n");
+       escputs(messagetext);
+
+       wprintf("<hr />\n");
+       wDumpContent(1);
+}
+
+
+/**
+ * \brief Display a blank page.
+ */
+void blank_page(void) {
+       output_headers(1, 1, 0, 0, 0, 0);
+       wDumpContent(2);
+}
+
+
+/**
+ * \brief A template has been requested
+ */
+void url_do_template(void) {
+       do_template(bstr("template"));
+}
+
+
+
+/**
+ * \brief Offer to make any page the user's "start page."
+ */
+void offer_start_page(void) {
+       wprintf("<a href=\"change_start_page?startpage=");
+       urlescputs(WC->this_page);
+       wprintf("\"><font size=-2 color=\"#AAAAAA\">");
+       wprintf(_("Make this my start page"));
+       wprintf("</font></a>");
+/*
+       wprintf("<br/><a href=\"rss?room=");
+       urlescputs(WC->wc_roomname);
+       wprintf("\" title=\"RSS 2.0 feed for ");
+       escputs(WC->wc_roomname);
+       wprintf("\"><img alt=\"RSS\" border=\"0\" src=\"static/xml_button.gif\"/></a>\n");
+*/
+}
+
+
+/**
+ * \brief Change the user's start page
+ */
+void change_start_page(void) {
+
+       if (bstr("startpage") == NULL) {
+               safestrncpy(WC->ImportantMessage,
+                       _("You no longer have a start page selected."),
+                       sizeof WC->ImportantMessage);
+               display_main_menu();
+               return;
+       }
+
+       set_preference("startpage", bstr("startpage"), 1);
+
+       output_headers(1, 1, 0, 0, 0, 0);
+       do_template("newstartpage");
+       wDumpContent(1);
+}
+
+
+
+/**
+ * \brief convenience function to indicate success
+ * \param successmessage the mesage itself
+ */
+void display_success(char *successmessage)
+{
+       convenience_page("007700", "OK", successmessage);
+}
+
+
+/**
+ * \brief Authorization required page 
+ * This is probably temporary and should be revisited 
+ * \param message message to put in header
+*/
+void authorization_required(const char *message)
+{
+       wprintf("HTTP/1.1 401 Authorization Required\r\n");
+       wprintf("WWW-Authenticate: Basic realm=\"\"\r\n", serv_info.serv_humannode);
+       wprintf("Content-Type: text/html\r\n\r\n");
+       wprintf("<h1>");
+       wprintf(_("Authorization Required"));
+       wprintf("</h1>\r\n");
+       wprintf(_("The resource you requested requires a valid username and password. "
+               "You could not be logged in: %s\n"), message);
+       wDumpContent(0);
+}
+
+/**
+ * \brief This function is called by the MIME parser to handle data uploaded by
+ *        the browser.  Form data, uploaded files, and the data from HTTP PUT
+ *        operations (such as those found in GroupDAV) all arrive this way.
+ *
+ * \param name Name of the item being uploaded
+ * \param filename Filename of the item being uploaded
+ * \param partnum MIME part identifier (not needed)
+ * \param disp MIME content disposition (not needed)
+ * \param content The actual data
+ * \param cbtype MIME content-type
+ * \param cbcharset Character set
+ * \param length Content length
+ * \param encoding MIME encoding type (not needed)
+ * \param userdata Not used here
+ */
+void upload_handler(char *name, char *filename, char *partnum, char *disp,
+                       void *content, char *cbtype, char *cbcharset,
+                       size_t length, char *encoding, void *userdata)
+{
+       struct urlcontent *u;
+
+       /* lprintf(9, "upload_handler() name=%s, type=%s, len=%d\n",
+               name, cbtype, length); */
+
+       /* Form fields */
+       if ( (length > 0) && (strlen(cbtype) == 0) ) {
+               u = (struct urlcontent *) malloc(sizeof(struct urlcontent));
+               u->next = WC->urlstrings;
+               WC->urlstrings = u;
+               safestrncpy(u->url_key, name, sizeof(u->url_key));
+               u->url_data = malloc(length + 1);
+               memcpy(u->url_data, content, length);
+               u->url_data[length] = 0;
+       }
+
+       /** Uploaded files */
+       if ( (length > 0) && (strlen(cbtype) > 0) ) {
+               WC->upload = malloc(length);
+               if (WC->upload != NULL) {
+                       WC->upload_length = length;
+                       safestrncpy(WC->upload_filename, filename,
+                                       sizeof(WC->upload_filename));
+                       safestrncpy(WC->upload_content_type, cbtype,
+                                       sizeof(WC->upload_content_type));
+                       memcpy(WC->upload, content, length);
+               }
+               else {
+                       lprintf(3, "malloc() failed: %s\n", strerror(errno));
+               }
+       }
+
+}
+
+/**
+ * \brief Convenience functions to wrap around asynchronous ajax responses
+ */
+void begin_ajax_response(void) {
+        output_headers(0, 0, 0, 0, 0, 0);
+
+        wprintf("Content-type: text/html; charset=UTF-8\r\n"
+                "Server: %s\r\n"
+                "Connection: close\r\n"
+                "Pragma: no-cache\r\n"
+                "Cache-Control: no-cache\r\n",
+                SERVER);
+        begin_burst();
+}
+
+/**
+ * \brief print ajax response footer 
+ */
+void end_ajax_response(void) {
+        wprintf("\r\n");
+        wDumpContent(0);
+}
+
+/**
+ * \brief Wraps a Citadel server command in an AJAX transaction.
+ */
+void ajax_servcmd(void)
+{
+       char buf[1024];
+       char gcontent[1024];
+       char *junk;
+       size_t len;
+
+       begin_ajax_response();
+
+       serv_printf("%s", bstr("g_cmd"));
+       serv_getln(buf, sizeof buf);
+       wprintf("%s\n", buf);
+
+       if (buf[0] == '8') {
+               serv_printf("\n\n000");
+       }
+       if ((buf[0] == '1') || (buf[0] == '8')) {
+               while (serv_getln(gcontent, sizeof gcontent), strcmp(gcontent, "000")) {
+                       wprintf("%s\n", gcontent);
+               }
+               wprintf("000");
+       }
+       if (buf[0] == '4') {
+               text_to_server(bstr("g_input"));
+               serv_puts("000");
+       }
+       if (buf[0] == '6') {
+               len = atol(&buf[4]);
+               junk = malloc(len);
+               serv_read(junk, len);
+               free(junk);
+       }
+       if (buf[0] == '7') {
+               len = atol(&buf[4]);
+               junk = malloc(len);
+               memset(junk, 0, len);
+               serv_write(junk, len);
+               free(junk);
+       }
+
+       end_ajax_response();
+       
+       /**
+        * This is kind of an ugly hack, but this is the only place it can go.
+        * If the command was GEXP, then the instant messenger window must be
+        * running, so reset the "last_pager_check" watchdog timer so
+        * that page_popup() doesn't try to open it a second time.
+        */
+       if (!strncasecmp(bstr("g_cmd"), "GEXP", 4)) {
+               WC->last_pager_check = time(NULL);
+       }
+}
+
+
+/**
+ * \brief Helper function for the asynchronous check to see if we need
+ * to open the instant messenger window.
+ */
+void seconds_since_last_gexp(void)
+{
+       char buf[256];
+
+       begin_ajax_response();
+       if ( (time(NULL) - WC->last_pager_check) < 30) {
+               wprintf("NO\n");
+       }
+       else {
+               serv_puts("NOOP");
+               serv_getln(buf, sizeof buf);
+               if (buf[3] == '*') {
+                       wprintf("YES");
+               }
+               else {
+                       wprintf("NO");
+               }
+       }
+       end_ajax_response();
+}
+
+
+
+
+/**
+ * \brief Entry point for WebCit transaction
+ */
+void session_loop(struct httprequest *req)
+{
+       char cmd[1024];
+       char action[1024];
+       char arg1[128];
+       char arg2[128];
+       char arg3[128];
+       char arg4[128];
+       char arg5[128];
+       char arg6[128];
+       char arg7[128];
+       char buf[SIZ];
+       char request_method[128];
+       char pathname[1024];
+       int a, b;
+       int ContentLength = 0;
+       int BytesRead = 0;
+       char ContentType[512];
+       char *content = NULL;
+       char *content_end = NULL;
+       struct httprequest *hptr;
+       char browser_host[256];
+       char user_agent[256];
+       int body_start = 0;
+       int is_static = 0;
+
+       /**
+        * We stuff these with the values coming from the client cookies,
+        * so we can use them to reconnect a timed out session if we have to.
+        */
+       char c_username[SIZ];
+       char c_password[SIZ];
+       char c_roomname[SIZ];
+       char c_httpauth_string[SIZ];
+       char c_httpauth_user[SIZ];
+       char c_httpauth_pass[SIZ];
+       char cookie[SIZ];
+
+       safestrncpy(c_username, "", sizeof c_username);
+       safestrncpy(c_password, "", sizeof c_password);
+       safestrncpy(c_roomname, "", sizeof c_roomname);
+       safestrncpy(c_httpauth_string, "", sizeof c_httpauth_string);
+       safestrncpy(c_httpauth_user, DEFAULT_HTTPAUTH_USER, sizeof c_httpauth_user);
+       safestrncpy(c_httpauth_pass, DEFAULT_HTTPAUTH_PASS, sizeof c_httpauth_pass);
+       strcpy(browser_host, "");
+
+       WC->upload_length = 0;
+       WC->upload = NULL;
+       WC->vars = NULL;
+       WC->is_wap = 0;
+
+       hptr = req;
+       if (hptr == NULL) return;
+
+       safestrncpy(cmd, hptr->line, sizeof cmd);
+       hptr = hptr->next;
+       extract_token(request_method, cmd, 0, ' ', sizeof request_method);
+       extract_token(pathname, cmd, 1, ' ', sizeof pathname);
+
+       /** Figure out the action */
+       extract_token(action, pathname, 1, '/', sizeof action);
+       if (strstr(action, "?")) *strstr(action, "?") = 0;
+       if (strstr(action, "&")) *strstr(action, "&") = 0;
+       if (strstr(action, " ")) *strstr(action, " ") = 0;
+
+       extract_token(arg1, pathname, 2, '/', sizeof arg1);
+       if (strstr(arg1, "?")) *strstr(arg1, "?") = 0;
+       if (strstr(arg1, "&")) *strstr(arg1, "&") = 0;
+       if (strstr(arg1, " ")) *strstr(arg1, " ") = 0;
+
+       extract_token(arg2, pathname, 3, '/', sizeof arg2);
+       if (strstr(arg2, "?")) *strstr(arg2, "?") = 0;
+       if (strstr(arg2, "&")) *strstr(arg2, "&") = 0;
+       if (strstr(arg2, " ")) *strstr(arg2, " ") = 0;
+
+       extract_token(arg3, pathname, 4, '/', sizeof arg3);
+       if (strstr(arg3, "?")) *strstr(arg3, "?") = 0;
+       if (strstr(arg3, "&")) *strstr(arg3, "&") = 0;
+       if (strstr(arg3, " ")) *strstr(arg3, " ") = 0;
+
+       extract_token(arg4, pathname, 5, '/', sizeof arg4);
+       if (strstr(arg4, "?")) *strstr(arg4, "?") = 0;
+       if (strstr(arg4, "&")) *strstr(arg4, "&") = 0;
+       if (strstr(arg4, " ")) *strstr(arg4, " ") = 0;
+
+       extract_token(arg5, pathname, 6, '/', sizeof arg5);
+       if (strstr(arg5, "?")) *strstr(arg5, "?") = 0;
+       if (strstr(arg5, "&")) *strstr(arg5, "&") = 0;
+       if (strstr(arg5, " ")) *strstr(arg5, " ") = 0;
+
+       extract_token(arg6, pathname, 7, '/', sizeof arg6);
+       if (strstr(arg6, "?")) *strstr(arg6, "?") = 0;
+       if (strstr(arg6, "&")) *strstr(arg6, "&") = 0;
+       if (strstr(arg6, " ")) *strstr(arg6, " ") = 0;
+
+       extract_token(arg7, pathname, 8, '/', sizeof arg7);
+       if (strstr(arg7, "?")) *strstr(arg7, "?") = 0;
+       if (strstr(arg7, "&")) *strstr(arg7, "&") = 0;
+       if (strstr(arg7, " ")) *strstr(arg7, " ") = 0;
+
+       while (hptr != NULL) {
+               safestrncpy(buf, hptr->line, sizeof buf);
+               /* lprintf(9, "HTTP HEADER: %s\n", buf); */
+               hptr = hptr->next;
+
+               if (!strncasecmp(buf, "Cookie: webcit=", 15)) {
+                       safestrncpy(cookie, &buf[15], sizeof cookie);
+                       cookie_to_stuff(cookie, NULL,
+                                       c_username, sizeof c_username,
+                                       c_password, sizeof c_password,
+                                       c_roomname, sizeof c_roomname);
+               }
+               else if (!strncasecmp(buf, "Authorization: Basic ", 21)) {
+                       CtdlDecodeBase64(c_httpauth_string, &buf[21], strlen(&buf[21]));
+                       extract_token(c_httpauth_user, c_httpauth_string, 0, ':', sizeof c_httpauth_user);
+                       extract_token(c_httpauth_pass, c_httpauth_string, 1, ':', sizeof c_httpauth_pass);
+               }
+               else if (!strncasecmp(buf, "Content-length: ", 16)) {
+                       ContentLength = atoi(&buf[16]);
+               }
+               else if (!strncasecmp(buf, "Content-type: ", 14)) {
+                       safestrncpy(ContentType, &buf[14], sizeof ContentType);
+               }
+               else if (!strncasecmp(buf, "User-agent: ", 12)) {
+                       safestrncpy(user_agent, &buf[12], sizeof user_agent);
+               }
+               else if (!strncasecmp(buf, "X-Forwarded-Host: ", 18)) {
+                       if (follow_xff) {
+                               safestrncpy(WC->http_host, &buf[18], sizeof WC->http_host);
+                       }
+               }
+               else if (!strncasecmp(buf, "Host: ", 6)) {
+                       if (strlen(WC->http_host) == 0) {
+                               safestrncpy(WC->http_host, &buf[6], sizeof WC->http_host);
+                       }
+               }
+               else if (!strncasecmp(buf, "X-Forwarded-For: ", 17)) {
+                       safestrncpy(browser_host, &buf[17], sizeof browser_host);
+                       while (num_tokens(browser_host, ',') > 1) {
+                               remove_token(browser_host, 0, ',');
+                       }
+                       striplt(browser_host);
+               }
+               /** Only WAP gateways explicitly name this content-type */
+               else if (strstr(buf, "text/vnd.wap.wml")) {
+                       WC->is_wap = 1;
+               }
+       }
+
+       if (ContentLength > 0) {
+               content = malloc(ContentLength + SIZ);
+               memset(content, 0, ContentLength + SIZ);
+               sprintf(content, "Content-type: %s\n"
+                               "Content-length: %d\n\n",
+                               ContentType, ContentLength);
+               body_start = strlen(content);
+
+               /** Read the entire input data at once. */
+               client_read(WC->http_sock, &content[BytesRead+body_start],
+                       ContentLength);
+
+               if (!strncasecmp(ContentType,
+                             "application/x-www-form-urlencoded", 33)) {
+                       addurls(&content[body_start]);
+               } else if (!strncasecmp(ContentType, "multipart", 9)) {
+                       content_end = content + ContentLength + body_start;
+                       mime_parser(content, content_end, *upload_handler,
+                                       NULL, NULL, NULL, 0);
+               }
+       } else {
+               content = NULL;
+       }
+
+       /** make a note of where we are in case the user wants to save it */
+       safestrncpy(WC->this_page, cmd, sizeof(WC->this_page));
+       remove_token(WC->this_page, 2, ' ');
+       remove_token(WC->this_page, 0, ' ');
+
+       /** If there are variables in the URL, we must grab them now */
+       for (a = 0; a < strlen(cmd); ++a) {
+               if ((cmd[a] == '?') || (cmd[a] == '&')) {
+                       for (b = a; b < strlen(cmd); ++b)
+                               if (isspace(cmd[b]))
+                                       cmd[b] = 0;
+                       addurls(&cmd[a + 1]);
+                       cmd[a] = 0;
+               }
+       }
+
+       /** If it's a "force 404" situation then display the error and bail. */
+       if (!strcmp(action, "404")) {
+               wprintf("HTTP/1.1 404 Not found\r\n");
+               wprintf("Content-Type: text/plain\r\n");
+               wprintf("\r\n");
+               wprintf("Not found\r\n");
+               goto SKIP_ALL_THIS_CRAP;
+       }
+
+       /** Static content can be sent without connecting to Citadel. */
+       is_static = 0;
+       for (a=0; a<(sizeof(static_content_dirs) / sizeof(char *)); ++a) {
+               if (!strcasecmp(action, static_content_dirs[a])) {
+                       is_static = 1;
+               }
+       }
+       if (is_static) {
+               snprintf(buf, sizeof buf, "%s/%s/%s/%s/%s/%s/%s/%s",
+                       action, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+               for (a=0; a<8; ++a) {
+                       if (buf[strlen(buf)-1] == '/') {
+                               buf[strlen(buf)-1] = 0;
+                       }
+               }
+               for (a = 0; a < strlen(buf); ++a) {
+                       if (isspace(buf[a])) {
+                               buf[a] = 0;
+                       }
+               }
+               output_static(buf);
+               goto SKIP_ALL_THIS_CRAP;        /* Don't try to connect */
+       }
+
+       /**
+        * If we're not connected to a Citadel server, try to hook up the
+        * connection now.
+        */
+       if (!WC->connected) {
+               if (!strcasecmp(ctdlhost, "uds")) {
+                       /* unix domain socket */
+                       sprintf(buf, "%s/citadel.socket", ctdlport);
+                       WC->serv_sock = uds_connectsock(buf);
+               }
+               else {
+                       /* tcp socket */
+                       WC->serv_sock = tcp_connectsock(ctdlhost, ctdlport);
+               }
+
+               if (WC->serv_sock < 0) {
+                       do_logout();
+                       goto SKIP_ALL_THIS_CRAP;
+               }
+               else {
+                       WC->connected = 1;
+                       serv_getln(buf, sizeof buf);    /** get the server welcome message */
+
+                       /**
+                        * From what host is our user connecting?  Go with
+                        * the host at the other end of the HTTP socket,
+                        * unless we are following X-Forwarded-For: headers
+                        * and such a header has already turned up something.
+                        */
+                       if ( (!follow_xff) || (strlen(browser_host) == 0) ) {
+                               locate_host(browser_host, WC->http_sock);
+                       }
+
+                       get_serv_info(browser_host, user_agent);
+                       if (serv_info.serv_rev_level < MINIMUM_CIT_VERSION) {
+                               wprintf(_("You are connected to a Citadel "
+                                       "server running Citadel %d.%02d. \n"
+                                       "In order to run this version of WebCit "
+                                       "you must also have Citadel %d.%02d or"
+                                       " newer.\n\n\n"),
+                                               serv_info.serv_rev_level / 100,
+                                               serv_info.serv_rev_level % 100,
+                                               MINIMUM_CIT_VERSION / 100,
+                                               MINIMUM_CIT_VERSION % 100
+                                       );
+                               end_webcit_session();
+                               goto SKIP_ALL_THIS_CRAP;
+                       }
+               }
+       }
+
+       /**
+        * Functions which can be performed without logging in
+        */
+       if (!strcasecmp(action, "listsub")) {
+               do_listsub();
+               goto SKIP_ALL_THIS_CRAP;
+       }
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+       if (!strcasecmp(action, "freebusy")) {
+               do_freebusy(cmd);
+               goto SKIP_ALL_THIS_CRAP;
+       }
+#endif
+
+       /**
+        * If we're not logged in, but we have HTTP Authentication data,
+        * try logging in to Citadel using that.
+        */
+       if ((!WC->logged_in)
+          && (strlen(c_httpauth_user) > 0)
+          && (strlen(c_httpauth_pass) > 0)) {
+               serv_printf("USER %s", c_httpauth_user);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '3') {
+                       serv_printf("PASS %s", c_httpauth_pass);
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] == '2') {
+                               become_logged_in(c_httpauth_user,
+                                               c_httpauth_pass, buf);
+                               safestrncpy(WC->httpauth_user, c_httpauth_user, sizeof WC->httpauth_user);
+                               safestrncpy(WC->httpauth_pass, c_httpauth_pass, sizeof WC->httpauth_pass);
+                       } else {
+                               /** Should only display when password is wrong */
+                               authorization_required(&buf[4]);
+                               goto SKIP_ALL_THIS_CRAP;
+                       }
+               }
+       }
+
+       /** This needs to run early */
+       if (!strcasecmp(action, "rss")) {
+               display_rss(bstr("room"), request_method);
+               goto SKIP_ALL_THIS_CRAP;
+       }
+
+       /** 
+        * The GroupDAV stuff relies on HTTP authentication instead of
+        * our session's authentication.
+        */
+       if (!strncasecmp(action, "groupdav", 8)) {
+               groupdav_main(req, ContentType, /* do GroupDAV methods */
+                       ContentLength, content+body_start);
+               if (!WC->logged_in) {
+                       WC->killthis = 1;       /* If not logged in, don't */
+               }                               /* keep the session active */
+               goto SKIP_ALL_THIS_CRAP;
+       }
+
+
+       /**
+        * Automatically send requests with any method other than GET or
+        * POST to the GroupDAV code as well.
+        */
+       if ((strcasecmp(request_method, "GET")) && (strcasecmp(request_method, "POST"))) {
+               groupdav_main(req, ContentType, /** do GroupDAV methods */
+                       ContentLength, content+body_start);
+               if (!WC->logged_in) {
+                       WC->killthis = 1;       /** If not logged in, don't */
+               }                               /** keep the session active */
+               goto SKIP_ALL_THIS_CRAP;
+       }
+
+       /**
+        * If we're not logged in, but we have username and password cookies
+        * supplied by the browser, try using them to log in.
+        */
+       if ((!WC->logged_in)
+          && (strlen(c_username) > 0)
+          && (strlen(c_password) > 0)) {
+               serv_printf("USER %s", c_username);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '3') {
+                       serv_printf("PASS %s", c_password);
+                       serv_getln(buf, sizeof buf);
+                       if (buf[0] == '2') {
+                               become_logged_in(c_username, c_password, buf);
+                       }
+               }
+       }
+       /**
+        * If we don't have a current room, but a cookie specifying the
+        * current room is supplied, make an effort to go there.
+        */
+       if ((strlen(WC->wc_roomname) == 0) && (strlen(c_roomname) > 0)) {
+               serv_printf("GOTO %s", c_roomname);
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '2') {
+                       safestrncpy(WC->wc_roomname, c_roomname, sizeof WC->wc_roomname);
+               }
+       }
+
+       if (!strcasecmp(action, "image")) {
+               output_image();
+
+               /**
+                * All functions handled below this point ... make sure we log in
+                * before doing anything else!
+                */
+       } else if ((!WC->logged_in) && (!strcasecmp(action, "login"))) {
+               do_login();
+       } else if (!WC->logged_in) {
+               display_login(NULL);
+       }
+
+       /**
+        * Various commands...
+        */
+
+       else if (!strcasecmp(action, "do_welcome")) {
+               do_welcome();
+       } else if (!strcasecmp(action, "blank")) {
+               blank_page();
+       } else if (!strcasecmp(action, "do_template")) {
+               url_do_template();
+       } else if (!strcasecmp(action, "display_aide_menu")) {
+               display_aide_menu();
+       } else if (!strcasecmp(action, "display_main_menu")) {
+               display_main_menu();
+       } else if (!strcasecmp(action, "who")) {
+               who();
+       } else if (!strcasecmp(action, "sslg")) {
+               seconds_since_last_gexp();
+       } else if (!strcasecmp(action, "who_inner_html")) {
+               begin_ajax_response();
+               who_inner_div();
+               end_ajax_response();
+       } else if (!strcasecmp(action, "iconbar_ajax_menu")) {
+               begin_ajax_response();
+               do_iconbar();
+               end_ajax_response();
+       } else if (!strcasecmp(action, "iconbar_ajax_rooms")) {
+               begin_ajax_response();
+               do_iconbar_roomlist();
+               end_ajax_response();
+       } else if (!strcasecmp(action, "knrooms")) {
+               knrooms();
+       } else if (!strcasecmp(action, "gotonext")) {
+               slrp_highest();
+               gotonext();
+       } else if (!strcasecmp(action, "skip")) {
+               gotonext();
+       } else if (!strcasecmp(action, "ungoto")) {
+               ungoto();
+       } else if (!strcasecmp(action, "dotgoto")) {
+               if (WC->wc_view != VIEW_MAILBOX) {      /* dotgoto acts like dotskip when we're in a mailbox view */
+                       slrp_highest();
+               }
+               smart_goto(bstr("room"));
+       } else if (!strcasecmp(action, "dotskip")) {
+               smart_goto(bstr("room"));
+       } else if (!strcasecmp(action, "termquit")) {
+               do_logout();
+       } else if (!strcasecmp(action, "readnew")) {
+               readloop("readnew");
+       } else if (!strcasecmp(action, "readold")) {
+               readloop("readold");
+       } else if (!strcasecmp(action, "readfwd")) {
+               readloop("readfwd");
+       } else if (!strcasecmp(action, "headers")) {
+               readloop("headers");
+       } else if (!strcasecmp(action, "msg")) {
+               embed_message(arg1);
+       } else if (!strcasecmp(action, "printmsg")) {
+               print_message(arg1);
+       } else if (!strcasecmp(action, "msgheaders")) {
+               display_headers(arg1);
+       } else if (!strcasecmp(action, "wiki")) {
+               display_wiki_page();
+       } else if (!strcasecmp(action, "display_enter")) {
+               display_enter();
+       } else if (!strcasecmp(action, "post")) {
+               post_message();
+       } else if (!strcasecmp(action, "move_msg")) {
+               move_msg();
+       } else if (!strcasecmp(action, "delete_msg")) {
+               delete_msg();
+       } else if (!strcasecmp(action, "userlist")) {
+               userlist();
+       } else if (!strcasecmp(action, "showuser")) {
+               showuser();
+       } else if (!strcasecmp(action, "display_page")) {
+               display_page();
+       } else if (!strcasecmp(action, "page_user")) {
+               page_user();
+       } else if (!strcasecmp(action, "chat")) {
+               do_chat();
+       } else if (!strcasecmp(action, "display_private")) {
+               display_private("", 0);
+       } else if (!strcasecmp(action, "goto_private")) {
+               goto_private();
+       } else if (!strcasecmp(action, "zapped_list")) {
+               zapped_list();
+       } else if (!strcasecmp(action, "display_zap")) {
+               display_zap();
+       } else if (!strcasecmp(action, "zap")) {
+               zap();
+       } else if (!strcasecmp(action, "display_entroom")) {
+               display_entroom();
+       } else if (!strcasecmp(action, "entroom")) {
+               entroom();
+       } else if (!strcasecmp(action, "display_whok")) {
+               display_whok();
+       } else if (!strcasecmp(action, "do_invt_kick")) {
+               do_invt_kick();
+       } else if (!strcasecmp(action, "display_editroom")) {
+               display_editroom();
+       } else if (!strcasecmp(action, "netedit")) {
+               netedit();
+       } else if (!strcasecmp(action, "editroom")) {
+               editroom();
+       } else if (!strcasecmp(action, "display_editinfo")) {
+               display_edit(_("Room info"), "EINF 0", "RINF", "/editinfo", 1);
+       } else if (!strcasecmp(action, "editinfo")) {
+               save_edit(_("Room info"), "EINF 1", 1);
+       } else if (!strcasecmp(action, "display_editbio")) {
+               sprintf(buf, "RBIO %s", WC->wc_fullname);
+               display_edit(_("Your bio"), "NOOP", buf, "editbio", 3);
+       } else if (!strcasecmp(action, "editbio")) {
+               save_edit(_("Your bio"), "EBIO", 0);
+       } else if (!strcasecmp(action, "confirm_move_msg")) {
+               confirm_move_msg();
+       } else if (!strcasecmp(action, "delete_room")) {
+               delete_room();
+       } else if (!strcasecmp(action, "validate")) {
+               validate();
+       } else if (!strcasecmp(action, "display_editpic")) {
+               display_graphics_upload(_("your photo"),
+                                       "UIMG 0|_userpic_",
+                                       "editpic");
+       } else if (!strcasecmp(action, "editpic")) {
+               do_graphics_upload("UIMG 1|_userpic_");
+       } else if (!strcasecmp(action, "display_editroompic")) {
+               display_graphics_upload(_("the icon for this room"),
+                                       "UIMG 0|_roompic_",
+                                       "editroompic");
+       } else if (!strcasecmp(action, "editroompic")) {
+               do_graphics_upload("UIMG 1|_roompic_");
+       } else if (!strcasecmp(action, "delete_floor")) {
+               delete_floor();
+       } else if (!strcasecmp(action, "rename_floor")) {
+               rename_floor();
+       } else if (!strcasecmp(action, "create_floor")) {
+               create_floor();
+       } else if (!strcasecmp(action, "display_editfloorpic")) {
+               sprintf(buf, "UIMG 0|_floorpic_|%s",
+                       bstr("which_floor"));
+               display_graphics_upload(_("the icon for this floor"),
+                                       buf,
+                                       "editfloorpic");
+       } else if (!strcasecmp(action, "editfloorpic")) {
+               sprintf(buf, "UIMG 1|_floorpic_|%s",
+                       bstr("which_floor"));
+               do_graphics_upload(buf);
+       } else if (!strcasecmp(action, "display_reg")) {
+               display_reg(0);
+       } else if (!strcasecmp(action, "display_changepw")) {
+               display_changepw();
+       } else if (!strcasecmp(action, "changepw")) {
+               changepw();
+       } else if (!strcasecmp(action, "display_edit_node")) {
+               display_edit_node();
+       } else if (!strcasecmp(action, "edit_node")) {
+               edit_node();
+       } else if (!strcasecmp(action, "display_netconf")) {
+               display_netconf();
+       } else if (!strcasecmp(action, "display_confirm_delete_node")) {
+               display_confirm_delete_node();
+       } else if (!strcasecmp(action, "delete_node")) {
+               delete_node();
+       } else if (!strcasecmp(action, "display_add_node")) {
+               display_add_node();
+       } else if (!strcasecmp(action, "add_node")) {
+               add_node();
+       } else if (!strcasecmp(action, "terminate_session")) {
+               slrp_highest();
+               terminate_session();
+       } else if (!strcasecmp(action, "edit_me")) {
+               edit_me();
+       } else if (!strcasecmp(action, "display_siteconfig")) {
+               display_siteconfig();
+       } else if (!strcasecmp(action, "chat_recv")) {
+               chat_recv();
+       } else if (!strcasecmp(action, "chat_send")) {
+               chat_send();
+       } else if (!strcasecmp(action, "siteconfig")) {
+               siteconfig();
+       } else if (!strcasecmp(action, "display_generic")) {
+               display_generic();
+       } else if (!strcasecmp(action, "do_generic")) {
+               do_generic();
+       } else if (!strcasecmp(action, "ajax_servcmd")) {
+               ajax_servcmd();
+       } else if (!strcasecmp(action, "display_menubar")) {
+               display_menubar(1);
+       } else if (!strcasecmp(action, "mimepart")) {
+               mimepart(arg1, arg2, 0);
+       } else if (!strcasecmp(action, "mimepart_download")) {
+               mimepart(arg1, arg2, 1);
+       } else if (!strcasecmp(action, "edit_vcard")) {
+               edit_vcard();
+       } else if (!strcasecmp(action, "submit_vcard")) {
+               submit_vcard();
+       } else if (!strcasecmp(action, "select_user_to_edit")) {
+               select_user_to_edit(NULL, NULL);
+       } else if (!strcasecmp(action, "display_edituser")) {
+               display_edituser(NULL, 0);
+       } else if (!strcasecmp(action, "edituser")) {
+               edituser();
+       } else if (!strcasecmp(action, "create_user")) {
+               create_user();
+       } else if (!strcasecmp(action, "changeview")) {
+               change_view();
+       } else if (!strcasecmp(action, "change_start_page")) {
+               change_start_page();
+       } else if (!strcasecmp(action, "display_floorconfig")) {
+               display_floorconfig(NULL);
+       } else if (!strcasecmp(action, "toggle_self_service")) {
+               toggle_self_service();
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+       } else if (!strcasecmp(action, "display_edit_task")) {
+               display_edit_task();
+       } else if (!strcasecmp(action, "save_task")) {
+               save_task();
+       } else if (!strcasecmp(action, "display_edit_event")) {
+               display_edit_event();
+       } else if (!strcasecmp(action, "save_event")) {
+               save_event();
+       } else if (!strcasecmp(action, "respond_to_request")) {
+               respond_to_request();
+       } else if (!strcasecmp(action, "handle_rsvp")) {
+               handle_rsvp();
+#endif
+       } else if (!strcasecmp(action, "summary")) {
+               summary();
+       } else if (!strcasecmp(action, "summary_inner_div")) {
+               begin_ajax_response();
+               summary_inner_div();
+               end_ajax_response();
+       } else if (!strcasecmp(action, "display_customize_iconbar")) {
+               display_customize_iconbar();
+       } else if (!strcasecmp(action, "commit_iconbar")) {
+               commit_iconbar();
+       } else if (!strcasecmp(action, "set_room_policy")) {
+               set_room_policy();
+       } else if (!strcasecmp(action, "display_inetconf")) {
+               display_inetconf();
+       } else if (!strcasecmp(action, "save_inetconf")) {
+               save_inetconf();
+       } else if (!strcasecmp(action, "setup_wizard")) {
+               do_setup_wizard();
+       } else if (!strcasecmp(action, "display_preferences")) {
+               display_preferences();
+       } else if (!strcasecmp(action, "set_preferences")) {
+               set_preferences();
+       } else if (!strcasecmp(action, "recp_autocomplete")) {
+               recp_autocomplete(bstr("recp"));
+       } else if (!strcasecmp(action, "cc_autocomplete")) {
+               recp_autocomplete(bstr("cc"));
+       } else if (!strcasecmp(action, "bcc_autocomplete")) {
+               recp_autocomplete(bstr("bcc"));
+       } else if (!strcasecmp(action, "set_floordiv_expanded")) {
+               set_floordiv_expanded(arg1);
+       } else if (!strcasecmp(action, "diagnostics")) {
+               output_headers(1, 1, 1, 0, 0, 0);
+               wprintf("Session: %d<hr />\n", WC->wc_session);
+               wprintf("Command: <br /><PRE>\n");
+               escputs(cmd);
+               wprintf("</PRE><hr />\n");
+               wprintf("Variables: <br /><PRE>\n");
+               dump_vars();
+               wprintf("</PRE><hr />\n");
+               wDumpContent(1);
+       } else if (!strcasecmp(action, "updatenote")) {
+               updatenote();
+       }
+
+       /** When all else fais, display the main menu. */
+       else {
+               display_main_menu();
+       }
+
+SKIP_ALL_THIS_CRAP:
+       fflush(stdout);
+       if (content != NULL) {
+               free(content);
+               content = NULL;
+       }
+       free_urls();
+       if (WC->upload_length > 0) {
+               free(WC->upload);
+               WC->upload_length = 0;
+       }
+}
+
+
+/*@}*/
diff --git a/webcit/src/webcit.h b/webcit/src/webcit.h
new file mode 100644 (file)
index 0000000..92eae15
--- /dev/null
@@ -0,0 +1,746 @@
+/* $Id$ */
+
+/** we need _GNU_SOURCE for various functions arround the NLS-Stuff */
+#define _GNU_SOURCE
+
+
+#include <ctype.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <sys/poll.h>
+#include <string.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/utsname.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#include <locale.h>
+extern locale_t wc_locales[];
+#define _(string)      gettext(string)
+#else
+#define _(string)      (string)
+#endif
+
+/*
+ * Uncomment to dump an HTTP trace to stderr
+#define HTTP_TRACING 1
+ */
+
+#ifdef HTTP_TRACING
+#undef HAVE_ZLIB_H
+#undef HAVE_ZLIB
+#endif
+
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#ifdef HAVE_ICAL_H
+#ifdef HAVE_LIBICAL
+#define WEBCIT_WITH_CALENDAR_SERVICE 1
+#endif
+#endif
+
+
+
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+/* Work around PACKAGE/VERSION defs that are (not supposed to be?) in ical.h */
+#ifdef PACKAGE
+# define CTDL_PACKAGE PACKAGE
+# undef PACKAGE
+#endif
+#ifdef VERSION
+# define CTDL_VERSION VERSION
+# undef VERSION
+#endif
+#include <ical.h>
+#ifdef CTDL_PACKAGE
+# ifdef PACKAGE
+#  undef PACKAGE
+# endif
+# define PACKAGE CTDL_PACKAGE
+# undef CTDL_PACKAGE
+#endif
+#ifdef CTDL_VERSION
+# ifdef VERSION
+#  undef VERSION
+# endif
+# define VERSION CTDL_VERSION
+# undef CTDL_VERSION
+#endif
+#endif
+
+
+
+#ifdef HAVE_OPENSSL
+/* Work around RedHat's b0rken OpenSSL includes */
+#define OPENSSL_NO_KRB5
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#endif
+
+#define CALENDAR_ROOM_NAME     "Calendar"
+#define PRODID "-//Citadel//NONSGML Citadel Calendar//EN"
+
+#define SIZ                    4096            /* generic buffer size */
+
+#define TRACE fprintf(stderr, "Checkpoint: %s, %d\n", __FILE__, __LINE__)
+
+#define SLEEPING               180             /* TCP connection timeout */
+#define WEBCIT_TIMEOUT         900             /* WebCit session timeout */
+#define PORT_NUM               2000            /* port number to listen on */
+#define SERVER                 "WebCit v6.73"  /* who's in da house */
+#define DEVELOPER_ID           0
+#define CLIENT_ID              4
+#define CLIENT_VERSION         673             /* This version of WebCit */
+#define MINIMUM_CIT_VERSION    673             /* min required Citadel ver. */
+#define DEFAULT_HOST           "localhost"     /* Default Citadel server */
+#define DEFAULT_PORT           "504"
+#define LB                     (1)             /* Internal escape chars */
+#define RB                     (2)
+#define QU                     (3)
+#define TARGET                 "webcit01"      /* Target for inline URL's */
+#define HOUSEKEEPING           15              /* Housekeeping frequency */
+#define MIN_WORKER_THREADS     5
+#define MAX_WORKER_THREADS     250
+#define LISTEN_QUEUE_LENGTH    100             /* listen() backlog queue */
+
+#define USERCONFIGROOM         "My Citadel Config"
+#define DEFAULT_MAXMSGS                20
+
+
+/*
+ * Room flags (from Citadel)
+ *
+ * bucket one...
+ */
+#define QR_PERMANENT   1               /**< Room does not purge                */
+#define QR_INUSE       2               /**< Set if in use, clear if avail      */
+#define QR_PRIVATE     4               /**< Set for any type of private room   */
+#define QR_PASSWORDED  8               /**< Set if there's a password too      */
+#define QR_GUESSNAME   16              /**< Set if it's a guessname room       */
+#define QR_DIRECTORY   32              /**< Directory room                     */
+#define QR_UPLOAD      64              /**< Allowed to upload                  */
+#define QR_DOWNLOAD    128             /**< Allowed to download                */
+#define QR_VISDIR      256             /**< Visible directory                  */
+#define QR_ANONONLY    512             /**< Anonymous-Only room                */
+#define QR_ANONOPT     1024            /**< Anonymous-Option room              */
+#define QR_NETWORK     2048            /**< Shared network room                */
+#define QR_PREFONLY    4096            /**< Preferred status needed to enter   */
+#define QR_READONLY    8192            /**< Aide status required to post       */
+#define QR_MAILBOX     16384           /**< Set if this is a private mailbox   */
+
+/**
+ * bucket two...
+ */
+#define QR2_SYSTEM     1               /**< System room; hide by default       */
+#define QR2_SELFLIST   2               /**< Self-service mailing list mgmt     */
+
+/**
+ * user/room access
+ */
+#define UA_KNOWN       2
+#define UA_GOTOALLOWED 4
+#define UA_HASNEWMSGS  8
+#define UA_ZAPPED      16
+
+
+/**
+ * User flags (from Citadel)
+ */
+#define US_NEEDVALID   1               /**< User needs to be validated         */
+#define US_PERM                4               /**< Permanent user                     */
+#define US_LASTOLD     16              /**< Print last old message with new    */
+#define US_EXPERT      32              /**< Experienced user                   */
+#define US_UNLISTED    64              /**< Unlisted userlog entry             */
+#define US_NOPROMPT    128             /**< Don't prompt after each message    */
+#define US_PROMPTCTL   256             /**< <N>ext & <S>top work at prompt     */
+#define US_DISAPPEAR   512             /**< Use "disappearing msg prompts"     */
+#define US_REGIS       1024            /**< Registered user                    */
+#define US_PAGINATOR   2048            /**< Pause after each screen of text    */
+#define US_INTERNET    4096            /**< Internet mail privileges           */
+#define US_FLOORS      8192            /**< User wants to see floors           */
+#define US_COLOR       16384           /**< User wants ANSI color support      */
+#define US_USER_SET    (US_LASTOLD | US_EXPERT | US_UNLISTED | \
+                       US_NOPROMPT | US_DISAPPEAR | US_PAGINATOR | \
+                       US_FLOORS | US_COLOR | US_PROMPTCTL )
+
+
+
+/** \brief     Linked list of lines appearing in an HTTP client request */
+struct httprequest {
+       struct httprequest *next;  /**< the next request in the list */
+       char line[SIZ];            /**< the request line */
+};
+
+/**
+ * \brief      Linked list of session variables encoded in an x-www-urlencoded content type
+ */
+struct urlcontent {
+       struct urlcontent *next;   /**< the next variable in the list */ 
+       char url_key[32];          /**< the variable name */
+       char *url_data;            /**< its value */
+};
+
+/**
+ * \brief information about us ???
+ */ 
+struct serv_info {
+       int serv_pid;                   /**< Process ID of the Citadel server */
+       char serv_nodename[32];         /**< Node name of the Citadel server */
+       char serv_humannode[64];        /**< human readable node name of the Citadel server */
+       char serv_fqdn[64];             /**< fully quallified Domain Name (such as uncensored.citadel.org) */
+       char serv_software[64];         /**< What version does our connected citadel server use */
+       int serv_rev_level;             /**< Whats the citadel server revision */
+       char serv_bbs_city[64];         /**< Geographic location of the Citadel server */
+       char serv_sysadm[64];           /**< Name of system administrator */
+       char serv_moreprompt[SIZ];      /**< Whats the commandline textprompt */
+       int serv_ok_floors;             /**< nonzero == server supports floors */
+       int serv_supports_ldap;         /**< is the server linked against an ldap tree for adresses? */
+       int serv_newuser_disabled;      /**< Has the server disabled self-service new user creation? */
+};
+
+
+
+/**
+ * \brief This struct holds a list of rooms for \\\<G\\\>oto operations.
+ */
+struct march {
+       struct march *next;       /**< pointer to next in linked list */
+       char march_name[128];     /**< name of room */
+       int march_floor;          /**< floor number of room */
+       int march_order;          /**< sequence in which we are to visit this room */
+};
+
+/* *
+ * \brief      This struct holds a list of rooms for client display.
+ *             It is a binary tree.
+ */
+struct roomlisting {
+       struct roomlisting *lnext;      /**< pointer to 'left' tree node */
+       struct roomlisting *rnext;      /**< pointer to 'right' tree node */
+       char rlname[128];               /**< name of room */
+       unsigned rlflags;               /**< room flags */
+       int rlfloor;                    /**< the floor it resides on */
+       int rlorder;                    /**< room listing order */
+};
+
+
+
+/**
+ * \brief Dynamic content for variable substitution in templates
+ */
+struct wcsubst {
+       struct wcsubst *next;       /**< next item in the list */  
+       int wcs_type;                       /**< which type of ??? */
+       char wcs_key[32];                   /**< ??? what?*/
+       void *wcs_value;                    /**< ???? what?*/
+       void (*wcs_function)(void); /**< funcion hook ???*/
+};
+
+/**
+ * \brief Values for wcs_type
+ */
+enum {
+       WCS_STRING,   /**< its a string */
+       WCS_FUNCTION, /**< its a function callback */
+       WCS_SERVCMD   /**< its a command to send to the citadel server */
+};
+
+/**
+ * \brief mail attachment ???
+ */
+struct wc_attachment {
+       struct wc_attachment *next;/**< pointer to next in list */
+       size_t length;                     /**< length of the contenttype */
+       char content_type[SIZ];    /**< the content itself ???*/
+       char filename[SIZ];                /**< the filename hooked to this content ??? */
+       char *data;                /**< the data pool; aka this content */
+};
+
+/**
+ * \brief message summary structure. ???
+ */
+struct message_summary {
+       time_t date;        /**< its creation date */
+       long msgnum;            /**< the message number on the citadel server */
+       char from[128];         /**< the author */
+       char to[128];           /**< the recipient */
+       char subj[128];         /**< the title / subject */
+       int hasattachments;     /**< does it have atachments? */
+       int is_new;         /**< is it yet read? */
+};
+
+/**
+ * \brief  Data structure for roomlist-to-folderlist conversion 
+ */
+struct folder {
+       int floor;      /**< which floor is it on */
+       char room[SIZ]; /**< which roomname ??? */
+       char name[SIZ]; /**< which is its own name??? */
+       int hasnewmsgs; /**< are there unread messages inside */
+       int is_mailbox; /**< is it a mailbox?  */
+       int selectable; /**< can we select it ??? */
+       int view;       /**< whats its default view? inbox/calendar.... */
+};
+
+/**
+ * \brief One of these is kept for each active Citadel session.
+ * HTTP transactions are bound to on e at a time.
+ */
+struct wcsession {
+       struct wcsession *next;                 /**< Linked list */
+       int wc_session;                         /**< WebCit session ID */
+       char wc_username[128];                  /**< login name of current user */
+       char wc_fullname[128];                  /**< Screen name of current user */
+       char wc_password[128];                  /**< Password of current user */
+       char wc_roomname[256];                  /**< Room we are currently in */
+       int connected;                          /**< nonzero == we are connected to Citadel */
+       int logged_in;                          /**< nonzero == we are logged in  */
+       int axlevel;                            /**< this user's access level */
+       int is_aide;                            /**< nonzero == this user is an Aide */
+       int is_room_aide;                       /**< nonzero == this user is a Room Aide in this room */
+       int http_sock;                          /**< HTTP server socket */
+       int serv_sock;                          /**< Client socket to Citadel server */
+       int chat_sock;                          /**< Client socket to Citadel server - for chat */
+       unsigned room_flags;                    /**< flags associated with the current room */
+       int wc_view;                            /**< view for the current room */
+       int wc_default_view;                    /**< default view for the current room */
+       int wc_is_trash;                        /**< nonzero == current room is a Trash folder */
+       int wc_floor;                           /**< floor number of current room */
+       char ugname[128];                       /**< where does 'ungoto' take us */
+       long uglsn;                             /**< last seen message number for ungoto */
+       int upload_length;                      /**< content length of http-uploaded data */
+       char *upload;                           /**< pointer to http-uploaded data */
+       char upload_filename[PATH_MAX];         /**< filename of http-uploaded data */
+       char upload_content_type[256];          /**< content type of http-uploaded data */
+       int new_mail;                           /**< user has new mail waiting */
+       int remember_new_mail;                  /**< last count of new mail messages */
+       int need_regi;                          /**< This user needs to register. */
+       int need_vali;                          /**< New users require validation. */
+       char cs_inet_email[256];                /**< User's preferred Internet addr. */
+       pthread_mutex_t SessionMutex;           /**< mutex for exclusive access */
+       time_t lastreq;                         /**< Timestamp of most recent HTTP */
+       int killthis;                           /**< Nonzero == purge this session */
+       struct march *march;                    /**< march mode room list */
+       char reply_to[512];                     /**< reply-to address */
+       long msgarr[10000];                     /**< for read operations */
+       int num_summ;                           /**< number of messages in mailbox summary view */
+       struct message_summary *summ;           /**< array of messages for mailbox summary view */
+       int is_wap;                             /**< Client is a WAP gateway */
+       struct urlcontent *urlstrings;          /**< variables passed to webcit in a URL */
+       struct wcsubst *vars;                   /**< HTTP variable substitutions for this page */
+       char this_page[512];                    /**< URL of current page */
+       char http_host[512];                    /**< HTTP Host: header */
+       char *preferences;                      /**< WebCit preferences for this user */
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE            
+       /** \brief ical???? */                          
+       struct disp_cal {                                       
+               icalcomponent *cal;             /**< cal items for display */
+               long cal_msgnum;                /**< cal msgids for display */
+       } *disp_cal;                                            
+       int num_cal;                            /**< number of calendar items for display */
+#endif                                                                                 
+       struct wc_attachment *first_attachment; /**< linked list of attachments for 'enter message' */
+       char last_chat_user[256];               /**< ??? todo */
+       char ImportantMessage[SIZ];             /**< ??? todo */
+       int ctdl_pid;                           /**< Session ID on the Citadel server */
+       char httpauth_user[256];                /**< only for GroupDAV sessions */
+       char httpauth_pass[256];                /**< only for GroupDAV sessions */
+       size_t burst_len;                       /** <??? todo */
+       size_t burst_alloc;                     /** <??? todo */
+       char *burst;                            /** <??? todo */
+       int gzip_ok;                            /**< Nonzero if Accept-encoding: gzip */
+       int is_mailbox;                         /**< the current room is a private mailbox */
+       struct folder *cache_fold;              /**< cache the iconbar room list */
+       int cache_max_folders;                  /**< ??? todo */
+       int cache_num_floors;                   /**< ??? todo */
+       time_t cache_timestamp;                 /**< ??? todo */
+       int current_iconbar;                    /**< What is currently in the iconbar? */
+       char floordiv_expanded[32];             /**< which floordiv currently expanded */
+       int selected_language;                  /**< Language selected by user */
+       time_t last_pager_check;                /**< last time we polled for instant msgs */
+};
+
+/** values for WC->current_iconbar */
+enum {
+       current_iconbar_menu,     /**< view the icon menue */
+       current_iconbar_roomlist  /**< view the roomtree */
+};
+
+
+#define num_parms(source)              num_tokens(source, '|') 
+
+/* Per-session data */
+#define WC ((struct wcsession *)pthread_getspecific(MyConKey))
+extern pthread_key_t MyConKey;
+
+/* Per-thread SSL context */
+#ifdef HAVE_OPENSSL
+#define THREADSSL ((SSL *)pthread_getspecific(ThreadSSL))
+extern pthread_key_t ThreadSSL;
+#endif
+
+struct serv_info serv_info;
+extern char floorlist[128][SIZ];
+extern char *axdefs[];
+extern char *ctdlhost, *ctdlport;
+extern int http_port;
+extern char *server_cookie;
+extern int is_https;
+extern int setup_wizard;
+extern char wizard_filename[];
+extern time_t if_modified_since;
+extern int follow_xff;
+void do_setup_wizard(void);
+
+void stuff_to_cookie(char *cookie, int session,
+                       char *user, char *pass, char *room);
+void cookie_to_stuff(char *cookie, int *session,
+                char *user, size_t user_len,
+                char *pass, size_t pass_len,
+                char *room, size_t room_len);
+void locate_host(char *, int);
+void become_logged_in(char *, char *, char *);
+void do_login(void);
+void display_login(char *mesg);
+void do_welcome(void);
+void do_logout(void);
+void display_main_menu(void);
+void display_aide_menu(void);
+void display_advanced_menu(void);
+void slrp_highest(void);
+void gotonext(void);
+void ungoto(void);
+void get_serv_info(char *, char *);
+int uds_connectsock(char *);
+int tcp_connectsock(char *, char *);
+void serv_getln(char *strbuf, int bufsize);
+void serv_puts(char *string);
+void who(void);
+void who_inner_div(void);
+void fmout(char *align);
+void pullquote_fmout(void);
+void wDumpContent(int);
+void serv_printf(const char *format,...);
+char *bstr(char *key);
+void urlesc(char *, char *);
+void urlescputs(char *);
+void jsesc(char *, char *);
+void jsescputs(char *);
+void output_headers(    int do_httpheaders,
+                       int do_htmlhead,
+                       int do_room_banner,
+                       int unset_cookies,
+                       int suppress_check,
+                       int cache);
+void wprintf(const char *format,...);
+void output_static(char *what);
+void stresc(char *target, char *strbuf, int nbsp, int nolinebreaks);
+void escputs(char *strbuf);
+void url(char *buf);
+void escputs1(char *strbuf, int nbsp, int nolinebreaks);
+void msgesc(char *target, char *strbuf);
+void msgescputs(char *strbuf);
+int extract_int(const char *source, int parmnum);
+long extract_long(const char *source, int parmnum);
+void stripout(char *str, char leftboundary, char rightboundary);
+void dump_vars(void);
+void embed_main_menu(void);
+void serv_read(char *buf, int bytes);
+int haschar(char *, char);
+void readloop(char *oper);
+void read_message(long msgnum, int printable_view, char *section);
+void embed_message(char *msgnum_as_string);
+void print_message(char *msgnum_as_string);
+void display_headers(char *msgnum_as_string);
+void text_to_server(char *ptr);
+void text_to_server_qp(char *ptr);
+void display_enter(void);
+void post_message(void);
+void confirm_delete_msg(void);
+void delete_msg(void);
+void confirm_move_msg(void);
+void move_msg(void);
+void userlist(void);
+void showuser(void);
+void display_page(void);
+void page_user(void);
+void do_chat(void);
+void display_private(char *rname, int req_pass);
+void goto_private(void);
+void zapped_list(void);
+void display_zap(void);
+void zap(void);
+void display_success(char *);
+void authorization_required(const char *message);
+void display_entroom(void);
+void entroom(void);
+void display_editroom(void);
+void netedit(void);
+void editroom(void);
+void display_whok(void);
+void do_invt_kick(void);
+void server_to_text(void);
+void save_edit(char *description, char *enter_cmd, int regoto);
+void display_edit(char *description, char *check_cmd,
+                 char *read_cmd, char *save_cmd, int with_room_banner);
+int gotoroom(char *gname);
+void confirm_delete_room(void);
+void delete_room(void);
+void validate(void);
+void display_graphics_upload(char *, char *, char *);
+void do_graphics_upload(char *upl_cmd);
+void serv_read(char *buf, int bytes);
+void serv_gets(char *strbuf);
+void serv_write(char *buf, int nbytes);
+void serv_puts(char *string);
+void serv_printf(const char *format,...);
+void load_floorlist(void);
+void display_reg(int);
+void display_changepw(void);
+void changepw(void);
+void display_edit_node(void);
+void edit_node(void);
+void display_netconf(void);
+void display_confirm_delete_node(void);
+void delete_node(void);
+void display_add_node(void);
+void add_node(void);
+void terminate_session(void);
+void edit_me(void);
+void display_siteconfig(void);
+void siteconfig(void);
+void display_generic(void);
+void do_generic(void);
+void ajax_servcmd(void);
+void display_menubar(int);
+void smart_goto(char *);
+void worker_entry(void);
+void session_loop(struct httprequest *);
+size_t wc_strftime(char *s, size_t max, const char *format, const struct tm *tm);
+void fmt_date(char *buf, time_t thetime, int brief);
+void fmt_time(char *buf, time_t thetime);
+void httpdate(char *buf, time_t thetime);
+time_t httpdate_to_timestamp(char *buf);
+void end_webcit_session(void);
+void page_popup(void);
+void chat_recv(void);
+void chat_send(void);
+void http_redirect(char *);
+void clear_local_substs(void);
+void svprintf(char *keyname, int keytype, const char *format,...);
+void svcallback(char *keyname, void (*fcn_ptr)() );
+void do_template(void *templatename);
+int lingering_close(int fd);
+char *memreadline(char *start, char *buf, int maxlen);
+int num_tokens (char *source, char tok);
+void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen);
+void remove_token(char *source, int parmnum, char separator);
+char *load_mimepart(long msgnum, char *partnum);
+int pattern2(char *search, char *patn);
+void do_edit_vcard(long, char *, char *);
+void edit_vcard(void);
+void submit_vcard(void);
+void striplt(char *);
+void select_user_to_edit(char *message, char *preselect);
+void delete_user(char *);
+void display_edituser(char *who, int is_new);
+void create_user(void);
+void edituser(void);
+void do_change_view(int);
+void change_view(void);
+void folders(void);
+void load_preferences(void);
+void save_preferences(void);
+void get_preference(char *key, char *value, size_t value_len);
+void set_preference(char *key, char *value, int save_to_server);
+void knrooms(void);
+int is_msg_in_mset(char *mset, long msgnum);
+char *safestrncpy(char *dest, const char *src, size_t n);
+void display_addressbook(long msgnum, char alpha);
+void offer_start_page(void);
+void convenience_page(char *titlebarcolor, char *titlebarmsg, char *messagetext);
+void change_start_page(void);
+void output_html(char *, int);
+void display_floorconfig(char *);
+void delete_floor(void);
+void create_floor(void);
+void rename_floor(void);
+void do_listsub(void);
+void toggle_self_service(void);
+void summary(void);
+void summary_inner_div(void);
+ssize_t write(int fd, const void *buf, size_t count);
+void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum);
+void display_calendar(long msgnum);
+void display_task(long msgnum);
+void display_note(long msgnum);
+void updatenote(void);
+void do_calendar_view(void);
+void do_tasks_view(void);
+void free_calendar_buffer(void);
+void calendar_summary_view(void);
+int load_msg_ptrs(char *servcmd, int with_headers);
+void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen);
+int CtdlDecodeBase64(char *dest, const char *source, size_t length);
+void free_attachments(struct wcsession *sess);
+void free_march_list(struct wcsession *wcf);
+void set_room_policy(void);
+void display_inetconf(void);
+void save_inetconf(void);
+void generate_uuid(char *);
+void CtdlMakeTempFileName(char *, int);
+void display_preferences(void);
+void set_preferences(void);
+void recp_autocomplete(char *);
+void begin_ajax_response(void);
+void end_ajax_response(void);
+void initialize_viewdefs(void);
+void initialize_axdefs(void);
+void list_all_rooms_by_floor(char *viewpref);
+
+#ifdef WEBCIT_WITH_CALENDAR_SERVICE
+void display_edit_task(void);
+void save_task(void);
+void display_edit_event(void);
+void save_event(void);
+void display_icaltimetype_as_webform(struct icaltimetype *, char *);
+void icaltime_from_webform(struct icaltimetype *result, char *prefix);
+void icaltime_from_webform_dateonly(struct icaltimetype *result, char *prefix);
+void display_edit_individual_event(icalcomponent *supplied_vtodo, long msgnum);
+void save_individual_event(icalcomponent *supplied_vtodo, long msgnum);
+void respond_to_request(void);
+void handle_rsvp(void);
+void ical_dezonify(icalcomponent *cal);
+void partstat_as_string(char *buf, icalproperty *attendee);
+icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp);
+void check_attendee_availability(icalcomponent *supplied_vevent);
+void do_freebusy(char *req);
+#endif
+
+#ifdef ENABLE_NLS
+void initialize_locales(void);
+#endif
+
+extern char *months[];
+extern char *days[];
+void read_server_binary(char *buffer, size_t total_len);
+char *read_server_text(void);
+int goto_config_room(void);
+long locate_user_vcard(char *username, long usernum);
+void sleeeeeeeeeep(int);
+void http_transmit_thing(char *thing, size_t length, char *content_type,
+                        int is_static);
+void unescape_input(char *buf);
+void do_iconbar(void);
+void do_iconbar_roomlist(void);
+void do_selected_iconbar(void);
+void display_customize_iconbar(void);
+void commit_iconbar(void);
+int CtdlDecodeQuotedPrintable(char *decoded, char *encoded, int sourcelen);
+void spawn_another_worker_thread(void);
+void display_rss(char *roomname, char *request_method);
+void set_floordiv_expanded(char *which_floordiv);
+void offer_languages(void);
+void set_selected_language(char *);
+void go_selected_language(void);
+void stop_selected_language(void);
+void httplang_to_locale(char *LocaleString);
+void tabbed_dialog(int num_tabs, char *tabnames[]);
+void begin_tab(int tabnum, int num_tabs);
+void end_tab(int tabnum, int num_tabs);
+void str_wiki_index(char *s);
+void display_wiki_page(void);
+char *bmstrcasestr(char *text, char *pattern);
+
+#ifdef HAVE_ICONV
+iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode);
+#endif
+
+void embed_room_banner(char *, int);
+
+/* navbar types that can be passed to embed_room_banner */
+enum {
+       navbar_none,
+       navbar_default
+};
+
+
+#ifdef HAVE_OPENSSL
+void init_ssl(void);
+void endtls(void);
+void ssl_lock(int mode, int n, const char *file, int line);
+int starttls(int sock);
+extern SSL_CTX *ssl_ctx;  
+int client_read_ssl(char *buf, int bytes, int timeout);
+void client_write_ssl(char *buf, int nbytes);
+#endif
+
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+int ZEXPORT compress_gzip(Bytef * dest, uLongf * destLen,
+                          const Bytef * source, uLong sourceLen, int level);
+#endif
+
+#ifdef HAVE_ICONV
+void utf8ify_rfc822_string(char *buf);
+#endif
+
+void begin_burst(void);
+void end_burst(void);
+
+extern char *hourname[];       /**< Names of hours (12am, 1am, etc.) */
+
+void http_datestring(char *buf, size_t n, time_t xtime);
+
+
+/** Views (from citadel.h) */
+#define        VIEW_BBS                0       /**< Traditional Citadel BBS view */
+#define VIEW_MAILBOX           1       /**< Mailbox summary */
+#define VIEW_ADDRESSBOOK       2       /**< Address book view */
+#define VIEW_CALENDAR          3       /**< Calendar view */
+#define VIEW_TASKS             4       /**< Tasks view */
+#define VIEW_NOTES             5       /**<  Notes view */
+#define VIEW_WIKI              6       /**<  Wiki view */
+#define VIEW_CALBRIEF          7       /**< Brief Calendar view */
+
+
+/* These should be empty, but we have them for testing */
+#define DEFAULT_HTTPAUTH_USER  ""
+#define DEFAULT_HTTPAUTH_PASS  ""
+
diff --git a/webcit/src/webserver.c b/webcit/src/webserver.c
new file mode 100644 (file)
index 0000000..985add6
--- /dev/null
@@ -0,0 +1,764 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup Webserver This contains a simple multithreaded TCP server manager.  It sits around
+ * waiting on the specified port for incoming HTTP connections.  When a
+ * connection is established, it calls context_loop() from context_loop.c.
+ * \ingroup WebcitHttpServer
+ */
+
+/*@{*/
+#include "webcit.h"
+#include "webserver.h"
+
+#ifndef HAVE_SNPRINTF
+int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp);
+#endif
+
+int verbosity = 9;             /**< Logging level */
+int msock;                         /**< master listening socket */
+int is_https = 0;              /**< Nonzero if I am an HTTPS service */
+int follow_xff = 0;            /**< Follow X-Forwarded-For: header */
+extern void *context_loop(int);
+extern void *housekeeping_loop(void);
+extern pthread_mutex_t SessionListMutex;
+extern pthread_key_t MyConKey;
+
+
+char *server_cookie = NULL; /**< our Cookie connection to the client */
+
+int http_port = PORT_NUM;      /**< Port to listen on */
+
+char *ctdlhost = DEFAULT_HOST; /**< our name */
+char *ctdlport = DEFAULT_PORT; /**< our Port */
+int setup_wizard = 0;          /**< should we run the setup wizard? \todo */
+char wizard_filename[PATH_MAX];/**< where's the setup wizard? */
+
+/** 
+ * \brief This is a generic function to set up a master socket for listening on
+ * a TCP port.  The server shuts down if the bind fails.
+ * \param ip_addr ip to bind to
+ * \param port_number the port to bind to 
+ * \param queue_len the size of the input queue ????
+ */
+int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
+{
+       struct sockaddr_in sin;
+       int s, i;
+
+       memset(&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       if (ip_addr == NULL) {
+               sin.sin_addr.s_addr = INADDR_ANY;
+       } else {
+               sin.sin_addr.s_addr = inet_addr(ip_addr);
+       }
+
+       if (sin.sin_addr.s_addr == INADDR_NONE) {
+               sin.sin_addr.s_addr = INADDR_ANY;
+       }
+
+       if (port_number == 0) {
+               lprintf(1, "Cannot start: no port number specified.\n");
+               exit(1);
+       }
+       sin.sin_port = htons((u_short) port_number);
+
+       s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto));
+       if (s < 0) {
+               lprintf(1, "Can't create a socket: %s\n", strerror(errno));
+               exit(errno);
+       }
+       /** Set some socket options that make sense. */
+       i = 1;
+       setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
+
+       if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+               lprintf(1, "Can't bind: %s\n", strerror(errno));
+               exit(errno);
+       }
+       if (listen(s, queue_len) < 0) {
+               lprintf(1, "Can't listen: %s\n", strerror(errno));
+               exit(errno);
+       }
+       return (s);
+}
+
+
+
+/**
+ * \brief Create a Unix domain socket and listen on it
+ * \param sockpath file name of the unix domain socket
+ * \param queue_len queue size of the kernel fifo????
+ */
+int ig_uds_server(char *sockpath, int queue_len)
+{
+       struct sockaddr_un addr;
+       int s;
+       int i;
+       int actual_queue_len;
+
+       actual_queue_len = queue_len;
+       if (actual_queue_len < 5) actual_queue_len = 5;
+
+       i = unlink(sockpath);
+       if (i != 0) if (errno != ENOENT) {
+               lprintf(1, "citserver: can't unlink %s: %s\n",
+                       sockpath, strerror(errno));
+               exit(errno);
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
+
+       s = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (s < 0) {
+               lprintf(1, "citserver: Can't create a socket: %s\n",
+                       strerror(errno));
+               exit(errno);
+       }
+
+       if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+               lprintf(1, "citserver: Can't bind: %s\n",
+                       strerror(errno));
+               exit(errno);
+       }
+
+       if (listen(s, actual_queue_len) < 0) {
+               lprintf(1, "citserver: Can't listen: %s\n",
+                       strerror(errno));
+               exit(errno);
+       }
+
+       chmod(sockpath, 0777);
+       return(s);
+}
+
+
+
+
+/**
+ * \brief Read data from the client socket.
+ * \param sock socket fd to read from ???
+ * \param buf buffer to read into 
+ * \param bytes how large is the read buffer?
+ * \param timeout how long should we wait for input?
+ * \return values are\
+ *      1       Requested number of bytes has been read.\
+ *      0       Request timed out.\
+ *        -1           Connection is broken, or other error.
+ */
+int client_read_to(int sock, char *buf, int bytes, int timeout)
+{
+       int len, rlen;
+       fd_set rfds;
+       struct timeval tv;
+       int retval;
+
+
+#ifdef HAVE_OPENSSL
+       if (is_https) {
+               return (client_read_ssl(buf, bytes, timeout));
+       }
+#endif
+
+       len = 0;
+       while (len < bytes) {
+               FD_ZERO(&rfds);
+               FD_SET(sock, &rfds);
+               tv.tv_sec = timeout;
+               tv.tv_usec = 0;
+
+               retval = select((sock) + 1, &rfds, NULL, NULL, &tv);
+               if (FD_ISSET(sock, &rfds) == 0) {
+                       return (0);
+               }
+
+               rlen = read(sock, &buf[len], bytes - len);
+
+               if (rlen < 1) {
+                       lprintf(2, "client_read() failed: %s\n",
+                               strerror(errno));
+                       return (-1);
+               }
+               len = len + rlen;
+       }
+
+#ifdef HTTP_TRACING
+       write(2, "\033[32m", 5);
+       write(2, buf, bytes);
+       write(2, "\033[30m", 5);
+#endif
+       return (1);
+}
+
+/**
+ * \brief write data to the client
+ * \param buf data to write to the client
+ * \param count size of buffer
+ */
+ssize_t client_write(const void *buf, size_t count)
+{
+       char *newptr;
+       size_t newalloc;
+
+       if (WC->burst != NULL) {
+               if ((WC->burst_len + count) >= WC->burst_alloc) {
+                       newalloc = (WC->burst_alloc * 2);
+                       if ((WC->burst_len + count) >= newalloc) {
+                               newalloc += count;
+                       }
+                       newptr = realloc(WC->burst, newalloc);
+                       if (newptr != NULL) {
+                               WC->burst = newptr;
+                               WC->burst_alloc = newalloc;
+                       }
+               }
+               if ((WC->burst_len + count) < WC->burst_alloc) {
+                       memcpy(&WC->burst[WC->burst_len], buf, count);
+                       WC->burst_len += count;
+                       return (count);
+               }
+               else {
+                       return(-1);
+               }
+       }
+#ifdef HAVE_OPENSSL
+       if (is_https) {
+               client_write_ssl((char *) buf, count);
+               return (count);
+       }
+#endif
+#ifdef HTTP_TRACING
+       write(2, "\033[34m", 5);
+       write(2, buf, count);
+       write(2, "\033[30m", 5);
+#endif
+       return (write(WC->http_sock, buf, count));
+}
+
+/**
+ * \brief what burst???
+ */
+void begin_burst(void)
+{
+       if (WC->burst != NULL) {
+               free(WC->burst);
+               WC->burst = NULL;
+       }
+       WC->burst_len = 0;
+       WC->burst_alloc = 32768;
+       WC->burst = malloc(WC->burst_alloc);
+}
+
+
+/**
+ * \brief uses the same calling syntax as compress2(), but it
+ * creates a stream compatible with HTTP "Content-encoding: gzip"
+ */
+#ifdef HAVE_ZLIB
+#define DEF_MEM_LEVEL 8 /**< memlevel??? */
+#define OS_CODE 0x03   /**< unix */
+int ZEXPORT compress_gzip(Bytef * dest,         /**< compressed buffer*/
+                                                 uLongf * destLen,     /**< length of the compresed data */
+                                                 const Bytef * source, /**< source to encode */
+                                                 uLong sourceLen,      /**< length of the source to encode */
+                                                 int level)            /**< what level??? */
+{
+       const int gz_magic[2] = { 0x1f, 0x8b }; /** gzip magic header */
+
+       /** write gzip header */
+       sprintf((char *) dest, "%c%c%c%c%c%c%c%c%c%c",
+               gz_magic[0], gz_magic[1], Z_DEFLATED,
+               0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /** xflags */ ,
+               OS_CODE);
+
+       /* normal deflate */
+       z_stream stream;
+       int err;
+       stream.next_in = (Bytef *) source;
+       stream.avail_in = (uInt) sourceLen;
+       stream.next_out = dest + 10L;   // after header
+       stream.avail_out = (uInt) * destLen;
+       if ((uLong) stream.avail_out != *destLen)
+               return Z_BUF_ERROR;
+
+       stream.zalloc = (alloc_func) 0;
+       stream.zfree = (free_func) 0;
+       stream.opaque = (voidpf) 0;
+
+       err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
+                          DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+       if (err != Z_OK)
+               return err;
+
+       err = deflate(&stream, Z_FINISH);
+       if (err != Z_STREAM_END) {
+               deflateEnd(&stream);
+               return err == Z_OK ? Z_BUF_ERROR : err;
+       }
+       *destLen = stream.total_out + 10L;
+
+       /* write CRC and Length */
+       uLong crc = crc32(0L, source, sourceLen);
+       int n;
+       for (n = 0; n < 4; ++n, ++*destLen) {
+               dest[*destLen] = (int) (crc & 0xff);
+               crc >>= 8;
+       }
+       uLong len = stream.total_in;
+       for (n = 0; n < 4; ++n, ++*destLen) {
+               dest[*destLen] = (int) (len & 0xff);
+               len >>= 8;
+       }
+       err = deflateEnd(&stream);
+       return err;
+}
+#endif
+
+/**
+ * \brief what burst???
+ */
+void end_burst(void)
+{
+       size_t the_len;
+       char *the_data;
+
+       if (WC->burst == NULL)
+               return;
+
+       the_len = WC->burst_len;
+       the_data = WC->burst;
+
+       WC->burst_len = 0;
+       WC->burst_alloc = 0;
+       WC->burst = NULL;
+
+#ifdef HAVE_ZLIB
+       /* Handle gzip compression */
+       if (WC->gzip_ok) {
+               char *compressed_data = NULL;
+               uLongf compressed_len;
+
+               compressed_len = (uLongf) ((the_len * 101) / 100) + 100;
+               compressed_data = malloc(compressed_len);
+
+               if (compress_gzip((Bytef *) compressed_data,
+                                 &compressed_len,
+                                 (Bytef *) the_data,
+                                 (uLongf) the_len, Z_BEST_SPEED) == Z_OK) {
+                       wprintf("Content-encoding: gzip\r\n");
+                       free(the_data);
+                       the_data = compressed_data;
+                       the_len = compressed_len;
+               } else {
+                       free(compressed_data);
+               }
+       }
+#endif                         /* HAVE_ZLIB */
+
+       wprintf("Content-length: %d\r\n\r\n", the_len);
+       client_write(the_data, the_len);
+       free(the_data);
+       return;
+}
+
+
+
+/**
+ * \brief Read data from the client socket with default timeout.
+ * (This is implemented in terms of client_read_to() and could be
+ * justifiably moved out of sysdep.c)
+ * \param sock the socket fd to read from???
+ * \param buf the buffer to write to
+ * \param bytes how large is the buffer
+ */
+int client_read(int sock, char *buf, int bytes)
+{
+       return (client_read_to(sock, buf, bytes, SLEEPING));
+}
+
+
+/**
+ * \brief Get a LF-terminated line of text from the client.
+ * (This is implemented in terms of client_read() and could be
+ * justifiably moved out of sysdep.c)
+ * \param sock socket fd to get client line from???
+ * \param buf buffer to write read data to
+ * \param bufsiz how many bytes to read
+ * \return  numer of bytes read???
+ */
+int client_getln(int sock, char *buf, int bufsiz)
+{
+       int i, retval;
+
+       /** Read one character at a time.*/
+       for (i = 0;; i++) {
+               retval = client_read(sock, &buf[i], 1);
+               if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1))
+                       break;
+               if ( (!isspace(buf[i])) && (!isprint(buf[i])) ) {
+                       /** Non printable character recieved from client */
+                       return(-1);
+               }
+       }
+
+       /** If we got a long line, discard characters until the newline. */
+       if (i == (bufsiz-1))
+               while (buf[i] != '\n' && retval == 1)
+                       retval = client_read(sock, &buf[i], 1);
+
+       /**
+        * Strip any trailing non-printable characters.
+        */
+       buf[i] = 0;
+       while ((strlen(buf) > 0) && (!isprint(buf[strlen(buf) - 1]))) {
+               buf[strlen(buf) - 1] = 0;
+       }
+       return (retval);
+}
+
+
+/**
+ * \brief      Start running as a daemon.  
+ *
+ * param       do_close_stdio          Only close stdio if set.
+ */
+void start_daemon(int do_close_stdio)
+{
+       if (do_close_stdio) {
+               /* close(0); */
+               close(1);
+               close(2);
+       }
+       signal(SIGHUP, SIG_IGN);
+       signal(SIGINT, SIG_IGN);
+       signal(SIGQUIT, SIG_IGN);
+       if (fork() != 0) {
+               exit(0);
+       }
+}
+
+/**
+ * \brief      Spawn an additional worker thread into the pool.
+ */
+void spawn_another_worker_thread()
+{
+       pthread_t SessThread;   /**< Thread descriptor */
+       pthread_attr_t attr;    /**< Thread attributes */
+       int ret;
+
+       lprintf(3, "Creating a new thread\n");
+
+       /** set attributes for the new thread */
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+       /**
+        * Our per-thread stacks need to be bigger than the default size, otherwise
+        * the MIME parser crashes on FreeBSD, and the IMAP service crashes on
+        * 64-bit Linux.
+        */
+       if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) {
+               lprintf(1, "pthread_attr_setstacksize: %s\n",
+                       strerror(ret));
+               pthread_attr_destroy(&attr);
+       }
+
+       /** now create the thread */
+       if (pthread_create(&SessThread, &attr,
+                          (void *(*)(void *)) worker_entry, NULL)
+           != 0) {
+               lprintf(1, "Can't create thread: %s\n", strerror(errno));
+       }
+
+       /** free up the attributes */
+       pthread_attr_destroy(&attr);
+}
+
+/**
+ * \brief Here's where it all begins.
+ * \param argc number of commandline args
+ * \param argv the commandline arguments
+ */
+int main(int argc, char **argv)
+{
+       pthread_t SessThread;   /**< Thread descriptor */
+       pthread_attr_t attr;    /**< Thread attributes */
+       int a, i;                       /**< General-purpose variables */
+       char tracefile[PATH_MAX];
+       char ip_addr[256];
+       char *webcitdir = WEBCITDIR;
+#ifdef ENABLE_NLS
+       char *locale = NULL;
+       char *mo = NULL;
+#endif /* ENABLE_NLS */
+       char uds_listen_path[PATH_MAX]; /**< listen on a unix domain socket? */
+
+       strcpy(uds_listen_path, "");
+
+       /** Parse command line */
+#ifdef HAVE_OPENSSL
+       while ((a = getopt(argc, argv, "h:i:p:t:x:cfs")) != EOF)
+#else
+       while ((a = getopt(argc, argv, "h:i:p:t:x:cf")) != EOF)
+#endif
+               switch (a) {
+               case 'h':
+                       webcitdir = strdup(optarg);
+                       break;
+               case 'i':
+                       safestrncpy(ip_addr, optarg, sizeof ip_addr);
+                       break;
+               case 'p':
+                       http_port = atoi(optarg);
+                       if (http_port == 0) {
+                               safestrncpy(uds_listen_path, optarg, sizeof uds_listen_path);
+                       }
+                       break;
+               case 't':
+                       safestrncpy(tracefile, optarg, sizeof tracefile);
+                       freopen(tracefile, "w", stdout);
+                       freopen(tracefile, "w", stderr);
+                       freopen(tracefile, "r", stdin);
+                       break;
+               case 'x':
+                       verbosity = atoi(optarg);
+                       break;
+               case 'f':
+                       follow_xff = 1;
+                       break;
+               case 'c':
+                       server_cookie = malloc(256);
+                       if (server_cookie != NULL) {
+                               safestrncpy(server_cookie,
+                                      "Set-cookie: wcserver=",
+                                       256);
+                               if (gethostname
+                                   (&server_cookie[strlen(server_cookie)],
+                                    200) != 0) {
+                                       lprintf(2, "gethostname: %s\n",
+                                               strerror(errno));
+                                       free(server_cookie);
+                               }
+                       }
+                       break;
+               case 's':
+                       is_https = 1;
+                       break;
+               default:
+                       fprintf(stderr, "usage: webserver "
+                               "[-i ip_addr] [-p http_port] "
+                               "[-t tracefile] [-c] [-f] "
+#ifdef HAVE_OPENSSL
+                               "[-s] "
+#endif
+                               "[remotehost [remoteport]]\n");
+                       return 1;
+               }
+
+       if (optind < argc) {
+               ctdlhost = argv[optind];
+               if (++optind < argc)
+                       ctdlport = argv[optind];
+       }
+       /** Tell 'em who's in da house */
+       lprintf(1, SERVER "\n");
+       lprintf(1, "Copyright (C) 1996-2006 by the Citadel development team.\n"
+               "This software is distributed under the terms of the "
+               "GNU General Public License.\n\n"
+       );
+
+       lprintf(9, "Changing directory to %s\n", webcitdir);
+       if (chdir(webcitdir) != 0) {
+               perror("chdir");
+       }
+
+       /** initialize the International Bright Young Thing */
+#ifdef ENABLE_NLS
+
+       initialize_locales();
+
+       locale = setlocale(LC_ALL, "");
+
+       mo = malloc(strlen(webcitdir) + 20);
+       sprintf(mo, "%s/locale", webcitdir);
+       lprintf(9, "Message catalog directory: %s\n",
+               bindtextdomain("webcit", mo)
+       );
+       free(mo);
+       lprintf(9, "Text domain: %s\n",
+               textdomain("webcit")
+       );
+       lprintf(9, "Text domain Charset: %s\n",
+                       bind_textdomain_codeset("webcit","UTF8")
+       );
+#endif
+
+       initialize_viewdefs();
+       initialize_axdefs();
+
+       /**
+        * Set up a place to put thread-specific data.
+        * We only need a single pointer per thread - it points to the
+        * wcsession struct to which the thread is currently bound.
+        */
+       if (pthread_key_create(&MyConKey, NULL) != 0) {
+               lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
+       }
+
+       /**
+        * Set up a place to put thread-specific SSL data.
+        * We don't stick this in the wcsession struct because SSL starts
+        * up before the session is bound, and it gets torn down between
+        * transactions.
+        */
+#ifdef HAVE_OPENSSL
+       if (pthread_key_create(&ThreadSSL, NULL) != 0) {
+               lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
+       }
+#endif
+
+       /**
+        * Bind the server to our favorite port.
+        * There is no need to check for errors, because ig_tcp_server()
+        * exits if it doesn't succeed.
+        */
+
+       if (strlen(uds_listen_path) > 0) {
+               lprintf(2, "Attempting to create listener socket at %s...\n", uds_listen_path);
+               msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH);
+       }
+       else {
+               lprintf(2, "Attempting to bind to port %d...\n", http_port);
+               msock = ig_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH);
+       }
+
+       lprintf(2, "Listening on socket %d\n", msock);
+       signal(SIGPIPE, SIG_IGN);
+
+       pthread_mutex_init(&SessionListMutex, NULL);
+
+       /**
+        * Start up the housekeeping thread
+        */
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+       pthread_create(&SessThread, &attr,
+                      (void *(*)(void *)) housekeeping_loop, NULL);
+
+
+       /**
+        * If this is an HTTPS server, fire up SSL
+        */
+#ifdef HAVE_OPENSSL
+       if (is_https) {
+               init_ssl();
+       }
+#endif
+
+       /** Start a few initial worker threads */
+       for (i = 0; i < (MIN_WORKER_THREADS); ++i) {
+               spawn_another_worker_thread();
+       }
+
+       /* now the original thread becomes another worker */
+       worker_entry();
+       return 0;
+}
+
+
+/**
+ * Entry point for worker threads
+ */
+void worker_entry(void)
+{
+       int ssock;
+       int i = 0;
+       int time_to_die = 0;
+       int fail_this_transaction = 0;
+
+       do {
+               /** Only one thread can accept at a time */
+               fail_this_transaction = 0;
+               ssock = accept(msock, NULL, 0);
+               if (ssock < 0) {
+                       lprintf(2, "accept() failed: %s\n",
+                               strerror(errno));
+               } else {
+                       /** Set the SO_REUSEADDR socket option */
+                       i = 1;
+                       setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR,
+                                  &i, sizeof(i));
+
+                       /** If we are an HTTPS server, go crypto now. */
+#ifdef HAVE_OPENSSL
+                       if (is_https) {
+                               if (starttls(ssock) != 0) {
+                                       fail_this_transaction = 1;
+                                       close(ssock);
+                               }
+                       }
+#endif
+
+                       if (fail_this_transaction == 0) {
+                               /** Perform an HTTP transaction... */
+                               context_loop(ssock);
+                               /** ...and close the socket. */
+                               lingering_close(ssock);
+                       }
+
+               }
+
+       } while (!time_to_die);
+
+       pthread_exit(NULL);
+}
+
+/**
+ * \brief logprintf. log messages 
+ * logs to stderr if loglevel is lower than the verbosity set at startup
+ * \param loglevel level of the message
+ * \param format the printf like format string
+ * \param ... the strings to put into format
+ */
+int lprintf(int loglevel, const char *format, ...)
+{
+       va_list ap;
+
+       if (loglevel <= verbosity) {
+               va_start(ap, format);
+               vfprintf(stderr, format, ap);
+               va_end(ap);
+               fflush(stderr);
+       }
+       return 1;
+}
+
+
+/**
+ * \brief print the actual stack frame.
+ */
+void wc_backtrace(void)
+{
+#ifdef HAVE_BACKTRACE
+       void *stack_frames[50];
+       size_t size, i;
+       char **strings;
+
+
+       size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
+       strings = backtrace_symbols(stack_frames, size);
+       for (i = 0; i < size; i++) {
+               if (strings != NULL)
+                       lprintf(1, "%s\n", strings[i]);
+               else
+                       lprintf(1, "%p\n", stack_frames[i]);
+       }
+       free(strings);
+#endif
+}
+
+/*@}*/
diff --git a/webcit/src/webserver.h b/webcit/src/webserver.h
new file mode 100644 (file)
index 0000000..5809b1a
--- /dev/null
@@ -0,0 +1,6 @@
+/* $Id$ */
+int client_getln(int sock, char *buf, int bufsiz);
+int client_read(int sock, char *buf, int bytes);
+int client_read_to(int sock, char *buf, int bytes, int timeout);
+ssize_t client_write(const void *buf, size_t count);
+int lprintf(int loglevel, const char *format, ...);
diff --git a/webcit/src/who.c b/webcit/src/who.c
new file mode 100644 (file)
index 0000000..e81b911
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * $Id$
+ */
+/**
+ * \defgroup DislpayWho Display a list of all users currently logged on to the Citadel server.
+ * \ingroup WebcitDisplayItems
+ */
+/*@{*/
+#include "webcit.h"
+
+
+
+/**
+ * \brief Display inner div of Wholist
+ */
+void who_inner_div(void) {
+       char buf[SIZ], user[SIZ], room[SIZ], host[SIZ],
+               realroom[SIZ], realhost[SIZ];
+       int sess;
+       time_t last_activity;
+       time_t now;
+       int bg = 0;
+
+       wprintf("<table border=\"0\" cellspacing=\"0\" width=\"100%%\" bgcolor=\"#FFFFFF\">"
+               "<tr>\n");
+       wprintf("<th colspan=\"3\"> </th>\n");
+       wprintf("<th>%s</th>\n", _("User name"));
+       wprintf("<th>%s</th>", _("Room"));
+       wprintf("<th>%s</th>\n</tr>\n", _("From host"));
+
+       serv_puts("TIME");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '2') {
+               now = extract_long(&buf[4], 0);
+       }
+       else {
+               now = time(NULL);
+       }
+
+       serv_puts("RWHO");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                       sess = extract_int(buf, 0);
+                       extract_token(user, buf, 1, '|', sizeof user);
+                       extract_token(room, buf, 2, '|', sizeof room);
+                       extract_token(host, buf, 3, '|', sizeof host);
+                       extract_token(realroom, buf, 9, '|', sizeof realroom);
+                       extract_token(realhost, buf, 10, '|', sizeof realhost);
+                       last_activity = extract_long(buf, 5);
+
+                       bg = 1 - bg;
+                       wprintf("<tr bgcolor=\"#%s\">",
+                               (bg ? "DDDDDD" : "FFFFFF")
+                       );
+
+
+                       wprintf("<td>");
+                       if ((WC->is_aide) &&
+                           (sess != WC->ctdl_pid)) {
+                               wprintf(" <a href=\"terminate_session?which_session=%d", sess);
+                               wprintf("\" onClick=\"return ConfirmKill();\">%s</a>", _("(kill)"));
+                       }
+                       if (sess == WC->ctdl_pid) {
+                               wprintf(" <a href=\"edit_me\">%s</a>", _("(edit)"));
+                       }
+                       wprintf("</td>");
+
+                       /** (link to page this user) */
+                       wprintf("<td><a href=\"display_page?recp=");
+                       urlescputs(user);
+                       wprintf("\">"
+                               "<img align=\"middle\" "
+                               "src=\"static/citadelchat_24x.gif\" "
+                               "alt=\"(p)\""
+                               " border=\"0\" /></a> ");
+                       wprintf("</td>");
+
+                       /** (idle flag) */
+                       wprintf("<td>");
+                       if ((now - last_activity) > 900L) {
+                               wprintf(" "
+                                       "<img align=\"middle\" "
+                                       "src=\"static/inactiveuser_24x.gif\" "
+                                       "alt=\"(idle)\" border=\"0\" />");
+                       }
+                       else {
+                               wprintf(" "
+                                       "<img align=\"middle\" "
+                                       "src=\"static/activeuser_24x.gif\" "
+                                       "alt=\"(active)\" border=\"0\" />");
+                       }
+                       wprintf("</td>\n<td>");
+
+
+
+                       /** username (link to user bio/photo page) */
+                       wprintf("<a href=\"showuser?who=");
+                       urlescputs(user);
+                       wprintf("\">");
+                       escputs(user);
+                       wprintf("</a>");
+
+                       /** room */
+                       wprintf("</td>\n\t<td>");
+                       escputs(room);
+                       if (strlen(realroom) > 0) {
+                               wprintf("<br /><i>");
+                               escputs(realroom);
+                               wprintf("</i>");
+                       }
+                       wprintf("</td>\n\t<td>");
+
+                       /** hostname */
+                       escputs(host);
+                       if (strlen(realhost) > 0) {
+                               wprintf("<br /><i>");
+                               escputs(realhost);
+                               wprintf("</i>");
+                       }
+                       wprintf("</td>\n</tr>");
+               }
+       }
+       wprintf("</table>");
+}
+
+
+/**
+ * \brief who is on?
+ */
+void who(void)
+{
+       char title[256];
+
+       output_headers(1, 1, 2, 0, 0, 0);
+
+       wprintf("<script type=\"text/javascript\">\n"
+               "function ConfirmKill() { \n"
+               "return confirm('%s');\n"
+               "}\n"
+               "</script>\n", _("Do you really want to kill this session?")
+       );
+
+       wprintf("<div id=\"banner\">\n");
+       wprintf("<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>");
+       wprintf("<img src=\"static/usermanag_48x.gif\" alt=\" \" "
+               "align=middle "
+               ">");
+       wprintf("<span class=\"titlebar\"> ");
+
+       snprintf(title, sizeof title, _("Users currently on %s"), serv_info.serv_humannode);
+       escputs(title);
+
+       wprintf("</span></td><td align=right>");
+       offer_start_page();
+       wprintf("</td></tr></table>\n");
+       wprintf("</div>\n");
+
+       wprintf("<div id=\"content\">\n");
+
+       wprintf("<div style=\"display:inline\" id=\"fix_scrollbar_bug\">");
+       who_inner_div();
+       wprintf("</div>\n");
+
+       wprintf("<div id=\"instructions\" align=center>");
+       wprintf(_("Click on a name to read user info.  Click on %s "
+               "to send an instant message to that user."),
+               "<img align=\"middle\" src=\"static/citadelchat_16x.gif\" alt=\"(p)\" border=\"0\">"
+       );
+       wprintf("</div>\n");
+
+       /**
+        * JavaScript to make the ajax refresh happen:
+        * See http://www.sergiopereira.com/articles/prototype.js.html for info on Ajax.PeriodicalUpdater
+        * It wants: 1. The div being updated
+        *           2. The URL of the update source
+        *           3. Other flags (such as the HTTP method and the refresh frequency)
+        */
+       wprintf(
+               "<script type=\"text/javascript\">                                      "
+               " new Ajax.PeriodicalUpdater('fix_scrollbar_bug', 'who_inner_html',     "
+               "                            { method: 'get', frequency: 30 }  );       "
+               "</script>                                                              \n"
+       );
+       wDumpContent(1);
+}
+
+/**
+ * \brief end session \todo what??? does this belong here? 
+ */
+void terminate_session(void)
+{
+       char buf[SIZ];
+
+       serv_printf("TERM %s", bstr("which_session"));
+       serv_getln(buf, sizeof buf);
+       who();
+}
+
+
+/**
+ * \brief Change your session info (fake roomname and hostname)
+ */
+void edit_me(void)
+{
+       char buf[SIZ];
+
+       if (strlen(bstr("change_room_name_button")) > 0) {
+               serv_printf("RCHG %s", bstr("fake_roomname"));
+               serv_getln(buf, sizeof buf);
+               http_redirect("who");
+       } else if (strlen(bstr("change_host_name_button")) > 0) {
+               serv_printf("HCHG %s", bstr("fake_hostname"));
+               serv_getln(buf, sizeof buf);
+               http_redirect("who");
+       } else if (strlen(bstr("change_user_name_button")) > 0) {
+               serv_printf("UCHG %s", bstr("fake_username"));
+               serv_getln(buf, sizeof buf);
+               http_redirect("who");
+       } else if (strlen(bstr("cancel_button")) > 0) {
+               http_redirect("who");
+       } else {
+               output_headers(1, 1, 0, 0, 0, 0);
+
+               wprintf("<div id=\"banner\">\n");
+               wprintf("<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>");
+               wprintf("<span class=\"titlebar\">");
+               wprintf(_("Edit your session display"));
+               wprintf("</span></td></tr></table>\n");
+               wprintf("</div>\n<div id=\"content\">\n");
+
+               wprintf(_("This screen allows you to change the way your "
+                       "session appears in the 'Who is online' listing. "
+                       "To turn off any 'fake' name you've previously "
+                       "set, simply click the appropriate 'change' button "
+                       "without typing anything in the corresponding box. "));
+               wprintf("<br />\n");
+
+               wprintf("<form method=\"POST\" action=\"edit_me\">\n");
+
+               wprintf("<table border=0 width=100%%>\n");
+
+               wprintf("<tr><td><b>");
+               wprintf(_("Room name:"));
+               wprintf("</b></td>\n<td>");
+               wprintf("<input type=\"text\" name=\"fake_roomname\" maxlength=\"64\">\n");
+               wprintf("</td>\n<td align=center>");
+               wprintf("<input type=\"submit\" name=\"change_room_name_button\" value=\"%s\">",
+                       _("Change room name"));
+               wprintf("</td>\n</tr>\n");
+
+               wprintf("<tr><td><b>");
+               wprintf(_("Host name:"));
+               wprintf("</b></td><td>");
+               wprintf("<input type=\"text\" name=\"fake_hostname\" maxlength=\"64\">\n");
+               wprintf("</td>\n<td align=center>");
+               wprintf("<input type=\"submit\" name=\"change_host_name_button\" value=\"%s\">",
+                       _("Change host name"));
+               wprintf("</td>\n</tr>\n");
+
+               if (WC->is_aide) {
+                       wprintf("<tr><td><b>");
+                       wprintf(_("User name:"));
+                       wprintf("</b></td><td>");
+                       wprintf("<input type=\"text\" name=\"fake_username\" maxlength=\"64\">\n");
+                       wprintf("</td>\n<td align=center>");
+                       wprintf("<input type=\"submit\" name \"change_user_name_button\" value=\"%s\">",
+                               _("Change user name"));
+                       wprintf("</td>\n</tr>\n");
+               }
+               wprintf("<tr><td> </td><td> </td><td align=center>");
+               wprintf("<input type=\"submit\" name=\"cancel_button\" value=\"%s\">",
+                       _("Cancel"));
+               wprintf("</td></tr></table>\n");
+               wprintf("</form></center>\n");
+               wDumpContent(1);
+       }
+}
+
+
+/*@}*/
diff --git a/webcit/src/wiki.c b/webcit/src/wiki.c
new file mode 100644 (file)
index 0000000..0f35b61
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * $Id:  $
+ */
+/**
+ *
+ * \defgroup Wiki Wiki; Functions pertaining to rooms with a wiki view
+ * \ingroup WebcitDisplayItems
+ */
+
+/*@{*/
+#include "webcit.h"
+#include "groupdav.h"
+
+
+
+/** 
+ * \brief Convert a string to something suitable as a wiki index
+ *
+ * \param s The string to be converted.
+ */
+void str_wiki_index(char *s)
+{
+       int i;
+
+       if (s == NULL) return;
+
+       /* First remove all non-alphanumeric characters */
+       for (i=0; i<strlen(s); ++i) {
+               if (!isalnum(s[i])) {
+                       strcpy(&s[i], &s[i+1]);
+               }
+       }
+
+       /* Then make everything lower case */
+       for (i=0; i<strlen(s); ++i) {
+               s[i] = tolower(s[i]);
+       }
+}
+
+/**
+ * \brief Display a specific page from a wiki room
+ */
+void display_wiki_page(void)
+{
+       char roomname[128];
+       char pagename[128];
+       char errmsg[256];
+       long msgnum = (-1L);
+
+       safestrncpy(roomname, bstr("room"), sizeof roomname);
+       safestrncpy(pagename, bstr("page"), sizeof pagename);
+       str_wiki_index(pagename);
+
+       if (strlen(roomname) > 0) {
+
+               /* If we're not in the correct room, try going there. */
+               if (strcasecmp(roomname, WC->wc_roomname)) {
+                       gotoroom(roomname);
+               }
+       
+               /* If we're still not in the correct room, it doesn't exist. */
+               if (strcasecmp(roomname, WC->wc_roomname)) {
+                       snprintf(errmsg, sizeof errmsg,
+                               _("There is no room called '%s'."),
+                               roomname);
+                       convenience_page("FF0000", _("Error"), errmsg);
+                       return;
+               }
+
+       }
+
+       if (WC->wc_view != VIEW_WIKI) {
+               snprintf(errmsg, sizeof errmsg,
+                       _("'%s' is not a Wiki room."),
+                       roomname);
+               convenience_page("FF0000", _("Error"), errmsg);
+               return;
+       }
+
+       if (strlen(pagename) == 0) {
+               strcpy(pagename, "home");
+       }
+
+       /* Found it!  Now read it. */
+       msgnum = locate_message_by_uid(pagename);
+       if (msgnum >= 0L) {
+               output_headers(1, 1, 1, 0, 0, 0);
+               read_message(msgnum, 0, "");
+               wDumpContent(1);
+               return;
+       }
+
+       output_headers(1, 1, 1, 0, 0, 0);
+       wprintf("<br /><br />"
+               "<div align=\"center\">"
+               "<table border=\"0\" bgcolor=\"#ffffff\" cellpadding=\"10\">"
+               "<tr><td align=\"center\">"
+       );
+       wprintf("<br><b>");
+       wprintf(_("There is no page called '%s' here."), pagename);
+       wprintf("</b><br><br>");
+       wprintf(_("Select the 'Edit this page' link in the room banner "
+               "if you would like to create this page."));
+       wprintf("<br><br>");
+       wprintf("</td></tr></table></div>\n");
+       wDumpContent(1);
+}
+
+
+/** @} */
diff --git a/webcit/subst.c b/webcit/subst.c
deleted file mode 100644 (file)
index 4359306..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup Subst Variable substitution type stuff
- * \ingroup CitadelConfig
- */
-
-/*@{*/
-
-#include "webcit.h"
-
-
-/**
- * \brief Clear out the list of substitution variables local to this session
- */
-void clear_local_substs(void) {
-       struct wcsubst *ptr;
-
-       while (WC->vars != NULL) {
-               ptr = WC->vars->next;
-
-               if ((WC->vars->wcs_type == WCS_STRING)
-                  || (WC->vars->wcs_type == WCS_SERVCMD)) {
-                       free(WC->vars->wcs_value);
-               }
-
-               free(WC->vars);
-               WC->vars = ptr;
-       }
-
-       WC->vars = NULL;
-}
-
-
-/*
- * \brief Add a substitution variable (local to this session)
- * \param keyname the replacementstring to substitute
- * \param keytype the kind of the key
- * \param format the format string ala printf
- * \param ... the arguments to substitute in the formatstring
- */
-void svprintf(char *keyname, int keytype, const char *format,...)
-{
-       va_list arg_ptr;
-       char wbuf[SIZ];
-       struct wcsubst *ptr = NULL;
-       struct wcsubst *scan;
-
-       /**
-        * First scan through to see if we're doing a replacement of
-        * an existing key
-        */
-       for (scan=WC->vars; scan!=NULL; scan=scan->next) {
-               if (!strcasecmp(scan->wcs_key, keyname)) {
-                       ptr = scan;
-                       free(ptr->wcs_value);
-               }
-       }
-
-       /** Otherwise allocate a new one */
-       if (ptr == NULL) {
-               ptr = (struct wcsubst *) malloc(sizeof(struct wcsubst));
-               ptr->next = WC->vars;
-               safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
-               WC->vars = ptr;
-       }
-
-       /** Format the string and save it */
-
-       va_start(arg_ptr, format);
-       vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
-       va_end(arg_ptr);
-
-       ptr->wcs_type = keytype;
-       ptr->wcs_value = strdup(wbuf);
-}
-
-/**
- * \brief Add a substitution variable (local to this session) that does a callback
- * \param keyname the keystring to substitute
- * \param fcn_ptr the function callback to give the substitution string
- */
-void svcallback(char *keyname, void (*fcn_ptr)() )
-{
-       struct wcsubst *ptr;
-
-       ptr = (struct wcsubst *) malloc(sizeof(struct wcsubst));
-       ptr->next = WC->vars;
-       ptr->wcs_type = WCS_FUNCTION;
-       strcpy(ptr->wcs_key, keyname);
-       ptr->wcs_function = fcn_ptr;
-       WC->vars = ptr;
-}
-
-
-
-/**
- * \brief back end for print_value_of() ... does a server command
- * \param servcmd server command to execute on the citadel server
- */
-void pvo_do_cmd(char *servcmd) {
-       char buf[SIZ];
-
-       serv_puts(servcmd);
-       serv_getln(buf, sizeof buf);
-
-       switch(buf[0]) {
-               case '2':
-               case '3':
-               case '5':
-                       wprintf("%s\n", &buf[4]);
-                       break;
-               case '1':
-                       fmout("CENTER");
-                       break;
-               case '4':
-                       wprintf("%s\n", &buf[4]);
-                       serv_puts("000");
-                       break;
-       }
-}
-
-
-
-/**
- * \brief Print the value of a variable
- * \param keyname get a key to print
- */
-void print_value_of(char *keyname) {
-       struct wcsubst *ptr;
-       void *fcn();
-
-       if (keyname[0] == '=') {
-               do_template(&keyname[1]);
-       }
-
-       if (!strcasecmp(keyname, "SERV_PID")) {
-               wprintf("%d", WC->ctdl_pid);
-       }
-
-       else if (!strcasecmp(keyname, "SERV_NODENAME")) {
-               escputs(serv_info.serv_nodename);
-       }
-
-       else if (!strcasecmp(keyname, "SERV_HUMANNODE")) {
-               escputs(serv_info.serv_humannode);
-       }
-
-       else if (!strcasecmp(keyname, "SERV_FQDN")) {
-               escputs(serv_info.serv_fqdn);
-       }
-
-       else if (!strcasecmp(keyname, "SERV_SOFTWARE")) {
-               escputs(serv_info.serv_software);
-       }
-
-       else if (!strcasecmp(keyname, "SERV_REV_LEVEL")) {
-               wprintf("%d.%02d",
-                       serv_info.serv_rev_level / 100,
-                       serv_info.serv_rev_level % 100
-               );
-       }
-
-       else if (!strcasecmp(keyname, "SERV_BBS_CITY")) {
-               escputs(serv_info.serv_bbs_city);
-       }
-
-       else if (!strcasecmp(keyname, "CURRENT_USER")) {
-               escputs(WC->wc_fullname);
-       }
-
-       else if (!strcasecmp(keyname, "CURRENT_ROOM")) {
-               escputs(WC->wc_roomname);
-       }
-
-       /** Page-local variables */
-       else for (ptr = WC->vars; ptr != NULL; ptr = ptr->next) {
-               if (!strcasecmp(ptr->wcs_key, keyname)) {
-                       if (ptr->wcs_type == WCS_STRING) {
-                               wprintf("%s", ptr->wcs_value);
-                       }
-                       else if (ptr->wcs_type == WCS_SERVCMD) {
-                               pvo_do_cmd(ptr->wcs_value);
-                       }
-                       else if (ptr->wcs_type == WCS_FUNCTION) {
-                               (*ptr->wcs_function) ();
-                       }
-               }
-       }
-}
-
-
-
-/**
- * \brief Display a variable-substituted template
- * \param templatename template file to load
- */
-void do_template(void *templatename) {
-       char filename[PATH_MAX];
-       FILE *fp;
-       char inbuf[1024];
-       char outbuf[sizeof inbuf];
-       char key[sizeof inbuf];
-       int i, pos;
-
-       strcpy(filename, "static/");
-       strcat(filename, templatename);
-       if (WC->is_wap)
-               strcat(filename, ".wml");
-       else
-               strcat(filename, ".html");
-       
-       fp = fopen(filename, "r");
-       if (fp == NULL) {
-               wprintf(_("ERROR: could not open template "));
-               wprintf("'%s' - %s<br />\n",
-                       templatename, strerror(errno));
-               return;
-       }
-
-       strcpy(inbuf, "");
-
-       while (fgets(inbuf, sizeof inbuf, fp) != NULL) {
-               strcpy(outbuf, "");
-
-               while (strlen(inbuf) > 0) {
-                       pos = (-1);
-                       for (i=strlen(inbuf); i>=0; --i) {
-                               if ((inbuf[i]=='<')&&(inbuf[i+1]=='?')) pos = i;
-                       }
-                       if (pos < 0) {
-                               wprintf("%s", inbuf);
-                               strcpy(inbuf, "");
-                       }
-                       else {
-                               strncpy(outbuf, inbuf, pos);
-                               outbuf[pos] = 0;
-                               wprintf("%s", outbuf);
-                               strcpy(inbuf, &inbuf[pos]);
-                               pos = 1;
-                               for (i=strlen(inbuf); i>=0; --i) {
-                                       if (inbuf[i]=='>') pos = i;
-                               }
-                               strncpy(key, &inbuf[2], pos-2);
-                               key[pos-2] = 0;
-                               print_value_of(key);
-                               strcpy(inbuf, &inbuf[pos+1]);
-                       }
-               }
-       }
-
-       fclose(fp);
-}
-
-
-
-/*@}*/
diff --git a/webcit/summary.c b/webcit/summary.c
deleted file mode 100644 (file)
index 3d99403..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup SymaryFuncs Displays the "Summary Page"
- * \ingroup WebcitDisplayItems
- */
-/*@{*/
-#include "webcit.h"
-
-/**
- * \brief Display today's date in a friendly format
- */
-void output_date(void) {
-       struct tm tm;
-       time_t now;
-       char buf[128];
-
-       time(&now);
-       localtime_r(&now, &tm);
-
-       wc_strftime(buf, 32, "%A, %x", &tm);
-       wprintf("%s", buf);
-}
-
-
-
-
-/**
- * \brief Dummy section
- */
-void dummy_section(void) {
-       svprintf("BOXTITLE", WCS_STRING, "(dummy&nbsp;section)");
-       do_template("beginbox");
-       wprintf(_("(nothing)"));
-       do_template("endbox");
-}
-
-
-/**
- * \brief New messages section
- */
-void new_messages_section(void) {
-       char buf[SIZ];
-       char room[SIZ];
-       int i;
-       int number_of_rooms_to_check;
-       char *rooms_to_check = "Mail|Lobby";
-
-       svprintf("BOXTITLE", WCS_STRING, _("Messages"));
-       do_template("beginbox");
-
-       number_of_rooms_to_check = num_tokens(rooms_to_check, '|');
-       if (number_of_rooms_to_check == 0) return;
-
-       wprintf("<table border=0 width=100%%>\n");
-       for (i=0; i<number_of_rooms_to_check; ++i) {
-               extract_token(room, rooms_to_check, i, '|', sizeof room);
-
-               serv_printf("GOTO %s", room);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       extract_token(room, &buf[4], 0, '|', sizeof room);
-                       wprintf("<tr><td><a href=\"dotgoto?room=");
-                       urlescputs(room);
-                       wprintf("\">");
-                       escputs(room);
-                       wprintf("</a></td><td>%d/%d</td></tr>\n",
-                               extract_int(&buf[4], 1),
-                               extract_int(&buf[4], 2)
-                       );
-               }
-       }
-       wprintf("</table>\n");
-       do_template("endbox");
-
-}
-
-
-/**
- * \brief Wholist section
- */
-void wholist_section(void) {
-       char buf[SIZ];
-       char user[SIZ];
-
-       svprintf("BOXTITLE", WCS_STRING, _("Who's&nbsp;online&nbsp;now"));
-       do_template("beginbox");
-       serv_puts("RWHO");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               extract_token(user, buf, 1, '|', sizeof user);
-               escputs(user);
-               wprintf("<br />\n");
-       }
-       do_template("endbox");
-}
-
-
-/**
- * \brief Task list section
- */
-void tasks_section(void) {
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-       int num_msgs = 0;
-       int i;
-#endif
-
-       svprintf("BOXTITLE", WCS_STRING, _("Tasks"));
-       do_template("beginbox");
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-       gotoroom("_TASKS_");
-       if (WC->wc_view != VIEW_TASKS) {
-               num_msgs = 0;
-       }
-       else {
-               num_msgs = load_msg_ptrs("MSGS ALL", 0);
-       }
-
-       if (num_msgs < 1) {
-               wprintf("<i>");
-               wprintf(_("(None)"));
-               wprintf("</i><br />\n");
-       }
-       else {
-               for (i=0; i<num_msgs; ++i) {
-                       display_task(WC->msgarr[i]);
-               }
-       }
-
-       calendar_summary_view();
-
-#else /* WEBCIT_WITH_CALENDAR_SERVICE */
-       wprintf("<i>");
-       wprintf(_("(This server does not support task lists)"));
-       wprintf("</i>\n");
-#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
-       do_template("endbox");
-}
-
-
-/**
- * \brief Calendar section
- */
-void calendar_section(void) {
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-       int num_msgs = 0;
-       int i;
-#endif
-
-       svprintf("BOXTITLE", WCS_STRING, _("Today&nbsp;on&nbsp;your&nbsp;calendar"));
-       do_template("beginbox");
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-       gotoroom("_CALENDAR_");
-       if ( (WC->wc_view != VIEW_CALENDAR) && (WC->wc_view != VIEW_CALBRIEF) ) {
-               num_msgs = 0;
-       }
-       else {
-               num_msgs = load_msg_ptrs("MSGS ALL", 0);
-       }
-
-       if (num_msgs < 1) {
-               wprintf("<i>");
-               wprintf(_("(Nothing)"));
-               wprintf("</i><br />\n");
-       }
-       else {
-               for (i=0; i<num_msgs; ++i) {
-                       display_calendar(WC->msgarr[i]);
-               }
-               calendar_summary_view();
-       }
-
-#else /* WEBCIT_WITH_CALENDAR_SERVICE */
-       wprintf("<i>");
-       wprintf(_("(This server does not support calendars)"));
-       wprintf("</i>\n");
-#endif /* WEBCIT_WITH_CALENDAR_SERVICE */
-       do_template("endbox");
-}
-
-/**
- * \brief Server info section (fluff, really)
- */
-void server_info_section(void) {
-       char message[512];
-
-       svprintf("BOXTITLE", WCS_STRING, _("About&nbsp;this&nbsp;server"));
-       do_template("beginbox");
-
-       snprintf(message, sizeof message,
-               _("You are connected to %s, running %s with %s, and located in %s.  Your system administrator is %s."),
-               serv_info.serv_humannode,
-               serv_info.serv_software,
-               SERVER,
-               serv_info.serv_bbs_city,
-               serv_info.serv_sysadm);
-       escputs(message);
-       do_template("endbox");
-}
-
-/**
- * \brief summary of inner div????
- */
-void summary_inner_div(void) {
-       /**
-        * Now let's do three columns of crap.  All portals and all groupware
-        * clients seem to want to do three columns, so we'll do three
-        * columns too.  Conformity is not inherently a virtue, but there are
-        * a lot of really shallow people out there, and even though they're
-        * not people I consider worthwhile, I still want them to use WebCit.
-        */
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%%><tr valign=top>");
-
-       /**
-        * Column One
-        */
-       wprintf("<td width=33%%>");
-       wholist_section();
-
-       /**
-        * Column Two
-        */
-       wprintf("</td><td width=33%%>");
-       server_info_section();
-       wprintf("<br />");
-       tasks_section();
-
-       /**
-        * Column Three
-        */
-       wprintf("</td><td width=33%%>");
-       new_messages_section();
-       wprintf("<br />");
-       calendar_section();
-
-       /**
-        * End of columns
-        */
-       wprintf("</td></tr></table>");
-}
-
-
-/**
- * \brief Display this user's summary page
- */
-void summary(void) {
-       char title[256];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<table width=100%% border=0 bgcolor=#444455><tr>"
-               "<td><img src=\"static/summscreen_48x.gif\"></td><td>"
-               "<span class=\"titlebar\">"
-       );
-
-       snprintf(title, sizeof title, _("Summary page for %s"), WC->wc_fullname);
-       escputs(title);
-       wprintf("</span></td><td>\n");
-       wprintf("</td><td aling=right><span class=\"titlebar\">");
-       output_date();
-       wprintf("</span><br />");
-       offer_start_page();
-       wprintf("</td></tr></table>\n");
-
-       /**
-        * You guessed it ... we're going to refresh using ajax.
-        * In the future we might consider updating individual sections of the summary
-        * instead of the whole thing.
-        */
-       wprintf("</div>\n<div id=\"content\">\n");
-       summary_inner_div();
-       wprintf("</div>\n");
-
-       wprintf(
-               "<script type=\"text/javascript\">                                      "
-               " new Ajax.PeriodicalUpdater('content', 'summary_inner_div',            "
-               "                            { method: 'get', frequency: 60 }  );       "
-               "</script>                                                              \n"
-       );
-
-       wDumpContent(1);
-}
-
-
-/*@}*/
diff --git a/webcit/sysmsgs.c b/webcit/sysmsgs.c
deleted file mode 100644 (file)
index 7de3998..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup ShowSysMsgs Editing of various text files on the Citadel server.
- * \ingroup WebcitDisplayItems
- */
-/*@{*/
-#include "webcit.h"
-
-
-/**
- * \brief display the form for editing something (room info, bio, etc)
- * \param description the descriptive text for the box
- * \param check_cmd command to check????
- * \param read_cmd read answer from citadel server???
- * \param save_cmd save comand to the citadel server??
- * \param with_room_banner should we bisplay a room banner?
- */
-void display_edit(char *description, char *check_cmd,
-                 char *read_cmd, char *save_cmd, int with_room_banner)
-{
-       char buf[SIZ];
-
-       serv_puts(check_cmd);
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] != '2') {
-               safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage);
-               display_main_menu();
-               return;
-       }
-       if (with_room_banner) {
-               output_headers(1, 1, 1, 0, 0, 0);
-       }
-       else {
-               output_headers(1, 1, 0, 0, 0, 0);
-       }
-
-       svprintf("BOXTITLE", WCS_STRING, _("Edit %s"), description);
-       do_template("beginbox");
-
-       wprintf("<div align=\"center\">");
-       wprintf(_("Enter %s below.  Text is formatted to "
-               "the reader's screen width.  To defeat the "
-               "formatting, indent a line at least one space."), description);
-       wprintf("<br />");
-
-       wprintf("<FORM METHOD=\"POST\" action=\"%s\">\n", save_cmd);
-       wprintf("<TEXTAREA NAME=\"msgtext\" wrap=soft "
-               "ROWS=10 COLS=80 WIDTH=80>\n");
-       serv_puts(read_cmd);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1')
-               server_to_text();
-       wprintf("</TEXTAREA><br /><br />\n");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"save_button\" VALUE=\"%s\">", _("Save changes"));
-       wprintf("&nbsp;");
-       wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\"><br />\n", _("Cancel"));
-
-       wprintf("</FORM></div>\n");
-       do_template("endbox");
-       wDumpContent(1);
-}
-
-
-/**
- * \brief save a screen which was displayed with display_edit()
- * \param description the window title???
- * \param enter_cmd which command to enter at the citadel server???
- * \param regoto should we go to that room again after executing that command?
- */
-void save_edit(char *description, char *enter_cmd, int regoto)
-{
-       char buf[SIZ];
-
-       if (strlen(bstr("save_button")) == 0) {
-               sprintf(WC->ImportantMessage,
-                       _("Cancelled.  %s was not saved."),
-                       description);
-               display_main_menu();
-               return;
-       }
-       serv_puts(enter_cmd);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '4') {
-               safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage);
-               display_main_menu();
-               return;
-       }
-       text_to_server(bstr("msgtext"));
-       serv_puts("000");
-
-       if (regoto) {
-               smart_goto(WC->wc_roomname);
-       } else {
-               sprintf(WC->ImportantMessage,
-                       _("%s has been saved."),
-                       description);
-               display_main_menu();
-               return;
-       }
-}
-
-
-/*@}*/
diff --git a/webcit/tabs.c b/webcit/tabs.c
deleted file mode 100644 (file)
index 5ecd53a..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * $Id:  $
- */
-/**
- * \defgroup TabUtils Utility functions for creating tabbed dialogs
- * \ingroup WebcitDisplayItems
- */
-/*@{*/
-#include "webcit.h"
-
-/**
- * \brief print tabbed dialog
- * \param num_tabs how many tabs do we have?
- * \param tabnames the headers of the tables
- */
-void tabbed_dialog(int num_tabs, char *tabnames[]) {
-       int i;
-
-       wprintf("<script type=\"text/javascript\">                                              "
-               "var previously_selected_tab = '0';                                             "
-               "function tabsel(which_tab) {                                                   "
-               "       if (which_tab == previously_selected_tab) {                             "
-               "               return;                                                         "
-               "       }                                                                       "
-               "       $('tabtd'+previously_selected_tab).style.backgroundColor = '#cccccc';   "
-               "       $('tabdiv'+previously_selected_tab).style.display = 'none';             "
-               "       $('tabtd'+which_tab).style.backgroundColor = '#ffffff';                 "
-               "       $('tabdiv'+which_tab).style.display = 'block';                          "
-               "       previously_selected_tab = which_tab;                                    "
-               "}                                                                              "
-               "</script>                                                                      \n"
-       );
-
-       wprintf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%%\">"
-               "<tr align=\"center\" style=\"cursor:pointer\"><td>&nbsp;</td>"
-       );
-
-       for (i=0; i<num_tabs; ++i) {
-               wprintf("<td id=\"tabtd%d\" bgcolor=\"#%s\" onClick='tabsel(\"%d\");'>"
-                       "<span class=\"tablabel\">",
-                       i,
-                       ( (i==0) ? "ffffff" : "cccccc" ),
-                       i
-               );
-               wprintf("%s", tabnames[i]);
-               wprintf("</td>");
-
-               wprintf("<td>&nbsp;</td>\n");
-       }
-
-       wprintf("</tr></table>\n");
-       wprintf("<table border=\"0\" width=\"100%%\" bgcolor=\"#ffffff\"><tr><td>");
-}
-
-/**
- * \brief print the tab-header
- * \param tabnum number of the tab to print
- * \param num_tabs total number oftabs to be printed
- */
-void begin_tab(int tabnum, int num_tabs) {
-       wprintf("<div id=\"tabdiv%d\" style=\"display:%s\">",
-               tabnum,
-               ( (tabnum == 0) ? "block" : "none" )
-       );
-}
-
-/**
- * \brief print the tab-footer
- * \param tabnum number of the tab to print
- * \param num_tabs total number oftabs to be printed
- */
-void end_tab(int tabnum, int num_tabs) {
-       wprintf("</div>\n");
-
-       if (tabnum == num_tabs-1) {
-               wprintf("</td></tr></table>\n");
-       }
-}
-
-
-/*@}*/
diff --git a/webcit/tcp_sockets.c b/webcit/tcp_sockets.c
deleted file mode 100644 (file)
index 2d5d985..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * $Id$
- */
-/** 
- * \defgroup TcpSockets TCP client socket module for WebCit
- * \ingroup CitadelCommunitacion
- */
-/*@{*/
-
-/*
- * Uncomment this to log all communications with the Citadel server
-#define SERV_TRACE 1
- */
-
-#include "webcit.h"
-#include "webserver.h"
-
-/**
- * \brief register the timeout
- * \param signum signalhandler number
- * \return signals
- */
-RETSIGTYPE timeout(int signum)
-{
-       lprintf(1, "Connection timed out.\n");
-       exit(3);
-}
-
-
-/**
- * \brief Connect a unix domain socket
- * \param sockpath where to open a unix domain socket
- */
-int uds_connectsock(char *sockpath)
-{
-       struct sockaddr_un addr;
-       int s;
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
-
-       s = socket(AF_UNIX, SOCK_STREAM, 0);
-       if (s < 0) {
-               lprintf(1, "Can't create socket: %s\n",
-                       strerror(errno));
-               return(-1);
-       }
-
-       if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               lprintf(1, "Can't connect: %s\n",
-                       strerror(errno));
-               close(s);
-               return(-1);
-       }
-
-       return s;
-}
-
-
-/**
- * \brief Connect a TCP/IP socket
- * \param host the host to connect to
- * \param service the service on the host to call
- */
-int tcp_connectsock(char *host, char *service)
-{
-       struct hostent *phe;
-       struct servent *pse;
-       struct protoent *ppe;
-       struct sockaddr_in sin;
-       int s;
-
-       memset(&sin, 0, sizeof(sin));
-       sin.sin_family = AF_INET;
-
-       pse = getservbyname(service, "tcp");
-       if (pse) {
-               sin.sin_port = pse->s_port;
-       } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) {
-               lprintf(1, "Can't get %s service entry\n", service);
-               return (-1);
-       }
-       phe = gethostbyname(host);
-       if (phe) {
-               memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
-       } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
-               lprintf(1, "Can't get %s host entry: %s\n",
-                       host, strerror(errno));
-               return (-1);
-       }
-       if ((ppe = getprotobyname("tcp")) == 0) {
-               lprintf(1, "Can't get TCP protocol entry: %s\n",
-                       strerror(errno));
-               return (-1);
-       }
-
-       s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
-       if (s < 0) {
-               lprintf(1, "Can't create socket: %s\n", strerror(errno));
-               return (-1);
-       }
-       signal(SIGALRM, timeout);
-       alarm(30);
-
-       if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
-               lprintf(1, "Can't connect to %s.%s: %s\n",
-                       host, service, strerror(errno));
-               close(s);
-               return (-1);
-       }
-       alarm(0);
-       signal(SIGALRM, SIG_IGN);
-
-       return (s);
-}
-
-
-
-
-/**
- * \brief Input binary data from socket
- * \param buf the buffer to get the input to
- * \param bytes the maximal number of bytes to read
- */
-void serv_read(char *buf, int bytes)
-{
-       int len, rlen;
-
-       len = 0;
-       while (len < bytes) {
-               rlen = read(WC->serv_sock, &buf[len], bytes - len);
-               if (rlen < 1) {
-                       lprintf(1, "Server connection broken: %s\n",
-                               strerror(errno));
-                       close(WC->serv_sock);
-                       WC->serv_sock = (-1);
-                       WC->connected = 0;
-                       WC->logged_in = 0;
-                       memset(buf, 0, bytes);
-                       return;
-               }
-               len = len + rlen;
-       }
-}
-
-
-/**
- * \brief input string from pipe
- */
-void serv_getln(char *strbuf, int bufsize)
-{
-       int ch, len;
-       char buf[2];
-
-       len = 0;
-       strbuf[0] = 0;
-       do {
-               serv_read(&buf[0], 1);
-               ch = buf[0];
-               if ((ch != 13) && (ch != 10)) {
-                       strbuf[len++] = ch;
-               }
-       } while ((ch != 10) && (ch != 0) && (len < (bufsize-1)));
-       strbuf[len] = 0;
-#ifdef SERV_TRACE
-       lprintf(9, "%3d>%s\n", WC->serv_sock, strbuf);
-#endif
-}
-
-
-
-/**
- * \brief send binary to server
- * \param buf the buffer to write to citadel server
- * \param nbytes how many bytes to send to citadel server
- */
-void serv_write(char *buf, int nbytes)
-{
-       int bytes_written = 0;
-       int retval;
-       while (bytes_written < nbytes) {
-               retval = write(WC->serv_sock, &buf[bytes_written],
-                              nbytes - bytes_written);
-               if (retval < 1) {
-                       lprintf(1, "Server connection broken: %s\n",
-                               strerror(errno));
-                       close(WC->serv_sock);
-                       WC->serv_sock = (-1);
-                       WC->connected = 0;
-                       WC->logged_in = 0;
-                       return;
-               }
-               bytes_written = bytes_written + retval;
-       }
-}
-
-
-/**
- * \brief send line to server
- * \param string the line to send to the citadel server
- */
-void serv_puts(char *string)
-{
-       char buf[SIZ];
-
-#ifdef SERV_TRACE
-       lprintf(9, "%3d<%s\n", WC->serv_sock, string);
-#endif
-       sprintf(buf, "%s\n", string);
-       serv_write(buf, strlen(buf));
-}
-
-
-/**
- * \brief convenience function to send stuff to the server
- * \param format the formatstring
- * \param ... the entities to insert into format 
- */
-void serv_printf(const char *format,...)
-{
-       va_list arg_ptr;
-       char buf[SIZ];
-
-       va_start(arg_ptr, format);
-       vsnprintf(buf, sizeof buf, format, arg_ptr);
-       va_end(arg_ptr);
-
-       strcat(buf, "\n");
-       serv_write(buf, strlen(buf));
-#ifdef SERV_TRACE
-       lprintf(9, "<%s", buf);
-#endif
-}
-
-
-/*@}*/
diff --git a/webcit/tools.c b/webcit/tools.c
deleted file mode 100644 (file)
index 0a07183..0000000
+++ /dev/null
@@ -1,618 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup MiscRout Miscellaneous routines 
- * \ingroup tools
- */
-
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-
-
-typedef unsigned char byte; /**< byte data type */
-
-#define FALSE 0 /**< no. */
-#define TRUE 1  /**< yes. */
-
-static byte dtable[256];       /**< base64 encode / decode table */
-
-/**
- * \brief sanitize strncopy.
- * \param dest destination string
- * \param src source string
- * \param n length of source to copy 
- * \return result string
- */
-char *safestrncpy(char *dest, const char *src, size_t n)
-{
-       if (dest == NULL || src == NULL) {
-               abort();
-       }
-       strncpy(dest, src, n);
-       dest[n - 1] = 0;
-       return dest;
-}
-
-
-
-/**
- * \brief discover number of parameters/tokens in a string
- * \param source string to inspect
- * \param tok seperation token
- * \return number of tokenized parts found
- */
-int num_tokens(char *source, char tok)
-{
-       int a = 0;
-       int count = 1;
-
-       if (source == NULL)
-               return (0);
-       for (a = 0; a < strlen(source); ++a) {
-               if (source[a] == tok)
-                       ++count;
-       }
-       return (count);
-}
-
-/**
- * brief a string tokenizer
- * \param dest destination string 
- * \param source the string to grab tokens from
- * \param parmnum the n'th token to grab
- * \param separator the tokenizer string
- * \param maxlen the length of dest
- */
-void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
-{
-       char *d;                /* dest */
-       const char *s;          /* source */
-       int count = 0;
-       int len = 0;
-
-       dest[0] = 0;
-
-       /* Locate desired parameter */
-       s = source;
-       while (count < parmnum) {
-               /* End of string, bail! */
-               if (!*s) {
-                       s = NULL;
-                       break;
-               }
-               if (*s == separator) {
-                       count++;
-               }
-               s++;
-       }
-       if (!s) return;         /* Parameter not found */
-
-       for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
-               *d = *s;
-       }
-       *d = 0;
-}
-
-
-
-/**
- * \brief a tokenizer that kills, maims, and destroys
- * \param source the string to process
- * \param parmnum which token to kill
- * \param separator the tokenizer string
- */
-void remove_token(char *source, int parmnum, char separator)
-{
-       int i;
-       int len;
-       int curr_parm;
-       int start, end;
-
-       len = 0;
-       curr_parm = 0;
-       start = (-1);
-       end = (-1);
-
-       if (strlen(source) == 0) {
-               return;
-       }
-
-       for (i = 0; i < strlen(source); ++i) {
-               if ((start < 0) && (curr_parm == parmnum)) {
-                       start = i;
-               }
-
-               if ((end < 0) && (curr_parm == (parmnum + 1))) {
-                       end = i;
-               }
-
-               if (source[i] == separator) {
-                       ++curr_parm;
-               }
-       }
-
-       if (end < 0)
-               end = strlen(source);
-
-       strcpy(&source[start], &source[end]);
-}
-
-
-
-
-/**
- * \brief extract an int parm w/o supplying a buffer
- * \param source the string to locate the int in
- * \param parmnum the n'th token to grab the int from
- * \return the integer
- */
-int extract_int(const char *source, int parmnum)
-{
-       char buf[32];
-       
-       extract_token(buf, source, parmnum, '|', sizeof buf);
-       return(atoi(buf));
-}
-
-/**
- * \brief extract an long parm w/o supplying a buffer
- * \param source string to examine
- * \param parmnum n'th token to search long in
- * \return the found long value
- */
-long extract_long(const char *source, int parmnum)
-{
-       char buf[32];
-       
-       extract_token(buf, source, parmnum, '|', sizeof buf);
-       return(atol(buf));
-}
-
-
-
-
-
-
-/**
- * \brief check for the presence of a character within a string (returns count)
- * \param st the string to examine
- * \param ch the char to search
- * \return the position inside of st
- */
-int haschar(char *st,char ch)
-{
-       int a, b;
-       b = 0;
-       for (a = 0; a < strlen(st); ++a)
-               if (st[a] == ch)
-                       ++b;
-       return (b);
-}
-
-
-/** 
- * \brief Utility function to "readline" from memory
- * \param start Location in memory from which we are reading.
- * \param buf the buffer to place the string in.
- * \param maxlen Size of string buffer
- * \return Pointer to the source memory right after we stopped reading.
- */
-char *memreadline(char *start, char *buf, int maxlen)
-{
-       char ch;
-       char *ptr;
-       int len = 0;            /**< tally our own length to avoid strlen() delays */
-
-       ptr = start;
-       memset(buf, 0, maxlen);
-
-       while (1) {
-               ch = *ptr++;
-               if ((len < (maxlen - 1)) && (ch != 13) && (ch != 10)) {
-                       buf[strlen(buf) + 1] = 0;
-                       buf[strlen(buf)] = ch;
-                       ++len;
-               }
-               if ((ch == 10) || (ch == 0)) {
-                       return ptr;
-               }
-       }
-}
-
-
-
-/**
- * \brief searches for a  paternn within asearch string
- * \param search the string to search 
- * \param patn the pattern to find in string
- * \returns position in string
- */
-int pattern2(char *search, char *patn)
-{
-       int a;
-       for (a = 0; a < strlen(search); ++a) {
-               if (!strncasecmp(&search[a], patn, strlen(patn)))
-                       return (a);
-       }
-       return (-1);
-}
-
-
-/**
- * \brief Strip leading and trailing spaces from a string
- * \param buf the string to modify
- */
-void striplt(char *buf)
-{
-       if (strlen(buf) == 0) return;
-       while ((strlen(buf) > 0) && (isspace(buf[0])))
-               strcpy(buf, &buf[1]);
-       if (strlen(buf) == 0) return;
-       while (isspace(buf[strlen(buf) - 1]))
-               buf[strlen(buf) - 1] = 0;
-}
-
-
-/**
- * \brief Determine whether the specified message number is contained within the
- * specified set.
- *
- * \param mset Message set string
- * \param msgnum Message number we are looking for
- *
- * \return Nonzero if the specified message number is in the specified message set string.
- */
-int is_msg_in_mset(char *mset, long msgnum) {
-       int num_sets;
-       int s;
-       char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
-       long lo, hi;
-
-       /*
-        * Now set it for all specified messages.
-        */
-       num_sets = num_tokens(mset, ',');
-       for (s=0; s<num_sets; ++s) {
-               extract_token(setstr, mset, s, ',', sizeof setstr);
-
-               extract_token(lostr, setstr, 0, ':', sizeof lostr);
-               if (num_tokens(setstr, ':') >= 2) {
-                       extract_token(histr, setstr, 1, ':', sizeof histr);
-                       if (!strcmp(histr, "*")) {
-                               snprintf(histr, sizeof histr, "%ld", LONG_MAX);
-                       }
-               } 
-               else {
-                       strcpy(histr, lostr);
-               }
-               lo = atol(lostr);
-               hi = atol(histr);
-
-               if ((msgnum >= lo) && (msgnum <= hi)) return(1);
-       }
-
-       return(0);
-}
-
-
-
-/**
- * \brief Strip a boundarized substring out of a string
- * (for example, remove
- * parentheses and anything inside them).
- *
- * This improved version can strip out *multiple* boundarized substrings.
- * \param str the string to process
- * \param leftboundary the boundary character on the left side of the target string 
- * \param rightboundary the boundary character on the right side of the target string
- */
-void stripout(char *str, char leftboundary, char rightboundary)
-{
-       int a;
-       int lb = (-1);
-       int rb = (-1);
-
-       do {
-               lb = (-1);
-               rb = (-1);
-
-               for (a = 0; a < strlen(str); ++a) {
-                       if (str[a] == leftboundary)
-                               lb = a;
-                       if (str[a] == rightboundary)
-                               rb = a;
-               }
-
-               if ((lb > 0) && (rb > lb)) {
-                       strcpy(&str[lb - 1], &str[rb + 1]);
-               }
-
-       } while ((lb > 0) && (rb > lb));
-
-}
-
-
-
-/**
- * \brief Replacement for sleep() that uses select() in order to avoid SIGALRM
- * \param seconds how many seconds should we sleep?
- */
-void sleeeeeeeeeep(int seconds)
-{
-       struct timeval tv;
-
-       tv.tv_sec = seconds;
-       tv.tv_usec = 0;
-       select(0, NULL, NULL, NULL, &tv);
-}
-
-
-
-/**
- * \brief encode a string into base64 to for example tunnel it through mail transport
- * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
- * John Walker, copied over from the Citadel server.
- * \param dest encrypted string
- * \param source the string to encrypt
- * \param sourcelen the length of the source data (may contain string terminators)
- */
-
-void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen)
-{
-       int i, hiteof = FALSE;
-       int spos = 0;
-       int dpos = 0;
-       int thisline = 0;
-
-       /**  Fill dtable with character encodings.  */
-
-       for (i = 0; i < 26; i++) {
-               dtable[i] = 'A' + i;
-               dtable[26 + i] = 'a' + i;
-       }
-       for (i = 0; i < 10; i++) {
-               dtable[52 + i] = '0' + i;
-       }
-       dtable[62] = '+';
-       dtable[63] = '/';
-
-       while (!hiteof) {
-               byte igroup[3], ogroup[4];
-               int c, n;
-
-               igroup[0] = igroup[1] = igroup[2] = 0;
-               for (n = 0; n < 3; n++) {
-                       if (spos >= sourcelen) {
-                               hiteof = TRUE;
-                               break;
-                       }
-                       c = source[spos++];
-                       igroup[n] = (byte) c;
-               }
-               if (n > 0) {
-                       ogroup[0] = dtable[igroup[0] >> 2];
-                       ogroup[1] =
-                           dtable[((igroup[0] & 3) << 4) |
-                                  (igroup[1] >> 4)];
-                       ogroup[2] =
-                           dtable[((igroup[1] & 0xF) << 2) |
-                                  (igroup[2] >> 6)];
-                       ogroup[3] = dtable[igroup[2] & 0x3F];
-
-                       /**
-                        * Replace characters in output stream with "=" pad
-                        * characters if fewer than three characters were
-                        * read from the end of the input stream. 
-                        */
-
-                       if (n < 3) {
-                               ogroup[3] = '=';
-                               if (n < 2) {
-                                       ogroup[2] = '=';
-                               }
-                       }
-                       for (i = 0; i < 4; i++) {
-                               dest[dpos++] = ogroup[i];
-                               dest[dpos] = 0;
-                       }
-                       thisline += 4;
-                       if (thisline > 70) {
-                               dest[dpos++] = '\r';
-                               dest[dpos++] = '\n';
-                               dest[dpos] = 0;
-                               thisline = 0;
-                       }
-               }
-       }
-       if (thisline > 70) {
-               dest[dpos++] = '\r';
-               dest[dpos++] = '\n';
-               dest[dpos] = 0;
-               thisline = 0;
-       }
-}
-
-
-/**
- * \brief Convert base64-encoded to binary.  
- * It will stop after reading 'length' bytes.
- *
- * \param dest The destination buffer 
- * \param source The base64 data to be decoded.
- * \param length The number of bytes to decode.
- * \return The actual length of the decoded data.
- */
-int CtdlDecodeBase64(char *dest, const char *source, size_t length)
-{
-       int i, c;
-       int dpos = 0;
-       int spos = 0;
-
-       for (i = 0; i < 255; i++) {
-               dtable[i] = 0x80;
-       }
-       for (i = 'A'; i <= 'Z'; i++) {
-               dtable[i] = 0 + (i - 'A');
-       }
-       for (i = 'a'; i <= 'z'; i++) {
-               dtable[i] = 26 + (i - 'a');
-       }
-       for (i = '0'; i <= '9'; i++) {
-               dtable[i] = 52 + (i - '0');
-       }
-       dtable['+'] = 62;
-       dtable['/'] = 63;
-       dtable['='] = 0;
-
-       /**CONSTANTCONDITION*/ while (TRUE) {
-               byte a[4], b[4], o[3];
-
-               for (i = 0; i < 4; i++) {
-                       if (spos >= length) {
-                               return (dpos);
-                       }
-                       c = source[spos++];
-
-                       if (c == 0) {
-                               if (i > 0) {
-                                       return (dpos);
-                               }
-                               return (dpos);
-                       }
-                       if (dtable[c] & 0x80) {
-                               /** Ignoring errors: discard invalid character */
-                               i--;
-                               continue;
-                       }
-                       a[i] = (byte) c;
-                       b[i] = (byte) dtable[c];
-               }
-               o[0] = (b[0] << 2) | (b[1] >> 4);
-               o[1] = (b[1] << 4) | (b[2] >> 2);
-               o[2] = (b[2] << 6) | b[3];
-               i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
-               if (i >= 1)
-                       dest[dpos++] = o[0];
-               if (i >= 2)
-                       dest[dpos++] = o[1];
-               if (i >= 3)
-                       dest[dpos++] = o[2];
-               dest[dpos] = 0;
-               if (i < 3) {
-                       return (dpos);
-               }
-       }
-}
-
-
-
-/**
- * \brief Generate a new, globally unique UID parameter for a calendar etc. object
- *
- * \param buf String buffer into which our newly created UUID should be placed
- */
-void generate_uuid(char *buf) {
-       static int seq = 0;
-
-       sprintf(buf, "%s-%lx-%x-%x",
-               serv_info.serv_nodename,
-               (long)time(NULL),
-               getpid(),
-               (seq++)
-       );
-}
-
-
-/**
- * \brief Local replacement for controversial C library function that generates
- * names for temporary files.  Included to shut up compiler warnings.
- * \todo return a fd to the file instead of the name for security reasons
- * \param name the created filename
- * \param len the length of the filename
- */
-void CtdlMakeTempFileName(char *name, int len) {
-       int i = 0;
-
-       while (i++, i < 100) {
-               snprintf(name, len, "/tmp/ctdl.%04x.%04x",
-                       getpid(),
-                       rand()
-               );
-               if (!access(name, F_OK)) {
-                       return;
-               }
-       }
-}
-
-
-
-/*
- * \brief      case-insensitive substring search
- *
- *             This uses the Boyer-Moore search algorithm and is therefore quite fast.
- *             The code is roughly based on the strstr() replacement from 'tin' written
- *             by Urs Jannsen.
- *
- * \param      text    String to be searched
- * \param      pattern String to search for
- */
-char *bmstrcasestr(char *text, char *pattern) {
-
-       register unsigned char *p, *t;
-       register int i, j, *delta;
-       register size_t p1;
-       int deltaspace[256];
-       size_t textlen;
-       size_t patlen;
-
-       textlen = strlen (text);
-       patlen = strlen (pattern);
-
-       /* algorithm fails if pattern is empty */
-       if ((p1 = patlen) == 0)
-               return (text);
-
-       /* code below fails (whenever i is unsigned) if pattern too long */
-       if (p1 > textlen)
-               return (NULL);
-
-       /* set up deltas */
-       delta = deltaspace;
-       for (i = 0; i <= 255; i++)
-               delta[i] = p1;
-       for (p = (unsigned char *) pattern, i = p1; --i > 0;)
-               delta[tolower(*p++)] = i;
-
-       /*
-        * From now on, we want patlen - 1.
-        * In the loop below, p points to the end of the pattern,
-        * t points to the end of the text to be tested against the
-        * pattern, and i counts the amount of text remaining, not
-        * including the part to be tested.
-        */
-       p1--;
-       p = (unsigned char *) pattern + p1;
-       t = (unsigned char *) text + p1;
-       i = textlen - patlen;
-       while(1) {
-               if (tolower(p[0]) == tolower(t[0])) {
-                       if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
-                               return ((char *)t - p1);
-                       }
-               }
-               j = delta[tolower(t[0])];
-               if (i < j)
-                       break;
-               i -= j;
-               t += j;
-       }
-       return (NULL);
-}
-
-
-
-
-
-/*@}*/
diff --git a/webcit/useredit.c b/webcit/useredit.c
deleted file mode 100644 (file)
index 265f915..0000000
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup AdminTasks Administrative screen to add/change/delete user accounts
- * \ingroup CitadelConfig
- *
- */
-/*@{*/
-
-#include "webcit.h"
-#include "webserver.h"
-
-
-/**
- * \brief show a list of available users to edit them
- * \param message the header message???
- * \param preselect which user should be selected in the browser
- */
-void select_user_to_edit(char *message, char *preselect)
-{
-       char buf[SIZ];
-       char username[SIZ];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<table width=100%% border=0 bgcolor=#444455><tr>"
-               "<td>"
-               "<span class=\"titlebar\">"
-               "<img src=\"static/usermanag_48x.gif\">");
-       wprintf(_("Edit or delete users"));
-       wprintf("</span></td></tr></table>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       if (message != NULL) wprintf(message);
-
-       wprintf("<table border=0 cellspacing=10><tr valign=top><td>\n");
-
-       svprintf("BOXTITLE", WCS_STRING, _("Add users"));
-       do_template("beginbox");
-
-       wprintf(_("To create a new user account, enter the desired "
-               "user name in the box below and click 'Create'."));
-       wprintf("<br /><br />");
-
-        wprintf("<center><form method=\"POST\" action=\"create_user\">\n");
-        wprintf(_("New user: "));
-        wprintf("<input type=\"text\" name=\"username\"><br />\n"
-               "<input type=\"submit\" name=\"create_button\" value=\"%s\">"
-               "</form></center>\n", _("Create"));
-
-       do_template("endbox");
-
-       wprintf("</td><td>");
-
-       svprintf("BOXTITLE", WCS_STRING, _("Edit or Delete users"));
-       do_template("beginbox");
-
-       wprintf(_("To edit an existing user account, select the user "
-               "name from the list and click 'Edit'."));
-       wprintf("<br /><br />");
-       
-        wprintf("<center>"
-               "<form method=\"POST\" action=\"display_edituser\">\n");
-        wprintf("<select name=\"username\" size=10 style=\"width:100%%\">\n");
-        serv_puts("LIST");
-        serv_getln(buf, sizeof buf);
-        if (buf[0] == '1') {
-                while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                        extract_token(username, buf, 0, '|', sizeof username);
-                        wprintf("<option");
-                       if (preselect != NULL)
-                          if (!strcasecmp(username, preselect))
-                             wprintf(" selected");
-                       wprintf(">");
-                        escputs(username);
-                        wprintf("\n");
-                }
-        }
-        wprintf("</select><br />\n");
-
-        wprintf("<input type=\"submit\" name=\"edit_config_button\" value=\"%s\">", _("Edit configuration"));
-        wprintf("<input type=\"submit\" name=\"edit_abe_button\" value=\"%s\">", _("Edit address book entry"));
-        wprintf("<input type=\"submit\" name=\"delete_button\" value=\"%s\" "
-               "onClick=\"return confirm('%s');\">", _("Delete user"), _("Delete this user?"));
-        wprintf("</form></center>\n");
-       do_template("endbox");
-
-       wprintf("</td></tr></table>\n");
-
-       wDumpContent(1);
-}
-
-
-
-/**
- * \brief Locate the message number of a user's vCard in the current room
- * \param username the plaintext name of the user
- * \param usernum the number of the user on the citadel server
- * \return the message id of his vcard
- */
-long locate_user_vcard(char *username, long usernum) {
-       char buf[SIZ];
-       long vcard_msgnum = (-1L);
-       char content_type[SIZ];
-       char partnum[SIZ];
-       int already_tried_creating_one = 0;
-
-       struct stuff_t {
-               struct stuff_t *next;
-               long msgnum;
-       };
-
-       struct stuff_t *stuff = NULL;
-       struct stuff_t *ptr;
-
-TRYAGAIN:
-       /** Search for the user's vCard */
-       serv_puts("MSGS ALL");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               ptr = malloc(sizeof(struct stuff_t));
-               ptr->msgnum = atol(buf);
-               ptr->next = stuff;
-               stuff = ptr;
-       }
-
-       /** Iterate through the message list looking for vCards */
-       while (stuff != NULL) {
-               serv_printf("MSG0 %ld|2", stuff->msgnum);
-               serv_getln(buf, sizeof buf);
-               if (buf[0]=='1') {
-                       while(serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                               if (!strncasecmp(buf, "part=", 5)) {
-                                       extract_token(partnum, &buf[5], 2, '|', sizeof partnum);
-                                       extract_token(content_type, &buf[5], 4, '|', sizeof content_type);
-                                       if (!strcasecmp(content_type,
-                                          "text/x-vcard")) {
-                                               vcard_msgnum = stuff->msgnum;
-                                       }
-                               }
-                       }
-               }
-
-               ptr = stuff->next;
-               free(stuff);
-               stuff = ptr;
-       }
-
-       /** If there's no vcard, create one */
-       if (vcard_msgnum < 0) if (already_tried_creating_one == 0) {
-               already_tried_creating_one = 1;
-               serv_puts("ENT0 1|||4");
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '4') {
-                       serv_puts("Content-type: text/x-vcard");
-                       serv_puts("");
-                       serv_puts("begin:vcard");
-                       serv_puts("end:vcard");
-                       serv_puts("000");
-               }
-               goto TRYAGAIN;
-       }
-
-       return(vcard_msgnum);
-}
-
-
-/**
- * \brief Display the form for editing a user's address book entry
- * \param username the name of the user
- * \param usernum the citadel-uid of the user
- */
-void display_edit_address_book_entry(char *username, long usernum) {
-       char roomname[SIZ];
-       char buf[SIZ];
-       char error_message[SIZ];
-       long vcard_msgnum = (-1L);
-
-       /** Locate the user's config room, creating it if necessary */
-       sprintf(roomname, "%010ld.%s", usernum, USERCONFIGROOM);
-       serv_printf("GOTO %s||1", roomname);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               serv_printf("CRE8 1|%s|5|||1|", roomname);
-               serv_getln(buf, sizeof buf);
-               serv_printf("GOTO %s||1", roomname);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '2') {
-                       sprintf(error_message,
-                               "<img src=\"static/error.gif\" align=center>"
-                               "%s<br /><br />\n", &buf[4]);
-                       select_user_to_edit(error_message, username);
-                       return;
-               }
-       }
-
-       vcard_msgnum = locate_user_vcard(username, usernum);
-
-       if (vcard_msgnum < 0) {
-               sprintf(error_message,
-                       "<img src=\"static/error.gif\" align=center>%s<br /><br />\n",
-                       _("An error occurred while trying to create or edit this address book entry.")
-               );
-               select_user_to_edit(error_message, username);
-               return;
-       }
-
-       do_edit_vcard(vcard_msgnum, "1", "select_user_to_edit");
-}
-
-
-
-
-/**
- * \brief Edit a user.  
- * If supplied_username is null, look in the "username"
- * web variable for the name of the user to edit.
- * 
- * If "is_new" is set to nonzero, this screen will set the web variables
- * to send the user to the vCard editor next.
- * \param supplied_username user to look up or NULL if to search in the environment
- * \param is_new should we create the user?
- */
-void display_edituser(char *supplied_username, int is_new) {
-       char buf[1024];
-       char error_message[1024];
-       time_t now;
-
-       char username[256];
-       char password[256];
-       unsigned int flags;
-       int timescalled;
-       int msgsposted;
-       int axlevel;
-       long usernum;
-       time_t lastcall;
-       int purgedays;
-       int i;
-
-       if (supplied_username != NULL) {
-               safestrncpy(username, supplied_username, sizeof username);
-       }
-       else {
-               safestrncpy(username, bstr("username"), sizeof username);
-       }
-
-       serv_printf("AGUP %s", username);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               sprintf(error_message,
-                       "<img src=\"static/error.gif\" align=center>"
-                       "%s<br /><br />\n", &buf[4]);
-               select_user_to_edit(error_message, username);
-               return;
-       }
-
-       extract_token(username, &buf[4], 0, '|', sizeof username);
-       extract_token(password, &buf[4], 1, '|', sizeof password);
-       flags = extract_int(&buf[4], 2);
-       timescalled = extract_int(&buf[4], 3);
-       msgsposted = extract_int(&buf[4], 4);
-       axlevel = extract_int(&buf[4], 5);
-       usernum = extract_long(&buf[4], 6);
-       lastcall = extract_long(&buf[4], 7);
-       purgedays = extract_long(&buf[4], 8);
-
-       if (strlen(bstr("edit_abe_button")) > 0) {
-               display_edit_address_book_entry(username, usernum);
-               return;
-       }
-
-       if (strlen(bstr("delete_button")) > 0) {
-               delete_user(username);
-               return;
-       }
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>");
-       wprintf("<span class=\"titlebar\">");
-       wprintf(_("Edit user account: "));
-       escputs(username);
-       wprintf("</span></td></tr></table>\n");
-       wprintf("</div>\n<div id=\"content\">\n");
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-       wprintf("<form method=\"POST\" action=\"edituser\">\n"
-               "<input type=\"hidden\" name=\"username\" value=\"");
-       escputs(username);
-       wprintf("\">\n");
-       wprintf("<input type=\"hidden\" name=\"is_new\" value=\"%d\">\n"
-               "<input type=\"hidden\" name=\"usernum\" value=\"%ld\">\n",
-               is_new, usernum);
-
-       wprintf("<input type=\"hidden\" name=\"flags\" value=\"%d\">\n", flags);
-
-       wprintf("<center><table>");
-
-       wprintf("<tr><td>");
-       wprintf(_("Password"));
-       wprintf("</td><td>"
-               "<input type=\"password\" name=\"password\" value=\"");
-       escputs(password);
-       wprintf("\" maxlength=\"20\"></td></tr>\n");
-
-       wprintf("<tr><td>");
-       wprintf(_("Permission to send Internet mail"));
-       wprintf("</td><td>");
-       wprintf("<input type=\"checkbox\" name=\"inetmail\" value=\"yes\" ");
-       if (flags & US_INTERNET) {
-               wprintf("checked ");
-       }
-       wprintf("></td></tr>\n");
-
-       wprintf("<tr><td>");
-       wprintf(_("Number of logins"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"timescalled\" value=\"");
-       wprintf("%d", timescalled);
-       wprintf("\" maxlength=\"6\"></td></tr>\n");
-
-       wprintf("<tr><td>");
-       wprintf(_("Messages submitted"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"msgsposted\" value=\"");
-       wprintf("%d", msgsposted);
-       wprintf("\" maxlength=\"6\"></td></tr>\n");
-
-       wprintf("<tr><td>");
-       wprintf(_("Access level"));
-       wprintf("</td><td>"
-               "<select name=\"axlevel\">\n");
-       for (i=0; i<7; ++i) {
-               wprintf("<option ");
-               if (axlevel == i) {
-                       wprintf("selected ");
-               }
-               wprintf("value=\"%d\">%d - %s</option>\n",
-                       i, i, axdefs[i]);
-       }
-       wprintf("</select></td></tr>\n");
-
-       wprintf("<tr><td>");
-       wprintf(_("User ID number"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"usernum\" value=\"");
-       wprintf("%ld", usernum);
-       wprintf("\" maxlength=\"7\"></td></tr>\n");
-
-       now = time(NULL);
-       wprintf("<tr><td>");
-       wprintf(_("Date and time of last login"));
-       wprintf("</td><td>"
-               "<select name=\"lastcall\">\n");
-
-       wprintf("<option selected value=\"%ld\">", lastcall);
-       escputs(asctime(localtime(&lastcall)));
-       wprintf("</option>\n");
-
-       wprintf("<option value=\"%ld\">", now);
-       escputs(asctime(localtime(&now)));
-       wprintf("</option>\n");
-
-       wprintf("</select></td></tr>");
-
-       wprintf("<tr><td>");
-       wprintf(_("Auto-purge after this many days"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"purgedays\" value=\"");
-       wprintf("%d", purgedays);
-       wprintf("\" maxlength=\"5\"></td></tr>\n");
-
-       wprintf("</table>\n");
-
-       wprintf("<input type=\"submit\" name=\"ok_button\" value=\"%s\">\n"
-               "&nbsp;"
-               "<input type=\"submit\" name=\"cancel\" value=\"%s\">\n"
-               "<br /><br /></form>\n", _("Save changes"), _("Cancel"));
-
-       wprintf("</center>\n");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-
-}
-
-
-/**
- * \brief do the backend operation of the user edit on the server
- */
-void edituser(void) {
-       char message[SIZ];
-       char buf[SIZ];
-       int is_new = 0;
-       unsigned int flags = 0;
-
-       is_new = atoi(bstr("is_new"));
-
-       if (strlen(bstr("ok_button")) == 0) {
-               safestrncpy(message, _("Changes were not saved."), sizeof message);
-       }
-       else {
-               flags = atoi(bstr("flags"));
-               if (!strcasecmp(bstr("inetmail"), "yes")) {
-                       flags |= US_INTERNET;
-               }
-               else {
-                       flags &= ~US_INTERNET ;
-               }
-
-               serv_printf("ASUP %s|%s|%d|%s|%s|%s|%s|%s|%s|",
-                       bstr("username"),
-                       bstr("password"),
-                       flags,
-                       bstr("timescalled"),
-                       bstr("msgsposted"),
-                       bstr("axlevel"),
-                       bstr("usernum"),
-                       bstr("lastcall"),
-                       bstr("purgedays")
-               );
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '2') {
-                       sprintf(message,
-                               "<img src=\"static/error.gif\" align=center>"
-                               "%s<br /><br />\n", &buf[4]);
-               }
-               else {
-                       safestrncpy(message, "", sizeof message);
-               }
-       }
-
-       /**
-        * If we are in the middle of creating a new user, move on to
-        * the vCard edit screen.
-        */
-       if (is_new) {
-               display_edit_address_book_entry( bstr("username"), atol(bstr("usernum")) );
-       }
-       else {
-               select_user_to_edit(message, bstr("username"));
-       }
-}
-
-/**
- * \brief burge a user 
- * \param username the name of the user to remove
- */
-void delete_user(char *username) {
-       char buf[SIZ];
-       char message[SIZ];
-
-       serv_printf("ASUP %s|0|0|0|0|0|", username);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '2') {
-               sprintf(message,
-                       "<img src=\"static/error.gif\" align=center>"
-                       "%s<br /><br />\n", &buf[4]);
-       }
-       else {
-               safestrncpy(message, "", sizeof message);
-       }
-       select_user_to_edit(message, bstr("username"));
-}
-               
-
-
-/**
- * \brief create a new user
- * take the web environment username and create it on the citadel server
- */
-void create_user(void) {
-       char buf[SIZ];
-       char error_message[SIZ];
-       char username[SIZ];
-
-       safestrncpy(username, bstr("username"), sizeof username);
-
-       serv_printf("CREU %s", username);
-       serv_getln(buf, sizeof buf);
-
-       if (buf[0] == '2') {
-               sprintf(WC->ImportantMessage,
-                       _("A new user has been created."));
-               display_edituser(username, 1);
-       }
-       else {
-               sprintf(error_message,
-                       "<img src=\"static/error.gif\" align=center>"
-                       "%s<br /><br />\n", &buf[4]);
-               select_user_to_edit(error_message, NULL);
-       }
-
-}
-
-
-
-/*@}*/
diff --git a/webcit/userlist.c b/webcit/userlist.c
deleted file mode 100644 (file)
index cbd6c66..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup AccDisplay Display a list of all accounts on a Citadel system.
- * \ingroup CitadelConfig
- */
-
-/*@{*/
-#include "webcit.h"
-
-/** 
- * \brief structure to keep namelists in
- */
-struct namelist {
-       struct namelist *next; /**< next item of the linked list */
-       char name[32];         /**< name of the userentry */
-};
-
-/**
- * \brief display the userlist
- */
-void userlist(void)
-{
-       char buf[256];
-       char fl[256];
-       char title[256];
-       struct tm tmbuf;
-       time_t lc;
-       struct namelist *bio = NULL;
-       struct namelist *bptr;
-       int has_bio;
-       int bg = 0;
-
-       serv_puts("LBIO");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1')
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       bptr = (struct namelist *) malloc(sizeof(struct namelist));
-                       bptr->next = bio;
-                       strcpy(bptr->name, buf);
-                       bio = bptr;
-               }
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>"
-               "<span class=\"titlebar\">");
-       snprintf(title, sizeof title, _("User list for %s"), serv_info.serv_humannode);
-       escputs(title);
-       wprintf("</span>"
-               "</td></tr></table>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       serv_puts("LIST");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '1') {
-               wprintf("<em>%s</em><br />\n", &buf[4]);
-               goto DONE;
-       }
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-       wprintf("<tr><th>%s</th><th>%s</th><th>%s</th>"
-                       "<th>%s</th><th>%s</th><th>%s</th></tr>",
-                       _("User Name"),
-                       _("Number"),
-                       _("Access Level"),
-                       _("Last Login"),
-                       _("Total Logins"),
-                       _("Total Posts"));
-
-       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-               extract_token(fl, buf, 0, '|', sizeof fl);
-               has_bio = 0;
-               for (bptr = bio; bptr != NULL; bptr = bptr->next) {
-                       if (!strcasecmp(fl, bptr->name))
-                               has_bio = 1;
-               }
-               bg = 1 - bg;
-               wprintf("<tr bgcolor=\"#%s\"><td>",
-                       (bg ? "DDDDDD" : "FFFFFF")
-               );
-               if (has_bio) {
-                       wprintf("<a href=\"showuser&who=");
-                       urlescputs(fl);
-                       wprintf("\">");
-                       escputs(fl);
-                       wprintf("</A>");
-               } else {
-                       escputs(fl);
-               }
-               wprintf("</td><td>%ld</td><td>%d</td><td>",
-                       extract_long(buf, 2),
-                       extract_int(buf, 1));
-               lc = extract_long(buf, 3);
-               localtime_r(&lc, &tmbuf);
-               wprintf("%02d/%02d/%04d ",
-                       (tmbuf.tm_mon + 1),
-                       tmbuf.tm_mday,
-                       (tmbuf.tm_year + 1900));
-
-
-               wprintf("</td><td>%ld</td><td>%5ld</td></tr>\n",
-                       extract_long(buf, 4), extract_long(buf, 5));
-
-       }
-       wprintf("</table></div>\n");
-DONE:  wDumpContent(1);
-}
-
-
-/**
- * \brief Display (non confidential) information about a particular user
- */
-void showuser(void)
-{
-       char who[256];
-       char buf[256];
-       int have_pic;
-
-       strcpy(who, bstr("who"));
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<table width=100%% border=0 bgcolor=\"#444455\"><tr>"
-               "<td><img src=\"static/usermanag_48x.gif\"></td>"
-               "<td align=left><span class=\"titlebar\">");
-       wprintf(_("User profile"));
-       wprintf("</span>"
-               "</td></tr></table>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       serv_printf("OIMG _userpic_|%s", who);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               have_pic = 1;
-               serv_puts("CLOS");
-               serv_getln(buf, sizeof buf);
-       } else {
-               have_pic = 0;
-       }
-
-       wprintf("<center><table><tr><td>");
-       if (have_pic == 1) {
-               wprintf("<img src=\"image&name=_userpic_&parm=");
-               urlescputs(who);
-               wprintf("\">");
-       }
-       wprintf("</td><td><h1>%s</h1></td></tr></table></center>\n", who);
-       serv_printf("RBIO %s", who);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               fmout("JUSTIFY");
-       }
-       wprintf("<br /><a href=\"display_page?recp=");
-       urlescputs(who);
-       wprintf("\">"
-               "<img src=\"static/citadelchat_24x.gif\" "
-               "align=middle border=0>&nbsp;&nbsp;");
-       snprintf(buf, sizeof buf, _("Click here to send an instant message to %s"), who);
-       escputs(buf);
-       wprintf("</a>\n");
-
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-
-/*@}*/
diff --git a/webcit/vcard.c b/webcit/vcard.c
deleted file mode 100644 (file)
index d236b00..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * $Id$
- * Copyright (C) 1999-2006 by Art Cancro
- * This code is freely redistributable under the terms of the GNU General
- * Public License.  All other rights reserved.
- */
-/**
- * \defgroup VCardMain vCard data type implementation for the Citadel system.
- * \ingroup VCards
- */
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-#include "vcard.h"
-
-/** 
- * \brief Constructor (empty vCard)
- * \return an empty vcard
- */
-struct vCard *vcard_new() {
-       struct vCard *v;
-
-       v = (struct vCard *) malloc(sizeof(struct vCard));
-       if (v == NULL) return v;
-
-       v->magic = CTDL_VCARD_MAGIC;
-       v->numprops = 0;
-       v->prop = NULL;
-
-       return v;
-}
-
-/**
- * \brief      Remove the "charset=" attribute from a vCard property name
- *
- * \param      strbuf          The property name string to be stripped
- */
-void remove_charset_attribute(char *strbuf)
-{
-       int i, t;
-       char compare[256];
-
-       t = num_tokens(strbuf, ';');
-       for (i=0; i<t; ++i) {
-               extract_token(compare, strbuf, i, ';', sizeof compare);
-               striplt(compare);
-               if (!strncasecmp(compare, "charset=", 8)) {
-                       remove_token(strbuf, i, ';');
-               }
-       }
-       if (strlen(strbuf) > 0) {
-               if (strbuf[strlen(strbuf)-1] == ';') {
-                       strbuf[strlen(strbuf)-1] = 0;
-               }
-       }
-}
-
-
-/*
- * \brief      Add a property to a vCard
- *
- * \param      v               vCard structure to which we are adding
- * \param      propname        name of new property
- * \param      propvalue       value of new property
- */
-void vcard_add_prop(struct vCard *v, char *propname, char *propvalue) {
-       ++v->numprops;
-       v->prop = realloc(v->prop,
-               (v->numprops * sizeof(struct vCardProp)) );
-       v->prop[v->numprops-1].name = strdup(propname);
-       v->prop[v->numprops-1].value = strdup(propvalue);
-}
-
-
-
-/**
- * \brief Constructor (supply serialized vCard)
- * \param vtext the text to parse into the new vcard
- * \return the parsed VCard
- */
-struct vCard *vcard_load(char *vtext) {
-       struct vCard *v;
-       int valid = 0;
-       char *mycopy, *ptr;
-       char *namebuf, *valuebuf;
-       int i;
-       int colonpos, nlpos;
-
-       if (vtext == NULL) return vcard_new();
-       mycopy = strdup(vtext);
-       if (mycopy == NULL) return NULL;
-
-       /**
-        * First, fix this big pile o' vCard to make it more parseable.
-        * To make it easier to parse, we convert CRLF to LF, and unfold any
-        * multi-line fields into single lines.
-        */
-       for (i=0; i<strlen(mycopy); ++i) {
-               if (!strncmp(&mycopy[i], "\r\n", 2)) {
-                       strcpy(&mycopy[i], &mycopy[i+1]);
-               }
-               if ( (mycopy[i]=='\n') && (isspace(mycopy[i+1])) ) {
-                       strcpy(&mycopy[i], &mycopy[i+1]);
-               }
-       }
-
-       v = vcard_new();
-       if (v == NULL) return v;
-
-       ptr = mycopy;
-       while (strlen(ptr)>0) {
-               colonpos = (-1);
-               nlpos = (-1);
-               colonpos = pattern2(ptr, ":");
-               nlpos = pattern2(ptr, "\n");
-
-               if ((nlpos > colonpos) && (colonpos > 0)) {
-                       namebuf = malloc(colonpos + 1);
-                       valuebuf = malloc(nlpos - colonpos + 1);
-                       strncpy(namebuf, ptr, colonpos);
-                       namebuf[colonpos] = 0;
-                       strncpy(valuebuf, &ptr[colonpos+1], nlpos-colonpos-1);
-                       valuebuf[nlpos-colonpos-1] = 0;
-
-                       if (!strcasecmp(namebuf, "end")) {
-                               valid = 0;
-                       }
-                       if (    (!strcasecmp(namebuf, "begin"))
-                               && (!strcasecmp(valuebuf, "vcard"))
-                       ) {
-                               valid = 1;
-                       }
-
-                       if ( (valid) && (strcasecmp(namebuf, "begin")) ) {
-                               remove_charset_attribute(namebuf);
-                               ++v->numprops;
-                               v->prop = realloc(v->prop,
-                                       (v->numprops * sizeof(struct vCardProp))
-                               );
-                               v->prop[v->numprops-1].name = namebuf;
-                               v->prop[v->numprops-1].value = valuebuf;
-                       } 
-                       else {
-                               free(namebuf);
-                               free(valuebuf);
-                       }
-
-               }
-
-               while ( (*ptr != '\n') && (strlen(ptr)>0) ) {
-                       ++ptr;
-               }
-               if (*ptr == '\n') ++ptr;
-       }
-
-       free(mycopy);
-       return v;
-}
-
-
-/**
- * \brief Fetch the value of a particular key.
- * If is_partial is set to 1, a partial match is ok (for example,
- * a key of "tel;home" will satisfy a search for "tel").
- * Set "instance" to a value higher than 0 to return subsequent instances
- * of the same key.
- * Set "get_propname" to nonzero to fetch the property name instead of value.
- * \param v vCard to get keyvalue from
- * \param propname key to retrieve
- * \param is_partial dunno???
- * \param instance if >0 return a later token of the value
- * \param get_propname if nonzero get the real property name???
- * \return the requested value / token / propertyname
- */
-char *vcard_get_prop(struct vCard *v, char *propname,
-                       int is_partial, int instance, int get_propname) {
-       int i;
-       int found_instance = 0;
-
-       if (v->numprops) for (i=0; i<(v->numprops); ++i) {
-               if ( (!strcasecmp(v->prop[i].name, propname))
-                  || (propname[0] == 0)
-                  || (  (!strncasecmp(v->prop[i].name,
-                                       propname, strlen(propname)))
-                        && (v->prop[i].name[strlen(propname)] == ';')
-                        && (is_partial) ) ) {
-                       if (instance == found_instance++) {
-                               if (get_propname) {
-                                       return(v->prop[i].name);
-                               }
-                               else {
-                                       return(v->prop[i].value);
-                               }
-                       }
-               }
-       }
-
-       return NULL;
-}
-
-
-
-
-/**
- * \brief Destructor
- * kill a vCard
- * \param v the vCard to purge from memory
- */
-void vcard_free(struct vCard *v) {
-       int i;
-       
-       if (v->magic != CTDL_VCARD_MAGIC) return;       /* Self-check */
-       
-       if (v->numprops) for (i=0; i<(v->numprops); ++i) {
-               free(v->prop[i].name);
-               free(v->prop[i].value);
-       }
-
-       if (v->prop != NULL) free(v->prop);
-       
-       memset(v, 0, sizeof(struct vCard));
-       free(v);
-}
-
-
-
-
-/**
- * \brief Set a name/value pair in the card
- * \param v vCard to inspect
- * \param name key to set
- * \param value the value to assign to key
- * \param append should we append the value to an existing one?
- */
-void vcard_set_prop(struct vCard *v, char *name, char *value, int append) {
-       int i;
-
-       if (v->magic != CTDL_VCARD_MAGIC) return;       /** Self-check */
-
-       /** If this key is already present, replace it */
-       if (!append) if (v->numprops) for (i=0; i<(v->numprops); ++i) {
-               if (!strcasecmp(v->prop[i].name, name)) {
-                       free(v->prop[i].name);
-                       free(v->prop[i].value);
-                       v->prop[i].name = strdup(name);
-                       v->prop[i].value = strdup(value);
-                       return;
-               }
-       }
-
-       /** Otherwise, append it */
-       ++v->numprops;
-       v->prop = realloc(v->prop,
-               (v->numprops * sizeof(struct vCardProp)) );
-       v->prop[v->numprops-1].name = strdup(name);
-       v->prop[v->numprops-1].value = strdup(value);
-}
-
-
-
-
-/**
- * \brief Serialize a struct vcard into a standard text/x-vcard MIME type.
- * \param v vCard to serialize
- * \return the serialized vCard
- */
-char *vcard_serialize(struct vCard *v)
-{
-       char *ser;
-       int i, j;
-       size_t len;
-       int is_utf8 = 0;
-
-       if (v->magic != CTDL_VCARD_MAGIC) return NULL;  /** self check */
-
-       /** Figure out how big a buffer we need to allocate */
-       len = 64;       /** for begin, end, and a little padding for safety */
-       if (v->numprops) for (i=0; i<(v->numprops); ++i) {
-               len = len +
-                       strlen(v->prop[i].name) +
-                       strlen(v->prop[i].value) + 16;
-       }
-
-       ser = malloc(len);
-       if (ser == NULL) return NULL;
-
-       safestrncpy(ser, "begin:vcard\r\n", len);
-       if (v->numprops) for (i=0; i<(v->numprops); ++i) {
-               if (strcasecmp(v->prop[i].name, "end")) {
-                       is_utf8 = 0;
-                       for (j=0; i<strlen(v->prop[i].value); ++i) {
-                               if ( (v->prop[i].value[j] < 32) || (v->prop[i].value[j] > 126) ) {
-                                       is_utf8 = 1;
-                               }
-                       }
-                       strcat(ser, v->prop[i].name);
-                       if (is_utf8) {
-                               strcat(ser, ";charset=UTF-8");
-                       }
-                       strcat(ser, ":");
-                       strcat(ser, v->prop[i].value);
-                       strcat(ser, "\r\n");
-               }
-       }
-       strcat(ser, "end:vcard\r\n");
-
-       return ser;
-}
-
-
-
-/*
- * \brief      Convert FN (Friendly Name) into N (Name)
- *
- * \param      vname           Supplied friendly-name
- * \param      n               Target buffer to store Name
- * \param      vname_size      Size of buffer
- */
-void vcard_fn_to_n(char *vname, char *n, size_t vname_size) {
-       char lastname[256];
-       char firstname[256];
-       char middlename[256];
-       char honorific_prefixes[256];
-       char honorific_suffixes[256];
-       char buf[256];
-
-       safestrncpy(buf, n, sizeof buf);
-
-       /* Try to intelligently convert the screen name to a
-        * fully expanded vCard name based on the number of
-        * words in the name
-        */
-       safestrncpy(lastname, "", sizeof lastname);
-       safestrncpy(firstname, "", sizeof firstname);
-       safestrncpy(middlename, "", sizeof middlename);
-       safestrncpy(honorific_prefixes, "", sizeof honorific_prefixes);
-       safestrncpy(honorific_suffixes, "", sizeof honorific_suffixes);
-
-       /* Honorific suffixes */
-       if (num_tokens(buf, ',') > 1) {
-               extract_token(honorific_suffixes, buf, (num_tokens(buf, ' ') - 1), ',',
-                       sizeof honorific_suffixes);
-               remove_token(buf, (num_tokens(buf, ',') - 1), ',');
-       }
-
-       /* Find a last name */
-       extract_token(lastname, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof lastname);
-       remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
-
-       /* Find honorific prefixes */
-       if (num_tokens(buf, ' ') > 2) {
-               extract_token(honorific_prefixes, buf, 0, ' ', sizeof honorific_prefixes);
-               remove_token(buf, 0, ' ');
-       }
-
-       /* Find a middle name */
-       if (num_tokens(buf, ' ') > 1) {
-               extract_token(middlename, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof middlename);
-               remove_token(buf, (num_tokens(buf, ' ') - 1), ' ');
-       }
-
-       /* Anything left is probably the first name */
-       safestrncpy(firstname, buf, sizeof firstname);
-       striplt(firstname);
-
-       /* Compose the structured name */
-       snprintf(vname, vname_size, "%s;%s;%s;%s;%s", lastname, firstname, middlename,
-               honorific_prefixes, honorific_suffixes);
-}
-
-
-
-
-
-
-/*@}*/
diff --git a/webcit/vcard.h b/webcit/vcard.h
deleted file mode 100644 (file)
index ac30d42..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * $Id$
- * Copyright (C) 1999 by Art Cancro
- * This code is freely redistributable under the terms of the GNU General
- * Public License.  All other rights reserved.
- */
-/**
- * \defgroup VcardHeader vCard implementation for Citadel
- * \ingroup VCards
- *
- */
-
-/*@{ */
-#define CTDL_VCARD_MAGIC       0xa1f9 /**< magic byte vcards start with??? */
-
-/**
- * \brief This data structure represents a vCard object currently in memory.
- */
-struct vCard {
-       int magic;          /**< the Magic Byte */
-       int numprops;       /**< number of properties this vcard will have */
-       struct vCardProp {  
-               char *name;         /**< Keyname of the property */
-               char *value;        /**< value of the property */
-       } *prop;            /**< Vcard Property. Linked list??? */
-};
-
-
-struct vCard *vcard_new(void);
-struct vCard *vcard_load(char *);
-void vcard_free(struct vCard *);
-void vcard_set_prop(struct vCard *v, char *name, char *value, int append);
-char *vcard_get_prop(struct vCard *v, char *propname, int is_partial,
-                       int instance, int return_propname);
-char *vcard_serialize(struct vCard *);
-
-
-/*@}*/
diff --git a/webcit/vcard_edit.c b/webcit/vcard_edit.c
deleted file mode 100644 (file)
index 2c78477..0000000
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup vCardEdit Handles on-screen editing of vCard objects.
- * \ingroup VCards
- */
-/*@{*/
-#include "webcit.h"
-#include "vcard.h"
-
-/**
- * \brief Edit the vCard component of a MIME message.  
- * Supply the message number
- * and MIME part number to fetch.  Or, specify -1 for the message number
- * to start with a blank card.
- * \param msgnum number of the item on the citadel server
- * \param partnum what???
- * \param return_to where to go back in the browser after edit ????
- */
-void do_edit_vcard(long msgnum, char *partnum, char *return_to) {
-       char buf[SIZ];
-       char *serialized_vcard = NULL;
-       size_t total_len = 0;
-       struct vCard *v;
-       int i;
-       char *key, *value;
-       char whatuser[256];
-
-       char lastname[256];
-       char firstname[256];
-       char middlename[256];
-       char prefix[256];
-       char suffix[256];
-       char pobox[256];
-       char extadr[256];
-       char street[256];
-       char city[256];
-       char state[256];
-       char zipcode[256];
-       char country[256];
-       char hometel[256];
-       char worktel[256];
-       char primary_inetemail[256];
-       char other_inetemail[SIZ];
-       char extrafields[SIZ];
-       char fullname[256];
-       char title[256];
-       char org[256];
-
-       lastname[0] = 0;
-       firstname[0] = 0;
-       middlename[0] = 0;
-       prefix[0] = 0;
-       suffix[0] = 0;
-       pobox[0] = 0;
-       extadr[0] = 0;
-       street[0] = 0;
-       city[0] = 0;
-       state[0] = 0;
-       zipcode[0] = 0;
-       country[0] = 0;
-       hometel[0] = 0;
-       worktel[0] = 0;
-       primary_inetemail[0] = 0;
-       other_inetemail[0] = 0;
-       title[0] = 0;
-       org[0] = 0;
-       extrafields[0] = 0;
-
-       safestrncpy(whatuser, "", sizeof whatuser);
-
-       if (msgnum >= 0) {
-               sprintf(buf, "MSG0 %ld|1", msgnum);
-               serv_puts(buf);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '1') {
-                       convenience_page("770000", _("Error"), &buf[4]);
-                       return;
-               }
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       if (!strncasecmp(buf, "from=", 5)) {
-                               safestrncpy(whatuser, &buf[5], sizeof whatuser);
-                       }
-                       else if (!strncasecmp(buf, "node=", 5)) {
-                               strcat(whatuser, " @ ");
-                               strcat(whatuser, &buf[5]);
-                       }
-               }
-       
-               sprintf(buf, "OPNA %ld|%s", msgnum, partnum);
-               serv_puts(buf);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] != '2') {
-                       convenience_page("770000", "Error", &buf[4]);
-                       return;
-               }
-       
-               total_len = atoi(&buf[4]);
-               serialized_vcard = malloc(total_len + 2);
-       
-               read_server_binary(serialized_vcard, total_len);
-       
-               serv_puts("CLOS");
-               serv_getln(buf, sizeof buf);
-               serialized_vcard[total_len] = 0;
-       
-               v = vcard_load(serialized_vcard);
-               free(serialized_vcard);
-       
-               /* Populate the variables for our form */
-               i = 0;
-               while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) {
-                       value = vcard_get_prop(v, "", 0, i++, 0);
-       
-                       if (!strcasecmp(key, "n")) {
-                               extract_token(lastname, value, 0, ';', sizeof lastname);
-                               extract_token(firstname, value, 1, ';', sizeof firstname);
-                               extract_token(middlename, value, 2, ';', sizeof middlename);
-                               extract_token(prefix, value, 3, ';', sizeof prefix);
-                               extract_token(suffix, value, 4, ';', sizeof suffix);
-                       }
-
-                       else if (!strcasecmp(key, "fn")) {
-                               safestrncpy(fullname, value, sizeof fullname);
-                       }
-
-                       else if (!strcasecmp(key, "title")) {
-                               safestrncpy(title, value, sizeof title);
-                       }
-       
-                       else if (!strcasecmp(key, "org")) {
-                               safestrncpy(org, value, sizeof org);
-                       }
-       
-                       else if (!strcasecmp(key, "adr")) {
-                               extract_token(pobox, value, 0, ';', sizeof pobox);
-                               extract_token(extadr, value, 1, ';', sizeof extadr);
-                               extract_token(street, value, 2, ';', sizeof street);
-                               extract_token(city, value, 3, ';', sizeof city);
-                               extract_token(state, value, 4, ';', sizeof state);
-                               extract_token(zipcode, value, 5, ';', sizeof zipcode);
-                               extract_token(country, value, 6, ';', sizeof country);
-                       }
-       
-                       else if (!strcasecmp(key, "tel;home")) {
-                               extract_token(hometel, value, 0, ';', sizeof hometel);
-                       }
-       
-                       else if (!strcasecmp(key, "tel;work")) {
-                               extract_token(worktel, value, 0, ';', sizeof worktel);
-                       }
-       
-                       else if (!strcasecmp(key, "email;internet")) {
-                               if (primary_inetemail[0] == 0) {
-                                       safestrncpy(primary_inetemail, value, sizeof primary_inetemail);
-                               }
-                               else {
-                                       if (other_inetemail[0] != 0) {
-                                               strcat(other_inetemail, "\n");
-                                       }
-                                       strcat(other_inetemail, value);
-                               }
-                       }
-       
-                       else {
-                               strcat(extrafields, key);
-                               strcat(extrafields, ":");
-                               strcat(extrafields, value);
-                               strcat(extrafields, "\n");
-                       }
-       
-               }
-       
-               vcard_free(v);
-       }
-
-       /** Display the form */
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n"
-               "<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>"
-               "<span class=\"titlebar\">"
-               "<img src=\"static/savecontact_48x.gif\">");
-       wprintf(_("Edit contact information"));
-       wprintf("</span>"
-               "</td></tr></table>\n"
-               "</div>\n<div id=\"content\">\n"
-       );
-
-       wprintf("<form method=\"POST\" action=\"submit_vcard\">\n");
-       wprintf("<div class=\"fix_scrollbar_bug\">"
-               "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
-
-       wprintf("<table border=0><tr>"
-               "<td>%s</td>"
-               "<td>%s</td>"
-               "<td>%s</td>"
-               "<td>%s</td>"
-               "<td>%s</td></tr>\n",
-               _("Prefix"), _("First"), _("Middle"), _("Last"), _("Suffix")
-       );
-       wprintf("<tr><td><input type=\"text\" name=\"prefix\" "
-               "value=\"%s\" maxlength=\"5\" size=\"5\"></td>",
-               prefix);
-       wprintf("<td><input type=\"text\" name=\"firstname\" "
-               "value=\"%s\" maxlength=\"29\"></td>",
-               firstname);
-       wprintf("<td><input type=\"text\" name=\"middlename\" "
-               "value=\"%s\" maxlength=\"29\"></td>",
-               middlename);
-       wprintf("<td><input type=\"text\" name=\"lastname\" "
-               "value=\"%s\" maxlength=\"29\"></td>",
-               lastname);
-       wprintf("<td><input type=\"text\" name=\"suffix\" "
-               "value=\"%s\" maxlength=\"10\" size=\"10\"></td></tr></table>\n",
-               suffix);
-
-       wprintf("<table border=0 width=100%% bgcolor=\"#dddddd\">");
-       wprintf("<tr><td>");
-
-       wprintf(_("Display name:"));
-       wprintf("<br>"
-               "<input type=\"text\" name=\"fullname\" "
-               "value=\"%s\" maxlength=\"40\"><br><br>\n",
-               fullname
-       );
-
-       wprintf(_("Title:"));
-       wprintf("<br>"
-               "<input type=\"text\" name=\"title\" "
-               "value=\"%s\" maxlength=\"40\"><br><br>\n",
-               title
-       );
-
-       wprintf(_("Organization:"));
-       wprintf("<br>"
-               "<input type=\"text\" name=\"org\" "
-               "value=\"%s\" maxlength=\"40\"><br><br>\n",
-               org
-       );
-
-       wprintf("</td><td>");
-
-       wprintf("<table border=0>");
-       wprintf("<tr><td>");
-       wprintf(_("PO box:"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"pobox\" "
-               "value=\"%s\" maxlength=\"29\"></td></tr>\n",
-               pobox);
-       wprintf("<tr><td>");
-       wprintf(_("Address:"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"extadr\" "
-               "value=\"%s\" maxlength=\"29\"></td></tr>\n",
-               extadr);
-       wprintf("<tr><td> </td><td>"
-               "<input type=\"text\" name=\"street\" "
-               "value=\"%s\" maxlength=\"29\"></td></tr>\n",
-               street);
-       wprintf("<tr><td>");
-       wprintf(_("City:"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"city\" "
-               "value=\"%s\" maxlength=\"29\"></td></tr>\n",
-               city);
-       wprintf("<tr><td>");
-       wprintf(_("State:"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"state\" "
-               "value=\"%s\" maxlength=\"2\"></td></tr>\n",
-               state);
-       wprintf("<tr><td>");
-       wprintf(_("ZIP code:"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"zipcode\" "
-               "value=\"%s\" maxlength=\"10\"></td></tr>\n",
-               zipcode);
-       wprintf("<tr><td>");
-       wprintf(_("Country:"));
-       wprintf("</td><td>"
-               "<input type=\"text\" name=\"country\" "
-               "value=\"%s\" maxlength=\"29\" width=\"5\"></td></tr>\n",
-               country);
-       wprintf("</table>\n");
-
-       wprintf("</table>\n");
-
-       wprintf("<table border=0><tr><td>");
-       wprintf(_("Home telephone:"));
-       wprintf("</td>"
-               "<td><input type=\"text\" name=\"hometel\" "
-               "value=\"%s\" maxlength=\"29\"></td>\n",
-               hometel);
-       wprintf("<td>");
-       wprintf(_("Work telephone:"));
-       wprintf("</td>"
-               "<td><input type=\"text\" name=\"worktel\" "
-               "value=\"%s\" maxlength=\"29\"></td></tr></table>\n",
-               worktel);
-
-       wprintf("<table border=0 width=100%% bgcolor=\"#dddddd\">");
-       wprintf("<tr><td>");
-
-       wprintf("<table border=0><TR>"
-               "<td valign=top>");
-       wprintf(_("Primary Internet e-mail address"));
-       wprintf("<br />"
-               "<input type=\"text\" name=\"primary_inetemail\" "
-               "size=40 maxlength=40 value=\"");
-       escputs(primary_inetemail);
-       wprintf("\"><br />"
-               "</td><td valign=top>");
-       wprintf(_("Internet e-mail aliases"));
-       wprintf("<br />"
-               "<textarea name=\"other_inetemail\" rows=5 cols=40 width=40>");
-       escputs(other_inetemail);
-       wprintf("</textarea></td></tr></table>\n");
-
-       wprintf("</td></tr></table>\n");
-
-       wprintf("<input type=\"hidden\" name=\"extrafields\" value=\"");
-       escputs(extrafields);
-       wprintf("\">\n");
-
-       wprintf("<input type=\"hidden\" name=\"return_to\" value=\"");
-       urlescputs(return_to);
-       wprintf("\">\n");
-
-       wprintf("<center>\n"
-               "<input type=\"submit\" name=\"ok_button\" value=\"%s\">"
-               "&nbsp;"
-               "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">"
-               "</center></form>\n",
-               _("Save changes"),
-               _("Cancel")
-       );
-       
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-
-/**
- * \brief commit the edits to the citadel server
- */
-void edit_vcard(void) {
-       long msgnum;
-       char *partnum;
-
-       msgnum = atol(bstr("msgnum"));
-       partnum = bstr("partnum");
-       do_edit_vcard(msgnum, partnum, "");
-}
-
-
-
-/**
- * \brief parse edited vcard from the browser
- */
-void submit_vcard(void) {
-       char buf[SIZ];
-       int i;
-
-       if (strlen(bstr("ok_button")) == 0) { 
-               readloop("readnew");
-               return;
-       }
-
-       sprintf(buf, "ENT0 1|||4||");
-       serv_puts(buf);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] != '4') {
-               edit_vcard();
-               return;
-       }
-
-       serv_puts("Content-type: text/x-vcard; charset=UTF-8");
-       serv_puts("");
-       serv_puts("begin:vcard");
-       serv_printf("n:%s;%s;%s;%s;%s",
-               bstr("lastname"),
-               bstr("firstname"),
-               bstr("middlename"),
-               bstr("prefix"),
-               bstr("suffix") );
-       serv_printf("title:%s", bstr("title") );
-       serv_printf("fn:%s", bstr("fullname") );
-       serv_printf("org:%s", bstr("org") );
-       serv_printf("adr:%s;%s;%s;%s;%s;%s;%s",
-               bstr("pobox"),
-               bstr("extadr"),
-               bstr("street"),
-               bstr("city"),
-               bstr("state"),
-               bstr("zipcode"),
-               bstr("country") );
-       serv_printf("tel;home:%s", bstr("hometel") );
-       serv_printf("tel;work:%s", bstr("worktel") );
-
-       serv_printf("email;internet:%s\n", bstr("primary_inetemail"));  
-       for (i=0; i<num_tokens(bstr("other_inetemail"), '\n'); ++i) {
-               extract_token(buf, bstr("other_inetemail"), i, '\n', sizeof buf);
-               if (strlen(buf) > 0) {
-                       serv_printf("email;internet:%s", buf);
-               }
-       }
-
-       serv_printf("%s", bstr("extrafields") );
-       serv_puts("end:vcard");
-       serv_puts("000");
-
-       if (!strcmp(bstr("return_to"), "select_user_to_edit")) {
-               select_user_to_edit(NULL, NULL);
-       }
-       else if (!strcmp(bstr("return_to"), "do_welcome")) {
-               do_welcome();
-       }
-       else {
-               readloop("readnew");
-       }
-}
-
-
-
-/*@}*/
diff --git a/webcit/webcit.c b/webcit/webcit.c
deleted file mode 100644 (file)
index 85ee1ca..0000000
+++ /dev/null
@@ -1,1676 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup MainServer This is the main transaction loop of the web service.  It maintains a
- * persistent session to the Citadel server, handling HTTP WebCit requests as
- * they arrive and presenting a user interface.
- * \ingroup WebcitHttpServer
- */
-/*@{*/
-#include "webcit.h"
-#include "groupdav.h"
-#include "webserver.h"
-#include "mime_parser.h"
-
-/**
- * Subdirectories from which the client may request static content
- */
-char *static_content_dirs[] = {
-       "static",                     /** static templates */
-       "tiny_mce"                    /** the JS editor */
-};
-
-/**
- * String to unset the cookie.
- * Any date "in the past" will work, so I chose my birthday, right down to
- * the exact minute.  :)
- */
-static char *unset = "; expires=28-May-1971 18:10:00 GMT";
-
-/**
- * \brief remove escaped strings from i.e. the url string (like %20 for blanks)
- * \param buf the buffer to examine
- */
-void unescape_input(char *buf)
-{
-       int a, b;
-       char hex[3];
-
-       while ((isspace(buf[strlen(buf) - 1])) && (strlen(buf) > 0))
-               buf[strlen(buf) - 1] = 0;
-
-       for (a = 0; a < strlen(buf); ++a) {
-               if (buf[a] == '+')
-                       buf[a] = ' ';
-               if (buf[a] == '%') {
-                       hex[0] = buf[a + 1];
-                       hex[1] = buf[a + 2];
-                       hex[2] = 0;
-                       b = 0;
-                       sscanf(hex, "%02x", &b);
-                       buf[a] = (char) b;
-                       strcpy(&buf[a + 1], &buf[a + 3]);
-               }
-       }
-
-}
-
-/**
- * \brief Extract variables from the URL.
- * \param url URL supplied by the HTTP parser
- */
-void addurls(char *url)
-{
-       char *up, *ptr;
-       char buf[SIZ];
-       int a, b;
-       struct urlcontent *u;
-
-       up = url;
-       while (strlen(up) > 0) {
-
-               /** locate the = sign */
-               safestrncpy(buf, up, sizeof buf);
-               b = (-1);
-               for (a = 255; a >= 0; --a)
-                       if (buf[a] == '=')
-                               b = a;
-               if (b < 0)
-                       return;
-               buf[b] = 0;
-
-               u = (struct urlcontent *) malloc(sizeof(struct urlcontent));
-               u->next = WC->urlstrings;
-               WC->urlstrings = u;
-               safestrncpy(u->url_key, buf, sizeof u->url_key);
-
-               /** now chop that part off */
-               for (a = 0; a <= b; ++a)
-                       ++up;
-
-               /** locate "&" and "?" delimiters */
-               ptr = up;
-               b = strlen(up);
-               for (a = 0; a < strlen(up); ++a) {
-                       if ( (ptr[0] == '&') || (ptr[0] == '?') ) {
-                               b = a;
-                               break;
-                       }
-                       ++ptr;
-               }
-               ptr = up;
-               for (a = 0; a < b; ++a)
-                       ++ptr;
-               strcpy(ptr, "");
-
-               u->url_data = malloc(strlen(up) + 2);
-               safestrncpy(u->url_data, up, strlen(up) + 1);
-               u->url_data[b] = 0;
-               unescape_input(u->url_data);
-               up = ptr;
-               ++up;
-       }
-}
-
-/**
- * \brief free urlstring memory
- */
-void free_urls(void)
-{
-       struct urlcontent *u;
-
-       while (WC->urlstrings != NULL) {
-               free(WC->urlstrings->url_data);
-               u = WC->urlstrings->next;
-               free(WC->urlstrings);
-               WC->urlstrings = u;
-       }
-}
-
-/**
- * \brief Diagnostic function to display the contents of all variables
- */
-void dump_vars(void)
-{
-       struct urlcontent *u;
-
-       for (u = WC->urlstrings; u != NULL; u = u->next) {
-               wprintf("%38s = %s\n", u->url_key, u->url_data);
-       }
-}
-
-/**
- * \brief Return the value of a variable supplied to the current web page (from the url or a form)
- * \param key The name of the variable we want
- */
-char *bstr(char *key)
-{
-       struct urlcontent *u;
-
-       for (u = WC->urlstrings; u != NULL; u = u->next) {
-               if (!strcasecmp(u->url_key, key))
-                       return (u->url_data);
-       }
-       return ("");
-}
-
-/**
- * \brief web-printing funcion. uses our vsnprintf wrapper
- * \param format printf format string 
- * \param ... the varargs to put into formatstring
- */
-void wprintf(const char *format,...)
-{
-       va_list arg_ptr;
-       char wbuf[4096];
-
-       va_start(arg_ptr, format);
-       vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
-       va_end(arg_ptr);
-
-       client_write(wbuf, strlen(wbuf));
-}
-
-
-/**
- * \brief wrap up an HTTP session, closes tags, etc.
- * \todo multiline params?
- * \param print_standard_html_footer should be set to 0 to transmit only, 1 to
- * append the main menu and closing tags, or 2 to
- * append the closing tags only.
- */
-void wDumpContent(int print_standard_html_footer)
-{
-       if (print_standard_html_footer) {
-               wprintf("</div>\n");    /* end of "text" div */
-               do_template("trailing");
-       }
-
-       /* If we've been saving it all up for one big output burst,
-        * go ahead and do that now.
-        */
-       end_burst();
-}
-
-
-/**
- * \brief Copy a string, escaping characters which have meaning in HTML.  
- * \param target target buffer
- * \param strbuf source buffer
- * \param nbsp If nonzero, spaces are converted to non-breaking spaces.
- * \param nolinebreaks if set, linebreaks are removed from the string.
- */
-void stresc(char *target, char *strbuf, int nbsp, int nolinebreaks)
-{
-       int a;
-       strcpy(target, "");
-
-       for (a = 0; a < strlen(strbuf); ++a) {
-               if (strbuf[a] == '<')
-                       strcat(target, "&lt;");
-               else if (strbuf[a] == '>')
-                       strcat(target, "&gt;");
-               else if (strbuf[a] == '&')
-                       strcat(target, "&amp;");
-               else if (strbuf[a] == '\"')
-                       strcat(target, "&quot;");
-               else if (strbuf[a] == '\'') 
-                       strcat(target, "&#39;");
-               else if (strbuf[a] == LB)
-                       strcat(target, "<");
-               else if (strbuf[a] == RB)
-                       strcat(target, ">");
-               else if (strbuf[a] == QU)
-                       strcat(target, "\"");
-               else if ((strbuf[a] == 32) && (nbsp == 1))
-                       strcat(target, "&nbsp;");
-               else if ((strbuf[a] == '\n') && (nolinebreaks))
-                       strcat(target, "");     /* nothing */
-               else if ((strbuf[a] == '\r') && (nolinebreaks))
-                       strcat(target, "");     /* nothing */
-               else
-                       strncat(target, &strbuf[a], 1);
-       }
-}
-
-/**
- * \brief WHAT???
- * \param strbuf what???
- * \param nbsp If nonzero, spaces are converted to non-breaking spaces.
- * \param nolinebreaks if set, linebreaks are removed from the string.
- */ 
-void escputs1(char *strbuf, int nbsp, int nolinebreaks)
-{
-       char *buf;
-
-       if (strbuf == NULL) return;
-       buf = malloc( (3 * strlen(strbuf)) + SIZ );
-       stresc(buf, strbuf, nbsp, nolinebreaks);
-       wprintf("%s", buf);
-       free(buf);
-}
-
-/** 
- * \brief static wrapper for ecsputs1
- * \param strbuf buffer to print escaped to client
- */
-void escputs(char *strbuf)
-{
-       escputs1(strbuf, 0, 0);
-}
-
-/** 
- * \brief Escape a string for feeding out as a URL.
- * Returns a pointer to a buffer that must be freed by the caller!
- * \param outbuf the output buffer
- * \param strbuf the input buffer
- */
-void urlesc(char *outbuf, char *strbuf)
-{
-       int a, b, c;
-       char *ec = " #&;`'|*?-~<>^()[]{}$\"\\";
-
-       strcpy(outbuf, "");
-
-       for (a = 0; a < strlen(strbuf); ++a) {
-               c = 0;
-               for (b = 0; b < strlen(ec); ++b) {
-                       if (strbuf[a] == ec[b])
-                               c = 1;
-               }
-               b = strlen(outbuf);
-               if (c == 1)
-                       sprintf(&outbuf[b], "%%%02x", strbuf[a]);
-               else
-                       sprintf(&outbuf[b], "%c", strbuf[a]);
-       }
-}
-
-/**
- * \brief urlescape buffer and print it to the client
- * \param strbuf buffer to urlescape
- */
-void urlescputs(char *strbuf)
-{
-       char outbuf[SIZ];
-       
-       urlesc(outbuf, strbuf);
-       wprintf("%s", outbuf);
-}
-
-
-/**
- * \brief Copy a string, escaping characters for JavaScript strings.
- * \param target output string
- * \param strbuf input string
- */
-void jsesc(char *target, char *strbuf)
-{
-       int a;
-       strcpy(target, "");
-
-       for (a = 0; a < strlen(strbuf); ++a) {
-               if (strbuf[a] == '<')
-                       strcat(target, "[");
-               else if (strbuf[a] == '>')
-                       strcat(target, "]");
-               else if (strbuf[a] == '\"')
-                       strcat(target, "&quot;");
-               else if (strbuf[a] == '&')
-                       strcat(target, "&amp;;");
-               else if (strbuf[a] == '\'') 
-                       strcat(target, "\\'");
-               else {
-                       strncat(target, &strbuf[a], 1);
-               }
-       }
-}
-
-/**
- * \brief escape and print java script
- * \param strbuf the js code
- */
-void jsescputs(char *strbuf)
-{
-       char outbuf[SIZ];
-       
-       jsesc(outbuf, strbuf);
-       wprintf("%s", outbuf);
-}
-
-/**
- * \brief Copy a string, escaping characters for message text hold
- * \param target target buffer
- * \param strbuf source buffer
- */
-void msgesc(char *target, char *strbuf)
-{
-       int a;
-       strcpy(target, "");
-
-       for (a = 0; a < strlen(strbuf); ++a) {
-               if (strbuf[a] == '\n')
-                       strcat(target, " ");
-               else if (strbuf[a] == '\r')
-                       strcat(target, " ");
-               else if (strbuf[a] == '\'')
-                       strcat(target, "&#39;");
-               else {
-                       strncat(target, &strbuf[a], 1);
-               }
-       }
-}
-
-/**
- * \brief print a string to the client after cleaning it with msgesc()
- * \param strbuf string to be printed
- */
-void msgescputs(char *strbuf) {
-       char *outbuf;
-
-       if (strbuf == NULL) return;
-       outbuf = malloc( (3 * strlen(strbuf)) + SIZ);
-       msgesc(outbuf, strbuf);
-       wprintf("%s", outbuf);
-       free(outbuf);
-}
-
-
-
-
-/**
- * \brief Output all that important stuff that the browser will want to see
- */
-void output_headers(   int do_httpheaders,     /**< 1 = output HTTP headers                          */
-                       int do_htmlhead,        /**< 1 = output HTML <head> section and <body> opener */
-
-                       int do_room_banner,     /**< 0=no, 1=yes,                                     
-                                                                * 2 = I'm going to embed my own, so don't open the 
-                                                                *     <div id="content"> either.                   
-                                                                */
-
-                       int unset_cookies,      /**< 1 = session is terminating, so unset the cookies */
-                       int suppress_check,     /**< 1 = suppress check for instant messages          */
-                       int cache               /**< 1 = allow browser to cache this page             */
-) {
-       char cookie[1024];
-       char httpnow[128];
-
-       wprintf("HTTP/1.1 200 OK\n");
-       http_datestring(httpnow, sizeof httpnow, time(NULL));
-
-       if (do_httpheaders) {
-               wprintf("Content-type: text/html; charset=utf-8\r\n"
-                       "Server: %s / %s\n"
-                       "Connection: close\r\n",
-                       SERVER, serv_info.serv_software
-               );
-       }
-
-       if (cache) {
-               wprintf("Pragma: public\r\n"
-                       "Cache-Control: max-age=3600, must-revalidate\r\n"
-                       "Last-modified: %s\r\n",
-                       httpnow
-               );
-       }
-       else {
-               wprintf("Pragma: no-cache\r\n"
-                       "Cache-Control: no-store\r\n"
-               );
-       }
-
-       stuff_to_cookie(cookie, WC->wc_session, WC->wc_username,
-                       WC->wc_password, WC->wc_roomname);
-
-       if (unset_cookies) {
-               wprintf("Set-cookie: webcit=%s; path=/\r\n", unset);
-       } else {
-               wprintf("Set-cookie: webcit=%s; path=/\r\n", cookie);
-               if (server_cookie != NULL) {
-                       wprintf("%s\n", server_cookie);
-               }
-       }
-
-       if (do_htmlhead) {
-               begin_burst();
-               do_template("head");
-       }
-
-       /** ICONBAR */
-       if (do_htmlhead) {
-
-
-               /** check for ImportantMessages (these display in a div overlaying the main screen) */
-               if (strlen(WC->ImportantMessage) > 0) {
-                       wprintf("<div id=\"important_message\">\n");
-                       wprintf("<span class=\"imsg\">"
-                               "%s</span><br />\n", WC->ImportantMessage);
-                       wprintf("</div>\n");
-                       wprintf("<script type=\"text/javascript\">\n"
-                               "        setTimeout('hide_imsg_popup()', 3000); \n"
-                               "</script>\n");
-                       safestrncpy(WC->ImportantMessage, "", sizeof WC->ImportantMessage);
-               }
-
-               if ( (WC->logged_in) && (!unset_cookies) ) {
-                       wprintf("<div id=\"iconbar\">");
-                       do_selected_iconbar();
-                       /** check for instant messages (these display in a new window) */
-                       page_popup();
-                       wprintf("</div>");
-               }
-
-               if (do_room_banner == 1) {
-                       wprintf("<div id=\"banner\">\n");
-                       embed_room_banner(NULL, navbar_default);
-                       wprintf("</div>\n");
-               }
-       }
-
-       if (do_room_banner == 1) {
-               wprintf("<div id=\"content\">\n");
-       }
-}
-
-
-/**
- * \brief Generic function to do an HTTP redirect.  Easy and fun.
- * \param whichpage target url to 302 to
- */
-void http_redirect(char *whichpage) {
-       wprintf("HTTP/1.1 302 Moved Temporarily\n");
-       wprintf("Location: %s\r\n", whichpage);
-       wprintf("URI: %s\r\n", whichpage);
-       wprintf("Content-type: text/html; charset=utf-8\r\n\r\n");
-       wprintf("<html><body>");
-       wprintf("Go <a href=\"%s\">here</A>.", whichpage);
-       wprintf("</body></html>\n");
-}
-
-
-
-/**
- * \brief Output a piece of content to the web browser
- */
-void http_transmit_thing(char *thing, size_t length, char *content_type,
-                        int is_static) {
-
-       output_headers(0, 0, 0, 0, 0, is_static);
-
-       wprintf("Content-type: %s\r\n"
-               "Server: %s\r\n"
-               "Connection: close\r\n",
-               content_type,
-               SERVER);
-
-#ifdef HAVE_ZLIB
-       /** If we can send the data out compressed, please do so. */
-       if (WC->gzip_ok) {
-               char *compressed_data = NULL;
-               uLongf compressed_len;
-
-               compressed_len = (uLongf) ((length * 101) / 100) + 100;
-               compressed_data = malloc(compressed_len);
-
-               if (compress_gzip((Bytef *) compressed_data,
-                                 &compressed_len,
-                                 (Bytef *) thing,
-                                 (uLongf) length, Z_BEST_SPEED) == Z_OK) {
-                       wprintf("Content-encoding: gzip\r\n"
-                               "Content-length: %ld\r\n"
-                               "\r\n",
-                               (long) compressed_len
-                       );
-                       client_write(compressed_data, (size_t)compressed_len);
-                       free(compressed_data);
-                       return;
-               }
-       }
-#endif
-
-       /** No compression ... just send it out as-is */
-       wprintf("Content-length: %ld\r\n"
-               "\r\n",
-               (long) length
-       );
-       client_write(thing, (size_t)length);
-}
-
-
-
-/**
- * \brief dump out static pages from disk
- * \param what the file urs to print
- */
-void output_static(char *what)
-{
-       FILE *fp;
-       struct stat statbuf;
-       off_t bytes;
-       char *bigbuffer;
-       char content_type[128];
-
-       fp = fopen(what, "rb");
-       if (fp == NULL) {
-               lprintf(9, "output_static('%s')  -- NOT FOUND --\n", what);
-               wprintf("HTTP/1.1 404 %s\n", strerror(errno));
-               wprintf("Content-Type: text/plain\r\n");
-               wprintf("\r\n");
-               wprintf("Cannot open %s: %s\n", what, strerror(errno));
-       } else {
-               if (!strncasecmp(&what[strlen(what) - 4], ".gif", 4))
-                       safestrncpy(content_type, "image/gif", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 4], ".txt", 4))
-                       safestrncpy(content_type, "text/plain", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 4], ".css", 4))
-                       safestrncpy(content_type, "text/css", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 4], ".jpg", 4))
-                       safestrncpy(content_type, "image/jpeg", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 4], ".png", 4))
-                       safestrncpy(content_type, "image/png", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 4], ".ico", 4))
-                       safestrncpy(content_type, "image/x-icon", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 5], ".html", 5))
-                       safestrncpy(content_type, "text/html", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 4], ".htm", 4))
-                       safestrncpy(content_type, "text/html", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 4], ".wml", 4))
-                       safestrncpy(content_type, "text/vnd.wap.wml", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 5], ".wmls", 5))
-                       safestrncpy(content_type, "text/vnd.wap.wmlscript", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 5], ".wmlc", 5))
-                       safestrncpy(content_type, "application/vnd.wap.wmlc", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 6], ".wmlsc", 6))
-                       safestrncpy(content_type, "application/vnd.wap.wmlscriptc", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 5], ".wbmp", 5))
-                       safestrncpy(content_type, "image/vnd.wap.wbmp", sizeof content_type);
-               else if (!strncasecmp(&what[strlen(what) - 3], ".js", 3))
-                       safestrncpy(content_type, "text/javascript", sizeof content_type);
-               else
-                       safestrncpy(content_type, "application/octet-stream", sizeof content_type);
-
-               fstat(fileno(fp), &statbuf);
-               bytes = statbuf.st_size;
-               bigbuffer = malloc(bytes + 2);
-               fread(bigbuffer, bytes, 1, fp);
-               fclose(fp);
-
-               lprintf(9, "output_static('%s')  %s\n", what, content_type);
-               http_transmit_thing(bigbuffer, (size_t)bytes, content_type, 1);
-               free(bigbuffer);
-       }
-       if (!strcasecmp(bstr("force_close_session"), "yes")) {
-               end_webcit_session();
-       }
-}
-
-
-/**
- * \brief When the browser requests an image file from the Citadel server,
- * this function is called to transmit it.
- */
-void output_image()
-{
-       char buf[SIZ];
-       char *xferbuf = NULL;
-       off_t bytes;
-
-       serv_printf("OIMG %s|%s", bstr("name"), bstr("parm"));
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               bytes = extract_long(&buf[4], 0);
-               xferbuf = malloc(bytes + 2);
-
-               /** Read it from the server */
-               read_server_binary(xferbuf, bytes);
-               serv_puts("CLOS");
-               serv_getln(buf, sizeof buf);
-
-               /** Write it to the browser */
-               http_transmit_thing(xferbuf, (size_t)bytes, "image/gif", 0);
-               free(xferbuf);
-
-       } else {
-               /**
-                * Instead of an ugly 404, send a 1x1 transparent GIF
-                * when there's no such image on the server.
-                */
-               output_static("static/blank.gif");
-       }
-
-
-
-}
-
-/**
- * \brief Generic function to output an arbitrary MIME part from an arbitrary
- *        message number on the server.
- *
- * \param msgnum               Number of the item on the citadel server
- * \param partnum              The MIME part to be output
- * \param force_download       Nonzero to force set the Content-Type: header
- *                              to "application/octet-stream"
- */
-void mimepart(char *msgnum, char *partnum, int force_download)
-{
-       char buf[256];
-       off_t bytes;
-       char content_type[256];
-       char *content = NULL;
-       
-       serv_printf("OPNA %s|%s", msgnum, partnum);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               bytes = extract_long(&buf[4], 0);
-               content = malloc(bytes + 2);
-               if (force_download) {
-                       strcpy(content_type, "application/octet-stream");
-               }
-               else {
-                       extract_token(content_type, &buf[4], 3, '|', sizeof content_type);
-               }
-               output_headers(0, 0, 0, 0, 0, 0);
-               read_server_binary(content, bytes);
-               serv_puts("CLOS");
-               serv_getln(buf, sizeof buf);
-               http_transmit_thing(content, bytes, content_type, 0);
-               free(content);
-       } else {
-               wprintf("HTTP/1.1 404 %s\n", &buf[4]);
-               output_headers(0, 0, 0, 0, 0, 0);
-               wprintf("Content-Type: text/plain\r\n");
-               wprintf("\r\n");
-               wprintf(_("An error occurred while retrieving this part: %s\n"), &buf[4]);
-       }
-
-}
-
-
-/**
- * \brief Read any MIME part of a message, from the server, into memory.
- * \param msgnum number of the message on the citadel server
- * \param partnum the MIME part to be loaded
- */
-char *load_mimepart(long msgnum, char *partnum)
-{
-       char buf[SIZ];
-       off_t bytes;
-       char content_type[SIZ];
-       char *content;
-       
-       serv_printf("OPNA %ld|%s", msgnum, partnum);
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               bytes = extract_long(&buf[4], 0);
-               extract_token(content_type, &buf[4], 3, '|', sizeof content_type);
-
-               content = malloc(bytes + 2);
-               read_server_binary(content, bytes);
-
-               serv_puts("CLOS");
-               serv_getln(buf, sizeof buf);
-               content[bytes] = 0;     /* null terminate for good measure */
-               return(content);
-       }
-       else {
-               return(NULL);
-       }
-
-}
-
-
-/**
- * \brief Convenience functions to display a page containing only a string
- * \param titlebarcolor color of the titlebar of the frame
- * \param titlebarmsg text to display in the title bar
- * \param messagetext body of the box
- */
-void convenience_page(char *titlebarcolor, char *titlebarmsg, char *messagetext)
-{
-       wprintf("HTTP/1.1 200 OK\n");
-       output_headers(1, 1, 2, 0, 0, 0);
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<table width=100%% border=0 bgcolor=\"#%s\"><tr><td>", titlebarcolor);
-       wprintf("<span class=\"titlebar\">%s</span>\n", titlebarmsg);
-       wprintf("</td></tr></table>\n");
-       wprintf("</div>\n<div id=\"content\">\n");
-       escputs(messagetext);
-
-       wprintf("<hr />\n");
-       wDumpContent(1);
-}
-
-
-/**
- * \brief Display a blank page.
- */
-void blank_page(void) {
-       output_headers(1, 1, 0, 0, 0, 0);
-       wDumpContent(2);
-}
-
-
-/**
- * \brief A template has been requested
- */
-void url_do_template(void) {
-       do_template(bstr("template"));
-}
-
-
-
-/**
- * \brief Offer to make any page the user's "start page."
- */
-void offer_start_page(void) {
-       wprintf("<a href=\"change_start_page?startpage=");
-       urlescputs(WC->this_page);
-       wprintf("\"><font size=-2 color=\"#AAAAAA\">");
-       wprintf(_("Make this my start page"));
-       wprintf("</font></a>");
-/*
-       wprintf("<br/><a href=\"rss?room=");
-       urlescputs(WC->wc_roomname);
-       wprintf("\" title=\"RSS 2.0 feed for ");
-       escputs(WC->wc_roomname);
-       wprintf("\"><img alt=\"RSS\" border=\"0\" src=\"static/xml_button.gif\"/></a>\n");
-*/
-}
-
-
-/**
- * \brief Change the user's start page
- */
-void change_start_page(void) {
-
-       if (bstr("startpage") == NULL) {
-               safestrncpy(WC->ImportantMessage,
-                       _("You no longer have a start page selected."),
-                       sizeof WC->ImportantMessage);
-               display_main_menu();
-               return;
-       }
-
-       set_preference("startpage", bstr("startpage"), 1);
-
-       output_headers(1, 1, 0, 0, 0, 0);
-       do_template("newstartpage");
-       wDumpContent(1);
-}
-
-
-
-/**
- * \brief convenience function to indicate success
- * \param successmessage the mesage itself
- */
-void display_success(char *successmessage)
-{
-       convenience_page("007700", "OK", successmessage);
-}
-
-
-/**
- * \brief Authorization required page 
- * This is probably temporary and should be revisited 
- * \param message message to put in header
-*/
-void authorization_required(const char *message)
-{
-       wprintf("HTTP/1.1 401 Authorization Required\r\n");
-       wprintf("WWW-Authenticate: Basic realm=\"\"\r\n", serv_info.serv_humannode);
-       wprintf("Content-Type: text/html\r\n\r\n");
-       wprintf("<h1>");
-       wprintf(_("Authorization Required"));
-       wprintf("</h1>\r\n");
-       wprintf(_("The resource you requested requires a valid username and password. "
-               "You could not be logged in: %s\n"), message);
-       wDumpContent(0);
-}
-
-/**
- * \brief This function is called by the MIME parser to handle data uploaded by
- *        the browser.  Form data, uploaded files, and the data from HTTP PUT
- *        operations (such as those found in GroupDAV) all arrive this way.
- *
- * \param name Name of the item being uploaded
- * \param filename Filename of the item being uploaded
- * \param partnum MIME part identifier (not needed)
- * \param disp MIME content disposition (not needed)
- * \param content The actual data
- * \param cbtype MIME content-type
- * \param cbcharset Character set
- * \param length Content length
- * \param encoding MIME encoding type (not needed)
- * \param userdata Not used here
- */
-void upload_handler(char *name, char *filename, char *partnum, char *disp,
-                       void *content, char *cbtype, char *cbcharset,
-                       size_t length, char *encoding, void *userdata)
-{
-       struct urlcontent *u;
-
-       /* lprintf(9, "upload_handler() name=%s, type=%s, len=%d\n",
-               name, cbtype, length); */
-
-       /* Form fields */
-       if ( (length > 0) && (strlen(cbtype) == 0) ) {
-               u = (struct urlcontent *) malloc(sizeof(struct urlcontent));
-               u->next = WC->urlstrings;
-               WC->urlstrings = u;
-               safestrncpy(u->url_key, name, sizeof(u->url_key));
-               u->url_data = malloc(length + 1);
-               memcpy(u->url_data, content, length);
-               u->url_data[length] = 0;
-       }
-
-       /** Uploaded files */
-       if ( (length > 0) && (strlen(cbtype) > 0) ) {
-               WC->upload = malloc(length);
-               if (WC->upload != NULL) {
-                       WC->upload_length = length;
-                       safestrncpy(WC->upload_filename, filename,
-                                       sizeof(WC->upload_filename));
-                       safestrncpy(WC->upload_content_type, cbtype,
-                                       sizeof(WC->upload_content_type));
-                       memcpy(WC->upload, content, length);
-               }
-               else {
-                       lprintf(3, "malloc() failed: %s\n", strerror(errno));
-               }
-       }
-
-}
-
-/**
- * \brief Convenience functions to wrap around asynchronous ajax responses
- */
-void begin_ajax_response(void) {
-        output_headers(0, 0, 0, 0, 0, 0);
-
-        wprintf("Content-type: text/html; charset=UTF-8\r\n"
-                "Server: %s\r\n"
-                "Connection: close\r\n"
-                "Pragma: no-cache\r\n"
-                "Cache-Control: no-cache\r\n",
-                SERVER);
-        begin_burst();
-}
-
-/**
- * \brief print ajax response footer 
- */
-void end_ajax_response(void) {
-        wprintf("\r\n");
-        wDumpContent(0);
-}
-
-/**
- * \brief Wraps a Citadel server command in an AJAX transaction.
- */
-void ajax_servcmd(void)
-{
-       char buf[1024];
-       char gcontent[1024];
-       char *junk;
-       size_t len;
-
-       begin_ajax_response();
-
-       serv_printf("%s", bstr("g_cmd"));
-       serv_getln(buf, sizeof buf);
-       wprintf("%s\n", buf);
-
-       if (buf[0] == '8') {
-               serv_printf("\n\n000");
-       }
-       if ((buf[0] == '1') || (buf[0] == '8')) {
-               while (serv_getln(gcontent, sizeof gcontent), strcmp(gcontent, "000")) {
-                       wprintf("%s\n", gcontent);
-               }
-               wprintf("000");
-       }
-       if (buf[0] == '4') {
-               text_to_server(bstr("g_input"));
-               serv_puts("000");
-       }
-       if (buf[0] == '6') {
-               len = atol(&buf[4]);
-               junk = malloc(len);
-               serv_read(junk, len);
-               free(junk);
-       }
-       if (buf[0] == '7') {
-               len = atol(&buf[4]);
-               junk = malloc(len);
-               memset(junk, 0, len);
-               serv_write(junk, len);
-               free(junk);
-       }
-
-       end_ajax_response();
-       
-       /**
-        * This is kind of an ugly hack, but this is the only place it can go.
-        * If the command was GEXP, then the instant messenger window must be
-        * running, so reset the "last_pager_check" watchdog timer so
-        * that page_popup() doesn't try to open it a second time.
-        */
-       if (!strncasecmp(bstr("g_cmd"), "GEXP", 4)) {
-               WC->last_pager_check = time(NULL);
-       }
-}
-
-
-/**
- * \brief Helper function for the asynchronous check to see if we need
- * to open the instant messenger window.
- */
-void seconds_since_last_gexp(void)
-{
-       char buf[256];
-
-       begin_ajax_response();
-       if ( (time(NULL) - WC->last_pager_check) < 30) {
-               wprintf("NO\n");
-       }
-       else {
-               serv_puts("NOOP");
-               serv_getln(buf, sizeof buf);
-               if (buf[3] == '*') {
-                       wprintf("YES");
-               }
-               else {
-                       wprintf("NO");
-               }
-       }
-       end_ajax_response();
-}
-
-
-
-
-/**
- * \brief Entry point for WebCit transaction
- */
-void session_loop(struct httprequest *req)
-{
-       char cmd[1024];
-       char action[1024];
-       char arg1[128];
-       char arg2[128];
-       char arg3[128];
-       char arg4[128];
-       char arg5[128];
-       char arg6[128];
-       char arg7[128];
-       char buf[SIZ];
-       char request_method[128];
-       char pathname[1024];
-       int a, b;
-       int ContentLength = 0;
-       int BytesRead = 0;
-       char ContentType[512];
-       char *content = NULL;
-       char *content_end = NULL;
-       struct httprequest *hptr;
-       char browser_host[256];
-       char user_agent[256];
-       int body_start = 0;
-       int is_static = 0;
-
-       /**
-        * We stuff these with the values coming from the client cookies,
-        * so we can use them to reconnect a timed out session if we have to.
-        */
-       char c_username[SIZ];
-       char c_password[SIZ];
-       char c_roomname[SIZ];
-       char c_httpauth_string[SIZ];
-       char c_httpauth_user[SIZ];
-       char c_httpauth_pass[SIZ];
-       char cookie[SIZ];
-
-       safestrncpy(c_username, "", sizeof c_username);
-       safestrncpy(c_password, "", sizeof c_password);
-       safestrncpy(c_roomname, "", sizeof c_roomname);
-       safestrncpy(c_httpauth_string, "", sizeof c_httpauth_string);
-       safestrncpy(c_httpauth_user, DEFAULT_HTTPAUTH_USER, sizeof c_httpauth_user);
-       safestrncpy(c_httpauth_pass, DEFAULT_HTTPAUTH_PASS, sizeof c_httpauth_pass);
-       strcpy(browser_host, "");
-
-       WC->upload_length = 0;
-       WC->upload = NULL;
-       WC->vars = NULL;
-       WC->is_wap = 0;
-
-       hptr = req;
-       if (hptr == NULL) return;
-
-       safestrncpy(cmd, hptr->line, sizeof cmd);
-       hptr = hptr->next;
-       extract_token(request_method, cmd, 0, ' ', sizeof request_method);
-       extract_token(pathname, cmd, 1, ' ', sizeof pathname);
-
-       /** Figure out the action */
-       extract_token(action, pathname, 1, '/', sizeof action);
-       if (strstr(action, "?")) *strstr(action, "?") = 0;
-       if (strstr(action, "&")) *strstr(action, "&") = 0;
-       if (strstr(action, " ")) *strstr(action, " ") = 0;
-
-       extract_token(arg1, pathname, 2, '/', sizeof arg1);
-       if (strstr(arg1, "?")) *strstr(arg1, "?") = 0;
-       if (strstr(arg1, "&")) *strstr(arg1, "&") = 0;
-       if (strstr(arg1, " ")) *strstr(arg1, " ") = 0;
-
-       extract_token(arg2, pathname, 3, '/', sizeof arg2);
-       if (strstr(arg2, "?")) *strstr(arg2, "?") = 0;
-       if (strstr(arg2, "&")) *strstr(arg2, "&") = 0;
-       if (strstr(arg2, " ")) *strstr(arg2, " ") = 0;
-
-       extract_token(arg3, pathname, 4, '/', sizeof arg3);
-       if (strstr(arg3, "?")) *strstr(arg3, "?") = 0;
-       if (strstr(arg3, "&")) *strstr(arg3, "&") = 0;
-       if (strstr(arg3, " ")) *strstr(arg3, " ") = 0;
-
-       extract_token(arg4, pathname, 5, '/', sizeof arg4);
-       if (strstr(arg4, "?")) *strstr(arg4, "?") = 0;
-       if (strstr(arg4, "&")) *strstr(arg4, "&") = 0;
-       if (strstr(arg4, " ")) *strstr(arg4, " ") = 0;
-
-       extract_token(arg5, pathname, 6, '/', sizeof arg5);
-       if (strstr(arg5, "?")) *strstr(arg5, "?") = 0;
-       if (strstr(arg5, "&")) *strstr(arg5, "&") = 0;
-       if (strstr(arg5, " ")) *strstr(arg5, " ") = 0;
-
-       extract_token(arg6, pathname, 7, '/', sizeof arg6);
-       if (strstr(arg6, "?")) *strstr(arg6, "?") = 0;
-       if (strstr(arg6, "&")) *strstr(arg6, "&") = 0;
-       if (strstr(arg6, " ")) *strstr(arg6, " ") = 0;
-
-       extract_token(arg7, pathname, 8, '/', sizeof arg7);
-       if (strstr(arg7, "?")) *strstr(arg7, "?") = 0;
-       if (strstr(arg7, "&")) *strstr(arg7, "&") = 0;
-       if (strstr(arg7, " ")) *strstr(arg7, " ") = 0;
-
-       while (hptr != NULL) {
-               safestrncpy(buf, hptr->line, sizeof buf);
-               /* lprintf(9, "HTTP HEADER: %s\n", buf); */
-               hptr = hptr->next;
-
-               if (!strncasecmp(buf, "Cookie: webcit=", 15)) {
-                       safestrncpy(cookie, &buf[15], sizeof cookie);
-                       cookie_to_stuff(cookie, NULL,
-                                       c_username, sizeof c_username,
-                                       c_password, sizeof c_password,
-                                       c_roomname, sizeof c_roomname);
-               }
-               else if (!strncasecmp(buf, "Authorization: Basic ", 21)) {
-                       CtdlDecodeBase64(c_httpauth_string, &buf[21], strlen(&buf[21]));
-                       extract_token(c_httpauth_user, c_httpauth_string, 0, ':', sizeof c_httpauth_user);
-                       extract_token(c_httpauth_pass, c_httpauth_string, 1, ':', sizeof c_httpauth_pass);
-               }
-               else if (!strncasecmp(buf, "Content-length: ", 16)) {
-                       ContentLength = atoi(&buf[16]);
-               }
-               else if (!strncasecmp(buf, "Content-type: ", 14)) {
-                       safestrncpy(ContentType, &buf[14], sizeof ContentType);
-               }
-               else if (!strncasecmp(buf, "User-agent: ", 12)) {
-                       safestrncpy(user_agent, &buf[12], sizeof user_agent);
-               }
-               else if (!strncasecmp(buf, "X-Forwarded-Host: ", 18)) {
-                       if (follow_xff) {
-                               safestrncpy(WC->http_host, &buf[18], sizeof WC->http_host);
-                       }
-               }
-               else if (!strncasecmp(buf, "Host: ", 6)) {
-                       if (strlen(WC->http_host) == 0) {
-                               safestrncpy(WC->http_host, &buf[6], sizeof WC->http_host);
-                       }
-               }
-               else if (!strncasecmp(buf, "X-Forwarded-For: ", 17)) {
-                       safestrncpy(browser_host, &buf[17], sizeof browser_host);
-                       while (num_tokens(browser_host, ',') > 1) {
-                               remove_token(browser_host, 0, ',');
-                       }
-                       striplt(browser_host);
-               }
-               /** Only WAP gateways explicitly name this content-type */
-               else if (strstr(buf, "text/vnd.wap.wml")) {
-                       WC->is_wap = 1;
-               }
-       }
-
-       if (ContentLength > 0) {
-               content = malloc(ContentLength + SIZ);
-               memset(content, 0, ContentLength + SIZ);
-               sprintf(content, "Content-type: %s\n"
-                               "Content-length: %d\n\n",
-                               ContentType, ContentLength);
-               body_start = strlen(content);
-
-               /** Read the entire input data at once. */
-               client_read(WC->http_sock, &content[BytesRead+body_start],
-                       ContentLength);
-
-               if (!strncasecmp(ContentType,
-                             "application/x-www-form-urlencoded", 33)) {
-                       addurls(&content[body_start]);
-               } else if (!strncasecmp(ContentType, "multipart", 9)) {
-                       content_end = content + ContentLength + body_start;
-                       mime_parser(content, content_end, *upload_handler,
-                                       NULL, NULL, NULL, 0);
-               }
-       } else {
-               content = NULL;
-       }
-
-       /** make a note of where we are in case the user wants to save it */
-       safestrncpy(WC->this_page, cmd, sizeof(WC->this_page));
-       remove_token(WC->this_page, 2, ' ');
-       remove_token(WC->this_page, 0, ' ');
-
-       /** If there are variables in the URL, we must grab them now */
-       for (a = 0; a < strlen(cmd); ++a) {
-               if ((cmd[a] == '?') || (cmd[a] == '&')) {
-                       for (b = a; b < strlen(cmd); ++b)
-                               if (isspace(cmd[b]))
-                                       cmd[b] = 0;
-                       addurls(&cmd[a + 1]);
-                       cmd[a] = 0;
-               }
-       }
-
-       /** If it's a "force 404" situation then display the error and bail. */
-       if (!strcmp(action, "404")) {
-               wprintf("HTTP/1.1 404 Not found\r\n");
-               wprintf("Content-Type: text/plain\r\n");
-               wprintf("\r\n");
-               wprintf("Not found\r\n");
-               goto SKIP_ALL_THIS_CRAP;
-       }
-
-       /** Static content can be sent without connecting to Citadel. */
-       is_static = 0;
-       for (a=0; a<(sizeof(static_content_dirs) / sizeof(char *)); ++a) {
-               if (!strcasecmp(action, static_content_dirs[a])) {
-                       is_static = 1;
-               }
-       }
-       if (is_static) {
-               snprintf(buf, sizeof buf, "%s/%s/%s/%s/%s/%s/%s/%s",
-                       action, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
-               for (a=0; a<8; ++a) {
-                       if (buf[strlen(buf)-1] == '/') {
-                               buf[strlen(buf)-1] = 0;
-                       }
-               }
-               for (a = 0; a < strlen(buf); ++a) {
-                       if (isspace(buf[a])) {
-                               buf[a] = 0;
-                       }
-               }
-               output_static(buf);
-               goto SKIP_ALL_THIS_CRAP;        /* Don't try to connect */
-       }
-
-       /**
-        * If we're not connected to a Citadel server, try to hook up the
-        * connection now.
-        */
-       if (!WC->connected) {
-               if (!strcasecmp(ctdlhost, "uds")) {
-                       /* unix domain socket */
-                       sprintf(buf, "%s/citadel.socket", ctdlport);
-                       WC->serv_sock = uds_connectsock(buf);
-               }
-               else {
-                       /* tcp socket */
-                       WC->serv_sock = tcp_connectsock(ctdlhost, ctdlport);
-               }
-
-               if (WC->serv_sock < 0) {
-                       do_logout();
-                       goto SKIP_ALL_THIS_CRAP;
-               }
-               else {
-                       WC->connected = 1;
-                       serv_getln(buf, sizeof buf);    /** get the server welcome message */
-
-                       /**
-                        * From what host is our user connecting?  Go with
-                        * the host at the other end of the HTTP socket,
-                        * unless we are following X-Forwarded-For: headers
-                        * and such a header has already turned up something.
-                        */
-                       if ( (!follow_xff) || (strlen(browser_host) == 0) ) {
-                               locate_host(browser_host, WC->http_sock);
-                       }
-
-                       get_serv_info(browser_host, user_agent);
-                       if (serv_info.serv_rev_level < MINIMUM_CIT_VERSION) {
-                               wprintf(_("You are connected to a Citadel "
-                                       "server running Citadel %d.%02d. \n"
-                                       "In order to run this version of WebCit "
-                                       "you must also have Citadel %d.%02d or"
-                                       " newer.\n\n\n"),
-                                               serv_info.serv_rev_level / 100,
-                                               serv_info.serv_rev_level % 100,
-                                               MINIMUM_CIT_VERSION / 100,
-                                               MINIMUM_CIT_VERSION % 100
-                                       );
-                               end_webcit_session();
-                               goto SKIP_ALL_THIS_CRAP;
-                       }
-               }
-       }
-
-       /**
-        * Functions which can be performed without logging in
-        */
-       if (!strcasecmp(action, "listsub")) {
-               do_listsub();
-               goto SKIP_ALL_THIS_CRAP;
-       }
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-       if (!strcasecmp(action, "freebusy")) {
-               do_freebusy(cmd);
-               goto SKIP_ALL_THIS_CRAP;
-       }
-#endif
-
-       /**
-        * If we're not logged in, but we have HTTP Authentication data,
-        * try logging in to Citadel using that.
-        */
-       if ((!WC->logged_in)
-          && (strlen(c_httpauth_user) > 0)
-          && (strlen(c_httpauth_pass) > 0)) {
-               serv_printf("USER %s", c_httpauth_user);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '3') {
-                       serv_printf("PASS %s", c_httpauth_pass);
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] == '2') {
-                               become_logged_in(c_httpauth_user,
-                                               c_httpauth_pass, buf);
-                               safestrncpy(WC->httpauth_user, c_httpauth_user, sizeof WC->httpauth_user);
-                               safestrncpy(WC->httpauth_pass, c_httpauth_pass, sizeof WC->httpauth_pass);
-                       } else {
-                               /** Should only display when password is wrong */
-                               authorization_required(&buf[4]);
-                               goto SKIP_ALL_THIS_CRAP;
-                       }
-               }
-       }
-
-       /** This needs to run early */
-       if (!strcasecmp(action, "rss")) {
-               display_rss(bstr("room"), request_method);
-               goto SKIP_ALL_THIS_CRAP;
-       }
-
-       /** 
-        * The GroupDAV stuff relies on HTTP authentication instead of
-        * our session's authentication.
-        */
-       if (!strncasecmp(action, "groupdav", 8)) {
-               groupdav_main(req, ContentType, /* do GroupDAV methods */
-                       ContentLength, content+body_start);
-               if (!WC->logged_in) {
-                       WC->killthis = 1;       /* If not logged in, don't */
-               }                               /* keep the session active */
-               goto SKIP_ALL_THIS_CRAP;
-       }
-
-
-       /**
-        * Automatically send requests with any method other than GET or
-        * POST to the GroupDAV code as well.
-        */
-       if ((strcasecmp(request_method, "GET")) && (strcasecmp(request_method, "POST"))) {
-               groupdav_main(req, ContentType, /** do GroupDAV methods */
-                       ContentLength, content+body_start);
-               if (!WC->logged_in) {
-                       WC->killthis = 1;       /** If not logged in, don't */
-               }                               /** keep the session active */
-               goto SKIP_ALL_THIS_CRAP;
-       }
-
-       /**
-        * If we're not logged in, but we have username and password cookies
-        * supplied by the browser, try using them to log in.
-        */
-       if ((!WC->logged_in)
-          && (strlen(c_username) > 0)
-          && (strlen(c_password) > 0)) {
-               serv_printf("USER %s", c_username);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '3') {
-                       serv_printf("PASS %s", c_password);
-                       serv_getln(buf, sizeof buf);
-                       if (buf[0] == '2') {
-                               become_logged_in(c_username, c_password, buf);
-                       }
-               }
-       }
-       /**
-        * If we don't have a current room, but a cookie specifying the
-        * current room is supplied, make an effort to go there.
-        */
-       if ((strlen(WC->wc_roomname) == 0) && (strlen(c_roomname) > 0)) {
-               serv_printf("GOTO %s", c_roomname);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '2') {
-                       safestrncpy(WC->wc_roomname, c_roomname, sizeof WC->wc_roomname);
-               }
-       }
-
-       if (!strcasecmp(action, "image")) {
-               output_image();
-
-               /**
-                * All functions handled below this point ... make sure we log in
-                * before doing anything else!
-                */
-       } else if ((!WC->logged_in) && (!strcasecmp(action, "login"))) {
-               do_login();
-       } else if (!WC->logged_in) {
-               display_login(NULL);
-       }
-
-       /**
-        * Various commands...
-        */
-
-       else if (!strcasecmp(action, "do_welcome")) {
-               do_welcome();
-       } else if (!strcasecmp(action, "blank")) {
-               blank_page();
-       } else if (!strcasecmp(action, "do_template")) {
-               url_do_template();
-       } else if (!strcasecmp(action, "display_aide_menu")) {
-               display_aide_menu();
-       } else if (!strcasecmp(action, "display_main_menu")) {
-               display_main_menu();
-       } else if (!strcasecmp(action, "who")) {
-               who();
-       } else if (!strcasecmp(action, "sslg")) {
-               seconds_since_last_gexp();
-       } else if (!strcasecmp(action, "who_inner_html")) {
-               begin_ajax_response();
-               who_inner_div();
-               end_ajax_response();
-       } else if (!strcasecmp(action, "iconbar_ajax_menu")) {
-               begin_ajax_response();
-               do_iconbar();
-               end_ajax_response();
-       } else if (!strcasecmp(action, "iconbar_ajax_rooms")) {
-               begin_ajax_response();
-               do_iconbar_roomlist();
-               end_ajax_response();
-       } else if (!strcasecmp(action, "knrooms")) {
-               knrooms();
-       } else if (!strcasecmp(action, "gotonext")) {
-               slrp_highest();
-               gotonext();
-       } else if (!strcasecmp(action, "skip")) {
-               gotonext();
-       } else if (!strcasecmp(action, "ungoto")) {
-               ungoto();
-       } else if (!strcasecmp(action, "dotgoto")) {
-               if (WC->wc_view != VIEW_MAILBOX) {      /* dotgoto acts like dotskip when we're in a mailbox view */
-                       slrp_highest();
-               }
-               smart_goto(bstr("room"));
-       } else if (!strcasecmp(action, "dotskip")) {
-               smart_goto(bstr("room"));
-       } else if (!strcasecmp(action, "termquit")) {
-               do_logout();
-       } else if (!strcasecmp(action, "readnew")) {
-               readloop("readnew");
-       } else if (!strcasecmp(action, "readold")) {
-               readloop("readold");
-       } else if (!strcasecmp(action, "readfwd")) {
-               readloop("readfwd");
-       } else if (!strcasecmp(action, "headers")) {
-               readloop("headers");
-       } else if (!strcasecmp(action, "msg")) {
-               embed_message(arg1);
-       } else if (!strcasecmp(action, "printmsg")) {
-               print_message(arg1);
-       } else if (!strcasecmp(action, "msgheaders")) {
-               display_headers(arg1);
-       } else if (!strcasecmp(action, "wiki")) {
-               display_wiki_page();
-       } else if (!strcasecmp(action, "display_enter")) {
-               display_enter();
-       } else if (!strcasecmp(action, "post")) {
-               post_message();
-       } else if (!strcasecmp(action, "move_msg")) {
-               move_msg();
-       } else if (!strcasecmp(action, "delete_msg")) {
-               delete_msg();
-       } else if (!strcasecmp(action, "userlist")) {
-               userlist();
-       } else if (!strcasecmp(action, "showuser")) {
-               showuser();
-       } else if (!strcasecmp(action, "display_page")) {
-               display_page();
-       } else if (!strcasecmp(action, "page_user")) {
-               page_user();
-       } else if (!strcasecmp(action, "chat")) {
-               do_chat();
-       } else if (!strcasecmp(action, "display_private")) {
-               display_private("", 0);
-       } else if (!strcasecmp(action, "goto_private")) {
-               goto_private();
-       } else if (!strcasecmp(action, "zapped_list")) {
-               zapped_list();
-       } else if (!strcasecmp(action, "display_zap")) {
-               display_zap();
-       } else if (!strcasecmp(action, "zap")) {
-               zap();
-       } else if (!strcasecmp(action, "display_entroom")) {
-               display_entroom();
-       } else if (!strcasecmp(action, "entroom")) {
-               entroom();
-       } else if (!strcasecmp(action, "display_whok")) {
-               display_whok();
-       } else if (!strcasecmp(action, "do_invt_kick")) {
-               do_invt_kick();
-       } else if (!strcasecmp(action, "display_editroom")) {
-               display_editroom();
-       } else if (!strcasecmp(action, "netedit")) {
-               netedit();
-       } else if (!strcasecmp(action, "editroom")) {
-               editroom();
-       } else if (!strcasecmp(action, "display_editinfo")) {
-               display_edit(_("Room info"), "EINF 0", "RINF", "/editinfo", 1);
-       } else if (!strcasecmp(action, "editinfo")) {
-               save_edit(_("Room info"), "EINF 1", 1);
-       } else if (!strcasecmp(action, "display_editbio")) {
-               sprintf(buf, "RBIO %s", WC->wc_fullname);
-               display_edit(_("Your bio"), "NOOP", buf, "editbio", 3);
-       } else if (!strcasecmp(action, "editbio")) {
-               save_edit(_("Your bio"), "EBIO", 0);
-       } else if (!strcasecmp(action, "confirm_move_msg")) {
-               confirm_move_msg();
-       } else if (!strcasecmp(action, "delete_room")) {
-               delete_room();
-       } else if (!strcasecmp(action, "validate")) {
-               validate();
-       } else if (!strcasecmp(action, "display_editpic")) {
-               display_graphics_upload(_("your photo"),
-                                       "UIMG 0|_userpic_",
-                                       "editpic");
-       } else if (!strcasecmp(action, "editpic")) {
-               do_graphics_upload("UIMG 1|_userpic_");
-       } else if (!strcasecmp(action, "display_editroompic")) {
-               display_graphics_upload(_("the icon for this room"),
-                                       "UIMG 0|_roompic_",
-                                       "editroompic");
-       } else if (!strcasecmp(action, "editroompic")) {
-               do_graphics_upload("UIMG 1|_roompic_");
-       } else if (!strcasecmp(action, "delete_floor")) {
-               delete_floor();
-       } else if (!strcasecmp(action, "rename_floor")) {
-               rename_floor();
-       } else if (!strcasecmp(action, "create_floor")) {
-               create_floor();
-       } else if (!strcasecmp(action, "display_editfloorpic")) {
-               sprintf(buf, "UIMG 0|_floorpic_|%s",
-                       bstr("which_floor"));
-               display_graphics_upload(_("the icon for this floor"),
-                                       buf,
-                                       "editfloorpic");
-       } else if (!strcasecmp(action, "editfloorpic")) {
-               sprintf(buf, "UIMG 1|_floorpic_|%s",
-                       bstr("which_floor"));
-               do_graphics_upload(buf);
-       } else if (!strcasecmp(action, "display_reg")) {
-               display_reg(0);
-       } else if (!strcasecmp(action, "display_changepw")) {
-               display_changepw();
-       } else if (!strcasecmp(action, "changepw")) {
-               changepw();
-       } else if (!strcasecmp(action, "display_edit_node")) {
-               display_edit_node();
-       } else if (!strcasecmp(action, "edit_node")) {
-               edit_node();
-       } else if (!strcasecmp(action, "display_netconf")) {
-               display_netconf();
-       } else if (!strcasecmp(action, "display_confirm_delete_node")) {
-               display_confirm_delete_node();
-       } else if (!strcasecmp(action, "delete_node")) {
-               delete_node();
-       } else if (!strcasecmp(action, "display_add_node")) {
-               display_add_node();
-       } else if (!strcasecmp(action, "add_node")) {
-               add_node();
-       } else if (!strcasecmp(action, "terminate_session")) {
-               slrp_highest();
-               terminate_session();
-       } else if (!strcasecmp(action, "edit_me")) {
-               edit_me();
-       } else if (!strcasecmp(action, "display_siteconfig")) {
-               display_siteconfig();
-       } else if (!strcasecmp(action, "chat_recv")) {
-               chat_recv();
-       } else if (!strcasecmp(action, "chat_send")) {
-               chat_send();
-       } else if (!strcasecmp(action, "siteconfig")) {
-               siteconfig();
-       } else if (!strcasecmp(action, "display_generic")) {
-               display_generic();
-       } else if (!strcasecmp(action, "do_generic")) {
-               do_generic();
-       } else if (!strcasecmp(action, "ajax_servcmd")) {
-               ajax_servcmd();
-       } else if (!strcasecmp(action, "display_menubar")) {
-               display_menubar(1);
-       } else if (!strcasecmp(action, "mimepart")) {
-               mimepart(arg1, arg2, 0);
-       } else if (!strcasecmp(action, "mimepart_download")) {
-               mimepart(arg1, arg2, 1);
-       } else if (!strcasecmp(action, "edit_vcard")) {
-               edit_vcard();
-       } else if (!strcasecmp(action, "submit_vcard")) {
-               submit_vcard();
-       } else if (!strcasecmp(action, "select_user_to_edit")) {
-               select_user_to_edit(NULL, NULL);
-       } else if (!strcasecmp(action, "display_edituser")) {
-               display_edituser(NULL, 0);
-       } else if (!strcasecmp(action, "edituser")) {
-               edituser();
-       } else if (!strcasecmp(action, "create_user")) {
-               create_user();
-       } else if (!strcasecmp(action, "changeview")) {
-               change_view();
-       } else if (!strcasecmp(action, "change_start_page")) {
-               change_start_page();
-       } else if (!strcasecmp(action, "display_floorconfig")) {
-               display_floorconfig(NULL);
-       } else if (!strcasecmp(action, "toggle_self_service")) {
-               toggle_self_service();
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-       } else if (!strcasecmp(action, "display_edit_task")) {
-               display_edit_task();
-       } else if (!strcasecmp(action, "save_task")) {
-               save_task();
-       } else if (!strcasecmp(action, "display_edit_event")) {
-               display_edit_event();
-       } else if (!strcasecmp(action, "save_event")) {
-               save_event();
-       } else if (!strcasecmp(action, "respond_to_request")) {
-               respond_to_request();
-       } else if (!strcasecmp(action, "handle_rsvp")) {
-               handle_rsvp();
-#endif
-       } else if (!strcasecmp(action, "summary")) {
-               summary();
-       } else if (!strcasecmp(action, "summary_inner_div")) {
-               begin_ajax_response();
-               summary_inner_div();
-               end_ajax_response();
-       } else if (!strcasecmp(action, "display_customize_iconbar")) {
-               display_customize_iconbar();
-       } else if (!strcasecmp(action, "commit_iconbar")) {
-               commit_iconbar();
-       } else if (!strcasecmp(action, "set_room_policy")) {
-               set_room_policy();
-       } else if (!strcasecmp(action, "display_inetconf")) {
-               display_inetconf();
-       } else if (!strcasecmp(action, "save_inetconf")) {
-               save_inetconf();
-       } else if (!strcasecmp(action, "setup_wizard")) {
-               do_setup_wizard();
-       } else if (!strcasecmp(action, "display_preferences")) {
-               display_preferences();
-       } else if (!strcasecmp(action, "set_preferences")) {
-               set_preferences();
-       } else if (!strcasecmp(action, "recp_autocomplete")) {
-               recp_autocomplete(bstr("recp"));
-       } else if (!strcasecmp(action, "cc_autocomplete")) {
-               recp_autocomplete(bstr("cc"));
-       } else if (!strcasecmp(action, "bcc_autocomplete")) {
-               recp_autocomplete(bstr("bcc"));
-       } else if (!strcasecmp(action, "set_floordiv_expanded")) {
-               set_floordiv_expanded(arg1);
-       } else if (!strcasecmp(action, "diagnostics")) {
-               output_headers(1, 1, 1, 0, 0, 0);
-               wprintf("Session: %d<hr />\n", WC->wc_session);
-               wprintf("Command: <br /><PRE>\n");
-               escputs(cmd);
-               wprintf("</PRE><hr />\n");
-               wprintf("Variables: <br /><PRE>\n");
-               dump_vars();
-               wprintf("</PRE><hr />\n");
-               wDumpContent(1);
-       } else if (!strcasecmp(action, "updatenote")) {
-               updatenote();
-       }
-
-       /** When all else fais, display the main menu. */
-       else {
-               display_main_menu();
-       }
-
-SKIP_ALL_THIS_CRAP:
-       fflush(stdout);
-       if (content != NULL) {
-               free(content);
-               content = NULL;
-       }
-       free_urls();
-       if (WC->upload_length > 0) {
-               free(WC->upload);
-               WC->upload_length = 0;
-       }
-}
-
-
-/*@}*/
diff --git a/webcit/webcit.h b/webcit/webcit.h
deleted file mode 100644 (file)
index 92eae15..0000000
+++ /dev/null
@@ -1,746 +0,0 @@
-/* $Id$ */
-
-/** we need _GNU_SOURCE for various functions arround the NLS-Stuff */
-#define _GNU_SOURCE
-
-
-#include <ctype.h>
-#include <stdlib.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdio.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#include <sys/stat.h>
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/un.h>
-#include <netdb.h>
-#include <sys/poll.h>
-#include <string.h>
-#include <pwd.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <pthread.h>
-#include <signal.h>
-#include <sys/utsname.h>
-
-#ifndef INADDR_NONE
-#define INADDR_NONE 0xffffffff
-#endif
-
-#ifdef HAVE_ICONV
-#include <iconv.h>
-#endif
-
-#ifdef ENABLE_NLS
-#include <libintl.h>
-#include <locale.h>
-extern locale_t wc_locales[];
-#define _(string)      gettext(string)
-#else
-#define _(string)      (string)
-#endif
-
-/*
- * Uncomment to dump an HTTP trace to stderr
-#define HTTP_TRACING 1
- */
-
-#ifdef HTTP_TRACING
-#undef HAVE_ZLIB_H
-#undef HAVE_ZLIB
-#endif
-
-#ifdef HAVE_ZLIB_H
-#include <zlib.h>
-#endif
-
-#ifdef HAVE_ICAL_H
-#ifdef HAVE_LIBICAL
-#define WEBCIT_WITH_CALENDAR_SERVICE 1
-#endif
-#endif
-
-
-
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-/* Work around PACKAGE/VERSION defs that are (not supposed to be?) in ical.h */
-#ifdef PACKAGE
-# define CTDL_PACKAGE PACKAGE
-# undef PACKAGE
-#endif
-#ifdef VERSION
-# define CTDL_VERSION VERSION
-# undef VERSION
-#endif
-#include <ical.h>
-#ifdef CTDL_PACKAGE
-# ifdef PACKAGE
-#  undef PACKAGE
-# endif
-# define PACKAGE CTDL_PACKAGE
-# undef CTDL_PACKAGE
-#endif
-#ifdef CTDL_VERSION
-# ifdef VERSION
-#  undef VERSION
-# endif
-# define VERSION CTDL_VERSION
-# undef CTDL_VERSION
-#endif
-#endif
-
-
-
-#ifdef HAVE_OPENSSL
-/* Work around RedHat's b0rken OpenSSL includes */
-#define OPENSSL_NO_KRB5
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#endif
-
-#define CALENDAR_ROOM_NAME     "Calendar"
-#define PRODID "-//Citadel//NONSGML Citadel Calendar//EN"
-
-#define SIZ                    4096            /* generic buffer size */
-
-#define TRACE fprintf(stderr, "Checkpoint: %s, %d\n", __FILE__, __LINE__)
-
-#define SLEEPING               180             /* TCP connection timeout */
-#define WEBCIT_TIMEOUT         900             /* WebCit session timeout */
-#define PORT_NUM               2000            /* port number to listen on */
-#define SERVER                 "WebCit v6.73"  /* who's in da house */
-#define DEVELOPER_ID           0
-#define CLIENT_ID              4
-#define CLIENT_VERSION         673             /* This version of WebCit */
-#define MINIMUM_CIT_VERSION    673             /* min required Citadel ver. */
-#define DEFAULT_HOST           "localhost"     /* Default Citadel server */
-#define DEFAULT_PORT           "504"
-#define LB                     (1)             /* Internal escape chars */
-#define RB                     (2)
-#define QU                     (3)
-#define TARGET                 "webcit01"      /* Target for inline URL's */
-#define HOUSEKEEPING           15              /* Housekeeping frequency */
-#define MIN_WORKER_THREADS     5
-#define MAX_WORKER_THREADS     250
-#define LISTEN_QUEUE_LENGTH    100             /* listen() backlog queue */
-
-#define USERCONFIGROOM         "My Citadel Config"
-#define DEFAULT_MAXMSGS                20
-
-
-/*
- * Room flags (from Citadel)
- *
- * bucket one...
- */
-#define QR_PERMANENT   1               /**< Room does not purge                */
-#define QR_INUSE       2               /**< Set if in use, clear if avail      */
-#define QR_PRIVATE     4               /**< Set for any type of private room   */
-#define QR_PASSWORDED  8               /**< Set if there's a password too      */
-#define QR_GUESSNAME   16              /**< Set if it's a guessname room       */
-#define QR_DIRECTORY   32              /**< Directory room                     */
-#define QR_UPLOAD      64              /**< Allowed to upload                  */
-#define QR_DOWNLOAD    128             /**< Allowed to download                */
-#define QR_VISDIR      256             /**< Visible directory                  */
-#define QR_ANONONLY    512             /**< Anonymous-Only room                */
-#define QR_ANONOPT     1024            /**< Anonymous-Option room              */
-#define QR_NETWORK     2048            /**< Shared network room                */
-#define QR_PREFONLY    4096            /**< Preferred status needed to enter   */
-#define QR_READONLY    8192            /**< Aide status required to post       */
-#define QR_MAILBOX     16384           /**< Set if this is a private mailbox   */
-
-/**
- * bucket two...
- */
-#define QR2_SYSTEM     1               /**< System room; hide by default       */
-#define QR2_SELFLIST   2               /**< Self-service mailing list mgmt     */
-
-/**
- * user/room access
- */
-#define UA_KNOWN       2
-#define UA_GOTOALLOWED 4
-#define UA_HASNEWMSGS  8
-#define UA_ZAPPED      16
-
-
-/**
- * User flags (from Citadel)
- */
-#define US_NEEDVALID   1               /**< User needs to be validated         */
-#define US_PERM                4               /**< Permanent user                     */
-#define US_LASTOLD     16              /**< Print last old message with new    */
-#define US_EXPERT      32              /**< Experienced user                   */
-#define US_UNLISTED    64              /**< Unlisted userlog entry             */
-#define US_NOPROMPT    128             /**< Don't prompt after each message    */
-#define US_PROMPTCTL   256             /**< <N>ext & <S>top work at prompt     */
-#define US_DISAPPEAR   512             /**< Use "disappearing msg prompts"     */
-#define US_REGIS       1024            /**< Registered user                    */
-#define US_PAGINATOR   2048            /**< Pause after each screen of text    */
-#define US_INTERNET    4096            /**< Internet mail privileges           */
-#define US_FLOORS      8192            /**< User wants to see floors           */
-#define US_COLOR       16384           /**< User wants ANSI color support      */
-#define US_USER_SET    (US_LASTOLD | US_EXPERT | US_UNLISTED | \
-                       US_NOPROMPT | US_DISAPPEAR | US_PAGINATOR | \
-                       US_FLOORS | US_COLOR | US_PROMPTCTL )
-
-
-
-/** \brief     Linked list of lines appearing in an HTTP client request */
-struct httprequest {
-       struct httprequest *next;  /**< the next request in the list */
-       char line[SIZ];            /**< the request line */
-};
-
-/**
- * \brief      Linked list of session variables encoded in an x-www-urlencoded content type
- */
-struct urlcontent {
-       struct urlcontent *next;   /**< the next variable in the list */ 
-       char url_key[32];          /**< the variable name */
-       char *url_data;            /**< its value */
-};
-
-/**
- * \brief information about us ???
- */ 
-struct serv_info {
-       int serv_pid;                   /**< Process ID of the Citadel server */
-       char serv_nodename[32];         /**< Node name of the Citadel server */
-       char serv_humannode[64];        /**< human readable node name of the Citadel server */
-       char serv_fqdn[64];             /**< fully quallified Domain Name (such as uncensored.citadel.org) */
-       char serv_software[64];         /**< What version does our connected citadel server use */
-       int serv_rev_level;             /**< Whats the citadel server revision */
-       char serv_bbs_city[64];         /**< Geographic location of the Citadel server */
-       char serv_sysadm[64];           /**< Name of system administrator */
-       char serv_moreprompt[SIZ];      /**< Whats the commandline textprompt */
-       int serv_ok_floors;             /**< nonzero == server supports floors */
-       int serv_supports_ldap;         /**< is the server linked against an ldap tree for adresses? */
-       int serv_newuser_disabled;      /**< Has the server disabled self-service new user creation? */
-};
-
-
-
-/**
- * \brief This struct holds a list of rooms for \\\<G\\\>oto operations.
- */
-struct march {
-       struct march *next;       /**< pointer to next in linked list */
-       char march_name[128];     /**< name of room */
-       int march_floor;          /**< floor number of room */
-       int march_order;          /**< sequence in which we are to visit this room */
-};
-
-/* *
- * \brief      This struct holds a list of rooms for client display.
- *             It is a binary tree.
- */
-struct roomlisting {
-       struct roomlisting *lnext;      /**< pointer to 'left' tree node */
-       struct roomlisting *rnext;      /**< pointer to 'right' tree node */
-       char rlname[128];               /**< name of room */
-       unsigned rlflags;               /**< room flags */
-       int rlfloor;                    /**< the floor it resides on */
-       int rlorder;                    /**< room listing order */
-};
-
-
-
-/**
- * \brief Dynamic content for variable substitution in templates
- */
-struct wcsubst {
-       struct wcsubst *next;       /**< next item in the list */  
-       int wcs_type;                       /**< which type of ??? */
-       char wcs_key[32];                   /**< ??? what?*/
-       void *wcs_value;                    /**< ???? what?*/
-       void (*wcs_function)(void); /**< funcion hook ???*/
-};
-
-/**
- * \brief Values for wcs_type
- */
-enum {
-       WCS_STRING,   /**< its a string */
-       WCS_FUNCTION, /**< its a function callback */
-       WCS_SERVCMD   /**< its a command to send to the citadel server */
-};
-
-/**
- * \brief mail attachment ???
- */
-struct wc_attachment {
-       struct wc_attachment *next;/**< pointer to next in list */
-       size_t length;                     /**< length of the contenttype */
-       char content_type[SIZ];    /**< the content itself ???*/
-       char filename[SIZ];                /**< the filename hooked to this content ??? */
-       char *data;                /**< the data pool; aka this content */
-};
-
-/**
- * \brief message summary structure. ???
- */
-struct message_summary {
-       time_t date;        /**< its creation date */
-       long msgnum;            /**< the message number on the citadel server */
-       char from[128];         /**< the author */
-       char to[128];           /**< the recipient */
-       char subj[128];         /**< the title / subject */
-       int hasattachments;     /**< does it have atachments? */
-       int is_new;         /**< is it yet read? */
-};
-
-/**
- * \brief  Data structure for roomlist-to-folderlist conversion 
- */
-struct folder {
-       int floor;      /**< which floor is it on */
-       char room[SIZ]; /**< which roomname ??? */
-       char name[SIZ]; /**< which is its own name??? */
-       int hasnewmsgs; /**< are there unread messages inside */
-       int is_mailbox; /**< is it a mailbox?  */
-       int selectable; /**< can we select it ??? */
-       int view;       /**< whats its default view? inbox/calendar.... */
-};
-
-/**
- * \brief One of these is kept for each active Citadel session.
- * HTTP transactions are bound to on e at a time.
- */
-struct wcsession {
-       struct wcsession *next;                 /**< Linked list */
-       int wc_session;                         /**< WebCit session ID */
-       char wc_username[128];                  /**< login name of current user */
-       char wc_fullname[128];                  /**< Screen name of current user */
-       char wc_password[128];                  /**< Password of current user */
-       char wc_roomname[256];                  /**< Room we are currently in */
-       int connected;                          /**< nonzero == we are connected to Citadel */
-       int logged_in;                          /**< nonzero == we are logged in  */
-       int axlevel;                            /**< this user's access level */
-       int is_aide;                            /**< nonzero == this user is an Aide */
-       int is_room_aide;                       /**< nonzero == this user is a Room Aide in this room */
-       int http_sock;                          /**< HTTP server socket */
-       int serv_sock;                          /**< Client socket to Citadel server */
-       int chat_sock;                          /**< Client socket to Citadel server - for chat */
-       unsigned room_flags;                    /**< flags associated with the current room */
-       int wc_view;                            /**< view for the current room */
-       int wc_default_view;                    /**< default view for the current room */
-       int wc_is_trash;                        /**< nonzero == current room is a Trash folder */
-       int wc_floor;                           /**< floor number of current room */
-       char ugname[128];                       /**< where does 'ungoto' take us */
-       long uglsn;                             /**< last seen message number for ungoto */
-       int upload_length;                      /**< content length of http-uploaded data */
-       char *upload;                           /**< pointer to http-uploaded data */
-       char upload_filename[PATH_MAX];         /**< filename of http-uploaded data */
-       char upload_content_type[256];          /**< content type of http-uploaded data */
-       int new_mail;                           /**< user has new mail waiting */
-       int remember_new_mail;                  /**< last count of new mail messages */
-       int need_regi;                          /**< This user needs to register. */
-       int need_vali;                          /**< New users require validation. */
-       char cs_inet_email[256];                /**< User's preferred Internet addr. */
-       pthread_mutex_t SessionMutex;           /**< mutex for exclusive access */
-       time_t lastreq;                         /**< Timestamp of most recent HTTP */
-       int killthis;                           /**< Nonzero == purge this session */
-       struct march *march;                    /**< march mode room list */
-       char reply_to[512];                     /**< reply-to address */
-       long msgarr[10000];                     /**< for read operations */
-       int num_summ;                           /**< number of messages in mailbox summary view */
-       struct message_summary *summ;           /**< array of messages for mailbox summary view */
-       int is_wap;                             /**< Client is a WAP gateway */
-       struct urlcontent *urlstrings;          /**< variables passed to webcit in a URL */
-       struct wcsubst *vars;                   /**< HTTP variable substitutions for this page */
-       char this_page[512];                    /**< URL of current page */
-       char http_host[512];                    /**< HTTP Host: header */
-       char *preferences;                      /**< WebCit preferences for this user */
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE            
-       /** \brief ical???? */                          
-       struct disp_cal {                                       
-               icalcomponent *cal;             /**< cal items for display */
-               long cal_msgnum;                /**< cal msgids for display */
-       } *disp_cal;                                            
-       int num_cal;                            /**< number of calendar items for display */
-#endif                                                                                 
-       struct wc_attachment *first_attachment; /**< linked list of attachments for 'enter message' */
-       char last_chat_user[256];               /**< ??? todo */
-       char ImportantMessage[SIZ];             /**< ??? todo */
-       int ctdl_pid;                           /**< Session ID on the Citadel server */
-       char httpauth_user[256];                /**< only for GroupDAV sessions */
-       char httpauth_pass[256];                /**< only for GroupDAV sessions */
-       size_t burst_len;                       /** <??? todo */
-       size_t burst_alloc;                     /** <??? todo */
-       char *burst;                            /** <??? todo */
-       int gzip_ok;                            /**< Nonzero if Accept-encoding: gzip */
-       int is_mailbox;                         /**< the current room is a private mailbox */
-       struct folder *cache_fold;              /**< cache the iconbar room list */
-       int cache_max_folders;                  /**< ??? todo */
-       int cache_num_floors;                   /**< ??? todo */
-       time_t cache_timestamp;                 /**< ??? todo */
-       int current_iconbar;                    /**< What is currently in the iconbar? */
-       char floordiv_expanded[32];             /**< which floordiv currently expanded */
-       int selected_language;                  /**< Language selected by user */
-       time_t last_pager_check;                /**< last time we polled for instant msgs */
-};
-
-/** values for WC->current_iconbar */
-enum {
-       current_iconbar_menu,     /**< view the icon menue */
-       current_iconbar_roomlist  /**< view the roomtree */
-};
-
-
-#define num_parms(source)              num_tokens(source, '|') 
-
-/* Per-session data */
-#define WC ((struct wcsession *)pthread_getspecific(MyConKey))
-extern pthread_key_t MyConKey;
-
-/* Per-thread SSL context */
-#ifdef HAVE_OPENSSL
-#define THREADSSL ((SSL *)pthread_getspecific(ThreadSSL))
-extern pthread_key_t ThreadSSL;
-#endif
-
-struct serv_info serv_info;
-extern char floorlist[128][SIZ];
-extern char *axdefs[];
-extern char *ctdlhost, *ctdlport;
-extern int http_port;
-extern char *server_cookie;
-extern int is_https;
-extern int setup_wizard;
-extern char wizard_filename[];
-extern time_t if_modified_since;
-extern int follow_xff;
-void do_setup_wizard(void);
-
-void stuff_to_cookie(char *cookie, int session,
-                       char *user, char *pass, char *room);
-void cookie_to_stuff(char *cookie, int *session,
-                char *user, size_t user_len,
-                char *pass, size_t pass_len,
-                char *room, size_t room_len);
-void locate_host(char *, int);
-void become_logged_in(char *, char *, char *);
-void do_login(void);
-void display_login(char *mesg);
-void do_welcome(void);
-void do_logout(void);
-void display_main_menu(void);
-void display_aide_menu(void);
-void display_advanced_menu(void);
-void slrp_highest(void);
-void gotonext(void);
-void ungoto(void);
-void get_serv_info(char *, char *);
-int uds_connectsock(char *);
-int tcp_connectsock(char *, char *);
-void serv_getln(char *strbuf, int bufsize);
-void serv_puts(char *string);
-void who(void);
-void who_inner_div(void);
-void fmout(char *align);
-void pullquote_fmout(void);
-void wDumpContent(int);
-void serv_printf(const char *format,...);
-char *bstr(char *key);
-void urlesc(char *, char *);
-void urlescputs(char *);
-void jsesc(char *, char *);
-void jsescputs(char *);
-void output_headers(    int do_httpheaders,
-                       int do_htmlhead,
-                       int do_room_banner,
-                       int unset_cookies,
-                       int suppress_check,
-                       int cache);
-void wprintf(const char *format,...);
-void output_static(char *what);
-void stresc(char *target, char *strbuf, int nbsp, int nolinebreaks);
-void escputs(char *strbuf);
-void url(char *buf);
-void escputs1(char *strbuf, int nbsp, int nolinebreaks);
-void msgesc(char *target, char *strbuf);
-void msgescputs(char *strbuf);
-int extract_int(const char *source, int parmnum);
-long extract_long(const char *source, int parmnum);
-void stripout(char *str, char leftboundary, char rightboundary);
-void dump_vars(void);
-void embed_main_menu(void);
-void serv_read(char *buf, int bytes);
-int haschar(char *, char);
-void readloop(char *oper);
-void read_message(long msgnum, int printable_view, char *section);
-void embed_message(char *msgnum_as_string);
-void print_message(char *msgnum_as_string);
-void display_headers(char *msgnum_as_string);
-void text_to_server(char *ptr);
-void text_to_server_qp(char *ptr);
-void display_enter(void);
-void post_message(void);
-void confirm_delete_msg(void);
-void delete_msg(void);
-void confirm_move_msg(void);
-void move_msg(void);
-void userlist(void);
-void showuser(void);
-void display_page(void);
-void page_user(void);
-void do_chat(void);
-void display_private(char *rname, int req_pass);
-void goto_private(void);
-void zapped_list(void);
-void display_zap(void);
-void zap(void);
-void display_success(char *);
-void authorization_required(const char *message);
-void display_entroom(void);
-void entroom(void);
-void display_editroom(void);
-void netedit(void);
-void editroom(void);
-void display_whok(void);
-void do_invt_kick(void);
-void server_to_text(void);
-void save_edit(char *description, char *enter_cmd, int regoto);
-void display_edit(char *description, char *check_cmd,
-                 char *read_cmd, char *save_cmd, int with_room_banner);
-int gotoroom(char *gname);
-void confirm_delete_room(void);
-void delete_room(void);
-void validate(void);
-void display_graphics_upload(char *, char *, char *);
-void do_graphics_upload(char *upl_cmd);
-void serv_read(char *buf, int bytes);
-void serv_gets(char *strbuf);
-void serv_write(char *buf, int nbytes);
-void serv_puts(char *string);
-void serv_printf(const char *format,...);
-void load_floorlist(void);
-void display_reg(int);
-void display_changepw(void);
-void changepw(void);
-void display_edit_node(void);
-void edit_node(void);
-void display_netconf(void);
-void display_confirm_delete_node(void);
-void delete_node(void);
-void display_add_node(void);
-void add_node(void);
-void terminate_session(void);
-void edit_me(void);
-void display_siteconfig(void);
-void siteconfig(void);
-void display_generic(void);
-void do_generic(void);
-void ajax_servcmd(void);
-void display_menubar(int);
-void smart_goto(char *);
-void worker_entry(void);
-void session_loop(struct httprequest *);
-size_t wc_strftime(char *s, size_t max, const char *format, const struct tm *tm);
-void fmt_date(char *buf, time_t thetime, int brief);
-void fmt_time(char *buf, time_t thetime);
-void httpdate(char *buf, time_t thetime);
-time_t httpdate_to_timestamp(char *buf);
-void end_webcit_session(void);
-void page_popup(void);
-void chat_recv(void);
-void chat_send(void);
-void http_redirect(char *);
-void clear_local_substs(void);
-void svprintf(char *keyname, int keytype, const char *format,...);
-void svcallback(char *keyname, void (*fcn_ptr)() );
-void do_template(void *templatename);
-int lingering_close(int fd);
-char *memreadline(char *start, char *buf, int maxlen);
-int num_tokens (char *source, char tok);
-void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen);
-void remove_token(char *source, int parmnum, char separator);
-char *load_mimepart(long msgnum, char *partnum);
-int pattern2(char *search, char *patn);
-void do_edit_vcard(long, char *, char *);
-void edit_vcard(void);
-void submit_vcard(void);
-void striplt(char *);
-void select_user_to_edit(char *message, char *preselect);
-void delete_user(char *);
-void display_edituser(char *who, int is_new);
-void create_user(void);
-void edituser(void);
-void do_change_view(int);
-void change_view(void);
-void folders(void);
-void load_preferences(void);
-void save_preferences(void);
-void get_preference(char *key, char *value, size_t value_len);
-void set_preference(char *key, char *value, int save_to_server);
-void knrooms(void);
-int is_msg_in_mset(char *mset, long msgnum);
-char *safestrncpy(char *dest, const char *src, size_t n);
-void display_addressbook(long msgnum, char alpha);
-void offer_start_page(void);
-void convenience_page(char *titlebarcolor, char *titlebarmsg, char *messagetext);
-void change_start_page(void);
-void output_html(char *, int);
-void display_floorconfig(char *);
-void delete_floor(void);
-void create_floor(void);
-void rename_floor(void);
-void do_listsub(void);
-void toggle_self_service(void);
-void summary(void);
-void summary_inner_div(void);
-ssize_t write(int fd, const void *buf, size_t count);
-void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum);
-void display_calendar(long msgnum);
-void display_task(long msgnum);
-void display_note(long msgnum);
-void updatenote(void);
-void do_calendar_view(void);
-void do_tasks_view(void);
-void free_calendar_buffer(void);
-void calendar_summary_view(void);
-int load_msg_ptrs(char *servcmd, int with_headers);
-void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen);
-int CtdlDecodeBase64(char *dest, const char *source, size_t length);
-void free_attachments(struct wcsession *sess);
-void free_march_list(struct wcsession *wcf);
-void set_room_policy(void);
-void display_inetconf(void);
-void save_inetconf(void);
-void generate_uuid(char *);
-void CtdlMakeTempFileName(char *, int);
-void display_preferences(void);
-void set_preferences(void);
-void recp_autocomplete(char *);
-void begin_ajax_response(void);
-void end_ajax_response(void);
-void initialize_viewdefs(void);
-void initialize_axdefs(void);
-void list_all_rooms_by_floor(char *viewpref);
-
-#ifdef WEBCIT_WITH_CALENDAR_SERVICE
-void display_edit_task(void);
-void save_task(void);
-void display_edit_event(void);
-void save_event(void);
-void display_icaltimetype_as_webform(struct icaltimetype *, char *);
-void icaltime_from_webform(struct icaltimetype *result, char *prefix);
-void icaltime_from_webform_dateonly(struct icaltimetype *result, char *prefix);
-void display_edit_individual_event(icalcomponent *supplied_vtodo, long msgnum);
-void save_individual_event(icalcomponent *supplied_vtodo, long msgnum);
-void respond_to_request(void);
-void handle_rsvp(void);
-void ical_dezonify(icalcomponent *cal);
-void partstat_as_string(char *buf, icalproperty *attendee);
-icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp);
-void check_attendee_availability(icalcomponent *supplied_vevent);
-void do_freebusy(char *req);
-#endif
-
-#ifdef ENABLE_NLS
-void initialize_locales(void);
-#endif
-
-extern char *months[];
-extern char *days[];
-void read_server_binary(char *buffer, size_t total_len);
-char *read_server_text(void);
-int goto_config_room(void);
-long locate_user_vcard(char *username, long usernum);
-void sleeeeeeeeeep(int);
-void http_transmit_thing(char *thing, size_t length, char *content_type,
-                        int is_static);
-void unescape_input(char *buf);
-void do_iconbar(void);
-void do_iconbar_roomlist(void);
-void do_selected_iconbar(void);
-void display_customize_iconbar(void);
-void commit_iconbar(void);
-int CtdlDecodeQuotedPrintable(char *decoded, char *encoded, int sourcelen);
-void spawn_another_worker_thread(void);
-void display_rss(char *roomname, char *request_method);
-void set_floordiv_expanded(char *which_floordiv);
-void offer_languages(void);
-void set_selected_language(char *);
-void go_selected_language(void);
-void stop_selected_language(void);
-void httplang_to_locale(char *LocaleString);
-void tabbed_dialog(int num_tabs, char *tabnames[]);
-void begin_tab(int tabnum, int num_tabs);
-void end_tab(int tabnum, int num_tabs);
-void str_wiki_index(char *s);
-void display_wiki_page(void);
-char *bmstrcasestr(char *text, char *pattern);
-
-#ifdef HAVE_ICONV
-iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode);
-#endif
-
-void embed_room_banner(char *, int);
-
-/* navbar types that can be passed to embed_room_banner */
-enum {
-       navbar_none,
-       navbar_default
-};
-
-
-#ifdef HAVE_OPENSSL
-void init_ssl(void);
-void endtls(void);
-void ssl_lock(int mode, int n, const char *file, int line);
-int starttls(int sock);
-extern SSL_CTX *ssl_ctx;  
-int client_read_ssl(char *buf, int bytes, int timeout);
-void client_write_ssl(char *buf, int nbytes);
-#endif
-
-#ifdef HAVE_ZLIB
-#include <zlib.h>
-int ZEXPORT compress_gzip(Bytef * dest, uLongf * destLen,
-                          const Bytef * source, uLong sourceLen, int level);
-#endif
-
-#ifdef HAVE_ICONV
-void utf8ify_rfc822_string(char *buf);
-#endif
-
-void begin_burst(void);
-void end_burst(void);
-
-extern char *hourname[];       /**< Names of hours (12am, 1am, etc.) */
-
-void http_datestring(char *buf, size_t n, time_t xtime);
-
-
-/** Views (from citadel.h) */
-#define        VIEW_BBS                0       /**< Traditional Citadel BBS view */
-#define VIEW_MAILBOX           1       /**< Mailbox summary */
-#define VIEW_ADDRESSBOOK       2       /**< Address book view */
-#define VIEW_CALENDAR          3       /**< Calendar view */
-#define VIEW_TASKS             4       /**< Tasks view */
-#define VIEW_NOTES             5       /**<  Notes view */
-#define VIEW_WIKI              6       /**<  Wiki view */
-#define VIEW_CALBRIEF          7       /**< Brief Calendar view */
-
-
-/* These should be empty, but we have them for testing */
-#define DEFAULT_HTTPAUTH_USER  ""
-#define DEFAULT_HTTPAUTH_PASS  ""
-
diff --git a/webcit/webserver.c b/webcit/webserver.c
deleted file mode 100644 (file)
index 985add6..0000000
+++ /dev/null
@@ -1,764 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup Webserver This contains a simple multithreaded TCP server manager.  It sits around
- * waiting on the specified port for incoming HTTP connections.  When a
- * connection is established, it calls context_loop() from context_loop.c.
- * \ingroup WebcitHttpServer
- */
-
-/*@{*/
-#include "webcit.h"
-#include "webserver.h"
-
-#ifndef HAVE_SNPRINTF
-int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp);
-#endif
-
-int verbosity = 9;             /**< Logging level */
-int msock;                         /**< master listening socket */
-int is_https = 0;              /**< Nonzero if I am an HTTPS service */
-int follow_xff = 0;            /**< Follow X-Forwarded-For: header */
-extern void *context_loop(int);
-extern void *housekeeping_loop(void);
-extern pthread_mutex_t SessionListMutex;
-extern pthread_key_t MyConKey;
-
-
-char *server_cookie = NULL; /**< our Cookie connection to the client */
-
-int http_port = PORT_NUM;      /**< Port to listen on */
-
-char *ctdlhost = DEFAULT_HOST; /**< our name */
-char *ctdlport = DEFAULT_PORT; /**< our Port */
-int setup_wizard = 0;          /**< should we run the setup wizard? \todo */
-char wizard_filename[PATH_MAX];/**< where's the setup wizard? */
-
-/** 
- * \brief This is a generic function to set up a master socket for listening on
- * a TCP port.  The server shuts down if the bind fails.
- * \param ip_addr ip to bind to
- * \param port_number the port to bind to 
- * \param queue_len the size of the input queue ????
- */
-int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
-{
-       struct sockaddr_in sin;
-       int s, i;
-
-       memset(&sin, 0, sizeof(sin));
-       sin.sin_family = AF_INET;
-       if (ip_addr == NULL) {
-               sin.sin_addr.s_addr = INADDR_ANY;
-       } else {
-               sin.sin_addr.s_addr = inet_addr(ip_addr);
-       }
-
-       if (sin.sin_addr.s_addr == INADDR_NONE) {
-               sin.sin_addr.s_addr = INADDR_ANY;
-       }
-
-       if (port_number == 0) {
-               lprintf(1, "Cannot start: no port number specified.\n");
-               exit(1);
-       }
-       sin.sin_port = htons((u_short) port_number);
-
-       s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto));
-       if (s < 0) {
-               lprintf(1, "Can't create a socket: %s\n", strerror(errno));
-               exit(errno);
-       }
-       /** Set some socket options that make sense. */
-       i = 1;
-       setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
-
-       if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
-               lprintf(1, "Can't bind: %s\n", strerror(errno));
-               exit(errno);
-       }
-       if (listen(s, queue_len) < 0) {
-               lprintf(1, "Can't listen: %s\n", strerror(errno));
-               exit(errno);
-       }
-       return (s);
-}
-
-
-
-/**
- * \brief Create a Unix domain socket and listen on it
- * \param sockpath file name of the unix domain socket
- * \param queue_len queue size of the kernel fifo????
- */
-int ig_uds_server(char *sockpath, int queue_len)
-{
-       struct sockaddr_un addr;
-       int s;
-       int i;
-       int actual_queue_len;
-
-       actual_queue_len = queue_len;
-       if (actual_queue_len < 5) actual_queue_len = 5;
-
-       i = unlink(sockpath);
-       if (i != 0) if (errno != ENOENT) {
-               lprintf(1, "citserver: can't unlink %s: %s\n",
-                       sockpath, strerror(errno));
-               exit(errno);
-       }
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
-
-       s = socket(AF_UNIX, SOCK_STREAM, 0);
-       if (s < 0) {
-               lprintf(1, "citserver: Can't create a socket: %s\n",
-                       strerror(errno));
-               exit(errno);
-       }
-
-       if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-               lprintf(1, "citserver: Can't bind: %s\n",
-                       strerror(errno));
-               exit(errno);
-       }
-
-       if (listen(s, actual_queue_len) < 0) {
-               lprintf(1, "citserver: Can't listen: %s\n",
-                       strerror(errno));
-               exit(errno);
-       }
-
-       chmod(sockpath, 0777);
-       return(s);
-}
-
-
-
-
-/**
- * \brief Read data from the client socket.
- * \param sock socket fd to read from ???
- * \param buf buffer to read into 
- * \param bytes how large is the read buffer?
- * \param timeout how long should we wait for input?
- * \return values are\
- *      1       Requested number of bytes has been read.\
- *      0       Request timed out.\
- *        -1           Connection is broken, or other error.
- */
-int client_read_to(int sock, char *buf, int bytes, int timeout)
-{
-       int len, rlen;
-       fd_set rfds;
-       struct timeval tv;
-       int retval;
-
-
-#ifdef HAVE_OPENSSL
-       if (is_https) {
-               return (client_read_ssl(buf, bytes, timeout));
-       }
-#endif
-
-       len = 0;
-       while (len < bytes) {
-               FD_ZERO(&rfds);
-               FD_SET(sock, &rfds);
-               tv.tv_sec = timeout;
-               tv.tv_usec = 0;
-
-               retval = select((sock) + 1, &rfds, NULL, NULL, &tv);
-               if (FD_ISSET(sock, &rfds) == 0) {
-                       return (0);
-               }
-
-               rlen = read(sock, &buf[len], bytes - len);
-
-               if (rlen < 1) {
-                       lprintf(2, "client_read() failed: %s\n",
-                               strerror(errno));
-                       return (-1);
-               }
-               len = len + rlen;
-       }
-
-#ifdef HTTP_TRACING
-       write(2, "\033[32m", 5);
-       write(2, buf, bytes);
-       write(2, "\033[30m", 5);
-#endif
-       return (1);
-}
-
-/**
- * \brief write data to the client
- * \param buf data to write to the client
- * \param count size of buffer
- */
-ssize_t client_write(const void *buf, size_t count)
-{
-       char *newptr;
-       size_t newalloc;
-
-       if (WC->burst != NULL) {
-               if ((WC->burst_len + count) >= WC->burst_alloc) {
-                       newalloc = (WC->burst_alloc * 2);
-                       if ((WC->burst_len + count) >= newalloc) {
-                               newalloc += count;
-                       }
-                       newptr = realloc(WC->burst, newalloc);
-                       if (newptr != NULL) {
-                               WC->burst = newptr;
-                               WC->burst_alloc = newalloc;
-                       }
-               }
-               if ((WC->burst_len + count) < WC->burst_alloc) {
-                       memcpy(&WC->burst[WC->burst_len], buf, count);
-                       WC->burst_len += count;
-                       return (count);
-               }
-               else {
-                       return(-1);
-               }
-       }
-#ifdef HAVE_OPENSSL
-       if (is_https) {
-               client_write_ssl((char *) buf, count);
-               return (count);
-       }
-#endif
-#ifdef HTTP_TRACING
-       write(2, "\033[34m", 5);
-       write(2, buf, count);
-       write(2, "\033[30m", 5);
-#endif
-       return (write(WC->http_sock, buf, count));
-}
-
-/**
- * \brief what burst???
- */
-void begin_burst(void)
-{
-       if (WC->burst != NULL) {
-               free(WC->burst);
-               WC->burst = NULL;
-       }
-       WC->burst_len = 0;
-       WC->burst_alloc = 32768;
-       WC->burst = malloc(WC->burst_alloc);
-}
-
-
-/**
- * \brief uses the same calling syntax as compress2(), but it
- * creates a stream compatible with HTTP "Content-encoding: gzip"
- */
-#ifdef HAVE_ZLIB
-#define DEF_MEM_LEVEL 8 /**< memlevel??? */
-#define OS_CODE 0x03   /**< unix */
-int ZEXPORT compress_gzip(Bytef * dest,         /**< compressed buffer*/
-                                                 uLongf * destLen,     /**< length of the compresed data */
-                                                 const Bytef * source, /**< source to encode */
-                                                 uLong sourceLen,      /**< length of the source to encode */
-                                                 int level)            /**< what level??? */
-{
-       const int gz_magic[2] = { 0x1f, 0x8b }; /** gzip magic header */
-
-       /** write gzip header */
-       sprintf((char *) dest, "%c%c%c%c%c%c%c%c%c%c",
-               gz_magic[0], gz_magic[1], Z_DEFLATED,
-               0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /** xflags */ ,
-               OS_CODE);
-
-       /* normal deflate */
-       z_stream stream;
-       int err;
-       stream.next_in = (Bytef *) source;
-       stream.avail_in = (uInt) sourceLen;
-       stream.next_out = dest + 10L;   // after header
-       stream.avail_out = (uInt) * destLen;
-       if ((uLong) stream.avail_out != *destLen)
-               return Z_BUF_ERROR;
-
-       stream.zalloc = (alloc_func) 0;
-       stream.zfree = (free_func) 0;
-       stream.opaque = (voidpf) 0;
-
-       err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
-                          DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
-       if (err != Z_OK)
-               return err;
-
-       err = deflate(&stream, Z_FINISH);
-       if (err != Z_STREAM_END) {
-               deflateEnd(&stream);
-               return err == Z_OK ? Z_BUF_ERROR : err;
-       }
-       *destLen = stream.total_out + 10L;
-
-       /* write CRC and Length */
-       uLong crc = crc32(0L, source, sourceLen);
-       int n;
-       for (n = 0; n < 4; ++n, ++*destLen) {
-               dest[*destLen] = (int) (crc & 0xff);
-               crc >>= 8;
-       }
-       uLong len = stream.total_in;
-       for (n = 0; n < 4; ++n, ++*destLen) {
-               dest[*destLen] = (int) (len & 0xff);
-               len >>= 8;
-       }
-       err = deflateEnd(&stream);
-       return err;
-}
-#endif
-
-/**
- * \brief what burst???
- */
-void end_burst(void)
-{
-       size_t the_len;
-       char *the_data;
-
-       if (WC->burst == NULL)
-               return;
-
-       the_len = WC->burst_len;
-       the_data = WC->burst;
-
-       WC->burst_len = 0;
-       WC->burst_alloc = 0;
-       WC->burst = NULL;
-
-#ifdef HAVE_ZLIB
-       /* Handle gzip compression */
-       if (WC->gzip_ok) {
-               char *compressed_data = NULL;
-               uLongf compressed_len;
-
-               compressed_len = (uLongf) ((the_len * 101) / 100) + 100;
-               compressed_data = malloc(compressed_len);
-
-               if (compress_gzip((Bytef *) compressed_data,
-                                 &compressed_len,
-                                 (Bytef *) the_data,
-                                 (uLongf) the_len, Z_BEST_SPEED) == Z_OK) {
-                       wprintf("Content-encoding: gzip\r\n");
-                       free(the_data);
-                       the_data = compressed_data;
-                       the_len = compressed_len;
-               } else {
-                       free(compressed_data);
-               }
-       }
-#endif                         /* HAVE_ZLIB */
-
-       wprintf("Content-length: %d\r\n\r\n", the_len);
-       client_write(the_data, the_len);
-       free(the_data);
-       return;
-}
-
-
-
-/**
- * \brief Read data from the client socket with default timeout.
- * (This is implemented in terms of client_read_to() and could be
- * justifiably moved out of sysdep.c)
- * \param sock the socket fd to read from???
- * \param buf the buffer to write to
- * \param bytes how large is the buffer
- */
-int client_read(int sock, char *buf, int bytes)
-{
-       return (client_read_to(sock, buf, bytes, SLEEPING));
-}
-
-
-/**
- * \brief Get a LF-terminated line of text from the client.
- * (This is implemented in terms of client_read() and could be
- * justifiably moved out of sysdep.c)
- * \param sock socket fd to get client line from???
- * \param buf buffer to write read data to
- * \param bufsiz how many bytes to read
- * \return  numer of bytes read???
- */
-int client_getln(int sock, char *buf, int bufsiz)
-{
-       int i, retval;
-
-       /** Read one character at a time.*/
-       for (i = 0;; i++) {
-               retval = client_read(sock, &buf[i], 1);
-               if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1))
-                       break;
-               if ( (!isspace(buf[i])) && (!isprint(buf[i])) ) {
-                       /** Non printable character recieved from client */
-                       return(-1);
-               }
-       }
-
-       /** If we got a long line, discard characters until the newline. */
-       if (i == (bufsiz-1))
-               while (buf[i] != '\n' && retval == 1)
-                       retval = client_read(sock, &buf[i], 1);
-
-       /**
-        * Strip any trailing non-printable characters.
-        */
-       buf[i] = 0;
-       while ((strlen(buf) > 0) && (!isprint(buf[strlen(buf) - 1]))) {
-               buf[strlen(buf) - 1] = 0;
-       }
-       return (retval);
-}
-
-
-/**
- * \brief      Start running as a daemon.  
- *
- * param       do_close_stdio          Only close stdio if set.
- */
-void start_daemon(int do_close_stdio)
-{
-       if (do_close_stdio) {
-               /* close(0); */
-               close(1);
-               close(2);
-       }
-       signal(SIGHUP, SIG_IGN);
-       signal(SIGINT, SIG_IGN);
-       signal(SIGQUIT, SIG_IGN);
-       if (fork() != 0) {
-               exit(0);
-       }
-}
-
-/**
- * \brief      Spawn an additional worker thread into the pool.
- */
-void spawn_another_worker_thread()
-{
-       pthread_t SessThread;   /**< Thread descriptor */
-       pthread_attr_t attr;    /**< Thread attributes */
-       int ret;
-
-       lprintf(3, "Creating a new thread\n");
-
-       /** set attributes for the new thread */
-       pthread_attr_init(&attr);
-       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
-       /**
-        * Our per-thread stacks need to be bigger than the default size, otherwise
-        * the MIME parser crashes on FreeBSD, and the IMAP service crashes on
-        * 64-bit Linux.
-        */
-       if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) {
-               lprintf(1, "pthread_attr_setstacksize: %s\n",
-                       strerror(ret));
-               pthread_attr_destroy(&attr);
-       }
-
-       /** now create the thread */
-       if (pthread_create(&SessThread, &attr,
-                          (void *(*)(void *)) worker_entry, NULL)
-           != 0) {
-               lprintf(1, "Can't create thread: %s\n", strerror(errno));
-       }
-
-       /** free up the attributes */
-       pthread_attr_destroy(&attr);
-}
-
-/**
- * \brief Here's where it all begins.
- * \param argc number of commandline args
- * \param argv the commandline arguments
- */
-int main(int argc, char **argv)
-{
-       pthread_t SessThread;   /**< Thread descriptor */
-       pthread_attr_t attr;    /**< Thread attributes */
-       int a, i;                       /**< General-purpose variables */
-       char tracefile[PATH_MAX];
-       char ip_addr[256];
-       char *webcitdir = WEBCITDIR;
-#ifdef ENABLE_NLS
-       char *locale = NULL;
-       char *mo = NULL;
-#endif /* ENABLE_NLS */
-       char uds_listen_path[PATH_MAX]; /**< listen on a unix domain socket? */
-
-       strcpy(uds_listen_path, "");
-
-       /** Parse command line */
-#ifdef HAVE_OPENSSL
-       while ((a = getopt(argc, argv, "h:i:p:t:x:cfs")) != EOF)
-#else
-       while ((a = getopt(argc, argv, "h:i:p:t:x:cf")) != EOF)
-#endif
-               switch (a) {
-               case 'h':
-                       webcitdir = strdup(optarg);
-                       break;
-               case 'i':
-                       safestrncpy(ip_addr, optarg, sizeof ip_addr);
-                       break;
-               case 'p':
-                       http_port = atoi(optarg);
-                       if (http_port == 0) {
-                               safestrncpy(uds_listen_path, optarg, sizeof uds_listen_path);
-                       }
-                       break;
-               case 't':
-                       safestrncpy(tracefile, optarg, sizeof tracefile);
-                       freopen(tracefile, "w", stdout);
-                       freopen(tracefile, "w", stderr);
-                       freopen(tracefile, "r", stdin);
-                       break;
-               case 'x':
-                       verbosity = atoi(optarg);
-                       break;
-               case 'f':
-                       follow_xff = 1;
-                       break;
-               case 'c':
-                       server_cookie = malloc(256);
-                       if (server_cookie != NULL) {
-                               safestrncpy(server_cookie,
-                                      "Set-cookie: wcserver=",
-                                       256);
-                               if (gethostname
-                                   (&server_cookie[strlen(server_cookie)],
-                                    200) != 0) {
-                                       lprintf(2, "gethostname: %s\n",
-                                               strerror(errno));
-                                       free(server_cookie);
-                               }
-                       }
-                       break;
-               case 's':
-                       is_https = 1;
-                       break;
-               default:
-                       fprintf(stderr, "usage: webserver "
-                               "[-i ip_addr] [-p http_port] "
-                               "[-t tracefile] [-c] [-f] "
-#ifdef HAVE_OPENSSL
-                               "[-s] "
-#endif
-                               "[remotehost [remoteport]]\n");
-                       return 1;
-               }
-
-       if (optind < argc) {
-               ctdlhost = argv[optind];
-               if (++optind < argc)
-                       ctdlport = argv[optind];
-       }
-       /** Tell 'em who's in da house */
-       lprintf(1, SERVER "\n");
-       lprintf(1, "Copyright (C) 1996-2006 by the Citadel development team.\n"
-               "This software is distributed under the terms of the "
-               "GNU General Public License.\n\n"
-       );
-
-       lprintf(9, "Changing directory to %s\n", webcitdir);
-       if (chdir(webcitdir) != 0) {
-               perror("chdir");
-       }
-
-       /** initialize the International Bright Young Thing */
-#ifdef ENABLE_NLS
-
-       initialize_locales();
-
-       locale = setlocale(LC_ALL, "");
-
-       mo = malloc(strlen(webcitdir) + 20);
-       sprintf(mo, "%s/locale", webcitdir);
-       lprintf(9, "Message catalog directory: %s\n",
-               bindtextdomain("webcit", mo)
-       );
-       free(mo);
-       lprintf(9, "Text domain: %s\n",
-               textdomain("webcit")
-       );
-       lprintf(9, "Text domain Charset: %s\n",
-                       bind_textdomain_codeset("webcit","UTF8")
-       );
-#endif
-
-       initialize_viewdefs();
-       initialize_axdefs();
-
-       /**
-        * Set up a place to put thread-specific data.
-        * We only need a single pointer per thread - it points to the
-        * wcsession struct to which the thread is currently bound.
-        */
-       if (pthread_key_create(&MyConKey, NULL) != 0) {
-               lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
-       }
-
-       /**
-        * Set up a place to put thread-specific SSL data.
-        * We don't stick this in the wcsession struct because SSL starts
-        * up before the session is bound, and it gets torn down between
-        * transactions.
-        */
-#ifdef HAVE_OPENSSL
-       if (pthread_key_create(&ThreadSSL, NULL) != 0) {
-               lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
-       }
-#endif
-
-       /**
-        * Bind the server to our favorite port.
-        * There is no need to check for errors, because ig_tcp_server()
-        * exits if it doesn't succeed.
-        */
-
-       if (strlen(uds_listen_path) > 0) {
-               lprintf(2, "Attempting to create listener socket at %s...\n", uds_listen_path);
-               msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH);
-       }
-       else {
-               lprintf(2, "Attempting to bind to port %d...\n", http_port);
-               msock = ig_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH);
-       }
-
-       lprintf(2, "Listening on socket %d\n", msock);
-       signal(SIGPIPE, SIG_IGN);
-
-       pthread_mutex_init(&SessionListMutex, NULL);
-
-       /**
-        * Start up the housekeeping thread
-        */
-       pthread_attr_init(&attr);
-       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-       pthread_create(&SessThread, &attr,
-                      (void *(*)(void *)) housekeeping_loop, NULL);
-
-
-       /**
-        * If this is an HTTPS server, fire up SSL
-        */
-#ifdef HAVE_OPENSSL
-       if (is_https) {
-               init_ssl();
-       }
-#endif
-
-       /** Start a few initial worker threads */
-       for (i = 0; i < (MIN_WORKER_THREADS); ++i) {
-               spawn_another_worker_thread();
-       }
-
-       /* now the original thread becomes another worker */
-       worker_entry();
-       return 0;
-}
-
-
-/**
- * Entry point for worker threads
- */
-void worker_entry(void)
-{
-       int ssock;
-       int i = 0;
-       int time_to_die = 0;
-       int fail_this_transaction = 0;
-
-       do {
-               /** Only one thread can accept at a time */
-               fail_this_transaction = 0;
-               ssock = accept(msock, NULL, 0);
-               if (ssock < 0) {
-                       lprintf(2, "accept() failed: %s\n",
-                               strerror(errno));
-               } else {
-                       /** Set the SO_REUSEADDR socket option */
-                       i = 1;
-                       setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR,
-                                  &i, sizeof(i));
-
-                       /** If we are an HTTPS server, go crypto now. */
-#ifdef HAVE_OPENSSL
-                       if (is_https) {
-                               if (starttls(ssock) != 0) {
-                                       fail_this_transaction = 1;
-                                       close(ssock);
-                               }
-                       }
-#endif
-
-                       if (fail_this_transaction == 0) {
-                               /** Perform an HTTP transaction... */
-                               context_loop(ssock);
-                               /** ...and close the socket. */
-                               lingering_close(ssock);
-                       }
-
-               }
-
-       } while (!time_to_die);
-
-       pthread_exit(NULL);
-}
-
-/**
- * \brief logprintf. log messages 
- * logs to stderr if loglevel is lower than the verbosity set at startup
- * \param loglevel level of the message
- * \param format the printf like format string
- * \param ... the strings to put into format
- */
-int lprintf(int loglevel, const char *format, ...)
-{
-       va_list ap;
-
-       if (loglevel <= verbosity) {
-               va_start(ap, format);
-               vfprintf(stderr, format, ap);
-               va_end(ap);
-               fflush(stderr);
-       }
-       return 1;
-}
-
-
-/**
- * \brief print the actual stack frame.
- */
-void wc_backtrace(void)
-{
-#ifdef HAVE_BACKTRACE
-       void *stack_frames[50];
-       size_t size, i;
-       char **strings;
-
-
-       size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
-       strings = backtrace_symbols(stack_frames, size);
-       for (i = 0; i < size; i++) {
-               if (strings != NULL)
-                       lprintf(1, "%s\n", strings[i]);
-               else
-                       lprintf(1, "%p\n", stack_frames[i]);
-       }
-       free(strings);
-#endif
-}
-
-/*@}*/
diff --git a/webcit/webserver.h b/webcit/webserver.h
deleted file mode 100644 (file)
index 5809b1a..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-/* $Id$ */
-int client_getln(int sock, char *buf, int bufsiz);
-int client_read(int sock, char *buf, int bytes);
-int client_read_to(int sock, char *buf, int bytes, int timeout);
-ssize_t client_write(const void *buf, size_t count);
-int lprintf(int loglevel, const char *format, ...);
diff --git a/webcit/who.c b/webcit/who.c
deleted file mode 100644 (file)
index e81b911..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * $Id$
- */
-/**
- * \defgroup DislpayWho Display a list of all users currently logged on to the Citadel server.
- * \ingroup WebcitDisplayItems
- */
-/*@{*/
-#include "webcit.h"
-
-
-
-/**
- * \brief Display inner div of Wholist
- */
-void who_inner_div(void) {
-       char buf[SIZ], user[SIZ], room[SIZ], host[SIZ],
-               realroom[SIZ], realhost[SIZ];
-       int sess;
-       time_t last_activity;
-       time_t now;
-       int bg = 0;
-
-       wprintf("<table border=\"0\" cellspacing=\"0\" width=\"100%%\" bgcolor=\"#FFFFFF\">"
-               "<tr>\n");
-       wprintf("<th colspan=\"3\"> </th>\n");
-       wprintf("<th>%s</th>\n", _("User name"));
-       wprintf("<th>%s</th>", _("Room"));
-       wprintf("<th>%s</th>\n</tr>\n", _("From host"));
-
-       serv_puts("TIME");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '2') {
-               now = extract_long(&buf[4], 0);
-       }
-       else {
-               now = time(NULL);
-       }
-
-       serv_puts("RWHO");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       sess = extract_int(buf, 0);
-                       extract_token(user, buf, 1, '|', sizeof user);
-                       extract_token(room, buf, 2, '|', sizeof room);
-                       extract_token(host, buf, 3, '|', sizeof host);
-                       extract_token(realroom, buf, 9, '|', sizeof realroom);
-                       extract_token(realhost, buf, 10, '|', sizeof realhost);
-                       last_activity = extract_long(buf, 5);
-
-                       bg = 1 - bg;
-                       wprintf("<tr bgcolor=\"#%s\">",
-                               (bg ? "DDDDDD" : "FFFFFF")
-                       );
-
-
-                       wprintf("<td>");
-                       if ((WC->is_aide) &&
-                           (sess != WC->ctdl_pid)) {
-                               wprintf(" <a href=\"terminate_session?which_session=%d", sess);
-                               wprintf("\" onClick=\"return ConfirmKill();\">%s</a>", _("(kill)"));
-                       }
-                       if (sess == WC->ctdl_pid) {
-                               wprintf(" <a href=\"edit_me\">%s</a>", _("(edit)"));
-                       }
-                       wprintf("</td>");
-
-                       /** (link to page this user) */
-                       wprintf("<td><a href=\"display_page?recp=");
-                       urlescputs(user);
-                       wprintf("\">"
-                               "<img align=\"middle\" "
-                               "src=\"static/citadelchat_24x.gif\" "
-                               "alt=\"(p)\""
-                               " border=\"0\" /></a> ");
-                       wprintf("</td>");
-
-                       /** (idle flag) */
-                       wprintf("<td>");
-                       if ((now - last_activity) > 900L) {
-                               wprintf(" "
-                                       "<img align=\"middle\" "
-                                       "src=\"static/inactiveuser_24x.gif\" "
-                                       "alt=\"(idle)\" border=\"0\" />");
-                       }
-                       else {
-                               wprintf(" "
-                                       "<img align=\"middle\" "
-                                       "src=\"static/activeuser_24x.gif\" "
-                                       "alt=\"(active)\" border=\"0\" />");
-                       }
-                       wprintf("</td>\n<td>");
-
-
-
-                       /** username (link to user bio/photo page) */
-                       wprintf("<a href=\"showuser?who=");
-                       urlescputs(user);
-                       wprintf("\">");
-                       escputs(user);
-                       wprintf("</a>");
-
-                       /** room */
-                       wprintf("</td>\n\t<td>");
-                       escputs(room);
-                       if (strlen(realroom) > 0) {
-                               wprintf("<br /><i>");
-                               escputs(realroom);
-                               wprintf("</i>");
-                       }
-                       wprintf("</td>\n\t<td>");
-
-                       /** hostname */
-                       escputs(host);
-                       if (strlen(realhost) > 0) {
-                               wprintf("<br /><i>");
-                               escputs(realhost);
-                               wprintf("</i>");
-                       }
-                       wprintf("</td>\n</tr>");
-               }
-       }
-       wprintf("</table>");
-}
-
-
-/**
- * \brief who is on?
- */
-void who(void)
-{
-       char title[256];
-
-       output_headers(1, 1, 2, 0, 0, 0);
-
-       wprintf("<script type=\"text/javascript\">\n"
-               "function ConfirmKill() { \n"
-               "return confirm('%s');\n"
-               "}\n"
-               "</script>\n", _("Do you really want to kill this session?")
-       );
-
-       wprintf("<div id=\"banner\">\n");
-       wprintf("<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>");
-       wprintf("<img src=\"static/usermanag_48x.gif\" alt=\" \" "
-               "align=middle "
-               ">");
-       wprintf("<span class=\"titlebar\"> ");
-
-       snprintf(title, sizeof title, _("Users currently on %s"), serv_info.serv_humannode);
-       escputs(title);
-
-       wprintf("</span></td><td align=right>");
-       offer_start_page();
-       wprintf("</td></tr></table>\n");
-       wprintf("</div>\n");
-
-       wprintf("<div id=\"content\">\n");
-
-       wprintf("<div style=\"display:inline\" id=\"fix_scrollbar_bug\">");
-       who_inner_div();
-       wprintf("</div>\n");
-
-       wprintf("<div id=\"instructions\" align=center>");
-       wprintf(_("Click on a name to read user info.  Click on %s "
-               "to send an instant message to that user."),
-               "<img align=\"middle\" src=\"static/citadelchat_16x.gif\" alt=\"(p)\" border=\"0\">"
-       );
-       wprintf("</div>\n");
-
-       /**
-        * JavaScript to make the ajax refresh happen:
-        * See http://www.sergiopereira.com/articles/prototype.js.html for info on Ajax.PeriodicalUpdater
-        * It wants: 1. The div being updated
-        *           2. The URL of the update source
-        *           3. Other flags (such as the HTTP method and the refresh frequency)
-        */
-       wprintf(
-               "<script type=\"text/javascript\">                                      "
-               " new Ajax.PeriodicalUpdater('fix_scrollbar_bug', 'who_inner_html',     "
-               "                            { method: 'get', frequency: 30 }  );       "
-               "</script>                                                              \n"
-       );
-       wDumpContent(1);
-}
-
-/**
- * \brief end session \todo what??? does this belong here? 
- */
-void terminate_session(void)
-{
-       char buf[SIZ];
-
-       serv_printf("TERM %s", bstr("which_session"));
-       serv_getln(buf, sizeof buf);
-       who();
-}
-
-
-/**
- * \brief Change your session info (fake roomname and hostname)
- */
-void edit_me(void)
-{
-       char buf[SIZ];
-
-       if (strlen(bstr("change_room_name_button")) > 0) {
-               serv_printf("RCHG %s", bstr("fake_roomname"));
-               serv_getln(buf, sizeof buf);
-               http_redirect("who");
-       } else if (strlen(bstr("change_host_name_button")) > 0) {
-               serv_printf("HCHG %s", bstr("fake_hostname"));
-               serv_getln(buf, sizeof buf);
-               http_redirect("who");
-       } else if (strlen(bstr("change_user_name_button")) > 0) {
-               serv_printf("UCHG %s", bstr("fake_username"));
-               serv_getln(buf, sizeof buf);
-               http_redirect("who");
-       } else if (strlen(bstr("cancel_button")) > 0) {
-               http_redirect("who");
-       } else {
-               output_headers(1, 1, 0, 0, 0, 0);
-
-               wprintf("<div id=\"banner\">\n");
-               wprintf("<table width=100%% border=0 bgcolor=\"#444455\"><tr><td>");
-               wprintf("<span class=\"titlebar\">");
-               wprintf(_("Edit your session display"));
-               wprintf("</span></td></tr></table>\n");
-               wprintf("</div>\n<div id=\"content\">\n");
-
-               wprintf(_("This screen allows you to change the way your "
-                       "session appears in the 'Who is online' listing. "
-                       "To turn off any 'fake' name you've previously "
-                       "set, simply click the appropriate 'change' button "
-                       "without typing anything in the corresponding box. "));
-               wprintf("<br />\n");
-
-               wprintf("<form method=\"POST\" action=\"edit_me\">\n");
-
-               wprintf("<table border=0 width=100%%>\n");
-
-               wprintf("<tr><td><b>");
-               wprintf(_("Room name:"));
-               wprintf("</b></td>\n<td>");
-               wprintf("<input type=\"text\" name=\"fake_roomname\" maxlength=\"64\">\n");
-               wprintf("</td>\n<td align=center>");
-               wprintf("<input type=\"submit\" name=\"change_room_name_button\" value=\"%s\">",
-                       _("Change room name"));
-               wprintf("</td>\n</tr>\n");
-
-               wprintf("<tr><td><b>");
-               wprintf(_("Host name:"));
-               wprintf("</b></td><td>");
-               wprintf("<input type=\"text\" name=\"fake_hostname\" maxlength=\"64\">\n");
-               wprintf("</td>\n<td align=center>");
-               wprintf("<input type=\"submit\" name=\"change_host_name_button\" value=\"%s\">",
-                       _("Change host name"));
-               wprintf("</td>\n</tr>\n");
-
-               if (WC->is_aide) {
-                       wprintf("<tr><td><b>");
-                       wprintf(_("User name:"));
-                       wprintf("</b></td><td>");
-                       wprintf("<input type=\"text\" name=\"fake_username\" maxlength=\"64\">\n");
-                       wprintf("</td>\n<td align=center>");
-                       wprintf("<input type=\"submit\" name \"change_user_name_button\" value=\"%s\">",
-                               _("Change user name"));
-                       wprintf("</td>\n</tr>\n");
-               }
-               wprintf("<tr><td> </td><td> </td><td align=center>");
-               wprintf("<input type=\"submit\" name=\"cancel_button\" value=\"%s\">",
-                       _("Cancel"));
-               wprintf("</td></tr></table>\n");
-               wprintf("</form></center>\n");
-               wDumpContent(1);
-       }
-}
-
-
-/*@}*/
diff --git a/webcit/wiki.c b/webcit/wiki.c
deleted file mode 100644 (file)
index 0f35b61..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * $Id:  $
- */
-/**
- *
- * \defgroup Wiki Wiki; Functions pertaining to rooms with a wiki view
- * \ingroup WebcitDisplayItems
- */
-
-/*@{*/
-#include "webcit.h"
-#include "groupdav.h"
-
-
-
-/** 
- * \brief Convert a string to something suitable as a wiki index
- *
- * \param s The string to be converted.
- */
-void str_wiki_index(char *s)
-{
-       int i;
-
-       if (s == NULL) return;
-
-       /* First remove all non-alphanumeric characters */
-       for (i=0; i<strlen(s); ++i) {
-               if (!isalnum(s[i])) {
-                       strcpy(&s[i], &s[i+1]);
-               }
-       }
-
-       /* Then make everything lower case */
-       for (i=0; i<strlen(s); ++i) {
-               s[i] = tolower(s[i]);
-       }
-}
-
-/**
- * \brief Display a specific page from a wiki room
- */
-void display_wiki_page(void)
-{
-       char roomname[128];
-       char pagename[128];
-       char errmsg[256];
-       long msgnum = (-1L);
-
-       safestrncpy(roomname, bstr("room"), sizeof roomname);
-       safestrncpy(pagename, bstr("page"), sizeof pagename);
-       str_wiki_index(pagename);
-
-       if (strlen(roomname) > 0) {
-
-               /* If we're not in the correct room, try going there. */
-               if (strcasecmp(roomname, WC->wc_roomname)) {
-                       gotoroom(roomname);
-               }
-       
-               /* If we're still not in the correct room, it doesn't exist. */
-               if (strcasecmp(roomname, WC->wc_roomname)) {
-                       snprintf(errmsg, sizeof errmsg,
-                               _("There is no room called '%s'."),
-                               roomname);
-                       convenience_page("FF0000", _("Error"), errmsg);
-                       return;
-               }
-
-       }
-
-       if (WC->wc_view != VIEW_WIKI) {
-               snprintf(errmsg, sizeof errmsg,
-                       _("'%s' is not a Wiki room."),
-                       roomname);
-               convenience_page("FF0000", _("Error"), errmsg);
-               return;
-       }
-
-       if (strlen(pagename) == 0) {
-               strcpy(pagename, "home");
-       }
-
-       /* Found it!  Now read it. */
-       msgnum = locate_message_by_uid(pagename);
-       if (msgnum >= 0L) {
-               output_headers(1, 1, 1, 0, 0, 0);
-               read_message(msgnum, 0, "");
-               wDumpContent(1);
-               return;
-       }
-
-       output_headers(1, 1, 1, 0, 0, 0);
-       wprintf("<br /><br />"
-               "<div align=\"center\">"
-               "<table border=\"0\" bgcolor=\"#ffffff\" cellpadding=\"10\">"
-               "<tr><td align=\"center\">"
-       );
-       wprintf("<br><b>");
-       wprintf(_("There is no page called '%s' here."), pagename);
-       wprintf("</b><br><br>");
-       wprintf(_("Select the 'Edit this page' link in the room banner "
-               "if you would like to create this page."));
-       wprintf("<br><br>");
-       wprintf("</td></tr></table></div>\n");
-       wDumpContent(1);
-}
-
-
-/** @} */