+++ /dev/null
-/*
- * $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 "Login." "
- "<li><b>If you are a new user</b>, enter the name and password "
- "you wish to use, "
- "and click "New User." "
- "<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> "
- "<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> \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(" ");
- 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();
- }
-}
-
-
-
-/** @} */
+++ /dev/null
-/*
- * $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);
-}
-
-
-/** @} */
+++ /dev/null
-/*
- * $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 */
-
-/** @} */
+++ /dev/null
-/*
- * $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\">"
- " "
- "<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\">"
- " "
- "<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\">"
- " "
- "<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\">"
- " "
- "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
- " "
- "<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 */
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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
-/*@}*/
+++ /dev/null
-/*
- * $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(" "
- "<font size=+1 color=\"#FFFFFF\">"
- "%s %d"
- "</font>"
- " ", 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(" "
- "<font size=+1 color=\"#FFFFFF\">"
- "%s %d"
- "</font>"
- " ", 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> ");
- 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 */
-
-/** @} */
+++ /dev/null
-/*
- * $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();
-}
-/*@}*/
+++ /dev/null
-/*
- * $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);
-}
-/*@}*/
+++ /dev/null
-/*
- * $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 */
-/*@}*/
+++ /dev/null
-/*
- * 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
- */
-
-/*@{*/
-/*@}*/
-
-
-
+++ /dev/null
-/*
- * $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(" ");
-
- 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\">"
- " "
- "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
- " "
- "<INPUT TYPE=\"submit\" NAME=\"check_button\" "
- "VALUE=\"%s\">\n"
- " "
- "<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 */
-
-/*@}*/
+++ /dev/null
-/*
- * $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> </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> </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);
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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;
-}
-
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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 */
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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(" ");
- wprintf("<INPUT TYPE=\"RESET\" VALUE=\"%s\">\n", _("Reset form"));
- wprintf(" ");
- 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;
- }
-}
+++ /dev/null
-/* $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);
+++ /dev/null
-/*
- * $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;
-}
+++ /dev/null
-/*
- * $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();
-}
+++ /dev/null
-/*
- * $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);
- }
-}
+++ /dev/null
-/*
- * $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");
-}
+++ /dev/null
-/*
- * $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);
- }
-}
+++ /dev/null
-/*
- * $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;
-}
+++ /dev/null
-/*
- * $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);
-}
-
-/*@}*/
+++ /dev/null
-/*
- * $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
- );
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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 */
-/*@}*/
+++ /dev/null
-/*
- * $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=\" \">\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=\" \">\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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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=\" \">"
- "</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\">"
- " "
- "<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\">"
- " ");
- wprintf(_("Your icon bar has been updated. Please select any of its "
- "choices to continue."));
- wprintf("</td></tr></table>\n");
- wDumpContent(2);
-}
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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);
-}
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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 "");
- escputs(room);
- wprintf("" 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 "
- "<INPUT TYPE=\"radio\" NAME=\"subtype\""
- "VALUE=\"digest\" CHECKED>Digest format "
- "<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();
-}
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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);
-}
-
-/*@}*/
+++ /dev/null
-/*
- * $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(" ");
- 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);
- }
-
-
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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(" ");
- }
- 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> ", &buf[5]);
- }
- if (!strncasecmp(buf, "rfca=", 5)) {
- strcpy(rfca, &buf[5]);
- wprintf("<");
- escputs(rfca);
- wprintf("> ");
- }
-
- 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> ", &buf[5]);
- }
- if (!strncasecmp(buf, "rfca=", 5)) {
- strcpy(rfca, &buf[5]);
- wprintf("<");
- msgescputs(rfca);
- wprintf("> ");
- }
-
- 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 - %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"
- " "
- "<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(" <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(" "
- "<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("\"> "
- "<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("<");
- }
- else if (buf[i] == '>') {
- wprintf(">");
- }
- else if (buf[i] == '&') {
- wprintf("&");
- }
- else if (buf[i] == '\"') {
- wprintf(""");
- }
- else if (buf[i] == '\'') {
- wprintf("'");
- }
- 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(" ");
- wprintf(_("Attach file:"));
- wprintf(" <input NAME=\"attachfile\" "
- "SIZE=16 TYPE=\"file\">\n "
- "<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("\"> "
- "<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(" ");
- 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");
-
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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);
-}
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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
- );
+++ /dev/null
-/*
- * $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(" ");
- 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(" ");
- 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> ");
- 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();
- }
- }
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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();
-}
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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="#FFFFFF">"
- "<TR><TD></TR></TD></TABLE>"
- );
-
- }
-
- wprintf("<TABLE border=0 WIDTH=100%% "
- "CELLSPACING=0 CELLPADDING=0 "
- "BGCOLOR="#EEEEEE">");
-
- 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="#FF0000">");
- }
- else {
- wprintf("<FONT COLOR="#0000FF">");
- }
- jsescputs(cl_user);
-
- wprintf("</FONT>: </B>");
- }
- else {
- wprintf(" ");
- }
- 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);
-}
-
-/*@}*/
+++ /dev/null
-/*
- * $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\"> ");
- 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\">"
- " "
- "<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();
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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(">");
- 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(" ");
- 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(" ");
-}
-
-
-/**
- * \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(" ");
- }
-}
-
-
-
-
-/**
- * \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> </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> </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> </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> </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> </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> </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> </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\">"
- " "
- "<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(" ");
- 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(" ");
- 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\">"
- " "
- "<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(" ");
- 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(" ");
- if (levels>2) for (t=0; t<(levels-2); ++t) wprintf(" ");
- 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(" ");
- if (levels>2) for (t=0; t<(levels-2); ++t) wprintf(" ");
-
- /** 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();
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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);
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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);
-}
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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;
-}
+++ /dev/null
-/*
- * $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\"> 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);
-}
-
-
+++ /dev/null
-/*
- * $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(" ");
- 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();
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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;
-}
-
-
-
-/*@}*/
--- /dev/null
+/*
+ * $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 "Login." "
+ "<li><b>If you are a new user</b>, enter the name and password "
+ "you wish to use, "
+ "and click "New User." "
+ "<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> "
+ "<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> \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(" ");
+ 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();
+ }
+}
+
+
+
+/** @} */
--- /dev/null
+/*
+ * $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);
+}
+
+
+/** @} */
--- /dev/null
+/*
+ * $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 */
+
+/** @} */
--- /dev/null
+/*
+ * $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\">"
+ " "
+ "<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\">"
+ " "
+ "<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\">"
+ " "
+ "<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\">"
+ " "
+ "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
+ " "
+ "<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 */
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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
+/*@}*/
--- /dev/null
+/*
+ * $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(" "
+ "<font size=+1 color=\"#FFFFFF\">"
+ "%s %d"
+ "</font>"
+ " ", 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(" "
+ "<font size=+1 color=\"#FFFFFF\">"
+ "%s %d"
+ "</font>"
+ " ", 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> ");
+ 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 */
+
+/** @} */
--- /dev/null
+/*
+ * $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();
+}
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+/*@}*/
--- /dev/null
+/*
+ * $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 */
+/*@}*/
--- /dev/null
+/*
+ * 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
+ */
+
+/*@{*/
+/*@}*/
+
+
+
--- /dev/null
+/*
+ * $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(" ");
+
+ 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\">"
+ " "
+ "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
+ " "
+ "<INPUT TYPE=\"submit\" NAME=\"check_button\" "
+ "VALUE=\"%s\">\n"
+ " "
+ "<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 */
+
+/*@}*/
--- /dev/null
+/*
+ * $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> </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> </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);
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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;
+}
+
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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 */
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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(" ");
+ wprintf("<INPUT TYPE=\"RESET\" VALUE=\"%s\">\n", _("Reset form"));
+ wprintf(" ");
+ 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;
+ }
+}
--- /dev/null
+/* $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);
--- /dev/null
+/*
+ * $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;
+}
--- /dev/null
+/*
+ * $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();
+}
--- /dev/null
+/*
+ * $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);
+ }
+}
--- /dev/null
+/*
+ * $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");
+}
--- /dev/null
+/*
+ * $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);
+ }
+}
--- /dev/null
+/*
+ * $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;
+}
--- /dev/null
+/*
+ * $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);
+}
+
+/*@}*/
--- /dev/null
+/*
+ * $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
+ );
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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 */
+/*@}*/
--- /dev/null
+/*
+ * $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=\" \">\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=\" \">\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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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=\" \">"
+ "</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\">"
+ " "
+ "<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\">"
+ " ");
+ wprintf(_("Your icon bar has been updated. Please select any of its "
+ "choices to continue."));
+ wprintf("</td></tr></table>\n");
+ wDumpContent(2);
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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 "");
+ escputs(room);
+ wprintf("" 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 "
+ "<INPUT TYPE=\"radio\" NAME=\"subtype\""
+ "VALUE=\"digest\" CHECKED>Digest format "
+ "<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();
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+
+/*@}*/
--- /dev/null
+/*
+ * $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(" ");
+ 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);
+ }
+
+
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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(" ");
+ }
+ 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> ", &buf[5]);
+ }
+ if (!strncasecmp(buf, "rfca=", 5)) {
+ strcpy(rfca, &buf[5]);
+ wprintf("<");
+ escputs(rfca);
+ wprintf("> ");
+ }
+
+ 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> ", &buf[5]);
+ }
+ if (!strncasecmp(buf, "rfca=", 5)) {
+ strcpy(rfca, &buf[5]);
+ wprintf("<");
+ msgescputs(rfca);
+ wprintf("> ");
+ }
+
+ 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 - %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"
+ " "
+ "<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(" <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(" "
+ "<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("\"> "
+ "<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("<");
+ }
+ else if (buf[i] == '>') {
+ wprintf(">");
+ }
+ else if (buf[i] == '&') {
+ wprintf("&");
+ }
+ else if (buf[i] == '\"') {
+ wprintf(""");
+ }
+ else if (buf[i] == '\'') {
+ wprintf("'");
+ }
+ 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(" ");
+ wprintf(_("Attach file:"));
+ wprintf(" <input NAME=\"attachfile\" "
+ "SIZE=16 TYPE=\"file\">\n "
+ "<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("\"> "
+ "<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(" ");
+ 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");
+
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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
+ );
--- /dev/null
+/*
+ * $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(" ");
+ 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(" ");
+ 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> ");
+ 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();
+ }
+ }
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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();
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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="#FFFFFF">"
+ "<TR><TD></TR></TD></TABLE>"
+ );
+
+ }
+
+ wprintf("<TABLE border=0 WIDTH=100%% "
+ "CELLSPACING=0 CELLPADDING=0 "
+ "BGCOLOR="#EEEEEE">");
+
+ 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="#FF0000">");
+ }
+ else {
+ wprintf("<FONT COLOR="#0000FF">");
+ }
+ jsescputs(cl_user);
+
+ wprintf("</FONT>: </B>");
+ }
+ else {
+ wprintf(" ");
+ }
+ 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);
+}
+
+/*@}*/
--- /dev/null
+/*
+ * $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\"> ");
+ 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\">"
+ " "
+ "<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();
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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(">");
+ 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(" ");
+ 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(" ");
+}
+
+
+/**
+ * \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(" ");
+ }
+}
+
+
+
+
+/**
+ * \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> </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> </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> </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> </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> </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> </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> </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\">"
+ " "
+ "<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(" ");
+ 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(" ");
+ 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\">"
+ " "
+ "<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(" ");
+ 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(" ");
+ if (levels>2) for (t=0; t<(levels-2); ++t) wprintf(" ");
+ 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(" ");
+ if (levels>2) for (t=0; t<(levels-2); ++t) wprintf(" ");
+
+ /** 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();
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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;
+}
--- /dev/null
+/*
+ * $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\"> 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);
+}
+
+
--- /dev/null
+/*
+ * $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(" ");
+ 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();
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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;
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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 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 online 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 on your 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 this 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);
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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(" ");
+ 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;
+ }
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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> </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> </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");
+ }
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+
+
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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"
+ " "
+ "<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);
+ }
+
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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> ");
+ 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);
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+
+
+
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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 *);
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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\">"
+ " "
+ "<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");
+ }
+}
+
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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, "<");
+ else if (strbuf[a] == '>')
+ strcat(target, ">");
+ else if (strbuf[a] == '&')
+ strcat(target, "&");
+ else if (strbuf[a] == '\"')
+ strcat(target, """);
+ else if (strbuf[a] == '\'')
+ strcat(target, "'");
+ 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, " ");
+ 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, """);
+ else if (strbuf[a] == '&')
+ strcat(target, "&;");
+ 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, "'");
+ 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;
+ }
+}
+
+
+/*@}*/
--- /dev/null
+/* $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 ""
+
--- /dev/null
+/*
+ * $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
+}
+
+/*@}*/
--- /dev/null
+/* $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, ...);
--- /dev/null
+/*
+ * $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);
+ }
+}
+
+
+/*@}*/
--- /dev/null
+/*
+ * $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);
+}
+
+
+/** @} */
+++ /dev/null
-/*
- * $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);
-}
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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 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 online 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 on your 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 this 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);
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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(" ");
- 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;
- }
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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> </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> </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");
- }
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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);
-}
-
-
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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"
- " "
- "<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);
- }
-
-}
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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> ");
- 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);
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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);
-}
-
-
-
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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 *);
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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\">"
- " "
- "<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");
- }
-}
-
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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, "<");
- else if (strbuf[a] == '>')
- strcat(target, ">");
- else if (strbuf[a] == '&')
- strcat(target, "&");
- else if (strbuf[a] == '\"')
- strcat(target, """);
- else if (strbuf[a] == '\'')
- strcat(target, "'");
- 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, " ");
- 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, """);
- else if (strbuf[a] == '&')
- strcat(target, "&;");
- 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, "'");
- 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;
- }
-}
-
-
-/*@}*/
+++ /dev/null
-/* $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 ""
-
+++ /dev/null
-/*
- * $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
-}
-
-/*@}*/
+++ /dev/null
-/* $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, ...);
+++ /dev/null
-/*
- * $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);
- }
-}
-
-
-/*@}*/
+++ /dev/null
-/*
- * $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);
-}
-
-
-/** @} */