From: Wilfried Göesgens Date: Sun, 12 Mar 2006 12:00:48 +0000 (+0000) Subject: move all source files to src/ X-Git-Tag: v7.86~4132 X-Git-Url: https://code.citadel.org/?a=commitdiff_plain;h=40581cf56848097d15ea45eb32bfb67c4e69cc9e;p=citadel.git move all source files to src/ --- diff --git a/webcit/auth.c b/webcit/auth.c deleted file mode 100644 index 0f594e163..000000000 --- a/webcit/auth.c +++ /dev/null @@ -1,562 +0,0 @@ -/* - * $Id$ - */ -/** - * - * \defgroup WebcitAuth WebcitAuth; Handles authentication of users to a Citadel server. - * \ingroup CitadelConfig - */ - -/*@{*/ -#include "webcit.h" - - - -/** - * \brief user states - * the plain text states of a user. filled in at \ function TODO initialize_ax_defs() - * due to NLS - */ -char *axdefs[7]; - -void initialize_axdefs(void) { - axdefs[0] = _("Deleted"); /*!0: an erased user */ - axdefs[1] = _("New User"); /*!1: a new user */ - axdefs[2] = _("Problem User"); /*!2: a trouble maker */ - axdefs[3] = _("Local User"); /*!3: user with normal privileges */ - axdefs[4] = _("Network User"); /*!4: a user that may access network resources */ - axdefs[5] = _("Preferred User");/*!5: a moderator */ - axdefs[6] = _("Aide"); /*!6: chief */ -} - - - - -/** - * \brief Display the login screen - * \param mesg The error message if last attempt failed. - */ -void display_login(char *mesg) -{ - char buf[SIZ]; - - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n"); - - if (mesg != NULL) if (strlen(mesg) > 0) { - stresc(buf, mesg, 0, 0); - svprintf("mesg", WCS_STRING, "%s", buf); - } - - svprintf("LOGIN_INSTRUCTIONS", WCS_STRING, - _(""), - 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, "
"); - svprintf("NEWUSER_BUTTON_POST", WCS_STRING, "
"); - } - 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; ilogged_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("
"); - 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("
"); - wprintf(_("Log in again")); - wprintf("   " - ""); - wprintf(_("Close window")); - wprintf("
\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("
\n" - "
" - ""); - wprintf(_("Validate new users")); - wprintf("
\n
\n
\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("%s
\n", &buf[4]); - } - } - } - - /** Now see if any more users require validation. */ - serv_puts("GNUR"); - serv_getln(buf, sizeof buf); - if (buf[0] == '2') { - wprintf(""); - wprintf(_("No users require validation at this time.")); - wprintf("
\n"); - wDumpContent(1); - return; - } - if (buf[0] != '3') { - wprintf("%s
\n", &buf[4]); - wDumpContent(1); - return; - } - - wprintf("
" - "
\n"); - wprintf("
"); - - 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

%s

", - buf, &cmd[4]); - if (a == 2) - wprintf("PW: %s
\n", buf); - if (a == 3) - wprintf("%s
\n", buf); - if (a == 4) - wprintf("%s
\n", buf); - if (a == 5) - wprintf("%s, ", buf); - if (a == 6) - wprintf("%s ", buf); - if (a == 7) - wprintf("%s
\n", buf); - if (a == 8) - wprintf("%s
\n", buf); - if (a == 9) - wprintf(_("Current access level: %d (%s)\n"), - atoi(buf), axdefs[atoi(buf)]); - } while (strcmp(buf, "000")); - } else { - wprintf("

%s

%s
\n", user, &cmd[4]); - } - - wprintf("
"); - wprintf(_("Select access level for this user:")); - wprintf("
\n"); - for (a = 0; a <= 6; ++a) { - wprintf("%s   \n", - a, axdefs[a]); - } - wprintf("
\n"); - - wprintf("
\n"); - wprintf("
\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("
\n" - "
" - ""); - wprintf(_("Change your password")); - wprintf("" - "
\n" - "
\n
\n" - ); - - if (strlen(WC->ImportantMessage) > 0) { - do_template("beginbox_nt"); - wprintf("" - "%s
\n", WC->ImportantMessage); - do_template("endbox"); - safestrncpy(WC->ImportantMessage, "", sizeof WC->ImportantMessage); - } - - wprintf("
" - "
\n"); - - wprintf("

"); - serv_puts("MESG changepw"); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') { - fmout("CENTER"); - } - - wprintf("
\n"); - wprintf("
" - "" - "\n"); - wprintf("\n"); - wprintf("\n"); - wprintf("\n"); - - wprintf("
"); - wprintf(_("Enter new password:")); - wprintf("
"); - wprintf(_("Enter it again to confirm:")); - wprintf("

\n"); - wprintf("", _("Change password")); - wprintf(" "); - wprintf("\n", _("Cancel")); - wprintf("
\n"); - wprintf("
\n"); - wDumpContent(1); -} - -/** - * \brief change password - * if passwords match, propagate it to citserver. - */ -void changepw(void) -{ - char buf[SIZ]; - char newpass1[32], newpass2[32]; - - if (strlen(bstr("change_action")) == 0) { - safestrncpy(WC->ImportantMessage, - _("Cancelled. Password was not changed."), - sizeof WC->ImportantMessage); - display_main_menu(); - return; - } - - safestrncpy(newpass1, bstr("newpass1"), sizeof newpass1); - safestrncpy(newpass2, bstr("newpass2"), sizeof newpass2); - - if (strcasecmp(newpass1, newpass2)) { - safestrncpy(WC->ImportantMessage, - _("They don't match. Password was not changed."), - sizeof WC->ImportantMessage); - display_changepw(); - return; - } - - if (strlen(newpass1) == 0) { - safestrncpy(WC->ImportantMessage, - _("Blank passwords are not allowed."), - sizeof WC->ImportantMessage); - display_changepw(); - return; - } - - serv_printf("SETP %s", newpass1); - serv_getln(buf, sizeof buf); - sprintf(WC->ImportantMessage, "%s", &buf[4]); - if (buf[0] == '2') { - safestrncpy(WC->wc_password, buf, sizeof WC->wc_password); - display_main_menu(); - } - else { - display_changepw(); - } -} - - - -/** @} */ diff --git a/webcit/autocompletion.c b/webcit/autocompletion.c deleted file mode 100644 index 0b9373ea2..000000000 --- a/webcit/autocompletion.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * $Id$ - *//** - * \defgroup AjaxAutoCompletion ajax-powered autocompletion... - * \ingroup ClientPower - */ - -/*@{*/ -#include "webcit.h" - -/** - * \brief Recipient autocompletion results - * \param partial the account to search for ?????? - */ -void recp_autocomplete(char *partial) { - char buf[1024]; - char name[128]; - - output_headers(0, 0, 0, 0, 0, 0); - - wprintf("Content-type: text/html\r\n" - "Server: %s\r\n" - "Connection: close\r\n" - "Pragma: no-cache\r\n" - "Cache-Control: no-store\r\n", - SERVER); - begin_burst(); - - wprintf("
    "); - - 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("
  • "); - escputs(name); - wprintf("
  • "); - } - } - - wprintf("
"); - - wprintf("\r\n\r\n"); - wDumpContent(0); -} - - -/** @} */ diff --git a/webcit/availability.c b/webcit/availability.c deleted file mode 100644 index 0c1198a26..000000000 --- a/webcit/availability.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * $Id$ - */ -/** - * - * \defgroup CalendarAv Check attendee availability for scheduling a meeting. - * \ingroup Calendaring - */ -/*@{*/ - - -#include "webcit.h" -#include "webserver.h" - -/** only available if we have calendaring */ -#ifdef WEBCIT_WITH_CALENDAR_SERVICE - - - -/** - * \brief verify users avaiability - * Utility function to fetch a VFREEBUSY type of thing for - * any specified user. - * \param who string of the user to search - */ -icalcomponent *get_freebusy_for_user(char *who) { - char buf[SIZ]; - char *serialized_fb = NULL; - icalcomponent *fb = NULL; - - serv_printf("ICAL freebusy|%s", who); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') { - serialized_fb = read_server_text(); - } - - if (serialized_fb == NULL) { - return NULL; - } - - fb = icalcomponent_new_from_string(serialized_fb); - free(serialized_fb); - if (fb == NULL) { - return NULL; - } - - return(fb); -} - - - - -/** - * \brief Check if dates are overlapping - * Check to see if two events overlap. - * (This function is used in both Citadel and WebCit. If you change it in - * one place, change it in the other. Better yet, put it in a library.) - * \param t1start date one start - * \param t1end date one end - * \param t2start date one start - * \param t2end date two end - * \returns nonzero if they do. - */ -int ical_ctdl_is_overlap( - struct icaltimetype t1start, - struct icaltimetype t1end, - struct icaltimetype t2start, - struct icaltimetype t2end -) { - - if (icaltime_is_null_time(t1start)) return(0); - if (icaltime_is_null_time(t2start)) return(0); - - /** First, check for all-day events */ - if (t1start.is_date) { - if (!icaltime_compare_date_only(t1start, t2start)) { - return(1); - } - if (!icaltime_is_null_time(t2end)) { - if (!icaltime_compare_date_only(t1start, t2end)) { - return(1); - } - } - } - - if (t2start.is_date) { - if (!icaltime_compare_date_only(t2start, t1start)) { - return(1); - } - if (!icaltime_is_null_time(t1end)) { - if (!icaltime_compare_date_only(t2start, t1end)) { - return(1); - } - } - } - - /** Now check for overlaps using date *and* time. */ - - /** First, bail out if either event 1 or event 2 is missing end time. */ - if (icaltime_is_null_time(t1end)) return(0); - if (icaltime_is_null_time(t2end)) return(0); - - /** If event 1 ends before event 2 starts, we're in the clear. */ - if (icaltime_compare(t1end, t2start) <= 0) return(0); - - /** If event 2 ends before event 1 starts, we're also ok. */ - if (icaltime_compare(t2end, t1start) <= 0) return(0); - - /** Otherwise, they overlap. */ - return(1); -} - - - -/* - * \brief dig availability on citserver - * Back end function for check_attendee_availability() - * This one checks an individual attendee against a supplied - * event start and end time. All these fields have already been - * broken out. - * \param attendee_string name of the attendee - * \param event_start starttime of the event to check - * \param event_end endtime of the event to check - * \return The result is placed in 'annotation'. - */ -void check_individual_attendee(char *attendee_string, - struct icaltimetype event_start, - struct icaltimetype event_end, - char *annotation) { - - icalcomponent *fbc = NULL; - icalcomponent *fb = NULL; - icalproperty *thisfb = NULL; - struct icalperiodtype period; - - /** - * Set to 'unknown' right from the beginning. Unless we learn - * something else, that's what we'll go with. - */ - strcpy(annotation, _("availability unknown")); - - fbc = get_freebusy_for_user(attendee_string); - if (fbc == NULL) { - return; - } - - /** - * Make sure we're looking at a VFREEBUSY by itself. What we're probably - * looking at initially is a VFREEBUSY encapsulated in a VCALENDAR. - */ - if (icalcomponent_isa(fbc) == ICAL_VCALENDAR_COMPONENT) { - fb = icalcomponent_get_first_component(fbc, ICAL_VFREEBUSY_COMPONENT); - } - else if (icalcomponent_isa(fbc) == ICAL_VFREEBUSY_COMPONENT) { - fb = fbc; - } - - /** Iterate through all FREEBUSY's looking for conflicts. */ - if (fb != NULL) { - - strcpy(annotation, _("free")); - - for (thisfb = icalcomponent_get_first_property(fb, ICAL_FREEBUSY_PROPERTY); - thisfb != NULL; - thisfb = icalcomponent_get_next_property(fb, ICAL_FREEBUSY_PROPERTY) ) { - - /** Do the check */ - period = icalproperty_get_freebusy(thisfb); - if (ical_ctdl_is_overlap(period.start, period.end, - event_start, event_end)) { - strcpy(annotation, _("BUSY")); - } - - } - } - - icalcomponent_free(fbc); -} - - - - -/** - * \brief check attendees availability - * Check the availability of all attendees for an event (when possible) - * and annotate accordingly. - * \param vevent the event which should be compared with attendees calendar - */ -void check_attendee_availability(icalcomponent *vevent) { - icalproperty *attendee = NULL; - icalproperty *dtstart_p = NULL; - icalproperty *dtend_p = NULL; - struct icaltimetype dtstart_t; - struct icaltimetype dtend_t; - char attendee_string[SIZ]; - char annotated_attendee_string[SIZ]; - char annotation[SIZ]; - - if (vevent == NULL) { - return; - } - - /** - * If we're looking at a fully encapsulated VCALENDAR - * rather than a VEVENT component, attempt to use the first - * relevant VEVENT subcomponent. If there is none, the - * NULL returned by icalcomponent_get_first_component() will - * tell the next iteration of this function to create a - * new one. - */ - if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) { - check_attendee_availability( - icalcomponent_get_first_component( - vevent, ICAL_VEVENT_COMPONENT - ) - ); - return; - } - - ical_dezonify(vevent); /**< Convert everything to UTC */ - - /** - * Learn the start and end times. - */ - dtstart_p = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY); - if (dtstart_p != NULL) dtstart_t = icalproperty_get_dtstart(dtstart_p); - - dtend_p = icalcomponent_get_first_property(vevent, ICAL_DTEND_PROPERTY); - if (dtend_p != NULL) dtend_t = icalproperty_get_dtend(dtend_p); - - /** - * Iterate through attendees. - */ - for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); - attendee != NULL; - attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) { - - strcpy(attendee_string, icalproperty_get_attendee(attendee)); - if (!strncasecmp(attendee_string, "MAILTO:", 7)) { - - /** screen name or email address */ - strcpy(attendee_string, &attendee_string[7]); - striplt(attendee_string); - - check_individual_attendee(attendee_string, - dtstart_t, dtend_t, - annotation); - - /** Replace the attendee name with an annotated one. */ - snprintf(annotated_attendee_string, sizeof annotated_attendee_string, - "MAILTO:%s (%s)", attendee_string, annotation); - icalproperty_set_attendee(attendee, annotated_attendee_string); - - } - } - -} - - -#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ - -/** @} */ diff --git a/webcit/calendar.c b/webcit/calendar.c deleted file mode 100644 index 5353dfa67..000000000 --- a/webcit/calendar.c +++ /dev/null @@ -1,1008 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup calav Functions which handle calendar objects and their processing/display. - * \ingroup Calendaring - */ -/* @{ */ - -#include "webcit.h" -#include "webserver.h" - -#ifndef WEBCIT_WITH_CALENDAR_SERVICE - -/** - * \brief get around non existing types - * Handler stubs for builds with no calendar library available - * \param part_source dummy pointer to the source - * \param msgnum number of the mesage in the db - * \param cal_partnum number of the calendar part - */ -void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) { - - wprintf(_("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.
\n") - ); - -} - -/** - * \brief say we can't display calendar items - * \param msgnum number of the mesage in our db - */ -void display_calendar(long msgnum) { - wprintf(_("" - "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." - "
\n")); -} - -/** - * \brief say we can't display task items - * \param msgnum number of the mesage in our db - */ -void display_task(long msgnum) { - wprintf(_("" - "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." - "
\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("
\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("\n"); - break; - case ICAL_METHOD_REPLY: - wprintf("\n"); - break; - case ICAL_METHOD_PUBLISH: - wprintf("\n"); - break; - default: - wprintf("\n"); - break; - } - } - - p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY); - if (p != NULL) { - wprintf("\n"); - } - - p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY); - if (p != NULL) { - wprintf("\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("", d_str); - } - else { - tt = icaltime_as_timet(t); - fmt_date(buf, tt, 0); - wprintf("", 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("", buf); - } - - } - - p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY); - if (p != NULL) { - wprintf("\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("\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("\n"); - } - } - lprintf(9, "...done.\n"); - - /** Display the Accept/Decline buttons */ - wprintf("" - "\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("\n", - _("Click Update 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("
\n" - "" - "  " - ""); - wprintf(_("Meeting invitation")); - wprintf("
\n" - "" - "  " - ""); - wprintf(_("Attendee's reply to your invitation")); - wprintf("
\n" - "" - "  " - ""); - wprintf(_("Published event")); - wprintf("
"); - wprintf(_("This is an unknown type of calendar item.")); - wprintf("
"); - wprintf(_("Summary:")); - wprintf(""); - escputs((char *)icalproperty_get_comment(p)); - wprintf("
"); - wprintf(_("Location:")); - wprintf(""); - escputs((char *)icalproperty_get_comment(p)); - wprintf("
"); - wprintf(_("Date:")); - wprintf("%s
"); - wprintf(_("Starting date/time:")); - wprintf("%s
"); - wprintf(_("Ending date/time:")); - wprintf("%s
"); - wprintf(_("Description:")); - wprintf(""); - escputs((char *)icalproperty_get_comment(p)); - wprintf("
"); - wprintf(_("Attendee:")); - wprintf(""); - 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("
%s", - (is_update ? - _("Update:") : - _("CONFLICT:") - ) - ); - escputs(conflict_message); - wprintf("
%s" - "%s" - " | " - "%s" - " | " - "%s" - "
" - "%s" - "" - "%s" - " | " - "%s" - "" - "
\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("
\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("
\n"); - wprintf("
" - ""); - wprintf(_("Respond to meeting request")); - wprintf("" - "
\n" - ); - wprintf("
\n
\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("
" - "" - "" - ); - 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 not been entered into your calendar.") - ); - } - wprintf(" "); - wprintf(_("A reply has been sent to the meeting organizer.")); - wprintf("
\n"); - } else { - wprintf("" - "%s\n", &buf[4]); - } - - wprintf("wc_roomname); - wprintf("\">
"); - wprintf(_("Return to messages")); - wprintf("

\n"); - - wDumpContent(1); -} - - - -/** - * \brief Handle an incoming RSVP - */ -void handle_rsvp(void) { - char buf[SIZ]; - - output_headers(1, 1, 2, 0, 0, 0); - - wprintf("
\n"); - wprintf("
" - ""); - wprintf(_("Update your calendar with this RSVP")); - wprintf("" - "
\n" - "
\n
\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("
" - "" - "" - ); - 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 not been updated.") - ); - } - wprintf("
\n" - ); - } else { - wprintf("" - "%s\n", &buf[4]); - } - - wprintf("wc_roomname); - wprintf("\">
"); - wprintf(_("Return to messages")); - wprintf("

\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("
\n" - "" - "" - "
"); - wprintf(_("Edit task")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("
" - "
"); - - wprintf("
\n"); - wprintf("\n", - msgnum); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - wprintf("
"); - wprintf(_("Summary:")); - wprintf("" - "
"); - wprintf(_("Start date:")); - wprintf(""); - 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("
"); - wprintf(_("Due date:")); - wprintf(""); - 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("
"); - wprintf(_("Description:")); - wprintf(""); - wprintf("
\n"); - - wprintf("
" - "" - "  " - "\n" - "  " - "\n" - "
\n", - _("Save"), - _("Delete"), - _("Cancel") - ); - - wprintf("
\n"); - - wprintf("
\n"); - wDumpContent(1); - - if (created_new_vtodo) { - icalcomponent_free(vtodo); - } -} - -/* - * \brief Save an edited task - * \param supplied_vtodo the task to save - * \param msgnum number of the mesage in our db - */ -void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { - char buf[SIZ]; - int delete_existing = 0; - icalproperty *prop; - icalcomponent *vtodo, *encaps; - int created_new_vtodo = 0; - int i; - int sequence = 0; - struct icaltimetype t; - - if (supplied_vtodo != NULL) { - vtodo = supplied_vtodo; - /** - * If we're looking at a fully encapsulated VCALENDAR - * rather than a VTODO component, attempt to use the first - * relevant VTODO subcomponent. If there is none, the - * NULL returned by icalcomponent_get_first_component() will - * tell the next iteration of this function to create a - * new one. - */ - if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) { - save_individual_task( - icalcomponent_get_first_component( - vtodo, ICAL_VTODO_COMPONENT - ), msgnum - ); - return; - } - } - else { - vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); - created_new_vtodo = 1; - } - - if (strlen(bstr("save_button")) > 0) { - - /** Replace values in the component with ones from the form */ - - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_SUMMARY_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo, prop); - icalproperty_free(prop); - } - icalcomponent_add_property(vtodo, - icalproperty_new_summary(bstr("summary"))); - - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_DESCRIPTION_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo, prop); - icalproperty_free(prop); - } - icalcomponent_add_property(vtodo, - icalproperty_new_description(bstr("description"))); - - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_DTSTART_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo, prop); - icalproperty_free(prop); - } - icaltime_from_webform(&t, "dtstart"); - icalcomponent_add_property(vtodo, - icalproperty_new_dtstart(t) - ); - - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_DUE_PROPERTY), prop != NULL) { - icalcomponent_remove_property(vtodo, prop); - icalproperty_free(prop); - } - icaltime_from_webform(&t, "due"); - icalcomponent_add_property(vtodo, - icalproperty_new_due(t) - ); - - /** Give this task a UID if it doesn't have one. */ - lprintf(9, "Give this task a UID if it doesn't have one.\n"); - if (icalcomponent_get_first_property(vtodo, - ICAL_UID_PROPERTY) == NULL) { - generate_uuid(buf); - icalcomponent_add_property(vtodo, - icalproperty_new_uid(buf) - ); - } - - /** Increment the sequence ID */ - lprintf(9, "Increment the sequence ID\n"); - while (prop = icalcomponent_get_first_property(vtodo, - ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) { - i = icalproperty_get_sequence(prop); - lprintf(9, "Sequence was %d\n", i); - if (i > sequence) sequence = i; - icalcomponent_remove_property(vtodo, prop); - icalproperty_free(prop); - } - ++sequence; - lprintf(9, "New sequence is %d. Adding...\n", sequence); - icalcomponent_add_property(vtodo, - icalproperty_new_sequence(sequence) - ); - - /** - * Encapsulate event into full VCALENDAR component. Clone it first, - * for two reasons: one, it's easier to just free the whole thing - * when we're done instead of unbundling, but more importantly, we - * can't encapsulate something that may already be encapsulated - * somewhere else. - */ - lprintf(9, "Encapsulating into full VCALENDAR component\n"); - encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo)); - - /* Serialize it and save it to the message base */ - serv_puts("ENT0 1|||4"); - serv_getln(buf, sizeof buf); - if (buf[0] == '4') { - serv_puts("Content-type: text/calendar"); - serv_puts(""); - serv_puts(icalcomponent_as_ical_string(encaps)); - serv_puts("000"); - - /** - * Probably not necessary; the server will see the UID - * of the object and delete the old one anyway, but - * just in case... - */ - delete_existing = 1; - } - icalcomponent_free(encaps); - } - - /** - * If the user clicked 'Delete' then explicitly delete the message. - */ - if (strlen(bstr("delete_button")) > 0) { - delete_existing = 1; - } - - if ( (delete_existing) && (msgnum > 0L) ) { - serv_printf("DELE %ld", atol(bstr("msgnum"))); - serv_getln(buf, sizeof buf); - } - - if (created_new_vtodo) { - icalcomponent_free(vtodo); - } - - /** Go back to the task list */ - readloop("readfwd"); -} - - - -/** - * \brief generic item handler - * Code common to all display handlers. Given a message number and a MIME - * type, we load the message and hunt for that MIME type. If found, we load - * the relevant part, deserialize it into a libical component, filter it for - * the requested object type, and feed it to the specified handler. - * \param mimetype mimetyp of our object - * \param which_kind sort of ical type - * \param msgnum number of the mesage in our db - * \param callback a funcion \todo - * - */ -void display_using_handler(long msgnum, - char *mimetype, - icalcomponent_kind which_kind, - void (*callback)(icalcomponent *, long) - ) { - char buf[SIZ]; - char mime_partnum[SIZ]; - char mime_filename[SIZ]; - char mime_content_type[SIZ]; - char mime_disposition[SIZ]; - int mime_length; - char relevant_partnum[SIZ]; - char *relevant_source = NULL; - icalcomponent *cal, *c; - - sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */ - serv_puts(buf); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') return; - - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - if (!strncasecmp(buf, "part=", 5)) { - extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename); - extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum); - extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition); - extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type); - mime_length = extract_int(&buf[5], 5); - - if (!strcasecmp(mime_content_type, "text/calendar")) { - strcpy(relevant_partnum, mime_partnum); - } - - } - } - - if (strlen(relevant_partnum) > 0) { - relevant_source = load_mimepart(msgnum, relevant_partnum); - if (relevant_source != NULL) { - - cal = icalcomponent_new_from_string(relevant_source); - if (cal != NULL) { - - ical_dezonify(cal); - - /** Simple components of desired type */ - if (icalcomponent_isa(cal) == which_kind) { - callback(cal, msgnum); - } - - /** Subcomponents of desired type */ - for (c = icalcomponent_get_first_component(cal, - which_kind); - (c != 0); - c = icalcomponent_get_next_component(cal, - which_kind)) { - callback(c, msgnum); - } - icalcomponent_free(cal); - } - free(relevant_source); - } - } - -} - -/** - * \brief display whole calendar - * \param msgnum number of the mesage in our db - */ -void display_calendar(long msgnum) { - display_using_handler(msgnum, "text/calendar", - ICAL_VEVENT_COMPONENT, - display_individual_cal); -} - -/** - * \brief display whole taksview - * \param msgnum number of the mesage in our db - */ -void display_task(long msgnum) { - display_using_handler(msgnum, "text/calendar", - ICAL_VTODO_COMPONENT, - display_individual_cal); -} - -/** - * \brief display the editor component for a task - */ -void display_edit_task(void) { - long msgnum = 0L; - - /** Force change the room if we have to */ - if (strlen(bstr("taskrm")) > 0) { - gotoroom(bstr("taskrm")); - } - - msgnum = atol(bstr("msgnum")); - if (msgnum > 0L) { - /** existing task */ - display_using_handler(msgnum, "text/calendar", - ICAL_VTODO_COMPONENT, - display_edit_individual_task); - } - else { - /** new task */ - display_edit_individual_task(NULL, 0L); - } -} - -/** - *\brief save an edited task - */ -void save_task(void) { - long msgnum = 0L; - - msgnum = atol(bstr("msgnum")); - if (msgnum > 0L) { - display_using_handler(msgnum, "text/calendar", - ICAL_VTODO_COMPONENT, - save_individual_task); - } - else { - save_individual_task(NULL, 0L); - } -} - -/** - * \brief display the editor component for an event - */ -void display_edit_event(void) { - long msgnum = 0L; - - msgnum = atol(bstr("msgnum")); - if (msgnum > 0L) { - /* existing event */ - display_using_handler(msgnum, "text/calendar", - ICAL_VEVENT_COMPONENT, - display_edit_individual_event); - } - else { - /* new event */ - display_edit_individual_event(NULL, 0L); - } -} - -/** - * \brief save an edited event - */ -void save_event(void) { - long msgnum = 0L; - - msgnum = atol(bstr("msgnum")); - - if (msgnum > 0L) { - display_using_handler(msgnum, "text/calendar", - ICAL_VEVENT_COMPONENT, - save_individual_event); - } - else { - save_individual_event(NULL, 0L); - } -} - - - - - -/** - * \brief freebusy display (for client software) - * \param req dunno. ????? - */ -void do_freebusy(char *req) { - char who[SIZ]; - char buf[SIZ]; - char *fb; - - extract_token(who, req, 1, ' ', sizeof who); - if (!strncasecmp(who, "/freebusy/", 10)) { - strcpy(who, &who[10]); - } - unescape_input(who); - - if ( (!strcasecmp(&who[strlen(who)-4], ".vcf")) - || (!strcasecmp(&who[strlen(who)-4], ".ifb")) - || (!strcasecmp(&who[strlen(who)-4], ".vfb")) ) { - who[strlen(who)-4] = 0; - } - - lprintf(9, "freebusy requested for <%s>\n", who); - serv_printf("ICAL freebusy|%s", who); - serv_getln(buf, sizeof buf); - - if (buf[0] != '1') { - wprintf("HTTP/1.1 404 %s\n", &buf[4]); - output_headers(0, 0, 0, 0, 0, 0); - wprintf("Content-Type: text/plain\r\n"); - wprintf("\r\n"); - wprintf("%s\n", &buf[4]); - return; - } - - fb = read_server_text(); - http_transmit_thing(fb, strlen(fb), "text/calendar", 0); - free(fb); -} - - - -#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ - - -/*@}*/ diff --git a/webcit/calendar_tools.c b/webcit/calendar_tools.c deleted file mode 100644 index d62cb8296..000000000 --- a/webcit/calendar_tools.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup MiscCal Miscellaneous functions which handle calendar components. - * \ingroup Calendaring - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" - -/** Hour strings */ -char *hourname[] = { - "12am", "1am", "2am", "3am", "4am", "5am", "6am", - "7am", "8am", "9am", "10am", "11am", "12pm", - "1pm", "2pm", "3pm", "4pm", "5pm", "6pm", - "7pm", "8pm", "9pm", "10pm", "11pm" -}; - -#ifdef WEBCIT_WITH_CALENDAR_SERVICE - -/** - * \brief display and edit date/time - * The display_icaltimetype_as_webform() and icaltime_from_webform() functions - * handle the display and editing of date/time properties in web pages. The - * first one converts an icaltimetype into valid HTML markup -- a series of form - * fields for editing the date and time. When the user submits the form, the - * results can be fed back into the second function, which turns it back into - * an icaltimetype. The "prefix" string required by both functions is prepended - * to all field names. This allows a form to contain more than one date/time - * property (for example, a start and end time) by ensuring the field names are - * unique within the form. - * - * \todo NOTE: These functions assume that the icaltimetype being edited is in UTC, and - * will convert to/from local time for editing. "local" in this case is assumed - * to be the time zone in which the WebCit server is running. A future improvement - * might be to allow the user to specify his/her timezone. - * \param t the time we want to parse - * \param prefix ???? \todo - */ - - -void display_icaltimetype_as_webform(struct icaltimetype *t, char *prefix) { - int i; - time_t now; - struct tm tm_now; - int this_year; - time_t tt; - struct tm tm; - const int span = 10; - int all_day_event = 0; - time_t monthselect_time; - struct tm monthselect_tm; - char monthselect_str[32]; - char calhourformat[16]; - - get_preference("calhourformat", calhourformat, sizeof calhourformat); - - now = time(NULL); - localtime_r(&now, &tm_now); - this_year = tm_now.tm_year + 1900; - - if (t == NULL) return; - if (t->is_date) all_day_event = 1; - tt = icaltime_as_timet(*t); - if (all_day_event) { - gmtime_r(&tt, &tm); - } - else { - localtime_r(&tt, &tm); - } - - wprintf(_("Month: ")); - wprintf("\n"); - - wprintf(_("Day: ")); - wprintf("\n"); - - wprintf(_("Year: ")); - wprintf("\n"); - - wprintf(_("Hour: ")); - wprintf("\n"); - - wprintf(_("Minute: ")); - wprintf("\n"); -} - -/** - *\brief Get time from form - * get the time back from the user and convert it into internal structs. - * \param t our time element - * \param prefix whats that\todo ???? - */ -void icaltime_from_webform(struct icaltimetype *t, char *prefix) { - char vname[32]; - time_t tt; - struct tm tm; - struct icaltimetype t2; - - tt = time(NULL); - localtime_r(&tt, &tm); - - sprintf(vname, "%s_month", prefix); tm.tm_mon = atoi(bstr(vname)) - 1; - sprintf(vname, "%s_day", prefix); tm.tm_mday = atoi(bstr(vname)); - sprintf(vname, "%s_year", prefix); tm.tm_year = atoi(bstr(vname)) - 1900; - sprintf(vname, "%s_hour", prefix); tm.tm_hour = atoi(bstr(vname)); - sprintf(vname, "%s_minute", prefix); tm.tm_min = atoi(bstr(vname)); - - tt = mktime(&tm); - t2 = icaltime_from_timet(tt, 0); - memcpy(t, &t2, sizeof(struct icaltimetype)); -} - -/** - *\brief Get time from form - * get the time back from the user and convert it into internal structs. - * \param t our time element - * \param prefix whats that\todo ???? - */ - -void icaltime_from_webform_dateonly(struct icaltimetype *t, char *prefix) { - char vname[32]; - - memset(t, 0, sizeof(struct icaltimetype)); - - sprintf(vname, "%s_month", prefix); t->month = atoi(bstr(vname)); - sprintf(vname, "%s_day", prefix); t->day = atoi(bstr(vname)); - sprintf(vname, "%s_year", prefix); t->year = atoi(bstr(vname)); - t->is_utc = 1; - t->is_date = 1; -} - - -/** - * \brief Render PAPSTAT - * Render a PARTSTAT parameter as a string (and put it in parentheses) - * \param buf the string to put it to - * \param attendee the attendee to textify - */ -void partstat_as_string(char *buf, icalproperty *attendee) { - icalparameter *partstat_param; - icalparameter_partstat partstat; - - strcpy(buf, _("(status unknown)")); - - partstat_param = icalproperty_get_first_parameter( - attendee, - ICAL_PARTSTAT_PARAMETER - ); - if (partstat_param == NULL) { - return; - } - - partstat = icalparameter_get_partstat(partstat_param); - switch(partstat) { - case ICAL_PARTSTAT_X: - strcpy(buf, "(x)"); - break; - case ICAL_PARTSTAT_NEEDSACTION: - strcpy(buf, _("(needs action)")); - break; - case ICAL_PARTSTAT_ACCEPTED: - strcpy(buf, _("(accepted)")); - break; - case ICAL_PARTSTAT_DECLINED: - strcpy(buf, _("(declined)")); - break; - case ICAL_PARTSTAT_TENTATIVE: - strcpy(buf, _("(tenative)")); - break; - case ICAL_PARTSTAT_DELEGATED: - strcpy(buf, _("(delegated)")); - break; - case ICAL_PARTSTAT_COMPLETED: - strcpy(buf, _("(completed)")); - break; - case ICAL_PARTSTAT_INPROCESS: - strcpy(buf, _("(in process)")); - break; - case ICAL_PARTSTAT_NONE: - strcpy(buf, _("(none)")); - break; - } -} - - -/** - * \brief embedd - * Utility function to encapsulate a subcomponent into a full VCALENDAR - * \param subcomp the component to encapsulate - * \returns the meta object ??? - */ -icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp) { - icalcomponent *encaps; - - /* lprintf(9, "ical_encapsulate_subcomponent() called\n"); */ - - if (subcomp == NULL) { - lprintf(3, "ERROR: called with NULL argument!\n"); - return NULL; - } - - /** - * If we're already looking at a full VCALENDAR component, - * don't bother ... just return itself. - */ - if (icalcomponent_isa(subcomp) == ICAL_VCALENDAR_COMPONENT) { - return subcomp; - } - - /** Encapsulate the VEVENT component into a complete VCALENDAR */ - encaps = icalcomponent_new(ICAL_VCALENDAR_COMPONENT); - if (encaps == NULL) { - lprintf(3, "%s:%d: Error - could not allocate component!\n", - __FILE__, __LINE__); - return NULL; - } - - /** Set the Product ID */ - icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID)); - - /** Set the Version Number */ - icalcomponent_add_property(encaps, icalproperty_new_version("2.0")); - - /** Encapsulate the subcomponent inside */ - /* lprintf(9, "Doing the encapsulation\n"); */ - icalcomponent_add_component(encaps, subcomp); - - /** Convert all timestamps to UTC so we don't have to deal with - * stupid VTIMEZONE crap. - */ - ical_dezonify(encaps); - - /** Return the object we just created. */ - return(encaps); -} - - - - -#endif -/*@}*/ diff --git a/webcit/calendar_view.c b/webcit/calendar_view.c deleted file mode 100644 index dde6f2169..000000000 --- a/webcit/calendar_view.c +++ /dev/null @@ -1,984 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup CalHtmlHandles Handles the HTML display of calendar items. - * \ingroup Calendaring - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" - -#ifndef WEBCIT_WITH_CALENDAR_SERVICE - -/**\brief stub for non-libical builds */ -void do_calendar_view(void) { - wprintf("
"); - wprintf(_("The calendar view is not available.")); - wprintf("

\n"); -} - -/**\brief stub for non-libical builds */ -void do_tasks_view(void) { - wprintf("
"); - wprintf(_("The tasks view is not available.")); - wprintf("

\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("


\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("" - "
" - ); - } - - wprintf("" - "", - WC->disp_cal[i].cal_msgnum, - bstr("calview"), - bstr("year"), - bstr("month"), - bstr("day") - ); - escputs((char *) - icalproperty_get_comment(p)); - wprintf("
\n"); - - if (all_day_event) { - wprintf("
"); - } - - } - - } - - - } - } -} - - -/** - * \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("%i:%2i" - "" - "", - 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("" - "%s%s", - 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("
" - "
\n"); - - wprintf("\n"); - - wprintf("
"); - - localtime_r(&previous_month, &tm); - wprintf("", - (int)(tm.tm_year)+1900, tm.tm_mon + 1); - wprintf("\n"); - - wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm); - wprintf("  " - "" - "%s %d" - "" - "  ", colheader_label, year); - - localtime_r(&next_month, &tm); - wprintf("", - (int)(tm.tm_year)+1900, tm.tm_mon + 1); - wprintf("\n"); - - wprintf("
\n"); - - /** Inner table (the real one) */ - wprintf(""); - 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("\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(""); - } - - wprintf(""); - - /** After displaying Saturday, end the row */ - if ((i % 7) == 6) { - wprintf("\n"); - } - - thetime += (time_t)86400; /** ahead 24 hours */ - } - - wprintf("
" - "%s", colheader_label); - - } - wprintf("
", - ((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("" - "%d
", - 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("
" /** end of inner table */ - "
" /** end of outer table */ - "
\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("
" - "
\n"); - - wprintf("\n"); - - wprintf("
"); - - localtime_r(&previous_month, &tm); - wprintf("", - (int)(tm.tm_year)+1900, tm.tm_mon + 1); - wprintf("\n"); - - wc_strftime(month_label, sizeof month_label, "%B", &tm); - wprintf("  " - "" - "%s %d" - "" - "  ", month_label, year); - - localtime_r(&next_month, &tm); - wprintf("", - (int)(tm.tm_year)+1900, tm.tm_mon + 1); - wprintf("\n"); - - wprintf("
\n"); - - /** Inner table (the real one) */ - wprintf(""); - wprintf("\n"); - wprintf("
\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("" - " \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("\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("
%s %s
%s%s%s%s
%s,%i." - "
\n"); - } - - thetime += (time_t)86400; /** ahead 24 hours */ - } - - wprintf("
" /** end of inner table */ - "
" /** end of outer table */ - "
\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("
week view FIXME

\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("


\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("" - "
" - ); - } - - wprintf("" - "", - WC->disp_cal[i].cal_msgnum, - year, month, day - ); - escputs((char *) - icalproperty_get_comment(p)); - wprintf("
\n"); - - if (all_day_event) { - wprintf("
"); - } - } - - } - - - } - } -} - - -/** - * \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("
" - "
\n"); - - /** Inner table (the real one) */ - wprintf("\n"); - - /** Innermost table (contains hours etc.) */ - wprintf("" - ); - - wprintf(""); /** end stuff-on-the-right */ - - - - wprintf("
" - "\n"); - - /** Display events before 8:00 (hour=-1 is all-day events) */ - wprintf("" - "" - "\n"); - - /** Now the middle of the day... */ - for (hour = daystart; hour <= dayend; ++hour) { /* could do HEIGHT=xx */ - wprintf("\n"); - } - - /** Display events after 5:00... */ - wprintf("" - "" - "\n"); - - - wprintf("
"); - for (hour = (-1); hour <= (daystart-1); ++hour) { - calendar_day_view_display_events(year, month, day, hour); - } - wprintf("
"); - wprintf("", - year, month, day, hour - ); - - if (!strcasecmp(calhourformat, "24")) { - wprintf("%2d:00 ", hour); - } - else { - wprintf("%d:00%s ", - (hour <= 12 ? hour : hour-12), - (hour < 12 ? "am" : "pm") - ); - } - - wprintf(""); - - /* put the data here, stupid */ - calendar_day_view_display_events(year, month, day, hour); - - wprintf("
"); - for (hour = (dayend+1); hour <= 23; ++hour) { - calendar_day_view_display_events(year, month, day, hour); - } - wprintf("
" /* end of innermost table */ - "
"); /* begin stuff-on-the-right */ - - - /** Begin todays-date-with-left-and-right-arrows */ - wprintf("\n"); - wprintf(""); - - /** Left arrow */ - wprintf(""); - - /** 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, - "", - &d_tm - ); - wprintf("%s", d_str); - - /** Right arrow */ - wprintf(""); - - wprintf("
"); - wprintf("", - yesterday.year, yesterday.month, yesterday.day); - wprintf(""); - wprintf("" - "%B
" - "%d
" - "%Y
" - "
"); - wprintf("", - tomorrow.year, tomorrow.month, tomorrow.day); - wprintf("\n"); - wprintf("
\n"); - /** End todays-date-with-left-and-right-arrows */ - - /** \todo In the future we might want to put a month-o-matic here */ - - wprintf("\n"); - - wprintf("
" /** end of inner table */ - "
" /** 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)
\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("
" - "\n\n" - "\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("\n"); - - due = get_task_due_date(WC->disp_cal[i].cal); - fmt_date(buf, due, 0); - wprintf("\n", buf); - } - - wprintf("
"); - wprintf(_("Name of task")); - wprintf(""); - wprintf(_("Date due")); - wprintf("
", - (bg ? "DDDDDD" : "FFFFFF") - ); - - p = icalcomponent_get_first_property(WC->disp_cal[i].cal, - ICAL_SUMMARY_PROPERTY); - wprintf("disp_cal[i].cal_msgnum ); - urlescputs(WC->wc_roomname); - wprintf("\">"); - wprintf(" "); - if (p != NULL) { - escputs((char *)icalproperty_get_comment(p)); - } - wprintf("\n"); - wprintf("%s
\n"); - - /** Free the list */ - free_calendar_buffer(); - -} - -#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ - -/** @} */ diff --git a/webcit/context_loop.c b/webcit/context_loop.c deleted file mode 100644 index e276383f7..000000000 --- a/webcit/context_loop.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup WebServerII some of the webserver stuff. - * This is the other half of the webserver. It handles the task of hooking - * up HTTP requests with the sessions they belong to, using HTTP cookies to - * keep track of things. If the HTTP request doesn't belong to any currently - * active session, a new session is started. - * \ingroup WebcitHttpServer - * - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" - -/** Only one thread may manipulate SessionList at a time... */ -pthread_mutex_t SessionListMutex; - -struct wcsession *SessionList = NULL; /**< our sessions ????*/ - -pthread_key_t MyConKey; /**< TSD key for MySession() */ - - -/** - * \brief free the memory used for viewing atachments - * \param sess the session object to destroy - */ -void free_attachments(struct wcsession *sess) { - struct wc_attachment *att; - - while (sess->first_attachment != NULL) { - att = sess->first_attachment; - sess->first_attachment = sess->first_attachment->next; - free(att->data); - free(att); - } -} - -/** - * \brief what?????? - */ -void do_housekeeping(void) -{ - struct wcsession *sptr, *ss; - struct wcsession *sessions_to_kill = NULL; - int num_sessions = 0; - static int num_threads = MIN_WORKER_THREADS; - - /** - * Lock the session list, moving any candidates for euthanasia into - * a separate list. - */ - pthread_mutex_lock(&SessionListMutex); - num_sessions = 0; - for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { - ++num_sessions; - - /** Kill idle sessions */ - if ((time(NULL) - (sptr->lastreq)) > - (time_t) WEBCIT_TIMEOUT) { - sptr->killthis = 1; - } - - /** Remove sessions flagged for kill */ - if (sptr->killthis) { - - /** remove session from linked list */ - if (sptr == SessionList) { - SessionList = SessionList->next; - } - else for (ss=SessionList;ss!=NULL;ss=ss->next) { - if (ss->next == sptr) { - ss->next = ss->next->next; - } - } - - sptr->next = sessions_to_kill; - sessions_to_kill = sptr; - } - } - pthread_mutex_unlock(&SessionListMutex); - - /** - * Now free up and destroy the culled sessions. - */ - while (sessions_to_kill != NULL) { - lprintf(3, "Destroying session %d\n", sessions_to_kill->wc_session); - pthread_mutex_lock(&sessions_to_kill->SessionMutex); - close(sessions_to_kill->serv_sock); - close(sessions_to_kill->chat_sock); - if (sessions_to_kill->preferences != NULL) { - free(sessions_to_kill->preferences); - } - if (sessions_to_kill->cache_fold != NULL) { - free(sessions_to_kill->cache_fold); - } - free_attachments(sessions_to_kill); - free_march_list(sessions_to_kill); - pthread_mutex_unlock(&sessions_to_kill->SessionMutex); - sptr = sessions_to_kill->next; - free(sessions_to_kill); - sessions_to_kill = sptr; - --num_sessions; - } - - /** - * If there are more sessions than threads, then we should spawn - * more threads ... up to a predefined maximum. - */ - while ( (num_sessions > num_threads) - && (num_threads <= MAX_WORKER_THREADS) ) { - spawn_another_worker_thread(); - ++num_threads; - lprintf(3, "There are %d sessions and %d threads active.\n", - num_sessions, num_threads); - } -} - - -/** - * \brief Wake up occasionally and clean house - */ -void housekeeping_loop(void) -{ - while (1) { - sleeeeeeeeeep(HOUSEKEEPING); - do_housekeeping(); - } -} - - -/** - * \brief Create a Session id - * Generate a unique WebCit session ID (which is not the same thing as the - * Citadel session ID). - * - * \todo FIXME ... ensure that session number is truly unique - * - */ -int GenerateSessionID(void) -{ - static int seq = (-1); - - if (seq < 0) { - seq = (int) time(NULL); - } - - return ++seq; -} - - -/** - * \brief Collapse multiple cookies on one line - * \param sock a socket? - * \param buf some bunch of chars? - * \param hold hold what? - */ -int req_gets(int sock, char *buf, char *hold) -{ - int a; - - if (strlen(hold) == 0) { - strcpy(buf, ""); - a = client_getln(sock, buf, SIZ); - if (a<1) return(-1); - } else { - safestrncpy(buf, hold, SIZ); - } - strcpy(hold, ""); - - if (!strncasecmp(buf, "Cookie: ", 8)) { - for (a = 0; a < strlen(buf); ++a) - if (buf[a] == ';') { - sprintf(hold, "Cookie: %s", &buf[a + 1]); - buf[a] = 0; - while (isspace(hold[8])) - strcpy(&hold[8], &hold[9]); - return(0); - } - } - - return(0); -} - -/** - * \brief close some fd for some reason??? - * \param fd the fd to close?????? - * lingering_close() a`la Apache. see - * http://www.apache.org/docs/misc/fin_wait_2.html for rationale - */ - -int lingering_close(int fd) -{ - char buf[SIZ]; - int i; - fd_set set; - struct timeval tv, start; - - gettimeofday(&start, NULL); - shutdown(fd, 1); - do { - do { - gettimeofday(&tv, NULL); - tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec); - tv.tv_usec = start.tv_usec - tv.tv_usec; - if (tv.tv_usec < 0) { - tv.tv_sec--; - tv.tv_usec += 1000000; - } - FD_ZERO(&set); - FD_SET(fd, &set); - i = select(fd + 1, &set, NULL, NULL, &tv); - } while (i == -1 && errno == EINTR); - - if (i <= 0) - break; - - i = read(fd, buf, sizeof buf); - } while (i != 0 && (i != -1 || errno == EINTR)); - - return close(fd); -} - - - -/** - * \brief sanity requests - * Check for bogus requests coming from brain-dead Windows boxes. - * - * \param http_cmd The HTTP request to check - */ -int is_bogus(char *http_cmd) { - char *url; - - url = strstr(http_cmd, " "); - if (url == NULL) return(1); - ++url; - - /** Worms and trojans and viruses, oh my! */ - if (!strncasecmp(url, "/scripts/root.exe", 17)) return(2); - if (!strncasecmp(url, "/c/winnt", 8)) return(2); - if (!strncasecmp(url, "/MSADC/", 7)) return(2); - - /** Broken Microsoft DAV implementation */ - if (!strncasecmp(url, "/_vti", 5)) return(3); - - return(0); /* probably ok */ -} - - - -/** - * \brief handle one request - * This loop gets called once for every HTTP connection made to WebCit. At - * this entry point we have an HTTP socket with a browser allegedly on the - * other end, but we have not yet bound to a WebCit session. - * - * The job of this function is to locate the correct session and bind to it, - * or create a session if necessary and bind to it, then run the WebCit - * transaction loop. Afterwards, we unbind from the session. When this - * function returns, the worker thread is then free to handle another - * transaction. - * \param sock the socket we will put our answer to - */ -void context_loop(int sock) -{ - struct httprequest *req = NULL; - struct httprequest *last = NULL; - struct httprequest *hptr; - char buf[SIZ], hold[SIZ]; - int desired_session = 0; - int got_cookie = 0; - int gzip_ok = 0; - struct wcsession *TheSession, *sptr; - char httpauth_string[1024]; - char httpauth_user[1024]; - char httpauth_pass[1024]; - char accept_language[256]; - char *ptr = NULL; - int session_is_new = 0; - - strcpy(httpauth_string, ""); - strcpy(httpauth_user, DEFAULT_HTTPAUTH_USER); - strcpy(httpauth_pass, DEFAULT_HTTPAUTH_PASS); - - /** - * Find out what it is that the web browser is asking for - */ - memset(hold, 0, sizeof(hold)); - do { - if (req_gets(sock, buf, hold) < 0) return; - - /** - * Can we compress? - */ - if (!strncasecmp(buf, "Accept-encoding:", 16)) { - if (strstr(&buf[16], "gzip")) { - gzip_ok = 1; - } - } - - /** - * Browser-based sessions use cookies for session authentication - */ - if (!strncasecmp(buf, "Cookie: webcit=", 15)) { - cookie_to_stuff(&buf[15], &desired_session, - NULL, 0, NULL, 0, NULL, 0); - got_cookie = 1; - } - - /** - * GroupDAV-based sessions use HTTP authentication - */ - if (!strncasecmp(buf, "Authorization: Basic ", 21)) { - CtdlDecodeBase64(httpauth_string, &buf[21], strlen(&buf[21])); - extract_token(httpauth_user, httpauth_string, 0, ':', sizeof httpauth_user); - extract_token(httpauth_pass, httpauth_string, 1, ':', sizeof httpauth_pass); - } - - if (!strncasecmp(buf, "If-Modified-Since: ", 19)) { - if_modified_since = httpdate_to_timestamp(&buf[19]); - } - - if (!strncasecmp(buf, "Accept-Language: ", 17)) { - safestrncpy(accept_language, &buf[17], sizeof accept_language); - } - - /** - * Read in the request - */ - hptr = (struct httprequest *) - malloc(sizeof(struct httprequest)); - if (req == NULL) - req = hptr; - else - last->next = hptr; - hptr->next = NULL; - last = hptr; - - safestrncpy(hptr->line, buf, sizeof hptr->line); - - } while (strlen(buf) > 0); - - /** - * If the request is prefixed by "/webcit" then chop that off. This - * allows a front end web server to forward all /webcit requests to us - * while still using the same web server port for other things. - */ - - ptr = strstr(req->line, " /webcit "); /*< Handle "/webcit" */ - if (ptr != NULL) { - strcpy(ptr+2, ptr+8); - } - - ptr = strstr(req->line, " /webcit"); /*< Handle "/webcit/" */ - if (ptr != NULL) { - strcpy(ptr+1, ptr+8); - } - - /** Begin parsing the request. */ - - safestrncpy(buf, req->line, sizeof buf); - lprintf(5, "HTTP: %s\n", buf); - - /** Check for bogus requests */ - if (is_bogus(buf)) { - strcpy(req->line, "GET /404 HTTP/1.1"); - strcpy(buf, "GET /404 HTTP/1.1"); - } - - /** - * Strip out the method, leaving the URL up front... - */ - remove_token(buf, 0, ' '); - if (buf[1]==' ') buf[1]=0; - - /** - * While we're at it, gracefully handle requests for the - * robots.txt and favicon.ico files. - */ - if (!strncasecmp(buf, "/robots.txt", 11)) { - strcpy(req->line, "GET /static/robots.txt" - "?force_close_session=yes HTTP/1.1"); - } - else if (!strncasecmp(buf, "/favicon.ico", 12)) { - strcpy(req->line, "GET /static/favicon.ico"); - } - - /** - * These are the URL's which may be executed without a - * session cookie already set. If it's not one of these, - * force the session to close because cookies are - * probably disabled on the client browser. - */ - else if ( (strcmp(buf, "/")) - && (strncasecmp(buf, "/listsub", 8)) - && (strncasecmp(buf, "/freebusy", 9)) - && (strncasecmp(buf, "/do_logout", 10)) - && (strncasecmp(buf, "/groupdav", 9)) - && (strncasecmp(buf, "/static", 7)) - && (strncasecmp(buf, "/rss", 4)) - && (strncasecmp(buf, "/404", 4)) - && (got_cookie == 0)) { - strcpy(req->line, "GET /static/nocookies.html" - "?force_close_session=yes HTTP/1.1"); - } - - /** - * See if there's an existing session open with the desired ID or user/pass - */ - TheSession = NULL; - - if (TheSession == NULL) { - pthread_mutex_lock(&SessionListMutex); - for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { - - /** If HTTP-AUTH, look for a session with matching credentials */ - if ( (strlen(httpauth_user) > 0) - &&(!strcasecmp(sptr->httpauth_user, httpauth_user)) - &&(!strcasecmp(sptr->httpauth_pass, httpauth_pass)) ) { - TheSession = sptr; - } - - /** If cookie-session, look for a session with matching session ID */ - if ( (desired_session != 0) && (sptr->wc_session == desired_session)) { - TheSession = sptr; - } - - } - pthread_mutex_unlock(&SessionListMutex); - } - - /** - * Create a new session if we have to - */ - if (TheSession == NULL) { - lprintf(3, "Creating a new session\n"); - TheSession = (struct wcsession *) - malloc(sizeof(struct wcsession)); - memset(TheSession, 0, sizeof(struct wcsession)); - TheSession->serv_sock = (-1); - TheSession->chat_sock = (-1); - TheSession->wc_session = GenerateSessionID(); - strcpy(TheSession->httpauth_user, httpauth_user); - strcpy(TheSession->httpauth_pass, httpauth_pass); - pthread_mutex_init(&TheSession->SessionMutex, NULL); - pthread_mutex_lock(&SessionListMutex); - TheSession->next = SessionList; - SessionList = TheSession; - pthread_mutex_unlock(&SessionListMutex); - session_is_new = 1; - } - - /** - * A future improvement might be to check the session integrity - * at this point before continuing. - */ - - /** - * Bind to the session and perform the transaction - */ - pthread_mutex_lock(&TheSession->SessionMutex); /*< bind */ - pthread_setspecific(MyConKey, (void *)TheSession); - TheSession->http_sock = sock; - TheSession->lastreq = time(NULL); /*< log */ - TheSession->gzip_ok = gzip_ok; -#ifdef ENABLE_NLS - if (session_is_new) { - httplang_to_locale(accept_language); - } - go_selected_language(); /*< set locale */ -#endif - session_loop(req); /*< do transaction */ -#ifdef ENABLE_NLS - stop_selected_language(); /*< unset locale */ -#endif - pthread_mutex_unlock(&TheSession->SessionMutex); /*< unbind */ - - /** Free the request buffer */ - while (req != NULL) { - hptr = req->next; - free(req); - req = hptr; - } - - /** - * Free up any session-local substitution variables which - * were set during this transaction - */ - clear_local_substs(); -} -/*@}*/ diff --git a/webcit/cookie_conversion.c b/webcit/cookie_conversion.c deleted file mode 100644 index 24e29ce74..000000000 --- a/webcit/cookie_conversion.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup CookieConversion Grep Cookies - * Utility functions which convert the HTTP cookie format we use to and - * from user/password/room strings. - * - * \ingroup WebcitHttpServer - */ -/*@{*/ -#include "webcit.h" - - -#define TRUE 1 /**< for sure? */ -#define FALSE 0 /**< nope. */ - -typedef unsigned char byte; /**< Byte type */ - -/** - * \brief find cookie - * Pack all session info into one easy-to-digest cookie. Healthy and delicious! - * \param cookie cookie string to create??? - * \param session the session we want to convert into a cookie - * \param user the user to be associated with the cookie - * \param pass his passphrase - * \param room the room he wants to enter - */ -void stuff_to_cookie(char *cookie, int session, - char *user, char *pass, char *room) -{ - char buf[SIZ]; - int i; - - sprintf(buf, "%d|%s|%s|%s|", session, user, pass, room); - strcpy(cookie, ""); - for (i=0; i 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; ireq_info->subject); - X509_set_subject_name(cer, req->req_info->subject); - X509_gmtime_adj(X509_get_notBefore(cer), 0); - X509_gmtime_adj(X509_get_notAfter(cer),(long)60*60*24*SIGN_DAYS); - - req_pkey = X509_REQ_get_pubkey(req); - X509_set_pubkey(cer, req_pkey); - EVP_PKEY_free(req_pkey); - - /** Sign the cert */ - if (!X509_sign(cer, pk, EVP_md5())) { - lprintf(3, "X509_sign(): error\n"); - } - else { - /** Write it to disk. */ - fp = fopen(CTDL_CER_PATH, "w"); - if (fp != NULL) { - chmod(CTDL_CER_PATH, 0600); - PEM_write_X509(fp, cer); - fclose(fp); - } - } - X509_free(cer); - } - } - - RSA_free(rsa); - } - } - - /** - * Now try to bind to the key and certificate. - * Note that we use SSL_CTX_use_certificate_chain_file() which allows - * the certificate file to contain intermediate certificates. - */ - SSL_CTX_use_certificate_chain_file(ssl_ctx, CTDL_CER_PATH); - SSL_CTX_use_PrivateKey_file(ssl_ctx, CTDL_KEY_PATH, SSL_FILETYPE_PEM); - if ( !SSL_CTX_check_private_key(ssl_ctx) ) { - lprintf(3, "Cannot install certificate: %s\n", - ERR_reason_error_string(ERR_get_error())); - } - -} - - -/** - * \brief starts SSL/TLS encryption for the current session. - * \param sock the socket connection - * \return Zero if the SSL/TLS handshake succeeded, non-zero otherwise. - */ -int starttls(int sock) { - int retval, bits, alg_bits; - SSL *newssl; - - pthread_setspecific(ThreadSSL, NULL); - - if (!ssl_ctx) { - return(1); - } - if (!(newssl = SSL_new(ssl_ctx))) { - lprintf(3, "SSL_new failed: %s\n", - ERR_reason_error_string(ERR_get_error())); - return(2); - } - if (!(SSL_set_fd(newssl, sock))) { - lprintf(3, "SSL_set_fd failed: %s\n", - ERR_reason_error_string(ERR_get_error())); - SSL_free(newssl); - return(3); - } - retval = SSL_accept(newssl); - if (retval < 1) { - /** - * Can't notify the client of an error here; they will - * discover the problem at the SSL layer and should - * revert to unencrypted communications. - */ - long errval; - - errval = SSL_get_error(newssl, retval); - lprintf(3, "SSL_accept failed: %s\n", - ERR_reason_error_string(ERR_get_error())); - SSL_free(newssl); - newssl = NULL; - return(4); - } - BIO_set_close(newssl->rbio, BIO_NOCLOSE); - bits = - SSL_CIPHER_get_bits(SSL_get_current_cipher(newssl), - &alg_bits); - lprintf(5, "SSL/TLS using %s on %s (%d of %d bits)\n", - SSL_CIPHER_get_name(SSL_get_current_cipher(newssl)), - SSL_CIPHER_get_version(SSL_get_current_cipher(newssl)), - bits, alg_bits); - - pthread_setspecific(ThreadSSL, newssl); - return(0); -} - - - -/** - * \brief shuts down the TLS connection - * - * WARNING: This may make your session vulnerable to a known plaintext - * attack in the current implmentation. - */ -void endtls(void) -{ - if (THREADSSL == NULL) return; - - lprintf(5, "Ending SSL/TLS\n"); - SSL_shutdown(THREADSSL); - SSL_free(THREADSSL); - pthread_setspecific(ThreadSSL, NULL); -} - - -/** - * \brief callback for OpenSSL mutex locks - * \param mode which mode?????? - * \param n how many??? - * \param file which filename ??? - * \param line what line???? - */ -void ssl_lock(int mode, int n, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) - pthread_mutex_lock(SSLCritters[n]); - else - pthread_mutex_unlock(SSLCritters[n]); -} - -/** - * \brief Send binary data to the client encrypted. - * \param buf chars to send to the client - * \param nbytes how many chars - */ -void client_write_ssl(char *buf, int nbytes) -{ - int retval; - int nremain; - char junk[1]; - - if (THREADSSL == NULL) return; - - nremain = nbytes; - - while (nremain > 0) { - if (SSL_want_write(THREADSSL)) { - if ((SSL_read(THREADSSL, junk, 0)) < 1) { - lprintf(9, "SSL_read in client_write: %s\n", - ERR_reason_error_string(ERR_get_error())); - } - } - retval = SSL_write(THREADSSL, &buf[nbytes - nremain], nremain); - if (retval < 1) { - long errval; - - errval = SSL_get_error(THREADSSL, retval); - if (errval == SSL_ERROR_WANT_READ || - errval == SSL_ERROR_WANT_WRITE) { - sleep(1); - continue; - } - lprintf(9, "SSL_write got error %ld, ret %d\n", errval, retval); - if (retval == -1) { - lprintf(9, "errno is %d\n", errno); - } - endtls(); - return; - } - nremain -= retval; - } -} - - -/** - * \brief read data from the encrypted layer. - * \param buf charbuffer to read to - * \param bytes how many - * \param timeout how long should we wait? - * \returns what??? - */ -int client_read_ssl(char *buf, int bytes, int timeout) -{ -#if 0 - fd_set rfds; - struct timeval tv; - int retval; - int s; -#endif - int len, rlen; - char junk[1]; - - if (THREADSSL == NULL) return(0); - - len = 0; - while (len < bytes) { -#if 0 - /** - * This code is disabled because we don't need it when - * using blocking reads (which we are). -IO - */ - FD_ZERO(&rfds); - s = BIO_get_fd(THREADSSL->rbio, NULL); - FD_SET(s, &rfds); - tv.tv_sec = timeout; - tv.tv_usec = 0; - - retval = select(s + 1, &rfds, NULL, NULL, &tv); - - if (FD_ISSET(s, &rfds) == 0) { - return (0); - } - -#endif - if (SSL_want_read(THREADSSL)) { - if ((SSL_write(THREADSSL, junk, 0)) < 1) { - lprintf(9, "SSL_write in client_read: %s\n", ERR_reason_error_string(ERR_get_error())); - } - } - rlen = SSL_read(THREADSSL, &buf[len], bytes - len); - if (rlen < 1) { - long errval; - - errval = SSL_get_error(THREADSSL, rlen); - if (errval == SSL_ERROR_WANT_READ || - errval == SSL_ERROR_WANT_WRITE) { - sleep(1); - continue; - } - lprintf(9, "SSL_read got error %ld\n", errval); - endtls(); - return (0); - } - len += rlen; - } - return (1); -} - - -#endif /* HAVE_OPENSSL */ -/*@}*/ diff --git a/webcit/doxygen_groups.c b/webcit/doxygen_groups.c deleted file mode 100644 index 3fed3e629..000000000 --- a/webcit/doxygen_groups.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * ok, hacky, but gets us nice groups. so we define sub parts to join from other - * files here. NO CODE IN HERE! This is comment shouldn't appear in doxygen. - * we have: - * CitadelConfig; WebcitDisplayItems; WebcitHttpServer; WebcitHttpServerGDav; - * ClientPower; Calendaring; MenuInfrastructure; CitadelCommunitacion; - * VCards - * WebcitHttpServerRSS; tools; - */ - - -/** - * \defgroup CitadelConfig Configuration Mechanisms - * \brief Functions about configuring citadel / webcit - */ - -/*@{*/ -/*@}*/ - -/** - * \defgroup tools Utility Functions - * \brief Functions that aren't related to webcit topics - */ - -/*@{*/ -/*@}*/ - - -/** - * \defgroup WebcitDisplayItems Display some mime types through webcit - * \brief Functions that format mime types into HTML to the user - */ - -/*@{*/ -/*@}*/ - -/** - * \defgroup WebcitHttpServer the Webserver part - * \brief Functions that run the HTTP-Deamon - */ - -/*@{*/ -/*@}*/ - -/** - * \defgroup WebcitHttpServerGDav Groupdav Mechanisms - * \ingroup WebcitHttpServer - * \brief Functions that handle groupdav requests - */ -/*@{*/ -/*@}*/ - - -/** - * \defgroup WebcitHttpServerRSS RSS Mechanisms - * \ingroup WebcitHttpServer - * \brief Functions that handle RSS requests - */ - -/*@{*/ -/*@}*/ - -/** - * \defgroup ClientPower Client powered Functionality - * \brief Functions that spawn things on the webbrowser - */ - -/*@{*/ -/*@}*/ - -/** - * \defgroup Calendaring Calendaring background - * \brief Functions that make the Business-logic of the calendaring items - * \ingroup WebcitDisplayItems - */ - -/*@{*/ -/*@}*/ - -/** - * \defgroup VCards showing / editing VCards - * \brief Functions that make the Business-logic of the vcard stuff - * \ingroup WebcitDisplayItems - */ - -/*@{*/ -/*@}*/ - -/** - * \defgroup MenuInfrastructure Things that guide you through the webcit parts - * \brief Functions that display menues, trees etc. to connect the parts of the - * ui to a whole thing - * \ingroup WebcitDisplayItems - */ - -/*@{*/ -/*@}*/ - -/** - * \defgroup CitadelCommunitacion Talk to the citadel server - * \brief Functions that talk to the citadel server and process reviewed entities - * \ingroup WebcitDisplayItems - */ - -/*@{*/ -/*@}*/ - - - diff --git a/webcit/event.c b/webcit/event.c deleted file mode 100644 index 94fac68db..000000000 --- a/webcit/event.c +++ /dev/null @@ -1,721 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup EditCal Editing calendar events. - * \ingroup Calendaring - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" - - -#ifdef WEBCIT_WITH_CALENDAR_SERVICE - -/** - * \brief Display an event by itself (for editing) - * \param supplied_vevent the event to edit - * \param msgnum reference on the citserver - */ -void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum) { - icalcomponent *vevent; - icalproperty *p; - icalvalue *v; - struct icaltimetype t_start, t_end; - time_t now; - struct tm tm_now; - int created_new_vevent = 0; - icalproperty *organizer = NULL; - char organizer_string[SIZ]; - icalproperty *attendee = NULL; - char attendee_string[SIZ]; - char buf[SIZ]; - int organizer_is_me = 0; - int i; - int sequence = 0; - - now = time(NULL); - strcpy(organizer_string, ""); - strcpy(attendee_string, ""); - - if (supplied_vevent != NULL) { - vevent = supplied_vevent; - /** - * If we're looking at a fully encapsulated VCALENDAR - * rather than a VEVENT component, attempt to use the first - * relevant VEVENT subcomponent. If there is none, the - * NULL returned by icalcomponent_get_first_component() will - * tell the next iteration of this function to create a - * new one. - */ - if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) { - display_edit_individual_event( - icalcomponent_get_first_component( - vevent, ICAL_VEVENT_COMPONENT - ), msgnum - ); - return; - } - } - else { - vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT); - created_new_vevent = 1; - } - - /** Learn the sequence */ - p = icalcomponent_get_first_property(vevent, ICAL_SEQUENCE_PROPERTY); - if (p != NULL) { - sequence = icalproperty_get_sequence(p); - } - - /** Begin output */ - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n" - "
" - ""); - wprintf(_("Add or edit an event")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("\n" - ); - - - wprintf("
" - "
\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("
\n"); - wprintf("SEQUENCE == %d
\n", sequence); - *************************************************************/ - - wprintf("
\n"); - - wprintf("\n", - msgnum); - wprintf("\n", - bstr("calview")); - wprintf("\n", - bstr("year")); - wprintf("\n", - bstr("month")); - wprintf("\n", - bstr("day")); - - /** Put it in a borderless table so it lines up nicely */ - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("\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("\n"); - - wprintf(""); - - /** - * 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("\n"); - - /** Transparency */ - wprintf("\n"); - - /** Attendees */ - wprintf("\n"); - - /** Done with properties. */ - wprintf("
"); - wprintf(_("Summary")); - wprintf("\n" - "
"); - wprintf(_("Location")); - wprintf("\n" - "
"); - wprintf(_("Start")); - wprintf("\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("%s", - (t_start.is_date ? "CHECKED" : "" ), - _("All day event") - ); - - wprintf("
"); - wprintf(_("End")); - wprintf("\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("
"); - wprintf(_("Notes")); - wprintf("\n" - "
"); - wprintf(_("Organizer")); - wprintf(""); - escputs(organizer_string); - if (organizer_is_me) { - wprintf(" "); - wprintf(_("(you are the organizer)")); - wprintf("\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(""); - - wprintf("
"); - wprintf(_("Show time as:")); - wprintf(""); - - 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(""); - wprintf(_("Free")); - wprintf("  "); - - wprintf(""); - wprintf(_("Busy")); - - wprintf("
"); - wprintf(_("Attendees")); - wprintf("
" - ""); - wprintf(_("(One per line)")); - wprintf("
" - "
\n
" - "" - "  " - "\n" - "  " - "\n" - "  " - "\n" - "
\n", - _("Save"), - _("Delete"), - _("Check attendee availability"), - _("Cancel") - ); - - wprintf("
\n"); - - wprintf("
\n"); - wprintf("\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 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 0) ) { - serv_puts("ENT0 1|||4|||1|"); - serv_getln(buf, sizeof buf); - if (buf[0] == '8') { - serv_puts("Content-type: text/calendar"); - serv_puts(""); - serv_puts(icalcomponent_as_ical_string(encaps)); - serv_puts("000"); - } - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - lprintf(9, "ENT0 REPLY: %s\n", buf); - } - icalcomponent_free(encaps); - } - - /** Or, check attendee availability if the user asked for that. */ - if ( (encaps != NULL) && (strlen(bstr("check_button")) > 0) ) { - - /** Call this function, which does the real work */ - check_attendee_availability(encaps); - - /** This displays the form again, with our annotations */ - display_edit_individual_event(encaps, msgnum); - - icalcomponent_free(encaps); - } - - } - - /** - * If the user clicked 'Delete' then delete it. - */ - lprintf(9, "Checking to see if we have to delete an old event\n"); - if ( (strlen(bstr("delete_button")) > 0) && (msgnum > 0L) ) { - serv_printf("DELE %ld", atol(bstr("msgnum"))); - serv_getln(buf, sizeof buf); - } - - if (created_new_vevent) { - icalcomponent_free(vevent); - } - - /** If this was a save or deelete, go back to the calendar view. */ - if (strlen(bstr("check_button")) == 0) { - readloop("readfwd"); - } -} - - -#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ - -/*@}*/ diff --git a/webcit/floors.c b/webcit/floors.c deleted file mode 100644 index d91928540..000000000 --- a/webcit/floors.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup AdminFloor Administrative screens for floor maintenance - * \ingroup CitadelConfig - */ -/*@{*/ - -#include "webcit.h" -#include "webserver.h" - - - - -/** - * \brief Display floor config - * Display floor configuration. If prepend_html is not NULL, its contents - * will be displayed at the top of the screen. - * \param prepend_html pagetitle to prepend - */ -void display_floorconfig(char *prepend_html) -{ - char buf[SIZ]; - - int floornum; - char floorname[SIZ]; - int refcount; - - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n" - "
" - ""); - wprintf(_("Add/change/delete floors")); - wprintf("" - "
\n" - "
\n
\n" - ); - - if (prepend_html != NULL) { - wprintf("
"); - client_write(prepend_html, strlen(prepend_html)); - wprintf("

\n"); - } - - serv_printf("LFLR"); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - wprintf("
"); - wprintf(""); - wprintf(_("Error")); - wprintf("\n"); - wprintf("
\n"); - wprintf("%s
\n", &buf[4]); - wDumpContent(1); - return; - } - - wprintf("
" - "\n" - "\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(""); - - wprintf("", _("Change name")); - - wprintf("\n", refcount); - - wprintf("", _("Change CSS")); - - wprintf("\n"); - } - - wprintf("" - "" - "\n", _("Create new floor")); - - wprintf("
"); - wprintf(_("Floor number")); - wprintf(""); - wprintf(_("Floor name")); - wprintf(""); - wprintf(_("Number of rooms")); - wprintf(""); - wprintf(_("Floor CSS")); - wprintf("
%d", floornum); - if (refcount == 0) { - wprintf("" - "" - "", floornum); - wprintf(_("(delete floor)")); - wprintf("
"); - } - wprintf("" - "", floornum); - wprintf(_("(edit graphic)")); - wprintf("
"); - wprintf("
" - "
" - "" - "\n", - floornum, floorname); - wprintf("" - "
%d" - "
" - "" - "\n", - floornum, floorname); - wprintf("" - "
 
" - "\n" - "" - "
 
\n"); - wDumpContent(1); -} - - -/** - * \brief delete the actual floor - */ -void delete_floor(void) { - int floornum; - char buf[SIZ]; - char message[SIZ]; - - floornum = atoi(bstr("floornum")); - - serv_printf("KFLR %d|1", floornum); - serv_getln(buf, sizeof buf); - - if (buf[0] == '2') { - sprintf(message, _("Floor has been deleted.")); - } - else { - sprintf(message, "%s", &buf[4]); - } - - display_floorconfig(message); -} - -/** - * \brief tart creating a new floor - */ -void create_floor(void) { - char buf[SIZ]; - char message[SIZ]; - char floorname[SIZ]; - - strcpy(floorname, bstr("floorname")); - - serv_printf("CFLR %s|1", floorname); - serv_getln(buf, sizeof buf); - - if (buf[0] == '2') { - sprintf(message, _("New floor has been created.")); - } else { - sprintf(message, "%s", &buf[4]); - } - - display_floorconfig(message); -} - -/** - * \brief rename this floor - */ -void rename_floor(void) { - int floornum; - char buf[SIZ]; - char message[SIZ]; - char floorname[SIZ]; - - floornum = atoi(bstr("floornum")); - strcpy(floorname, bstr("floorname")); - - serv_printf("EFLR %d|%s", floornum, floorname); - serv_getln(buf, sizeof buf); - - sprintf(message, "%s", &buf[4]); - - display_floorconfig(message); -} - - -/*@}*/ diff --git a/webcit/fmt_date.c b/webcit/fmt_date.c deleted file mode 100644 index 2c7c9f6fd..000000000 --- a/webcit/fmt_date.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup FormatDates Miscellaneous routines formating dates - * \ingroup Calendaring - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" - -typedef unsigned char byte; /**< a byte. */ - -#define FALSE 0 /**< no. */ -#define TRUE 1 /**< yes. */ - -/** - * \brief Wrapper around strftime() or strftime_l() - * depending upon how our build is configured. - * - * \param s String target buffer - * \param max Maximum size of string target buffer - * \param format strftime() format - * \param tm Input date/time - */ -size_t wc_strftime(char *s, size_t max, const char *format, const struct tm *tm) -{ -#ifdef ENABLE_NLS - if (wc_locales[WC->selected_language] == NULL) { - return strftime(s, max, format, tm); - } - else { - return strftime_l(s, max, format, tm, wc_locales[WC->selected_language]); - } -#else - return strftime(s, max, format, tm); -#endif -} - - -/** - * \brief Format a date/time stamp for output - * \param buf the output buffer - * \param thetime time to convert to string - * \param brief do we want compact view????? - */ -void fmt_date(char *buf, time_t thetime, int brief) -{ - struct tm tm; - struct tm today_tm; - time_t today_timet; - int hour; - char calhourformat[16]; - - get_preference("calhourformat", calhourformat, sizeof calhourformat); - - today_timet = time(NULL); - localtime_r(&today_timet, &today_tm); - - localtime_r(&thetime, &tm); - hour = tm.tm_hour; - if (hour == 0) - hour = 12; - else if (hour > 12) - hour = hour - 12; - - buf[0] = 0; - - if (brief) { - - /** If date == today, show only the time */ - if ((tm.tm_year == today_tm.tm_year) - &&(tm.tm_mon == today_tm.tm_mon) - &&(tm.tm_mday == today_tm.tm_mday)) { - wc_strftime(buf, 32, "%l:%M%p", &tm); - } - /** Otherwise, for messages up to 6 months old, show the - * month and day, and the time */ - else if (today_timet - thetime < 15552000) { - wc_strftime(buf, 32, "%b %d %l:%M%p", &tm); - } - /** older than 6 months, show only the date */ - else { - wc_strftime(buf, 32, "%b %d %Y", &tm); - } - } - else { - wc_strftime(buf, 32, "%c", &tm); - } -} - - -/** - * \brief Format TIME ONLY for output - * \param buf the output buffer - * \param thetime time to format into buf - */ -void fmt_time(char *buf, time_t thetime) -{ - struct tm *tm; - int hour; - char calhourformat[16]; - - get_preference("calhourformat", calhourformat, sizeof calhourformat); - - buf[0] = 0; - tm = localtime(&thetime); - hour = tm->tm_hour; - if (hour == 0) - hour = 12; - else if (hour > 12) - hour = hour - 12; - - if (!strcasecmp(calhourformat, "24")) { - sprintf(buf, "%2d:%02d", - tm->tm_hour, tm->tm_min - ); - } - else { - sprintf(buf, "%d:%02d%s", - hour, tm->tm_min, ((tm->tm_hour > 12) ? "pm" : "am") - ); - } -} - - - - -/** - * \brief Break down the timestamp used in HTTP headers - * Should read rfc1123 and rfc850 dates OK - * \todo FIXME won't read asctime - * Doesn't understand timezone, but we only should be using GMT/UTC anyway - * \param buf time to parse - * \return the time found in buf - */ -time_t httpdate_to_timestamp(char *buf) -{ - time_t t = 0; - struct tm tt; - char *c; - char tz[256]; - - /** Skip day of week, to number */ - for (c = buf; *c != ' '; c++) - ; - c++; - - /* Get day of month */ - tt.tm_mday = atoi(c); - for (; *c != ' ' && *c != '-'; c++); - c++; - - /** Get month */ - switch (*c) { - case 'A': /** April, August */ - tt.tm_mon = (c[1] == 'p') ? 3 : 7; - break; - case 'D': /** December */ - tt.tm_mon = 11; - break; - case 'F': /** February */ - tt.tm_mon = 1; - break; - case 'M': /** March, May */ - tt.tm_mon = (c[2] == 'r') ? 2 : 4; - break; - case 'J': /** January, June, July */ - tt.tm_mon = (c[2] == 'n') ? ((c[1] == 'a') ? 0 : 5) : 6; - break; - case 'N': /** November */ - tt.tm_mon = 10; - break; - case 'O': /** October */ - tt.tm_mon = 9; - break; - case 'S': /** September */ - tt.tm_mon = 8; - break; - default: - return 42; - break; /** NOTREACHED */ - } - c += 4; - - tt.tm_year = 0; - /** Get year */ - tt.tm_year = atoi(c); - for (; *c != ' '; c++); - c++; - if (tt.tm_year >= 1900) - tt.tm_year -= 1900; - - /** Get hour */ - tt.tm_hour = atoi(c); - for (; *c != ':'; c++); - c++; - - /** Get minute */ - tt.tm_min = atoi(c); - for (; *c != ':'; c++); - c++; - - /** Get second */ - tt.tm_sec = atoi(c); - for (; *c && *c != ' '; c++); - - /** Got everything; let's go */ - /** First, change to UTC */ - if (getenv("TZ")) - sprintf(tz, "TZ=%s", getenv("TZ")); - else - strcpy(tz, "TZ="); - putenv("TZ=UTC"); - tzset(); - t = mktime(&tt); - putenv(tz); - tzset(); - return t; -} - - - - -/*@}*/ diff --git a/webcit/gettext.c b/webcit/gettext.c deleted file mode 100644 index ddee2f873..000000000 --- a/webcit/gettext.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * $Id - */ -/** - * \defgroup LocaleHeaderParser Parse the browser http locale headers and set the NLS stuff. - * \ingroup WebcitHttpServer - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" - -#ifdef ENABLE_NLS - -#define NUM_LANGS 5 /**< how many different locales do we know? */ -#define SEARCH_LANG 20 /**< how many langs should we parse? */ - -/** actual supported locales */ -char *AvailLang[NUM_LANGS] = { - "C", - "en_US", - "de_DE", - "it_IT", - "en_GB" -}; - -locale_t wc_locales[NUM_LANGS]; /**< here we keep the parsed stuff */ - -/** Keep information about one locale */ -typedef struct _lang_pref{ - char lang[16]; /**< the language locale string */ - char region[16]; /**< the region locale string */ - long priority; /**< which priority does it have */ - int availability; /**< do we know it? */ - int selectedlang; /**< is this the selected language? */ -} LangStruct; - -/* \brief parse browser locale header - * seems as most browsers just do a one after coma value even if more than 10 locales are available. Sample strings: - * opera: - * Accept-Language: sq;q=1.0,de;q=0.9,as;q=0.8,ar;q=0.7,bn;q=0.6,zh-cn;q=0.5,kn;q=0.4,ch;q=0.3,fo;q=0.2,gn;q=0.1,ce;q=0.1,ie;q=0.1 - * Firefox - * Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' - * Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 - * Accept-Language: de,en-us;q=0.9,it;q=0.9,de-de;q=0.8,en-ph;q=0.7,de-at;q=0.7,zh-cn;q=0.6,cy;q=0.5,ar-om;q=0.5,en-tt;q=0.4,xh;q=0.3,nl-be;q=0.3,cs;q=0.2,sv;q=0.1,tk;q=0.1 - * \param LocaleString the string from the browser http headers - */ - -void httplang_to_locale(char *LocaleString) -{ - LangStruct wanted_locales[SEARCH_LANG]; - LangStruct *ls; - - int i = 0; - int j = 0; - size_t len = strlen(LocaleString); - long prio; - int av; - int nBest; - int nParts; - char *search = (char *) malloc(len); - - memcpy(search, LocaleString, len); - search[len] = '\0'; - nParts=num_tokens(search,','); - for (i=0; ((ipriority=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; jregion[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; jlang[0], AvailLang[j]); - if ((result<0)&&(resultavailability)){ - 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; ((iavailability<=0)&& - (avavailability)&& - (priopriority)&& - (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\n"); - - for (i=0; i < NUM_LANGS; ++i) { - wprintf("\n", - ((WC->selected_language == i) ? "selected" : ""), - AvailLang[i], - AvailLang[i] - ); - } - - wprintf("\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; iselected_language = i; - } - } -} - -/** - * \brief Activate the selected language for this session. - */ -void go_selected_language(void) { - if (WC->selected_language < 0) return; - uselocale(wc_locales[WC->selected_language]); /** switch locales */ - textdomain(textdomain(NULL)); /** clear the cache */ -} - -/** - * \brief Deactivate the selected language for this session. - */ -void stop_selected_language(void) { - uselocale(LC_GLOBAL_LOCALE); /** switch locales */ - textdomain(textdomain(NULL)); /** clear the cache */ -} - - -/** - * \brief Create a locale_t for each available language - */ -void initialize_locales(void) { - int i; - locale_t Empty_Locale; - char buf[32]; - - /* create default locale */ - Empty_Locale = newlocale(LC_ALL_MASK, NULL, NULL); - - for (i = 0; i < NUM_LANGS; ++i) { - if (i == 0) { - sprintf(buf, "%s", AvailLang[i]); // locale 0 (C) is ascii, not utf-8 - } - else { - sprintf(buf, "%s.UTF8", AvailLang[i]); - } - wc_locales[i] = newlocale( - (LC_MESSAGES_MASK|LC_TIME_MASK), - buf, - (((i > 0) && (wc_locales[0] != NULL)) ? wc_locales[0] : Empty_Locale) - ); - if (wc_locales[i] == NULL) { - lprintf(1, "Error configuring locale for %s: %s\n", - buf, - strerror(errno) - ); - } - else { - lprintf(3, "Configured available locale: %s\n", buf); - } - } -} - - -#else /* ENABLE_NLS */ -/** \brief dummy for non NLS enabled systems */ -void offer_languages(void) { - wprintf("English (US)"); -} - -/** \brief dummy for non NLS enabled systems */ -void set_selected_language(char *lang) { -} - -/** \brief dummy for non NLS enabled systems */ -void go_selected_language(void) { -} - -/** \brief dummy for non NLS enabled systems */ -void stop_selected_language(void) { -} - -#endif /* ENABLE_NLS */ - - -/*@}*/ diff --git a/webcit/graphics.c b/webcit/graphics.c deleted file mode 100644 index 00f256b98..000000000 --- a/webcit/graphics.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * $Id$ - * - * Handles HTTP upload of graphics files into the system. - * \ingroup WebcitHttpServer - */ - -#include "webcit.h" - -void display_graphics_upload(char *description, char *check_cmd, char *uplurl) -{ - char buf[SIZ]; - - serv_puts(check_cmd); - serv_getln(buf, sizeof buf); - if (buf[0] != '2') { - strcpy(WC->ImportantMessage, &buf[4]); - display_main_menu(); - return; - } - output_headers(1, 1, 0, 0, 0, 0); - - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n" - "
" - ""); - wprintf(_("Image upload")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("
" - "
\n"); - - wprintf("
\n"); - - wprintf("
\n", uplurl); - - 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("

\n"); - - wprintf(_("Please select a file to upload:")); - wprintf("

\n"); - wprintf("\n"); - wprintf("

"); - wprintf("\n", _("Upload")); - wprintf(" "); - wprintf("\n", _("Reset form")); - wprintf(" "); - wprintf("\n", _("Cancel")); - wprintf("
\n"); - wprintf("
\n"); - wprintf("
\n"); - wDumpContent(1); -} - -void do_graphics_upload(char *upl_cmd) -{ - char buf[SIZ]; - int bytes_remaining; - int pos = 0; - int thisblock; - - if (strlen(bstr("cancel_button")) > 0) { - strcpy(WC->ImportantMessage, - _("Graphics upload has been cancelled.")); - display_main_menu(); - return; - } - - if (WC->upload_length == 0) { - strcpy(WC->ImportantMessage, - _("You didn't upload a file.")); - display_main_menu(); - return; - } - serv_puts(upl_cmd); - serv_getln(buf, sizeof buf); - if (buf[0] != '2') { - strcpy(WC->ImportantMessage, &buf[4]); - display_main_menu(); - return; - } - bytes_remaining = WC->upload_length; - while (bytes_remaining) { - thisblock = ((bytes_remaining > 4096) ? 4096 : bytes_remaining); - serv_printf("WRIT %d", thisblock); - serv_getln(buf, sizeof buf); - if (buf[0] != '7') { - strcpy(WC->ImportantMessage, &buf[4]); - serv_puts("UCLS 0"); - serv_getln(buf, sizeof buf); - display_main_menu(); - return; - } - thisblock = extract_int(&buf[4], 0); - serv_write(&WC->upload[pos], thisblock); - pos = pos + thisblock; - bytes_remaining = bytes_remaining - thisblock; - } - - serv_puts("UCLS 1"); - serv_getln(buf, sizeof buf); - if (buf[0] != 'x') { - display_success(&buf[4]); - return; - } -} diff --git a/webcit/groupdav.h b/webcit/groupdav.h deleted file mode 100644 index 2a933ad8e..000000000 --- a/webcit/groupdav.h +++ /dev/null @@ -1,14 +0,0 @@ -/* $Id$ */ - -void groupdav_common_headers(void); -void groupdav_main(struct httprequest *, char *, int, char *); -void groupdav_get(char *); -void groupdav_put(char *, char *, char *, char *, int); -void groupdav_delete(char *, char *); -void groupdav_propfind(char *, int, char *, char *); -void groupdav_options(char *); -long locate_message_by_uid(char *); -void groupdav_folder_list(void); -void euid_escapize(char *, char *); -void euid_unescapize(char *, char *); -void groupdav_identify_host(void); diff --git a/webcit/groupdav_delete.c b/webcit/groupdav_delete.c deleted file mode 100644 index 2d44b8fc1..000000000 --- a/webcit/groupdav_delete.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * $Id$ - * - * Handles GroupDAV DELETE requests. - * - */ - -#include "webcit.h" -#include "webserver.h" -#include "groupdav.h" - - -/* - * The pathname is always going to be /groupdav/room_name/euid - */ -void groupdav_delete(char *dav_pathname, char *dav_ifmatch) { - char dav_roomname[SIZ]; - char dav_uid[SIZ]; - long dav_msgnum = (-1); - char buf[SIZ]; - int n = 0; - - /* First, break off the "/groupdav/" prefix */ - remove_token(dav_pathname, 0, '/'); - remove_token(dav_pathname, 0, '/'); - - /* Now extract the message euid */ - n = num_tokens(dav_pathname, '/'); - extract_token(dav_uid, dav_pathname, n-1, '/', sizeof dav_uid); - remove_token(dav_pathname, n-1, '/'); - - /* What's left is the room name. Remove trailing slashes. */ - if (dav_pathname[strlen(dav_pathname)-1] == '/') { - dav_pathname[strlen(dav_pathname)-1] = 0; - } - strcpy(dav_roomname, dav_pathname); - - /* Go to the correct room. */ - if (strcasecmp(WC->wc_roomname, dav_roomname)) { - gotoroom(dav_roomname); - } - if (strcasecmp(WC->wc_roomname, dav_roomname)) { - wprintf("HTTP/1.1 404 not found\r\n"); - groupdav_common_headers(); - wprintf("Content-Length: 0\r\n\r\n"); - return; - } - - dav_msgnum = locate_message_by_uid(dav_uid); - - /* - * If no item exists with the requested uid ... simple error. - */ - if (dav_msgnum < 0L) { - wprintf("HTTP/1.1 404 Not Found\r\n"); - groupdav_common_headers(); - wprintf("Content-Length: 0\r\n\r\n"); - return; - } - - /* - * It's there ... check the ETag and make sure it matches - * the message number. - */ - if (strlen(dav_ifmatch) > 0) { - if (atol(dav_ifmatch) != dav_msgnum) { - wprintf("HTTP/1.1 412 Precondition Failed\r\n"); - groupdav_common_headers(); - wprintf("Content-Length: 0\r\n\r\n"); - return; - } - } - - /* - * Ok, attempt to delete the item. - */ - serv_printf("DELE %ld", dav_msgnum); - serv_getln(buf, sizeof buf); - if (buf[0] == '2') { - wprintf("HTTP/1.1 204 No Content\r\n"); /* success */ - groupdav_common_headers(); - wprintf("Content-Length: 0\r\n\r\n"); - } - else { - wprintf("HTTP/1.1 403 Forbidden\r\n"); /* access denied */ - groupdav_common_headers(); - wprintf("Content-Length: 0\r\n\r\n"); - } - return; -} diff --git a/webcit/groupdav_get.c b/webcit/groupdav_get.c deleted file mode 100644 index d773fe29b..000000000 --- a/webcit/groupdav_get.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * $Id$ - * - * Handles GroupDAV GET requests. - * - */ - -#include "webcit.h" -#include "webserver.h" -#include "groupdav.h" - - -/* - * Fetch the entire contents of the room as one big ics file. - * This is for "webcal://" type access. - */ -void groupdav_get_big_ics(void) { - char buf[1024]; - - serv_puts("ICAL getics"); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - wprintf("HTTP/1.1 404 not found\r\n"); - groupdav_common_headers(); - wprintf( - "Content-Type: text/plain\r\n" - "\r\n" - "%s\r\n", - &buf[4] - ); - return; - } - - wprintf("HTTP/1.1 200 OK\r\n"); - groupdav_common_headers(); - wprintf("Content-type: text/calendar; charset=UTF-8\r\n"); - begin_burst(); - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - wprintf("%s\r\n", buf); - } - end_burst(); -} - - -/* - * The pathname is always going to take one of two formats: - * /groupdav/room_name/euid (GroupDAV) - * /groupdav/room_name (webcal) - */ -void groupdav_get(char *dav_pathname) { - char dav_roomname[1024]; - char dav_uid[1024]; - long dav_msgnum = (-1); - char buf[1024]; - int in_body = 0; - int found_content_type = 0; - - if (num_tokens(dav_pathname, '/') < 3) { - wprintf("HTTP/1.1 404 not found\r\n"); - groupdav_common_headers(); - wprintf( - "Content-Type: text/plain\r\n" - "\r\n" - "The object you requested was not found.\r\n" - ); - return; - } - - extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname); - extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid); - if ((!strcasecmp(dav_uid, "ics")) || (!strcasecmp(dav_uid, "calendar.ics"))) { - strcpy(dav_uid, ""); - } - - /* Go to the correct room. */ - if (strcasecmp(WC->wc_roomname, dav_roomname)) { - gotoroom(dav_roomname); - } - if (strcasecmp(WC->wc_roomname, dav_roomname)) { - wprintf("HTTP/1.1 404 not found\r\n"); - groupdav_common_headers(); - wprintf( - "Content-Type: text/plain\r\n" - "\r\n" - "There is no folder called \"%s\" on this server.\r\n", - dav_roomname - ); - return; - } - - /** GET on the collection itself returns an ICS of the entire collection. - */ - if (!strcasecmp(dav_uid, "")) { - groupdav_get_big_ics(); - return; - } - - dav_msgnum = locate_message_by_uid(dav_uid); - serv_printf("MSG2 %ld", dav_msgnum); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - wprintf("HTTP/1.1 404 not found\r\n"); - groupdav_common_headers(); - wprintf( - "Content-Type: text/plain\r\n" - "\r\n" - "Object \"%s\" was not found in the \"%s\" folder.\r\n", - dav_uid, - dav_roomname - ); - return; - } - - wprintf("HTTP/1.1 200 OK\r\n"); - groupdav_common_headers(); - wprintf("etag: \"%ld\"\r\n", dav_msgnum); - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - if (in_body) { - wprintf("%s\r\n", buf); - } - else if (!strncasecmp(buf, "Date: ", 6)) { - wprintf("%s\r\n", buf); - } - else if (!strncasecmp(buf, "Content-type: ", 14)) { - wprintf("%s", buf); - if (bmstrcasestr(buf, "charset=")) { - wprintf("%s\r\n", buf); - } - else { - wprintf("%s;charset=UTF-8\r\n", buf); - } - found_content_type = 1; - } - else if ((strlen(buf) == 0) && (in_body == 0)) { - if (!found_content_type) { - wprintf("Content-type: text/plain\r\n"); - } - in_body = 1; - begin_burst(); - } - } - end_burst(); -} diff --git a/webcit/groupdav_main.c b/webcit/groupdav_main.c deleted file mode 100644 index ca31fe21e..000000000 --- a/webcit/groupdav_main.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * $Id$ - * - * Entry point for GroupDAV functions - * - */ - -#include "webcit.h" -#include "webserver.h" -#include "groupdav.h" - - -/* - * Output HTTP headers which are common to all requests. - * - * Please observe that we don't use the usual output_headers() - * and wDumpContent() functions in the GroupDAV subsystem, so we - * do our own header stuff here. - * - */ -void groupdav_common_headers(void) { - wprintf( - "Server: %s / %s\r\n" - "Connection: close\r\n", - SERVER, serv_info.serv_software - ); -} - - - -/* - * string conversion function - */ -void euid_escapize(char *target, char *source) { - int i; - int target_length = 0; - - strcpy(target, ""); - for (i=0; iline, 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; ihttp_host) > 0) { - wprintf("%s://%s", - (is_https ? "https" : "http"), - WC->http_host); - } -} diff --git a/webcit/groupdav_options.c b/webcit/groupdav_options.c deleted file mode 100644 index b92b79478..000000000 --- a/webcit/groupdav_options.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * $Id$ - * - * Handles DAV OPTIONS requests (experimental -- not required by GroupDAV) - * - */ - -#include "webcit.h" -#include "webserver.h" -#include "groupdav.h" - -/* - * The pathname is always going to be /groupdav/room_name/msg_num - */ -void groupdav_options(char *dav_pathname) { - char dav_roomname[256]; - char dav_uid[256]; - long dav_msgnum = (-1); - char datestring[256]; - time_t now; - - now = time(NULL); - http_datestring(datestring, sizeof datestring, now); - - extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname); - extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid); - - /* - * If the room name is blank, the client is doing a top-level OPTIONS. - */ - if (strlen(dav_roomname) == 0) { - wprintf("HTTP/1.1 200 OK\r\n"); - groupdav_common_headers(); - wprintf("Date: %s\r\n", datestring); - wprintf("DAV: 1\r\n"); - wprintf("Allow: OPTIONS, PROPFIND\r\n"); - wprintf("\r\n"); - return; - } - - /* Go to the correct room. */ - if (strcasecmp(WC->wc_roomname, dav_roomname)) { - gotoroom(dav_roomname); - } - - if (strcasecmp(WC->wc_roomname, dav_roomname)) { - wprintf("HTTP/1.1 404 not found\r\n"); - groupdav_common_headers(); - wprintf("Date: %s\r\n", datestring); - wprintf( - "Content-Type: text/plain\r\n" - "\r\n" - "There is no folder called \"%s\" on this server.\r\n", - dav_roomname - ); - return; - } - - /* If dav_uid is non-empty, client is requesting an OPTIONS on - * a specific item in the room. - */ - if (strlen(dav_uid) > 0) { - - dav_msgnum = locate_message_by_uid(dav_uid); - if (dav_msgnum < 0) { - wprintf("HTTP/1.1 404 not found\r\n"); - groupdav_common_headers(); - wprintf( - "Content-Type: text/plain\r\n" - "\r\n" - "Object \"%s\" was not found in the \"%s\" folder.\r\n", - dav_uid, - dav_roomname - ); - return; - } - - wprintf("HTTP/1.1 200 OK\r\n"); - groupdav_common_headers(); - wprintf("Date: %s\r\n", datestring); - wprintf("DAV: 1\r\n"); - wprintf("Allow: OPTIONS, PROPFIND, GET, PUT, DELETE\r\n"); - wprintf("\r\n"); - return; - } - - /* - * We got to this point, which means that the client is requesting - * an OPTIONS on the room itself. - */ - wprintf("HTTP/1.1 200 OK\r\n"); - groupdav_common_headers(); - wprintf("Date: %s\r\n", datestring); - wprintf("DAV: 1\r\n"); - wprintf("Allow: OPTIONS, PROPFIND, GET, PUT\r\n"); - wprintf("\r\n"); -} diff --git a/webcit/groupdav_propfind.c b/webcit/groupdav_propfind.c deleted file mode 100644 index 8e6154d61..000000000 --- a/webcit/groupdav_propfind.c +++ /dev/null @@ -1,438 +0,0 @@ -/* - * $Id$ - * - * Handles GroupDAV PROPFIND requests. - * - * A few notes about our XML output: - * - * --> Yes, we are spewing tags directly instead of using an XML library. - * If you would like to rewrite this using libxml2, code it up and submit - * a patch. Whining will be summarily ignored. - * - * --> XML is deliberately output with no whitespace/newlines between tags. - * This makes it difficult to read, but we have discovered clients which - * crash when you try to pretty it up. - * - */ - -#include "webcit.h" -#include "webserver.h" -#include "groupdav.h" - -/* - * Given an encoded UID, translate that to an unencoded Citadel EUID and - * then search for it in the current room. Return a message number or -1 - * if not found. - * - */ -long locate_message_by_uid(char *uid) { - char buf[256]; - char decoded_uid[1024]; - long retval = (-1L); - - /* Decode the uid */ - euid_unescapize(decoded_uid, uid); - -/************** THE NEW WAY ***********************/ - serv_printf("EUID %s", decoded_uid); - serv_getln(buf, sizeof buf); - if (buf[0] == '2') { - retval = atol(&buf[4]); - } -/***************************************************/ - -/************** THE OLD WAY *********************** - serv_puts("MSGS ALL|0|1"); - serv_getln(buf, sizeof buf); - if (buf[0] == '8') { - serv_printf("exti|%s", decoded_uid); - serv_puts("000"); - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - retval = atol(buf); - } - } - ***************************************************/ - - return(retval); -} - - - -/* - * List rooms (or "collections" in DAV terminology) which contain - * interesting groupware objects. - */ -void groupdav_collection_list(char *dav_pathname, int dav_depth) -{ - char buf[256]; - char roomname[256]; - int view; - char datestring[256]; - time_t now; - time_t mtime; - int is_groupware_collection = 0; - int starting_point = 1; /**< 0 for /, 1 for /groupdav/ */ - - if (!strcmp(dav_pathname, "/")) { - starting_point = 0; - } - else if (!strcasecmp(dav_pathname, "/groupdav")) { - starting_point = 1; - } - else if (!strcasecmp(dav_pathname, "/groupdav/")) { - starting_point = 1; - } - else if ( (!strncasecmp(dav_pathname, "/groupdav/", 10)) && (strlen(dav_pathname) > 10) ) { - starting_point = 2; - } - - now = time(NULL); - http_datestring(datestring, sizeof datestring, now); - - /** - * Be rude. Completely ignore the XML request and simply send them - * everything we know about. Let the client sort it out. - */ - wprintf("HTTP/1.0 207 Multi-Status\r\n"); - groupdav_common_headers(); - wprintf("Date: %s\r\n", datestring); - wprintf("Content-type: text/xml\r\n"); - wprintf("Content-encoding: identity\r\n"); - - begin_burst(); - - wprintf("" - "" - ); - - /** - * If the client is requesting the root, show a root node. - */ - if (starting_point == 0) { - wprintf(""); - wprintf(""); - groupdav_identify_host(); - wprintf("/"); - wprintf(""); - wprintf(""); - wprintf("HTTP/1.1 200 OK"); - wprintf(""); - wprintf("/"); - wprintf(""); - wprintf(""); - escputs(datestring); - wprintf(""); - wprintf(""); - wprintf(""); - wprintf(""); - } - - /** - * If the client is requesting "/groupdav", show a /groupdav subdirectory. - */ - if ((starting_point + dav_depth) >= 1) { - wprintf(""); - wprintf(""); - groupdav_identify_host(); - wprintf("/groupdav"); - wprintf(""); - wprintf(""); - wprintf("HTTP/1.1 200 OK"); - wprintf(""); - wprintf("GroupDAV"); - wprintf(""); - wprintf(""); - escputs(datestring); - wprintf(""); - wprintf(""); - wprintf(""); - wprintf(""); - } - - /** - * 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(""); - - wprintf(""); - groupdav_identify_host(); - wprintf("/groupdav/"); - urlescputs(roomname); - wprintf("/"); - - wprintf(""); - wprintf("HTTP/1.1 200 OK"); - wprintf(""); - wprintf(""); - escputs(roomname); - wprintf(""); - wprintf(""); - - switch(view) { - case VIEW_CALENDAR: - wprintf(""); - break; - case VIEW_TASKS: - wprintf(""); - break; - case VIEW_ADDRESSBOOK: - wprintf(""); - break; - } - - wprintf(""); - wprintf(""); - escputs(datestring); - wprintf(""); - wprintf(""); - wprintf(""); - wprintf(""); - } - } - wprintf("\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("" - "" - ); - - wprintf(""); - - wprintf(""); - groupdav_identify_host(); - wprintf("/groupdav/"); - urlescputs(WC->wc_roomname); - euid_escapize(encoded_uid, dav_uid); - wprintf("/%s", encoded_uid); - wprintf(""); - wprintf(""); - wprintf("HTTP/1.1 200 OK"); - wprintf("\"%ld\"", dav_msgnum); - wprintf(""); - - wprintf("\n"); - wprintf("\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("" - "" - ); - - - /** Transmit the collection resource (FIXME check depth and starting point) */ - wprintf(""); - - wprintf(""); - groupdav_identify_host(); - wprintf("/groupdav/"); - urlescputs(WC->wc_roomname); - wprintf(""); - - wprintf(""); - wprintf("HTTP/1.1 200 OK"); - wprintf(""); - wprintf(""); - escputs(WC->wc_roomname); - wprintf(""); - wprintf(""); - - switch(WC->wc_default_view) { - case VIEW_CALENDAR: - wprintf(""); - break; - case VIEW_TASKS: - wprintf(""); - break; - case VIEW_ADDRESSBOOK: - wprintf(""); - break; - } - - wprintf(""); - /* FIXME get the mtime - wprintf(""); - escputs(datestring); - wprintf(""); - */ - wprintf(""); - wprintf(""); - wprintf(""); - - /** 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"); - wprintf(""); - groupdav_identify_host(); - wprintf("/groupdav/"); - urlescputs(WC->wc_roomname); - euid_escapize(encoded_uid, uid); - wprintf("/%s", encoded_uid); - wprintf(""); - wprintf(""); - wprintf("HTTP/1.1 200 OK"); - wprintf(""); - wprintf("\"%ld\"", msgs[i]); - wprintf(""); - wprintf(""); - wprintf(""); - } - } - - wprintf("\n"); - end_burst(); - - if (msgs != NULL) { - free(msgs); - } -} diff --git a/webcit/groupdav_put.c b/webcit/groupdav_put.c deleted file mode 100644 index 21eaefbb8..000000000 --- a/webcit/groupdav_put.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * $Id$ - * - * Handles GroupDAV PUT requests. - * - */ - -#include "webcit.h" -#include "webserver.h" -#include "groupdav.h" - - -/* - * This function is for uploading an ENTIRE calendar, not just one - * component. This would be for webcal:// 'publish' operations, not - * for GroupDAV. - */ -void groupdav_put_bigics(char *dav_content, int dav_content_length) -{ - char buf[1024]; - - serv_puts("ICAL putics"); - serv_getln(buf, sizeof buf); - if (buf[0] != '4') { - wprintf("HTTP/1.1 502 Bad Gateway\r\n"); - groupdav_common_headers(); - wprintf("Content-type: text/plain\r\n" - "\r\n" - "%s\r\n", &buf[4] - ); - return; - } - - serv_write(dav_content, dav_content_length); - serv_printf("\n000"); - - /* Report success and not much else. */ - wprintf("HTTP/1.1 204 No Content\r\n"); - lprintf(9, "HTTP/1.1 204 No Content\r\n"); - groupdav_common_headers(); - wprintf("Content-Length: 0\r\n\r\n"); -} - - - -/* - * The pathname is always going to take one of two formats: - * /groupdav/room_name/euid (GroupDAV) - * /groupdav/room_name (webcal) - */ -void groupdav_put(char *dav_pathname, char *dav_ifmatch, - char *dav_content_type, char *dav_content, - int dav_content_length -) { - char dav_roomname[1024]; - char dav_uid[1024]; - long new_msgnum = (-2L); - long old_msgnum = (-1L); - char buf[SIZ]; - int n = 0; - - if (num_tokens(dav_pathname, '/') < 3) { - wprintf("HTTP/1.1 404 not found\r\n"); - groupdav_common_headers(); - wprintf( - "Content-Type: text/plain\r\n" - "\r\n" - "The object you requested was not found.\r\n" - ); - return; - } - - extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname); - extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid); - if ((!strcasecmp(dav_uid, "ics")) || (!strcasecmp(dav_uid, "calendar.ics"))) { - strcpy(dav_uid, ""); - } - - /* Go to the correct room. */ - if (strcasecmp(WC->wc_roomname, dav_roomname)) { - gotoroom(dav_roomname); - } - if (strcasecmp(WC->wc_roomname, dav_roomname)) { - wprintf("HTTP/1.1 404 not found\r\n"); - groupdav_common_headers(); - wprintf( - "Content-Type: text/plain\r\n" - "\r\n" - "There is no folder called \"%s\" on this server.\r\n", - dav_roomname - ); - return; - } - - /* - * If an HTTP If-Match: header is present, the client is attempting - * to replace an existing item. We have to check to see if the - * message number associated with the supplied uid matches what the - * client is expecting. If not, the server probably contains a newer - * version, so we fail... - */ - if (strlen(dav_ifmatch) > 0) { - lprintf(9, "dav_ifmatch: %s\n", dav_ifmatch); - old_msgnum = locate_message_by_uid(dav_uid); - lprintf(9, "old_msgnum: %ld\n", old_msgnum); - if (atol(dav_ifmatch) != old_msgnum) { - wprintf("HTTP/1.1 412 Precondition Failed\r\n"); - lprintf(9, "HTTP/1.1 412 Precondition Failed (ifmatch=%ld, old_msgnum=%ld)\r\n", - atol(dav_ifmatch), old_msgnum); - groupdav_common_headers(); - wprintf("Content-Length: 0\r\n\r\n"); - return; - } - } - - /** PUT on the collection itself uploads an ICS of the entire collection. - */ - if (!strcasecmp(dav_uid, "")) { - groupdav_put_bigics(dav_content, dav_content_length); - return; - } - - /* - * We are cleared for upload! We use the new calling syntax for ENT0 - * which allows a confirmation to be sent back to us. That's how we - * extract the message ID. - */ - serv_puts("ENT0 1|||4|||1|"); - serv_getln(buf, sizeof buf); - if (buf[0] != '8') { - wprintf("HTTP/1.1 502 Bad Gateway\r\n"); - groupdav_common_headers(); - wprintf("Content-type: text/plain\r\n" - "\r\n" - "%s\r\n", &buf[4] - ); - return; - } - - /* Send the content to the Citadel server */ - serv_printf("Content-type: %s\n\n", dav_content_type); - serv_puts(dav_content); - serv_puts("\n000"); - - /* Fetch the reply from the Citadel server */ - n = 0; - strcpy(dav_uid, ""); - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - switch(n++) { - case 0: new_msgnum = atol(buf); - break; - case 1: lprintf(9, "new_msgnum=%ld (%s)\n", new_msgnum, buf); - break; - case 2: strcpy(dav_uid, buf); - break; - default: - break; - } - } - - /* Tell the client what happened. */ - - /* Citadel failed in some way? */ - if (new_msgnum < 0L) { - wprintf("HTTP/1.1 502 Bad Gateway\r\n"); - groupdav_common_headers(); - wprintf("Content-type: text/plain\r\n" - "\r\n" - "new_msgnum is %ld\r\n" - "\r\n", new_msgnum - ); - return; - } - - /* We created this item for the first time. */ - if (old_msgnum < 0L) { - wprintf("HTTP/1.1 201 Created\r\n"); - lprintf(9, "HTTP/1.1 201 Created\r\n"); - groupdav_common_headers(); - wprintf("etag: \"%ld\"\r\n", new_msgnum); - wprintf("Content-Length: 0\r\n"); - wprintf("Location: "); - groupdav_identify_host(); - wprintf("/groupdav/"); - urlescputs(dav_roomname); - wprintf("/%s\r\n", dav_uid); - wprintf("\r\n"); - return; - } - - /* We modified an existing item. */ - wprintf("HTTP/1.1 204 No Content\r\n"); - lprintf(9, "HTTP/1.1 204 No Content\r\n"); - groupdav_common_headers(); - wprintf("etag: \"%ld\"\r\n", new_msgnum); - wprintf("Content-Length: 0\r\n\r\n"); - - /* The item we replaced has probably already been deleted by - * the Citadel server, but we'll do this anyway, just in case. - */ - serv_printf("DELE %ld", old_msgnum); - serv_getln(buf, sizeof buf); - - return; -} diff --git a/webcit/html2html.c b/webcit/html2html.c deleted file mode 100644 index ca8804c1a..000000000 --- a/webcit/html2html.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup HTML2HTML Output an HTML message, modifying it slightly to make sure it plays nice - * with the rest of our web framework. - * \ingroup WebcitHttpServer - */ -/*@{*/ -#include "webcit.h" -#include "vcard.h" -#include "webserver.h" - - -/** - * \brief Strip surrounding single or double quotes from a string. - * - * \param s String to be stripped. - */ -void stripquotes(char *s) -{ - int len; - - if (!s) return; - - len = strlen(s); - if (len < 2) return; - - if ( ( (s[0] == '\"') && (s[len-1] == '\"') ) || ( (s[0] == '\'') && (s[len-1] == '\'') ) ) { - s[len-1] = 0; - strcpy(s, &s[1]); - } -} - - -/** - * \brief Check to see if a META tag has overridden the declared MIME character set. - * - * \param charset Character set name (left unchanged if we don't do anything) - * \param meta_http_equiv Content of the "http-equiv" portion of the META tag - * \param meta_content Content of the "content" portion of the META tag - */ -void extract_charset_from_meta(char *charset, char *meta_http_equiv, char *meta_content) -{ - char *ptr; - char buf[64]; - - if (!charset) return; - if (!meta_http_equiv) return; - if (!meta_content) return; - - - if (strcasecmp(meta_http_equiv, "Content-type")) return; - - ptr = strchr(meta_content, ';'); - if (!ptr) return; - - safestrncpy(buf, ++ptr, sizeof buf); - striplt(buf); - if (!strncasecmp(buf, "charset=", 8)) { - strcpy(charset, &buf[8]); - } -} - - - -/** - * \brief Sanitize and enhance an HTML message for display. - * Also convert weird character sets to UTF-8 if necessary. - * - * \param supplied_charset the input charset as declared in the MIME headers - */ -void output_html(char *supplied_charset, int treat_as_wiki) { - char buf[SIZ]; - char *msg; - char *ptr; - char *msgstart; - char *msgend; - char *converted_msg; - int buffer_length = 1; - int line_length = 0; - int content_length = 0; - int output_length = 0; - char new_window[SIZ]; - int brak = 0; - int alevel = 0; - int i; - int linklen; - char charset[128]; -#ifdef HAVE_ICONV - iconv_t ic = (iconv_t)(-1) ; - char *ibuf; /**< Buffer of characters to be converted */ - char *obuf; /**< Buffer for converted characters */ - size_t ibuflen; /**< Length of input buffer */ - size_t obuflen; /**< Length of output buffer */ - char *osav; /**< Saved pointer to output buffer */ -#endif - - safestrncpy(charset, supplied_charset, sizeof charset); - msg = strdup(""); - sprintf(new_window, ""); - wprintf(_("realloc() error! couldn't get %d bytes: %s"), - buffer_length + 1, - strerror(errno)); - wprintf("

\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 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, "
'))) - ) { - /* 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, "') - ||(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"); - output_length += 2; - for (i=0; i"); - 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, "", 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("

\n"); - - /** Now give back the memory */ - free(converted_msg); - free(msg); -} - -/*@}*/ diff --git a/webcit/http_datestring.c b/webcit/http_datestring.c deleted file mode 100644 index 33f578464..000000000 --- a/webcit/http_datestring.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup HTTPDateTime Function to generate HTTP-compliant textual time/date stamp - * (This module was lifted directly from the Citadel server source) - * - * \ingroup WebcitHttpServer - */ -/*@{*/ -#include "webcit.h" - -/** HTTP Months - do not translate - these are not for human consumption */ -static char *httpdate_months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -/** HTTP Weekdays - do not translate - these are not for human consumption */ -static char *httpdate_weekdays[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" -}; - - -/** - * \brief Supplied with a unix timestamp, generate a textual time/date stamp - * \param buf the return buffer - * \param n the size of the buffer - * \param xtime the time to format as string - */ -void http_datestring(char *buf, size_t n, time_t xtime) { - struct tm t; - - long offset; - char offsign; - - localtime_r(&xtime, &t); - - /** Convert "seconds west of GMT" to "hours/minutes offset" */ -#ifdef HAVE_STRUCT_TM_TM_GMTOFF - offset = t.tm_gmtoff; -#else - offset = timezone; -#endif - if (offset > 0) { - offsign = '+'; - } - else { - offset = 0L - offset; - offsign = '-'; - } - offset = ( (offset / 3600) * 100 ) + ( offset % 60 ); - - snprintf(buf, n, "%s, %02d %s %04d %02d:%02d:%02d %c%04ld", - httpdate_weekdays[t.tm_wday], - t.tm_mday, - httpdate_months[t.tm_mon], - t.tm_year + 1900, - t.tm_hour, - t.tm_min, - t.tm_sec, - offsign, offset - ); -} - - -/*@}*/ diff --git a/webcit/ical_dezonify.c b/webcit/ical_dezonify.c deleted file mode 100644 index c2042fd5f..000000000 --- a/webcit/ical_dezonify.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup IcalDezonify normalize ical dates to UTC - * Function to go through an ical component set and convert all non-UTC - * date/time properties to UTC. It also strips out any VTIMEZONE - * subcomponents afterwards, because they're irrelevant. - * - * Everything here will work on both a fully encapsulated VCALENDAR component - * or any type of subcomponent. - * - * \ingroup Calendaring - */ -/*@{*/ - -#include "webcit.h" -#include "webserver.h" - - -#ifdef WEBCIT_WITH_CALENDAR_SERVICE - - -/** - * \brief Back end function for ical_dezonify() - * - * We supply this with the master component, the relevant component, - * and the property (which will be a DTSTART, DTEND, etc.) - * which we want to convert to UTC. - * \param cal dunno ??? - * \param rcal dunno ??? - * \param prop dunno ??? - */ -void ical_dezonify_backend(icalcomponent *cal, - icalcomponent *rcal, - icalproperty *prop) { - - icaltimezone *t = NULL; - icalparameter *param; - const char *tzid; - struct icaltimetype TheTime; - - /** Give me nothing and I will give you nothing in return. */ - if (cal == NULL) return; - - /** Hunt for a TZID parameter in this property. */ - param = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER); - - /** Get the stringish name of this TZID. */ - if (param != NULL) { - tzid = icalparameter_get_tzid(param); - - /** Convert it to an icaltimezone type. */ - if (tzid != NULL) { - t = icalcomponent_get_timezone(cal, tzid); - } - - } - - /** Now we know the timezone. Convert to UTC. */ - - if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) { - TheTime = icalproperty_get_dtstart(prop); - } - else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) { - TheTime = icalproperty_get_dtend(prop); - } - else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) { - TheTime = icalproperty_get_due(prop); - } - else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) { - TheTime = icalproperty_get_exdate(prop); - } - else { - return; - } - - /** Do the conversion. */ - if (t != NULL) { - icaltimezone_convert_time(&TheTime, - t, - icaltimezone_get_utc_timezone() - ); - } - TheTime.is_utc = 1; - icalproperty_remove_parameter_by_kind(prop, ICAL_TZID_PARAMETER); - - /** Now add the converted property back in. */ - if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) { - icalproperty_set_dtstart(prop, TheTime); - } - else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) { - icalproperty_set_dtend(prop, TheTime); - } - else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) { - icalproperty_set_due(prop, TheTime); - } - else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) { - icalproperty_set_exdate(prop, TheTime); - } -} - - -/** - * \brief Recursive portion of ical_dezonify() - * \param cal dunno ??? - * \param rcal dunno ??? - */ -void ical_dezonify_recur(icalcomponent *cal, icalcomponent *rcal) { - icalcomponent *c; - icalproperty *p; - - /** - * Recurse through all subcomponents *except* VTIMEZONE ones. - */ - for (c=icalcomponent_get_first_component( - rcal, ICAL_ANY_COMPONENT); - c != NULL; - c = icalcomponent_get_next_component( - rcal, ICAL_ANY_COMPONENT) - ) { - if (icalcomponent_isa(c) != ICAL_VTIMEZONE_COMPONENT) { - ical_dezonify_recur(cal, c); - } - } - - /** - * Now look for DTSTART and DTEND properties - */ - for (p=icalcomponent_get_first_property( - rcal, ICAL_ANY_PROPERTY); - p != NULL; - p = icalcomponent_get_next_property( - rcal, ICAL_ANY_PROPERTY) - ) { - if ( - (icalproperty_isa(p) == ICAL_DTSTART_PROPERTY) - || (icalproperty_isa(p) == ICAL_DTEND_PROPERTY) - || (icalproperty_isa(p) == ICAL_DUE_PROPERTY) - || (icalproperty_isa(p) == ICAL_EXDATE_PROPERTY) - ) { - ical_dezonify_backend(cal, rcal, p); - } - } -} - - -/** - * \brief Convert all DTSTART and DTEND properties in all subcomponents to UTC. - * This function will search any VTIMEZONE subcomponents to learn the - * relevant timezone information. - * \param cal item to process - */ -void ical_dezonify(icalcomponent *cal) { - icalcomponent *vt = NULL; - - /** Convert all times to UTC */ - ical_dezonify_recur(cal, cal); - - /** Strip out VTIMEZONE subcomponents -- we don't need them anymore */ - while (vt = icalcomponent_get_first_component( - cal, ICAL_VTIMEZONE_COMPONENT), vt != NULL) { - icalcomponent_remove_component(cal, vt); - icalcomponent_free(vt); - } - -} - - -#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ -/*@}*/ diff --git a/webcit/iconbar.c b/webcit/iconbar.c deleted file mode 100644 index bb9a4e417..000000000 --- a/webcit/iconbar.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup IconBar Displays and customizes the iconbar. - * \ingroup MenuInfrastructure - */ -/*@{*/ -#include "webcit.h" - - -/** Values for ib_displayas */ -#define IB_PICTEXT 0 /**< picture and text */ -#define IB_PICONLY 1 /**< just a picture */ -#define IB_TEXTONLY 2 /**< just text */ - - -/** - * \brief draw the icon bar????? - */ -void do_selected_iconbar(void) { - if (WC->current_iconbar == current_iconbar_roomlist) { - do_iconbar_roomlist(); - } - else { - do_iconbar(); - } -} - -/** - * \brief draw the icon bar??? - */ -void do_iconbar(void) { - char iconbar[SIZ]; - char buf[SIZ]; - char key[SIZ], value[SIZ]; - int i; - - WC->current_iconbar = current_iconbar_menu; - - /** - * The initialized values of these variables also happen to - * specify the default values for users who haven't customized - * their iconbars. These should probably be set in a master - * configuration somewhere. - */ - int ib_displayas = 0; /**< pictures and text, pictures, text */ - int ib_logo = 0; /**< Site logo */ - int ib_summary = 1; /**< Summary page icon */ - int ib_inbox = 1; /**< Inbox icon */ - int ib_calendar = 1; /**< Calendar icon */ - int ib_contacts = 1; /**< Contacts icon */ - int ib_notes = 1; /**< Notes icon */ - int ib_tasks = 1; /**< Tasks icon */ - int ib_rooms = 1; /**< Rooms icon */ - int ib_users = 1; /**< Users icon */ - int ib_chat = 1; /**< Chat icon */ - int ib_advanced = 1; /**< Advanced Options icon */ - int ib_citadel = 1; /**< 'Powered by Citadel' logo */ - /* - */ - - get_preference("iconbar", iconbar, sizeof iconbar); - for (i=0; i\n" - "
\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\n" - "
\n"); - - /** embed the room list */ - list_all_rooms_by_floor("iconbar"); - - wprintf("
\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\n" - "
" - ""); - wprintf(_("Customize the icon bar")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("
" - "
"); - - wprintf("
\n"); - - wprintf("
"); - wprintf(_("Display icons as:")); - wprintf(" "); - for (i=0; i<=2; ++i) { - 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("

\n"); - - wprintf(_("Select the icons you would like to see displayed " - "in the 'icon bar' menu on the left side of the " - "screen.")); - wprintf("

\n"); - - wprintf("\n"); - - wprintf("\n", - ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), - (ib_logo ? "CHECKED" : ""), - _("Site logo"), - _("An icon describing this site") - ); - - wprintf("\n", - ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), - (ib_summary ? "CHECKED" : ""), - _("Summary"), - _("Your summary page") - ); - - wprintf("\n", - ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), - (ib_inbox ? "CHECKED" : ""), - _("Mail (inbox)"), - _("A shortcut to your email Inbox") - ); - - wprintf("\n", - ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), - (ib_contacts ? "CHECKED" : ""), - _("Contacts"), - _("Your personal address book") - ); - - wprintf("\n", - ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), - (ib_notes ? "CHECKED" : ""), - _("Notes"), - _("Your personal notes") - ); - -#ifdef WEBCIT_WITH_CALENDAR_SERVICE - wprintf("\n", - ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), - (ib_calendar ? "CHECKED" : ""), - _("Calendar"), - _("A shortcut to your personal calendar") - ); - - wprintf("\n", - ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), - (ib_tasks ? "CHECKED" : ""), - _("Tasks"), - _("A shortcut to your personal task list") - ); -#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ - - wprintf("\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("\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("\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("\n", - ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), - (ib_advanced ? "CHECKED" : ""), - _("Advanced options"), - _("Access to the complete menu of Citadel functions.") - - ); - - wprintf("\n", - ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), - (ib_citadel ? "CHECKED" : ""), - _("Citadel logo"), - _("Displays the 'Powered by Citadel' icon") - ); - - wprintf("
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "
" - "" - "" - "\" \"" - "" - "%s
" - "%s" - "

\n" - "
" - "" - " " - "" - "
\n", - _("Save changes"), - _("Cancel") - ); - - wprintf("
\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( - "
" - "" - " "); - wprintf(_("Your icon bar has been updated. Please select any of its " - "choices to continue.")); - wprintf("
\n"); - wDumpContent(2); -} - - - -/*@}*/ diff --git a/webcit/inetconf.c b/webcit/inetconf.c deleted file mode 100644 index 85f98153b..000000000 --- a/webcit/inetconf.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup InetCfg Functions which handle Internet domain configuration etc. - * \ingroup CitadelConfig - */ -/*@{*/ -#include "webcit.h" - - -/** - * \brief display the inet config dialog - */ -void display_inetconf(void) -{ - char buf[SIZ]; - char ename[SIZ]; - char etype[SIZ]; - int i; - int which; - - enum { - ic_localhost, - ic_directory, - ic_gwdom, - ic_smarthost, - ic_rbl, - ic_spamass, - ic_max - }; - char *ic_spec[ic_max]; - char *ic_misc; - char *ic_keyword[ic_max]; - char *ic_boxtitle[ic_max]; - char *ic_desc[ic_max]; - - ic_keyword[0] = _("localhost"); - ic_keyword[1] = _("directory"); - ic_keyword[2] = _("gatewaydomain"); - ic_keyword[3] = _("smarthost"); - ic_keyword[4] = _("rbl"); - ic_keyword[5] = _("spamassassin"); - - ic_boxtitle[0] = _("Local host aliases"); - ic_boxtitle[1] = _("Directory domains"); - ic_boxtitle[2] = _("Gateway domains"); - ic_boxtitle[3] = _("Smart hosts"); - ic_boxtitle[4] = _("RBL hosts"); - ic_boxtitle[5] = _("SpamAssassin hosts"); - - ic_desc[0] = _("(domains for which this host receives mail)"); - ic_desc[1] = _("(domains mapped with the Global Address Book)"); - ic_desc[2] = _("(domains whose subdomains match Citadel hosts)"); - ic_desc[3] = _("(if present, forward all outbound mail to one of these hosts)"); - ic_desc[4] = _("(hosts running a Realtime Blackhole List)"); - ic_desc[5] = _("(hosts running the SpamAssassin service)"); - - for (i=0; i= 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("
\n"); - wprintf("
"); - wprintf(""); - wprintf(_("Internet configuration")); - wprintf("\n"); - wprintf("
\n"); - wprintf("
\n
\n"); - - wprintf("
" - "
\n"); - for (which=0; which"); - } - svprintf("BOXTITLE", WCS_STRING, ic_boxtitle[which]); - do_template("beginbox"); - wprintf(""); - escputs(ic_desc[which]); - wprintf("
"); - wprintf("\n"); - if (strlen(ic_spec[which]) > 0) { - for (i=0; i\n"); - } - } - wprintf("\n" - "
"); - extract_token(buf, ic_spec[which], i, '\n', sizeof buf); - escputs(buf); - wprintf("" - "", - _("Delete this entry?")); - wprintf(""); - wprintf(_("(Delete)")); - wprintf("
" - "" - "", ic_keyword[which]); - wprintf("" - "" - "
\n"); - do_template("endbox"); - } - wprintf("
\n"); - wDumpContent(1); - - for (i=0; iImportantMessage, _("%s has been deleted."), ename); - } - else { - if (strlen(newconfig) > 0) strcat(newconfig, "\n"); - strcat(newconfig, buf); - } - } - - serv_printf("CONF PUTSYS|application/x-citadel-internet-config"); - serv_getln(buf, SIZ); - if (buf[0] == '4') { - serv_puts(newconfig); - if (!strcasecmp(bstr("oper"), "add")) { - serv_printf("%s|%s", bstr("ename"), bstr("etype") ); - sprintf(WC->ImportantMessage, "%s added.", bstr("ename")); - } - serv_puts("000"); - } - - display_inetconf(); - - free(buf); - free(ename); - free(etype); - free(newconfig); -} - - - -/*@}*/ diff --git a/webcit/listsub.c b/webcit/listsub.c deleted file mode 100644 index 3f7e9cf53..000000000 --- a/webcit/listsub.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup ListSubForms Web forms for handling mailing list subscribe/unsubscribe requests. - * \ingroup WebcitDisplayItems - */ - -/*@{*/ -#include "webcit.h" - - - -/** - * \brief List subscription handling - */ -void do_listsub(void) -{ - char cmd[256]; - char room[256]; - char token[256]; - char email[256]; - char subtype[256]; - char escaped_email[256]; - char escaped_room[256]; - - char buf[SIZ]; - int self; - char sroom[SIZ]; - - strcpy(WC->wc_fullname, ""); - strcpy(WC->wc_username, ""); - strcpy(WC->wc_password, ""); - strcpy(WC->wc_roomname, ""); - - output_headers(1, 0, 0, 1, 1, 0); - begin_burst(); - - wprintf("\n" - "\n" - "\n" - "\n" - ); - wprintf(_("List subscription")); - wprintf("\n"); - - strcpy(cmd, bstr("cmd")); - strcpy(room, bstr("room")); - strcpy(token, bstr("token")); - strcpy(email, bstr("email")); - strcpy(subtype, bstr("subtype")); - - wprintf("
" - "
" - ""); - wprintf(_("List subscribe/unsubscribe")); - wprintf("

\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("

"); - wprintf(_("Confirmation request sent")); - wprintf("

"); - wprintf(_("You are subscribing %s" - " to the %s 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.

" - "Please click on the link which is being " - "e-mailed to you and your subscription will " - "be confirmed.
\n"), - escaped_email, escaped_room); - wprintf("%s
\n", _("Go back...")); - } - else { - wprintf("ERROR: %s" - "

\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("

Confirmation request sent

" - "You are unsubscribing "); - escputs(email); - wprintf(" 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.

" - "Please click on the link which is being " - "e-mailed to you and your unsubscription will " - "be confirmed.
\n" - "Back...
\n" - ); - } - else { - wprintf("ERROR: %s" - "

\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("

Confirmation successful!

"); - } - else { - wprintf("

Confirmation failed.

" - "This could mean one of two things:
    \n" - "
  • You waited too long to confirm your " - "subscribe/unsubscribe request (the " - "confirmation link is only valid for three " - "days)\n
  • You have already " - "successfully confirmed your " - "subscribe/unsubscribe request and are " - "attempting to do it again.
\n" - "The error returned by the server was: " - ); - } - wprintf("%s

\n", &buf[4]); - } - - /** - * Any other (invalid) command causes the form to be displayed - */ - else { -FORM: wprintf("
\n" - "\n" - ); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("
Name of list" - "" - "
Your e-mail address" - "
" - "(If subscribing) preferred format: " - "One message at a time  " - "Digest format  " - "
\n" - "\n" - "\n" - "
\n" - ); - - wprintf("
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.
\n" - ); - - } - - wprintf("\n"); - wDumpContent(0); - end_webcit_session(); -} - - - -/*@}*/ diff --git a/webcit/locate_host.c b/webcit/locate_host.c deleted file mode 100644 index 7b5869907..000000000 --- a/webcit/locate_host.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup Hostlookup Examine a socket and determine the name/address of the originating host. - * \ingroup WebcitHttpServer - */ -/*@{*/ - -#include "webcit.h" - -/** - * \brief get a hostname - * \todo buffersize? - * \param tbuf the returnbuffer - * \param client_socket the sock fd where the client is connected - */ -void locate_host(char *tbuf, int client_socket) -{ - struct sockaddr_in cs; - struct hostent *ch; - socklen_t len; - char *i; - int a1, a2, a3, a4; - - len = sizeof(cs); - if (getpeername(client_socket, (struct sockaddr *) &cs, &len) < 0) { - strcpy(tbuf, ""); - return; - } - if ((ch = gethostbyaddr((char *) &cs.sin_addr, sizeof(cs.sin_addr), - AF_INET)) == NULL) { - i = (char *) &cs.sin_addr; - a1 = ((*i++) & 0xff); - a2 = ((*i++) & 0xff); - a3 = ((*i++) & 0xff); - a4 = ((*i++) & 0xff); - sprintf(tbuf, "%d.%d.%d.%d", a1, a2, a3, a4); - return; - } - safestrncpy(tbuf, ch->h_name, 64); -} - -/*@}*/ diff --git a/webcit/mainmenu.c b/webcit/mainmenu.c deleted file mode 100644 index 3f66e6d58..000000000 --- a/webcit/mainmenu.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup DispAdvancedMenu Displays the "advanced" (main) menu. - * \ingroup MenuInfrastructure - * - */ -/*@{*/ -#include "webcit.h" - -/** - * \brief The Main Menu - */ -void display_main_menu(void) -{ - output_headers(1, 1, 1, 0, 0, 0); - - wprintf("
" - "" - "" - "
\n"); - - svprintf("BOXTITLE", WCS_STRING, _("Basic commands")); - do_template("beginbox"); - - wprintf("\n" - "" - "" - "
"); /**< start of first column */ - - wprintf(""); - wprintf(_("List known rooms")); - wprintf("
"); - wprintf(_("Where can I go from here?")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Goto next room")); - wprintf("
" - ""); - wprintf(_("...with unread messages")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Skip to next room")); - wprintf("
" - ""); - wprintf(_("(come back here later)")); - wprintf("\n"); - - if ((strlen(WC->ugname) > 0) && (strcasecmp(WC->ugname, WC->wc_roomname))) { - wprintf("
" - "" - ""); - wprintf(_("Ungoto")); - wprintf("
" - ""); - wprintf(_("(oops! Back to %s)"), WC->ugname); - wprintf("\n"); - } - - wprintf("
\n"); /* start of second column */ - - wprintf("" - ""); - wprintf(_("Read new messages")); - wprintf("
" - ""); - wprintf(_("...in this room")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Read all messages")); - wprintf("
" - ""); - wprintf(_("...old and new")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Enter a message")); - wprintf("
" - ""); - wprintf(_("(post in this room)")); - wprintf("\n"); - - wprintf("
"); /* start of third column */ - - wprintf("" - ""); - wprintf(_("Summary page")); - wprintf("
" - ""); - wprintf(_("Summary of my account")); - wprintf("
\n"); - - wprintf("\n" - ""); - wprintf(_("User list")); - wprintf("
" - ""); - wprintf(_("(all registered users)")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Log off")); - wprintf("
" - ""); - wprintf(_("Bye!")); - wprintf("\n"); - - wprintf("
\n"); - do_template("endbox"); - - wprintf("
"); - - svprintf("BOXTITLE", WCS_STRING, _("Your info")); - do_template("beginbox"); - - wprintf("" - ""); - wprintf(_("Change your preferences and settings")); - wprintf("
\n"); - - wprintf("
" - ""); - wprintf(_("Update your contact information")); - wprintf("
\n"); - - wprintf("
" - ""); - wprintf(_("Change your password")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Enter your 'bio'")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Edit your online photo")); - wprintf("\n"); - - do_template("endbox"); - - wprintf("
"); - - svprintf("BOXTITLE", WCS_STRING, _("Advanced room commands")); - do_template("beginbox"); - - if ((WC->axlevel >= 6) || (WC->is_room_aide)) { - wprintf("" - ""); - wprintf(_("Edit or delete this room")); - wprintf("
\n"); - } - - wprintf("" - ""); - wprintf(_("Go to a 'hidden' room")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Create a new room")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Zap (forget) this room (%s)"), WC->wc_roomname); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("List all forgotten rooms")); - wprintf("\n"); - - do_template("endbox"); - - wprintf("
"); - wDumpContent(2); -} - - -/** - * \brief System administration menu - */ -void display_aide_menu(void) -{ - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n" - "
" - ""); - wprintf(_("System Administration Menu")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("
" - "
"); - - svprintf("BOXTITLE", WCS_STRING, _("Global Configuration")); - do_template("beginbox"); - - wprintf("" - ""); - wprintf(_("Edit site-wide configuration")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Domain names and Internet mail configuration")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Configure replication with other Citadel servers")); - wprintf("\n"); - - do_template("endbox"); - - wprintf("
"); - - svprintf("BOXTITLE", WCS_STRING, _("User account management")); - do_template("beginbox"); - - wprintf("" - ""); - wprintf(_("Add, change, delete user accounts")); - wprintf("
\n"); - - wprintf("" - ""); - wprintf(_("Validate new users")); - wprintf("
\n"); - - do_template("endbox"); - - svprintf("BOXTITLE", WCS_STRING, _("Rooms and Floors")); - do_template("beginbox"); - - wprintf("" - ""); - wprintf(_("Add, change, or delete floors")); - wprintf("\n"); - - do_template("endbox"); - - wprintf("
"); - 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("
\n" - "
" - ""); - wprintf(_("Enter a server command")); - wprintf("
\n" - "
\n
\n" - ); - - wprintf("
" - "
\n"); - - wprintf("
"); - 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("
\n"); - - wprintf("
\n"); - - wprintf(_("Enter command:")); - wprintf("

\n"); - - wprintf(_("Command input (if requesting SEND_LISTING transfer mode):")); - wprintf("

\n"); - - wprintf(""); - wprintf(_("Detected host header is %s://%s"), (is_https ? "https" : "http"), WC->http_host); - wprintf("\n"); - wprintf("", _("Send command")); - wprintf(" "); - wprintf("
\n", _("Cancel")); - - wprintf("
\n"); - wprintf("
\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("
Command:"); - escputs(bstr("g_cmd")); - wprintf("
Result:"); - escputs(buf); - wprintf("

\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("
\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("
"); - wprintf("Enter another command
\n"); - wprintf("Return to menu\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("\n" - "\n" - "MenuBar\n" - "\n" - "\n"); - do_template("background"); - } - - do_template("menubar"); - - if (as_single_page) { - wDumpContent(2); - } - - -} - - -/*@}*/ diff --git a/webcit/messages.c b/webcit/messages.c deleted file mode 100644 index 1be863959..000000000 --- a/webcit/messages.c +++ /dev/null @@ -1,3183 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup MsgDisp Functions which deal with the fetching and displaying of messages. - * \ingroup WebcitDisplayItems - * - */ -/*@{*/ -#include "webcit.h" -#include "vcard.h" -#include "webserver.h" -#include "groupdav.h" - -#define SUBJ_COL_WIDTH_PCT 50 /**< Mailbox view column width */ -#define SENDER_COL_WIDTH_PCT 30 /**< Mailbox view column width */ -#define DATE_PLUS_BUTTONS_WIDTH_PCT 20 /**< Mailbox view column width */ - -/** - * Address book entry (keep it short and sweet, it's just a quickie lookup - * which we can use to get to the real meat and bones later) - */ -struct addrbookent { - char ab_name[64]; /**< name string */ - long ab_msgnum; /**< message number of address book entry */ -}; - - - -#ifdef HAVE_ICONV - -/** - * \brief Wrapper around iconv_open() - * Our version adds aliases for non-standard Microsoft charsets - * such as 'MS950', aliasing them to names like 'CP950' - * - * \param tocode Target encoding - * \param fromcode Source encoding - */ -iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode) -{ - iconv_t ic = (iconv_t)(-1) ; - ic = iconv_open(tocode, fromcode); - if (ic == (iconv_t)(-1) ) { - char alias_fromcode[64]; - if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) { - safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode); - alias_fromcode[0] = 'C'; - alias_fromcode[1] = 'P'; - ic = iconv_open(tocode, alias_fromcode); - } - } - return(ic); -} - - -/** - * \brief Handle subjects with RFC2047 encoding - * such as: - * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?= - * \param buf the stringbuffer to process - */ -void utf8ify_rfc822_string(char *buf) { - char *start, *end; - char newbuf[1024]; - char charset[128]; - char encoding[16]; - char istr[1024]; - iconv_t ic = (iconv_t)(-1) ; - char *ibuf; /**< Buffer of characters to be converted */ - char *obuf; /**< Buffer for converted characters */ - size_t ibuflen; /**< Length of input buffer */ - size_t obuflen; /**< Length of output buffer */ - char *isav; /**< Saved pointer to input buffer */ - char *osav; /**< Saved pointer to output buffer */ - int passes = 0; - int i; - int illegal_non_rfc2047_encoding = 0; - - /** Sometimes, badly formed messages contain strings which were simply - * written out directly in some foreign character set instead of - * using RFC2047 encoding. This is illegal but we will attempt to - * handle it anyway by converting from a user-specified default - * charset to UTF-8 if we see any nonprintable characters. - */ - for (i=0; i 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 126)) { - need_to_encode = 1; - } - } - - if (!need_to_encode) { - safestrncpy(target, source, maxlen); - return; - } - - strcpy(target, "=?UTF-8?Q?"); - for (i=0; i 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"); - 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(""); - return; - } - - wprintf("
"); - 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; jprop[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, "
"); - strcat(mailto, - ""); - - strcat(mailto, "\">"); - stresc(&mailto[strlen(mailto)], thisvalue, 1, 1); - strcat(mailto, ""); - } - else if (!strcasecmp(firsttoken, "tel")) { - if (strlen(phone) > 0) strcat(phone, "
"); - strcat(phone, thisvalue); - for (j=0; j
\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("\n"); - } - ***/ - } - - free(thisname); - free(thisvalue); - } - - if (pass == 1) { - wprintf("" - "\n"); - - if (strlen(phone) > 0) { - wprintf("\n", phone); - } - if (strlen(mailto) > 0) { - wprintf("\n", mailto); - } - } - - } - - wprintf("
"); - wprintf(_("Address:")); - wprintf(""); - for (j=0; j 0) { - escputs(buf); - if (j<3) wprintf("
"); - else wprintf(" "); - } - } - wprintf("
"); - escputs(thisname); - wprintf(""); - escputs(thisvalue); - wprintf("
" - "" - ""); - escputs(fullname); - wprintf(""); - if (strlen(title) > 0) { - wprintf("
"); - escputs(title); - wprintf("
"); - } - if (strlen(org) > 0) { - wprintf("
"); - escputs(org); - wprintf("
"); - } - wprintf("
"); - wprintf(_("Telephone:")); - wprintf("%s
"); - wprintf(_("E-mail:")); - wprintf("%s
\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(""); - wprintf(_("ERROR:")); - wprintf(" %s
\n", &buf[4]); - return; - } - - /** begin everythingamundo table */ - if (!printable_view) { - wprintf("
\n"); - wprintf("
\n"); - } - - /** begin message header table */ - wprintf("\n"); - - /** start msg buttons */ - if (!printable_view) { - wprintf(""); - } - - wprintf("
\n"); - - wprintf(""); - strcpy(m_subject, ""); - strcpy(m_cc, ""); - - while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) { - if (!strcmp(buf, "000")) { - wprintf(""); - wprintf(_("unexpected end of message")); - wprintf("

\n"); - wprintf("
\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(""); - escputs(from); - wprintf(" "); - } - 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), - "", - 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), - "\n" - "%s (%s, %d bytes) [ " - "%s" - " | " - "%s" - " ]
\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(""); -#ifdef HAVE_ICONV - utf8ify_rfc822_string(m_cc); - utf8ify_rfc822_string(m_subject); -#endif - if (strlen(m_cc) > 0) { - wprintf("
" - ""); - wprintf(_("CC:")); - wprintf(" "); - escputs(m_cc); - wprintf(""); - } - if (strlen(m_subject) > 0) { - wprintf("
" - ""); - wprintf(_("Subject:")); - wprintf(" "); - escputs(m_subject); - wprintf(""); - } - wprintf("
\n"); - - /** Reply */ - if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { - wprintf("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] ", _("Reply")); - } - - /** ReplyQuoted */ - if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { - if (!WC->is_mailbox) { - wprintf("[%s] ", _("ReplyQuoted")); - } - } - - /** ReplyAll */ - if (WC->wc_view == VIEW_MAILBOX) { - wprintf("[%s] ", _("ReplyAll")); - } - - /** Forward */ - if (WC->wc_view == VIEW_MAILBOX) { - wprintf("[%s] ", _("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("[%s] ", - msgnum, _("Move")); - - /** Delete */ - wprintf("" - "[%s] ", msgnum, _("Delete this message?"), _("Delete") - ); - } - - /** Headers */ - wprintf("" - "[%s]", msgnum, msgnum, _("Headers")); - - - /** Print */ - wprintf("" - "[%s]", msgnum, msgnum, _("Print")); - - wprintf("
\n"); - - /** Begin body */ - wprintf("
"); - - /** - * Learn the content type - */ - strcpy(mime_content_type, "text/plain"); - while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) { - if (!strcmp(buf, "000")) { - wprintf(""); - wprintf(_("unexpected end of message")); - wprintf("

\n"); - goto ENDBODY; - } - if (!strncasecmp(buf, "Content-type: ", 14)) { - safestrncpy(mime_content_type, &buf[14], - sizeof(mime_content_type)); - for (i=0; i 0) && (isspace(buf[strlen(buf) - 1]))) - buf[strlen(buf) - 1] = 0; - if ((bq == 0) && - ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) { - wprintf("
"); - bq = 1; - } else if ((bq == 1) && - (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) { - wprintf("
"); - bq = 0; - } - wprintf(""); - url(buf); - escputs(buf); - wprintf("
\n"); - } - wprintf("
"); - } - - 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("
\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"); - read_message(msgnum, 1, buf); - wprintf(""); - } - } - - - /** 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("", - msgnum, vcard_partnum); - wprintf("[%s]", _("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("
\n"); - - /** end everythingamundo table */ - if (!printable_view) { - wprintf("
\n"); - wprintf("

\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\n" - "Printable view\n" - "\n" - ); - - read_message(msgnum, 1, ""); - - wprintf("\n\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
", &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("
"); - -#ifdef HAVE_ICONV - utf8ify_rfc822_string(m_subject); -#endif - if (strlen(m_subject) > 0) { - wprintf(_("Subject:")); - wprintf(" "); - msgescputs(m_subject); - wprintf("
"); - } - - /** - * Begin body - */ - wprintf("
"); - } - - /** - * 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 0) && (isspace(buf[strlen(buf) - 1]))) - buf[strlen(buf) - 1] = 0; - if ((bq == 0) && - ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) { - wprintf("
"); - bq = 1; - } else if ((bq == 1) && - (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) { - wprintf("
"); - bq = 0; - } - wprintf(""); - url(buf); - msgescputs(buf); - wprintf("
"); - } - wprintf("
"); - } - - /** 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; ilength = 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("", - WC->summ[num].msgnum, - (WC->summ[num].is_new ? "bold" : "normal"), - WC->summ[num].msgnum - ); - - wprintf("", SUBJ_COL_WIDTH_PCT); - escputs(WC->summ[num].subj); - wprintf(""); - - wprintf("", SENDER_COL_WIDTH_PCT); - escputs(WC->summ[num].from); - wprintf(""); - - wprintf("", DATE_PLUS_BUTTONS_WIDTH_PCT); - fmt_date(datebuf, WC->summ[num].date, 1); /* brief */ - escputs(datebuf); - wprintf(""); - - wprintf("\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("", - msgnum, vcard_partnum); - wprintf("[%s]", _("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; iab_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("


"); - wprintf(_("This address book is empty.")); - wprintf("
\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("", i); - } - else { - wprintf(""); - } - 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("\n"); - } - else { - wprintf("\n"); - } - } - wprintf("
\n"); - - wprintf("\n" - ); - - for (i=0; i 0) { - wprintf("\n"); - } - bg = 1 - bg; - wprintf("", - (bg ? "DDDDDD" : "FFFFFF") - ); - } - - wprintf("\n"); - ++displayed; - } - } - - wprintf("
"); - - wprintf("", bstr("alpha")); - vcard_n_prettyize(addrbook[i].ab_name); - escputs(addrbook[i].ab_name); - wprintf("
\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(""); - if (!strcmp(oper, "readnew")) { - wprintf(_("No new messages.")); - } else if (!strcmp(oper, "readold")) { - wprintf(_("No old messages.")); - } else { - wprintf(_("No messages here.")); - } - wprintf("\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 = "" ; - } - else if (!strcasecmp(sortby, "rsubject")) { - subjsort_button = "" ; - } - else { - subjsort_button = "" ; - } - - if (!strcasecmp(sortby, "sender")) { - sendsort_button = "" ; - } - else if (!strcasecmp(sortby, "rsender")) { - sendsort_button = "" ; - } - else { - sendsort_button = "" ; - } - - if (!strcasecmp(sortby, "date")) { - datesort_button = "" ; - } - else if (!strcasecmp(sortby, "rdate")) { - datesort_button = "" ; - } - else { - datesort_button = "" ; - } - - if (is_summary) { - wprintf("
\n"); /** end of 'content' div */ - - wprintf("\n" - ); - - /** note that Date and Delete are now in the same column */ - wprintf("
" - "
" - "" - "" - ); - wprintf("" - "" - "" - "\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("
%s %s%s %s%s %s" - " " - "" - "
\n"); - - wprintf("
" - - "
\n" - - "" - ); - } - - if (is_notes) { - wprintf("
%s
\n", _("Click on any note to edit it.")); - wprintf("
\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) * - (maxmsgsmsgarr[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" - "\n"); /**< end of 'fix_scrollbar_bug' div */ - wprintf(""); /**< end of 'message_list' div */ - - /** Here's the grab-it-to-resize-the-message-list widget */ - wprintf("
" - "
" - "
" - "
\n" - ); - - wprintf("
"); /**< 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("
"); - wprintf(_("Reading #"), lowest_displayed, highest_displayed); - - wprintf(" "); - wprintf(_("of %d messages."), nummsgs); - - /** forward/reverse */ - wprintf(" 
\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
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("\r\n"); - text_to_server_qp(bstr("msgtext")); /** Transmit message in quoted-printable encoding */ - serv_puts("\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("
\n"); - embed_room_banner(NULL, navbar_none); - wprintf("
\n"); - wprintf("
\n" - "
" - "
"); - - /** 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("%s
\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("%s
\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)], _(" from ")); - 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)], _(" to ")); - stresc(&buf[strlen(buf)], bstr("recp"), 1, 1); - } - */ - - strcat(&buf[strlen(buf)], _(" in ")); - stresc(&buf[strlen(buf)], WC->wc_roomname, 1, 1); - - /** begin message entry screen */ - wprintf("
\n"); - wprintf("\n", now); - if (WC->wc_view == VIEW_WIKI) { - wprintf("\n", bstr("wikipage")); - } - wprintf("\n", bstr("return_to")); - - wprintf("\""); - wprintf("%s\n", buf); /** header bar */ - if (WC->room_flags & QR_ANONOPT) { - wprintf(" " - "", - (is_anonymous ? "checked" : "") - ); - wprintf("Anonymous"); - } - wprintf("
\n"); /** header bar */ - - wprintf("\n"); - if (recipient_required) { - - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - - /** Initialize the autocomplete ajax helpers (found in wclib.js) */ - wprintf(" \n" - ); - } - - wprintf("
"); - wprintf(""); - wprintf(_("To:")); - wprintf(""); - wprintf("" - ""); - wprintf("
"); - wprintf("
"); - wprintf(""); - wprintf(_("CC:")); - wprintf(""); - wprintf("" - ""); - wprintf("
"); - wprintf("
"); - wprintf(""); - wprintf(_("BCC:")); - wprintf(""); - wprintf("" - ""); - wprintf("
"); - wprintf("
"); - wprintf(""); - wprintf(_("Subject (optional):")); - wprintf(""); - wprintf("" - "\n"); - - wprintf(" " - "\n", _("Cancel")); - wprintf("
\n"); - - wprintf("
"); - - wprintf(""); - wprintf("

\n"); - - /** - * The following script embeds the TinyMCE richedit control, and automatically - * transforms the textarea into a richedit textarea. - */ - wprintf( - "\n" - "\n" - ); - - - /** Enumerate any attachments which are already in place... */ - wprintf(" "); - wprintf(_("Attachments:")); - wprintf(" "); - wprintf(""); - - /** Now offer the ability to attach additional files... */ - wprintf("   "); - wprintf(_("Attach file:")); - wprintf(" \n  " - "\n", _("Add")); - - /** Seth asked for these to be at the top *and* bottom... */ - wprintf(" " - "\n", _("Cancel")); - - /** Make sure we only insert our signature once */ - if (strcmp(bstr("sig_inserted"), "yes")) { - wprintf("\n"); - } - - wprintf("
\n"); - - wprintf("
\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("%s
\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("
\n"); - wprintf("
"); - wprintf(""); - wprintf(_("Confirm move of message")); - wprintf("\n"); - wprintf("
\n"); - wprintf("
\n
\n"); - - wprintf("
"); - - wprintf(_("Move this message to:")); - wprintf("
\n"); - - wprintf("
\n"); - wprintf("\n", bstr("msgid")); - - wprintf("\n"); - wprintf("
\n"); - - wprintf("", _("Move")); - wprintf(" "); - wprintf("", _("Cancel")); - wprintf("
\n"); - - wprintf("
\n"); - wDumpContent(1); -} - - -/** - * \brief move a message to another folder - */ -void move_msg(void) -{ - long msgid; - char buf[SIZ]; - - msgid = atol(bstr("msgid")); - - if (strlen(bstr("move_button")) > 0) { - sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room")); - serv_puts(buf); - serv_getln(buf, sizeof buf); - sprintf(WC->ImportantMessage, "%s", &buf[4]); - } else { - sprintf(WC->ImportantMessage, (_("The message was not moved."))); - } - - readloop("readnew"); - -} - - -/*@}*/ diff --git a/webcit/mime_parser.c b/webcit/mime_parser.c deleted file mode 100644 index 02edd1c58..000000000 --- a/webcit/mime_parser.c +++ /dev/null @@ -1,662 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup MIME This is the MIME parser for Citadel. - * - * Copyright (c) 1998-2005 by Art Cancro - * This code is distributed under the terms of the GNU General Public License. - * \ingroup WebcitHttpServer - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" -#include "mime_parser.h" - -/** - * \brief get mime key - * \param target where to put the mime buffer at??? - * \param source where to extract the mimetype from - * \param key what??? - */ -void extract_key(char *target, char *source, char *key) -{ - int a, b; - - strcpy(target, source); - for (a = 0; a < strlen(target); ++a) { - if ((!strncasecmp(&target[a], key, strlen(key))) - && (target[a + strlen(key)] == '=')) { - strcpy(target, &target[a + strlen(key) + 1]); - if (target[0] == 34) - strcpy(target, &target[1]); - for (b = 0; b < strlen(target); ++b) - if (target[b] == 34) - target[b] = 0; - return; - } - } - strcpy(target, ""); -} - - -/** - * \brief For non-multipart messages, we need to generate a quickie partnum of "1" - * to return to callback functions. Some callbacks demand it. - * \param supplied_partnum partnum to convert - * \return the converted num - */ -char *fixed_partnum(char *supplied_partnum) { - if (supplied_partnum == NULL) return "1"; - if (strlen(supplied_partnum)==0) return "1"; - return supplied_partnum; -} - - - -/** - * \brief Convert "quoted-printable" to binary. Returns number of bytes decoded. - * \param decoded the buffer with the decoded output - * \param encoded the encoded string to decode - * \param sourcelen length of the decoded buffer - */ -int CtdlDecodeQuotedPrintable(char *decoded, char *encoded, int sourcelen) { - char buf[SIZ]; - int buf_length = 0; - int soft_line_break = 0; - unsigned int ch; - int decoded_length = 0; - int i; - - decoded[0] = 0; - decoded_length = 0; - buf[0] = 0; - buf_length = 0; - - for (i = 0; i < sourcelen; ++i) { - - buf[buf_length++] = encoded[i]; - - if ( (encoded[i] == '\n') - || (encoded[i] == 0) - || (i == (sourcelen-1)) ) { - buf[buf_length++] = 0; - - /*** begin -- process one line ***/ - - if (buf[strlen(buf)-1] == '\n') { - buf[strlen(buf)-1] = 0; - } - if (buf[strlen(buf)-1] == '\r') { - buf[strlen(buf)-1] = 0; - } - while (isspace(buf[strlen(buf)-1])) { - buf[strlen(buf)-1] = 0; - } - soft_line_break = 0; - - while (strlen(buf) > 0) { - if (!strcmp(buf, "=")) { - soft_line_break = 1; - strcpy(buf, ""); - } else if ((strlen(buf)>=3) && (buf[0]=='=')) { - sscanf(&buf[1], "%02x", &ch); - decoded[decoded_length++] = ch; - strcpy(buf, &buf[3]); - } else { - decoded[decoded_length++] = buf[0]; - strcpy(buf, &buf[1]); - } - } - if (soft_line_break == 0) { - decoded[decoded_length++] = '\r'; - decoded[decoded_length++] = '\n'; - } - buf_length = 0; - /*** end -- process one line ***/ - } - } - - decoded[decoded_length++] = 0; - return(decoded_length); -} - -/** - * \brief fully decode a message - * Given a message or message-part body and a length, handle any necessary - * decoding and pass the request up the stack. - * \param partnum todo ????? - * \param part_start todo - * \param length todo - * \param content_type todo - * \param charset todo - * \param encoding todo - * \param disposition todo - * \param name todo - * \param filename todo - * \param CallBack todo - * \param PreMultiPartCallBack todo - * \param PostMultiPartCallBack todo - * \param userdata todo - * \param dont_decode todo - */ -void mime_decode(char *partnum, - char *part_start, size_t length, - char *content_type, char *charset, char *encoding, - char *disposition, - char *name, char *filename, - void (*CallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - void (*PreMultiPartCallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - void (*PostMultiPartCallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - void *userdata, - int dont_decode -) -{ - - char *decoded; - size_t bytes_decoded = 0; - - /* Some encodings aren't really encodings */ - if (!strcasecmp(encoding, "7bit")) - strcpy(encoding, ""); - if (!strcasecmp(encoding, "8bit")) - strcpy(encoding, ""); - if (!strcasecmp(encoding, "binary")) - strcpy(encoding, ""); - - /* If this part is not encoded, send as-is */ - if ( (strlen(encoding) == 0) || (dont_decode)) { - if (CallBack != NULL) { - CallBack(name, filename, fixed_partnum(partnum), - disposition, part_start, - content_type, charset, length, encoding, userdata); - } - return; - } - - if ((strcasecmp(encoding, "base64")) - && (strcasecmp(encoding, "quoted-printable"))) { - return; - } - /** - * Allocate a buffer for the decoded data. The output buffer is the - * same size as the input buffer; this assumes that the decoded data - * will never be larger than the encoded data. This is a safe - * assumption with base64, uuencode, and quoted-printable. - */ - decoded = malloc(length+2048); - if (decoded == NULL) { - return; - } - - if (!strcasecmp(encoding, "base64")) { - bytes_decoded = CtdlDecodeBase64(decoded, part_start, length); - } - else if (!strcasecmp(encoding, "quoted-printable")) { - bytes_decoded = CtdlDecodeQuotedPrintable(decoded, - part_start, length); - } - - if (bytes_decoded > 0) if (CallBack != NULL) { - CallBack(name, filename, fixed_partnum(partnum), - disposition, decoded, - content_type, charset, bytes_decoded, "binary", userdata); - } - - free(decoded); -} - -/** - * \brief Break out the components of a multipart message - * (This function expects to be fed HEADERS + CONTENT) - * Note: NULL can be supplied as content_end; in this case, the message is - * considered to have ended when the parser encounters a 0x00 byte. - * \param partnum todo - * \param content_start todo ????? - * \param content_end todo - * \param CallBack todo - * \param PreMultiPartCallBack - * \param PostMultiPartCallBack - * \param userdata todo - * \param dont_decode todo - */ -void the_mime_parser(char *partnum, - char *content_start, char *content_end, - void (*CallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - void (*PreMultiPartCallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - void (*PostMultiPartCallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - void *userdata, - int dont_decode -) -{ - - char *ptr; - char *srch = NULL; - char *part_start, *part_end = NULL; - char buf[SIZ]; - char *header; - char *boundary; - char *startary; - size_t startary_len = 0; - char *endary; - char *next_boundary; - char *content_type; - char *charset; - size_t content_length; - char *encoding; - char *disposition; - char *name = NULL; - char *content_type_name; - char *content_disposition_name; - char *filename; - int is_multipart; - int part_seq = 0; - int i; - size_t length; - char nested_partnum[SIZ]; - - ptr = content_start; - content_length = 0; - - boundary = malloc(SIZ); - memset(boundary, 0, SIZ); - - startary = malloc(SIZ); - memset(startary, 0, SIZ); - - endary = malloc(SIZ); - memset(endary, 0, SIZ); - - header = malloc(SIZ); - memset(header, 0, SIZ); - - content_type = malloc(SIZ); - memset(content_type, 0, SIZ); - - charset = malloc(SIZ); - memset(charset, 0, SIZ); - - encoding = malloc(SIZ); - memset(encoding, 0, SIZ); - - content_type_name = malloc(SIZ); - memset(content_type_name, 0, SIZ); - - content_disposition_name = malloc(SIZ); - memset(content_disposition_name, 0, SIZ); - - filename = malloc(SIZ); - memset(filename, 0, SIZ); - - disposition = malloc(SIZ); - memset(disposition, 0, SIZ); - - /** If the caller didn't supply an endpointer, generate one by measure */ - if (content_end == NULL) { - content_end = &content_start[strlen(content_start)]; - } - - /** Learn interesting things from the headers */ - strcpy(header, ""); - do { - ptr = memreadline(ptr, buf, SIZ); - if (ptr >= content_end) { - goto end_parser; - } - - for (i = 0; i < strlen(buf); ++i) { - if (isspace(buf[i])) { - buf[i] = ' '; - } - } - - if (!isspace(buf[0])) { - if (!strncasecmp(header, "Content-type: ", 14)) { - strcpy(content_type, &header[14]); - extract_key(content_type_name, content_type, "name"); - extract_key(charset, content_type, "charset"); - /** Deal with weird headers */ - if (strchr(content_type, ' ')) - *(strchr(content_type, ' ')) = '\0'; - if (strchr(content_type, ';')) - *(strchr(content_type, ';')) = '\0'; - } - if (!strncasecmp(header, "Content-Disposition: ", 21)) { - strcpy(disposition, &header[21]); - extract_key(content_disposition_name, disposition, "name"); - extract_key(filename, disposition, "filename"); - } - if (!strncasecmp(header, "Content-length: ", 16)) { - content_length = (size_t) atol(&header[16]); - } - if (!strncasecmp(header, - "Content-transfer-encoding: ", 27)) - strcpy(encoding, &header[27]); - if (strlen(boundary) == 0) - extract_key(boundary, header, "boundary"); - strcpy(header, ""); - } - if ((strlen(header) + strlen(buf) + 2) < SIZ) - strcat(header, buf); - } while ((strlen(buf) > 0) && (*ptr != 0)); - - if (strchr(disposition, ';')) - *(strchr(disposition, ';')) = '\0'; - striplt(disposition); - if (strchr(content_type, ';')) - *(strchr(content_type, ';')) = '\0'; - striplt(content_type); - - if (strlen(boundary) > 0) { - is_multipart = 1; - } else { - is_multipart = 0; - } - - /** If this is a multipart message, then recursively process it */ - part_start = NULL; - if (is_multipart) { - - /** Tell the client about this message's multipartedness */ - if (PreMultiPartCallBack != NULL) { - PreMultiPartCallBack("", "", partnum, "", - NULL, content_type, charset, - 0, encoding, userdata); - } - - /** Figure out where the boundaries are */ - snprintf(startary, SIZ, "--%s", boundary); - snprintf(endary, SIZ, "--%s--", boundary); - startary_len = strlen(startary); - - part_start = NULL; - do { - next_boundary = NULL; - for (srch=ptr; srch 0) { - snprintf(nested_partnum, - sizeof nested_partnum, - "%s.%d", partnum, - ++part_seq); - } - else { - snprintf(nested_partnum, - sizeof nested_partnum, - "%d", ++part_seq); - } - the_mime_parser(nested_partnum, - part_start, part_end, - CallBack, - PreMultiPartCallBack, - PostMultiPartCallBack, - userdata, - dont_decode); - } - - if (next_boundary != NULL) { - /** - * If we pass out of scope, don't attempt to - * read past the end boundary. */ - if (!strcmp(next_boundary, endary)) { - ptr = content_end; - } - else { - /** Set up for the next part. */ - part_start = strstr(next_boundary, "\n"); - ++part_start; - ptr = part_start; - } - } - else { - /** Invalid end of multipart. Bail out! */ - ptr = content_end; - } - } while ( (ptr < content_end) && (next_boundary != NULL) ); - - if (PostMultiPartCallBack != NULL) { - PostMultiPartCallBack("", "", partnum, "", NULL, - content_type, charset, 0, encoding, userdata); - } - goto end_parser; - } - - /** If it's not a multipart message, then do something with it */ - if (!is_multipart) { - part_start = ptr; - length = 0; - while (ptr < content_end) { - ++ptr; - ++length; - } - part_end = content_end; - /** fix an off-by-one error */ - --part_end; - --length; - - /** Truncate if the header told us to */ - if ( (content_length > 0) && (length > content_length) ) { - length = content_length; - } - - /** - * Sometimes the "name" field is tacked on to Content-type, - * and sometimes it's tacked on to Content-disposition. Use - * whichever one we have. - */ - if (strlen(content_disposition_name) > strlen(content_type_name)) { - name = content_disposition_name; - } - else { - name = content_type_name; - } - - /* - lprintf(9, "mime_decode part=%s, len=%d, type=%s, charset=%s, encoding=%s\n", - partnum, length, content_type, charset, encoding); - */ - - /** - * Ok, we've got a non-multipart part here, so do something with it. - */ - mime_decode(partnum, - part_start, length, - content_type, charset, encoding, disposition, - name, filename, - CallBack, NULL, NULL, - userdata, dont_decode - ); - - /** - * Now if it's an encapsulated message/rfc822 then we have to recurse into it - */ - if (!strcasecmp(content_type, "message/rfc822")) { - - if (PreMultiPartCallBack != NULL) { - PreMultiPartCallBack("", "", partnum, "", - NULL, content_type, charset, - 0, encoding, userdata); - } - if (CallBack != NULL) { - if (strlen(partnum) > 0) { - snprintf(nested_partnum, - sizeof nested_partnum, - "%s.%d", partnum, - ++part_seq); - } - else { - snprintf(nested_partnum, - sizeof nested_partnum, - "%d", ++part_seq); - } - the_mime_parser(nested_partnum, - part_start, part_end, - CallBack, - PreMultiPartCallBack, - PostMultiPartCallBack, - userdata, - dont_decode - ); - } - if (PostMultiPartCallBack != NULL) { - PostMultiPartCallBack("", "", partnum, "", NULL, - content_type, charset, 0, encoding, userdata); - } - - - } - - } - -end_parser: /** free the buffers! end the oppression!! */ - free(boundary); - free(startary); - free(endary); - free(header); - free(content_type); - free(charset); - free(encoding); - free(content_type_name); - free(content_disposition_name); - free(filename); - free(disposition); -} - - - -/** - * \brief Entry point for the MIME parser. - * (This function expects to be fed HEADERS + CONTENT) - * Note: NULL can be supplied as content_end; in this case, the message is - * considered to have ended when the parser encounters a 0x00 byte. - * \param content_start todo ????????? - * \param content_end todo - * \param CallBack todo - * \param PreMultiPartCallBack todo - * \param PostMultiPartCallBack todo - * \param userdata todo - * \param dont_decode todo - */ -void mime_parser(char *content_start, - char *content_end, - - void (*CallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - - void (*PreMultiPartCallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - - void (*PostMultiPartCallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - - void *userdata, - int dont_decode -) -{ - - the_mime_parser("", content_start, content_end, - CallBack, - PreMultiPartCallBack, - PostMultiPartCallBack, - userdata, dont_decode); -} - - - -/*@}*/ diff --git a/webcit/mime_parser.h b/webcit/mime_parser.h deleted file mode 100644 index b82cd6884..000000000 --- a/webcit/mime_parser.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * $Id$ - * - */ - -/* - * Here's a bunch of stupid magic to make the MIME parser portable between - * Citadel and WebCit. - */ -#ifndef SIZ -#define SIZ 4096 -#endif - - -/* - * Declarations for functions in the parser - */ - -void extract_key(char *target, char *source, char *key); - -void mime_parser(char *content_start, char *content_end, - void (*CallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - void (*PreMultiPartCallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - void (*PostMultiPartCallBack) - (char *cbname, - char *cbfilename, - char *cbpartnum, - char *cbdisp, - void *cbcontent, - char *cbtype, - char *cbcharset, - size_t cblength, - char *cbencoding, - void *cbuserdata), - void *userdata, - int dont_decode - ); diff --git a/webcit/netconf.c b/webcit/netconf.c deleted file mode 100644 index f3802961a..000000000 --- a/webcit/netconf.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup NetShareConf Functions which handle network and sharing configuration. - * - * \ingroup CitadelConfig - */ -/*@{*/ -#include "webcit.h" - -/** - * \brief edit a network node - */ -void edit_node(void) { - char buf[SIZ]; - char node[SIZ]; - char cnode[SIZ]; - FILE *fp; - - if (strlen(bstr("ok_button")) > 0) { - strcpy(node, bstr("node") ); - fp = tmpfile(); - if (fp != NULL) { - serv_puts("CONF getsys|application/x-citadel-ignet-config"); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') { - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - extract_token(cnode, buf, 0, '|', sizeof cnode); - if (strcasecmp(node, cnode)) { - fprintf(fp, "%s\n", buf); - } - } - fprintf(fp, "%s|%s|%s|%s\n", - bstr("node"), - bstr("secret"), - bstr("host"), - bstr("port") ); - } - rewind(fp); - - serv_puts("CONF putsys|application/x-citadel-ignet-config"); - serv_getln(buf, sizeof buf); - if (buf[0] == '4') { - while (fgets(buf, sizeof buf, fp) != NULL) { - buf[strlen(buf)-1] = 0; - serv_puts(buf); - } - serv_puts("000"); - } - fclose(fp); - } - } - - display_netconf(); -} - - -/** - * \brief add a node - */ -void display_add_node(void) -{ - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n"); - wprintf("
"); - wprintf(""); - wprintf(_("Add a new node")); - wprintf(""); - wprintf("
\n"); - wprintf("
\n
\n"); - - wprintf("
\n"); - wprintf("
\n"); - wprintf("", _("Node name")); - wprintf("\n"); - wprintf("", _("Shared secret")); - wprintf("\n"); - wprintf("", _("Host or IP address")); - wprintf("\n"); - wprintf("", _("Port number")); - wprintf("\n"); - wprintf("
%s
%s
%s
%s

"); - wprintf("", _("Add node")); - wprintf(" "); - wprintf("", _("Cancel")); - wprintf("
\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("
\n"); - wprintf("
"); - wprintf(""); - wprintf(_("Edit node configuration for ")); - escputs(node); - wprintf("\n"); - wprintf("
\n"); - wprintf("
\n
\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("
\n"); - wprintf("
\n"); - wprintf(""); - wprintf("\n", cnode); - wprintf(""); - wprintf("\n", csecret); - wprintf(""); - wprintf("\n", chost); - wprintf(""); - wprintf("\n", cport); - wprintf("
"); - wprintf(_("Node name")); - wprintf("
"); - wprintf(_("Shared secret")); - wprintf("
"); - wprintf(_("Host or IP address")); - wprintf("
"); - wprintf(_("Port number")); - wprintf("

"); - wprintf("", - _("Save changes")); - wprintf(" "); - wprintf("", - _("Cancel")); - wprintf("
\n"); - } - - } - } - - else { /** command error getting configuration */ - wprintf("%s
\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("
\n"); - wprintf("
"); - wprintf(""); - wprintf(_("Network configuration")); - wprintf("\n"); - wprintf("
\n"); - wprintf("
\n
\n"); - - wprintf("
"); - wprintf(""); - wprintf(_("Add a new node")); - wprintf("
\n"); - wprintf("
"); - - wprintf("
"); - wprintf(""); - wprintf(_("Currently configured nodes")); - wprintf("\n"); - wprintf("
\n"); - serv_puts("CONF getsys|application/x-citadel-ignet-config"); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') { - wprintf("
\n"); - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - extract_token(node, buf, 0, '|', sizeof node); - wprintf(""); - wprintf(""); - wprintf(""); - wprintf("\n"); - } - wprintf("
"); - escputs(node); - wprintf(""); - wprintf(_("(Edit)")); - wprintf(""); - wprintf(_("(Delete)")); - wprintf("
\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("
\n"); - wprintf("
"); - wprintf(""); - wprintf(_("Confirm delete")); - wprintf("\n"); - wprintf("
\n"); - wprintf("
\n
\n"); - - strcpy(node, bstr("node")); - wprintf("
"); - wprintf(_("Are you sure you want to delete ")); - wprintf(""); - escputs(node); - wprintf("?
\n"); - wprintf(""); - wprintf(_("Yes")); - wprintf("   "); - wprintf(""); - wprintf(_("No")); - wprintf("
\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(""); - wprintf(_("Back to menu")); - wprintf("\n"); - wDumpContent(1); - } else { - strcpy(WC->ImportantMessage, &buf[4]); - display_netconf(); - } - } -} - - -/*@}*/ diff --git a/webcit/notes.c b/webcit/notes.c deleted file mode 100644 index 137480a52..000000000 --- a/webcit/notes.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup StickyNotes Functions which handle "sticky notes" - * \ingroup WebcitDisplayItems - */ -/*@{*/ -#include "webcit.h" -#include "groupdav.h" -#include "webserver.h" - -/** - * \brief display sticky notes - * \param msgnum the citadel mesage number - */ -void display_note(long msgnum) -{ - char buf[SIZ]; - char notetext[SIZ]; - char display_notetext[SIZ]; - char eid[128]; - int in_text = 0; - int i; - - wprintf("\n"); - - serv_printf("MSG0 %ld", msgnum); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - wprintf("%s
\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 0) { - wprintf("%s
\n", eid, display_notetext); - } - else { - wprintf("%s
\n", msgnum, display_notetext); - } - - /** Offer in-place editing. */ - if (strlen(eid) > 0) { - wprintf("\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\n" - "
" - ""); - wprintf(_("Send instant message")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("
" - "
\n"); - - wprintf(_("Send an instant message to: ")); - escputs(recp); - wprintf("
\n"); - - wprintf("
\n"); - - wprintf("
\n"); - - wprintf("\n"); - - wprintf("\n"); - - wprintf(_("Enter message text:")); - wprintf("
"); - - wprintf("\n"); - - wprintf("

\n"); - - wprintf("", _("Send message")); - wprintf("
\n", _("Cancel")); - - wprintf("\n"); - wprintf("
\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("
\n" - "
" - ""); - wprintf(_("Add or edit an event")); - wprintf("" - "
\n" - "
\n
\n" - ); - - strcpy(recp, bstr("recp")); - strcpy(closewin, bstr("closewin")); - - if (strlen(bstr("send_button")) == 0) { - wprintf(""); - wprintf(_("Message was not sent.")); - wprintf("
\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(""); - wprintf(_("Message has been sent to ")); - escputs(recp); - wprintf(".
\n"); - } - else { - wprintf("%s
\n", &buf[4]); - } - } - - if (!strcasecmp(closewin, "yes")) { - wprintf("
"); - wprintf(_("[ close window ]")); - wprintf("
\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("\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("" - ); - } - } - - /** Then schedule it to happen again a minute from now if the user is idle. */ - wprintf(" " - ); -} - - - -/** - * \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("\n" - "\n" - "\n" - "\n" - - "\n" - ); - - if (setup_chat_socket() != 0) { - wprintf(_("An error occurred while setting up the chat socket.")); - wprintf("\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("\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("last_chat_user)) { - wprintf("" - "
" - ); - - } - - wprintf(""); - - wprintf("
"); - - if (!strcasecmp(cl_user, ":")) { - wprintf(""); - } - - if (strcasecmp(cl_user, WC->last_chat_user)) { - wprintf(""); - - if (!strcasecmp(cl_user, WC->wc_fullname)) { - wprintf(""); - } - else { - wprintf(""); - } - jsescputs(cl_user); - - wprintf(": "); - } - else { - wprintf("   "); - } - jsescputs(cl_text); - if (!strcasecmp(cl_user, ":")) { - wprintf(""); - } - - wprintf("
"); - wprintf("'); \n"); - - strcpy(WC->last_chat_user, cl_user); - } - } - - wprintf("parent.chat_transcript.scrollTo(999999,999999);\">\n"); - } - - free(output_data); - - wprintf("\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("" - "" - ); - - 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("\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("
\n"); - wprintf("\n", SIZ-10); - wprintf("
"); - wprintf("\n", _("Send")); - wprintf("\n", _("Help")); - wprintf("\n", _("List users")); - wprintf("\n", _("Exit")); - wprintf("
\n"); - - wprintf("\n"); - wDumpContent(0); -} - -/*@}*/ diff --git a/webcit/preferences.c b/webcit/preferences.c deleted file mode 100644 index a21468c63..000000000 --- a/webcit/preferences.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup ManagePrefs Manage user preferences with a little help from the Citadel server. - * \ingroup CitadelConfig - * - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" -#include "groupdav.h" - - -/** - * \brief display preferences dialog - */ -void load_preferences(void) { - char buf[SIZ]; - long msgnum = 0L; - - serv_printf("GOTO %s", USERCONFIGROOM); - serv_getln(buf, sizeof buf); - if (buf[0] != '2') return; - - serv_puts("MSGS ALL|0|1"); - serv_getln(buf, sizeof buf); - if (buf[0] == '8') { - serv_puts("subj|__ WebCit Preferences __"); - serv_puts("000"); - } - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - msgnum = atol(buf); - } - - if (msgnum > 0L) { - serv_printf("MSG0 %ld", msgnum); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') { - while (serv_getln(buf, sizeof buf), - (strcmp(buf, "text") && strcmp(buf, "000"))) { - } - if (!strcmp(buf, "text")) { - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - if (WC->preferences == NULL) { - WC->preferences = malloc(SIZ); - strcpy(WC->preferences, ""); - } - else { - WC->preferences = realloc( - WC->preferences, - strlen(WC->preferences) - +SIZ - ); - } - strcat(WC->preferences, buf); - strcat(WC->preferences, "\n"); - } - } - } - } - - /** Go back to the room we're supposed to be in */ - serv_printf("GOTO %s", WC->wc_roomname); - serv_getln(buf, sizeof buf); -} - -/** - * \brief Goto the user's configuration room, creating it if necessary. - * \return 0 on success or nonzero upon failure. - */ -int goto_config_room(void) { - char buf[SIZ]; - - serv_printf("GOTO %s", USERCONFIGROOM); - serv_getln(buf, sizeof buf); - if (buf[0] != '2') { /* try to create the config room if not there */ - serv_printf("CRE8 1|%s|4|0", USERCONFIGROOM); - serv_getln(buf, sizeof buf); - serv_printf("GOTO %s", USERCONFIGROOM); - serv_getln(buf, sizeof buf); - if (buf[0] != '2') return(1); - } - return(0); -} - -/** - * \brief save the modifications - */ -void save_preferences(void) { - char buf[SIZ]; - long msgnum = 0L; - - if (goto_config_room() != 0) return; /* oh well. */ - serv_puts("MSGS ALL|0|1"); - serv_getln(buf, sizeof buf); - if (buf[0] == '8') { - serv_puts("subj|__ WebCit Preferences __"); - serv_puts("000"); - } - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - msgnum = atol(buf); - } - - if (msgnum > 0L) { - serv_printf("DELE %ld", msgnum); - serv_getln(buf, sizeof buf); - } - - serv_printf("ENT0 1||0|1|__ WebCit Preferences __|"); - serv_getln(buf, sizeof buf); - if (buf[0] == '4') { - serv_puts(WC->preferences); - serv_puts(""); - serv_puts("000"); - } - - /** Go back to the room we're supposed to be in */ - serv_printf("GOTO %s", WC->wc_roomname); - serv_getln(buf, sizeof buf); -} - -/** - * \brief query the actual setting of key in the citadel database - * \param key config key to query - * \param value value to the key to get - * \param value_len length of the value string - */ -void get_preference(char *key, char *value, size_t value_len) { - int num_prefs; - int i; - char buf[SIZ]; - char thiskey[SIZ]; - - strcpy(value, ""); - - num_prefs = num_tokens(WC->preferences, '\n'); - for (i=0; ipreferences, 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; ipreferences, 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("
\n"); - wprintf("
"); - wprintf("\""); - wprintf(" "); - wprintf(_("Preferences and settings")); - wprintf(""); - offer_start_page(); - wprintf("
\n"); - wprintf("
\n" - "
\n"); - - wprintf("
" - "
\n"); - - /** begin form */ - wprintf("
\n" - "
\n" - "\n"); - - /** - * Room list view - */ - get_preference("roomlistview", buf, sizeof buf); - wprintf("\n"); - - /** - * Calendar hour format - */ - get_preference("calhourformat", calhourformat, sizeof calhourformat); - if (calhourformat[0] == 0) strcpy(calhourformat, "12"); - wprintf("\n"); - - /** - * Calendar day view -- day start time - */ - get_preference("daystart", buf, sizeof buf); - if (buf[0] == 0) strcpy(buf, "8"); - wprintf("\n"); - - /** - * Calendar day view -- day end time - */ - get_preference("dayend", buf, sizeof buf); - if (buf[0] == 0) strcpy(buf, "17"); - wprintf("\n"); - - /** - * Signature - */ - get_preference("use_sig", buf, sizeof buf); - if (buf[0] == 0) strcpy(buf, "no"); - wprintf("\n"); - - wprintf(" " - ); - - /** 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(""); - - /** submit buttons */ - wprintf("
"); - wprintf(_("Room list view")); - wprintf(""); - - wprintf(""); - wprintf(_("Tree (folders) view")); - wprintf("
\n"); - - wprintf(""); - wprintf(_("Table (rooms) view")); - wprintf("
\n"); - - wprintf("
"); - wprintf(_("Calendar hour format")); - wprintf(""); - - wprintf(""); - wprintf(_("12 hour (am/pm)")); - wprintf("
\n"); - - wprintf(""); - wprintf(_("24 hour")); - wprintf("
\n"); - - wprintf("
"); - wprintf(_("Calendar day view begins at:")); - wprintf(""); - - wprintf("\n"); - wprintf("
"); - wprintf(_("Calendar day view ends at:")); - wprintf(""); - - wprintf("\n"); - wprintf("
"); - wprintf(_("Attach signature to email messages?")); - wprintf(""); - - wprintf(" " - ); - - wprintf(""); - wprintf(_("No signature")); - wprintf("
\n"); - - wprintf(""); - wprintf(_("Use this signature:")); - wprintf("
" - "
" - "
" - ); - - wprintf("
\n"); - - wprintf("
"); - wprintf(_("Default character set for email headers:")); - wprintf(""); - wprintf("", buf); - wprintf("
\n" - "" - " " - "\n", - _("Change"), - _("Cancel") - ); - - /** end form */ - wprintf("
\n"); - wprintf("
\n"); - wDumpContent(1); -} - -/** - * \brief Commit new preferences and settings - */ -void set_preferences(void) -{ - char ebuf[300]; - - if (strlen(bstr("change_button")) == 0) { - safestrncpy(WC->ImportantMessage, - _("Cancelled. No settings were changed."), - sizeof WC->ImportantMessage); - display_main_menu(); - return; - } - - /** - * Set the last argument to 1 only for the final setting, so - * we don't send the prefs file to the server repeatedly - */ - set_preference("roomlistview", bstr("roomlistview"), 0); - set_preference("calhourformat", bstr("calhourformat"), 0); - set_preference("use_sig", bstr("use_sig"), 0); - set_preference("daystart", bstr("daystart"), 0); - set_preference("dayend", bstr("dayend"), 0); - set_preference("default_header_charset", bstr("default_header_charset"), 0); - - euid_escapize(ebuf, bstr("signature")); - set_preference("signature", ebuf, 1); - - display_main_menu(); -} - - -/*@}*/ diff --git a/webcit/roomops.c b/webcit/roomops.c deleted file mode 100644 index a7993a062..000000000 --- a/webcit/roomops.c +++ /dev/null @@ -1,3052 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup RoomOps Lots of different room-related operations. - * \ingroup CitadelCommunitacion - */ -/*@{*/ -#include "webcit.h" - -char floorlist[128][SIZ]; /**< list of our floor names */ - -char *viewdefs[8]; /**< the different kinds of available views */ - -/** - * \brief initialize the viewdefs with localized strings - */ -void initialize_viewdefs(void) { - viewdefs[0] = _("Bulletin Board"); - viewdefs[1] = _("Mail Folder"); - viewdefs[2] = _("Address Book"); - viewdefs[3] = _("Calendar"); - viewdefs[4] = _("Task List"); - viewdefs[5] = _("Notes List"); - viewdefs[6] = _("Wiki"); - viewdefs[7] = _("Calendar List"); -} - -/** - * \brief Determine which views are allowed as the default for creating a new room. - * - * \param which_view The view ID being queried. - */ -int is_view_allowed_as_default(int which_view) -{ - switch(which_view) { - case VIEW_BBS: return(1); - case VIEW_MAILBOX: return(1); - case VIEW_ADDRESSBOOK: return(1); - case VIEW_CALENDAR: return(1); - case VIEW_TASKS: return(1); - case VIEW_NOTES: return(1); - case VIEW_WIKI: return(0); /**< because it isn't finished yet */ - case VIEW_CALBRIEF: return(0); - default: return(0); /**< should never get here */ - } -} - - -/** - * \brief load the list of floors - */ -void load_floorlist(void) -{ - int a; - char buf[SIZ]; - - for (a = 0; a < 128; ++a) - floorlist[a][0] = 0; - - serv_puts("LFLR"); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - strcpy(floorlist[0], "Main Floor"); - return; - } - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - extract_token(floorlist[extract_int(buf, 0)], buf, 1, '|', sizeof floorlist[0]); - } -} - - -/** - * \brief Free a session's march list - * - * \param wcf Pointer to session being cleared - */ -void free_march_list(struct wcsession *wcf) -{ - struct march *mptr; - - while (wcf->march != NULL) { - mptr = wcf->march->next; - free(wcf->march); - wcf->march = mptr; - } - -} - - - -/** - * \brief remove a room from the march list - */ -void remove_march(char *aaa) -{ - struct march *mptr, *mptr2; - - if (WC->march == NULL) - return; - - if (!strcasecmp(WC->march->march_name, aaa)) { - mptr = WC->march->next; - free(WC->march); - WC->march = mptr; - return; - } - mptr2 = WC->march; - for (mptr = WC->march; mptr != NULL; mptr = mptr->next) { - if (!strcasecmp(mptr->march_name, aaa)) { - mptr2->next = mptr->next; - free(mptr); - mptr = mptr2; - } else { - mptr2 = mptr; - } - } -} - - - - -/** - * \brief display rooms in tree structure??? - * \param rp the roomlist to build a tree from - */ -void room_tree_list(struct roomlisting *rp) -{ - char rmname[64]; - int f; - - if (rp == NULL) { - return; - } - - room_tree_list(rp->lnext); - - strcpy(rmname, rp->rlname); - f = rp->rlflags; - - wprintf(""); - 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(" \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("

\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("wc_roomname); - wprintf("\">"); - serv_puts("CLOS"); - serv_getln(buf, sizeof buf); - } - else if (WC->wc_view == VIEW_ADDRESSBOOK) { - wprintf("" - ); - } - else if ( (WC->wc_view == VIEW_CALENDAR) || (WC->wc_view == VIEW_CALBRIEF) ) { - wprintf("" - ); - } - else if (WC->wc_view == VIEW_TASKS) { - wprintf("" - ); - } - else if (WC->wc_view == VIEW_NOTES) { - wprintf("" - ); - } - else if (WC->wc_view == VIEW_MAILBOX) { - wprintf("" - ); - } - else { - wprintf("" - ); - } - -} - - - -/** - * \brief Display the current view and offer an option to change it - */ -void embed_view_o_matic(void) { - int i; - - wprintf("
\n" - ""); - wprintf(_("View as:")); - wprintf(" " - "
\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("\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("
\n" - "\n"); - - - if (navbar_style == navbar_default) wprintf( - "\n", _("Ungoto") - ); - - if ( (navbar_style == navbar_default) && (WC->wc_view == VIEW_BBS) ) { - wprintf( - "\n", _("Read new messages") - ); - } - - if (navbar_style == navbar_default) { - switch(WC->wc_view) { - case VIEW_ADDRESSBOOK: - wprintf( - "\n", _("View contacts") - ); - break; - case VIEW_CALENDAR: - wprintf( - "\n", _("Day view") - ); - wprintf( - "\n", _("Month view") - ); - break; - case VIEW_CALBRIEF: - wprintf( - "\n", _("Calendar list") - ); - break; - case VIEW_TASKS: - wprintf( - "\n", _("View tasks") - ); - break; - case VIEW_NOTES: - wprintf( - "\n", _("View notes") - ); - break; - case VIEW_MAILBOX: - wprintf( - "\n", _("View message list") - ); - break; - case VIEW_WIKI: - wprintf( - "\n", _("Wiki home") - ); - break; - default: - wprintf( - "\n", _("Read all messages") - ); - break; - } - } - - if (navbar_style == navbar_default) { - switch(WC->wc_view) { - case VIEW_ADDRESSBOOK: - wprintf( - "\n", _("Add new contact") - ); - break; - case VIEW_CALENDAR: - case VIEW_CALBRIEF: - wprintf( - "\n", _("Add new event") - ); - break; - case VIEW_TASKS: - wprintf( - "\n", _("Add new task") - ); - break; - case VIEW_NOTES: - wprintf( - "\n", _("Add new note") - ); - break; - case VIEW_WIKI: - safestrncpy(buf, bstr("page"), sizeof buf); - str_wiki_index(buf); - wprintf( - "\n", buf, _("Edit this page") - ); - break; - default: - wprintf( - "\n", _("Enter a message") - ); - break; - } - } - - if (navbar_style == navbar_default) wprintf( - "\n", - _("Leave all messages marked as unread, go to next room with unread messages"), - _("Skip this room") - ); - - if (navbar_style == navbar_default) wprintf( - "\n", - _("Mark all messages as read, go to next room with unread messages"), - _("Goto next room") - ); - - wprintf("
" - "" - "" - "%s" - "" - "" - "" - "%s" - "" - "" - "" - "" - "%s" - "" - "" - "" - "" - "%s" - "" - "" - "" - "" - "%s" - "" - "" - "" - "" - "%s" - "" - "" - "" - "" - "%s" - "" - "" - "" - "" - "%s" - "" - "" - "" - "" - "%s" - "" - "" - "" - "" - "%s" - "" - "" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "%s" - "" - "" - "" - "%s" - "" - "" - "" - "%s" - "
\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 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("
" - "
" - "" - "" - "\n"); - - if (!strcmp(tab, "admin")) { - wprintf("\n"); - } - else { - wprintf("\n"); - } - - wprintf("\n"); - - if (!strcmp(tab, "config")) { - wprintf("\n"); - } - else { - wprintf("\n"); - } - - wprintf("\n"); - - if (!strcmp(tab, "expire")) { - wprintf("\n"); - } - else { - wprintf("\n"); - } - - wprintf("\n"); - - if (!strcmp(tab, "access")) { - wprintf("\n"); - } - else { - wprintf("\n"); - } - - wprintf("\n"); - - if (!strcmp(tab, "sharing")) { - wprintf("\n"); - } - else { - wprintf("\n"); - } - - wprintf("\n"); - - if (!strcmp(tab, "listserv")) { - wprintf("\n"); - } - else { - wprintf("\n"); - } - - wprintf("\n"); - - wprintf("
 "); - } - else { - wprintf(""); - } - wprintf(_("Administration")); - if (!strcmp(tab, "admin")) { - wprintf(" "); - } - else { - wprintf(""); - } - wprintf(_("Configuration")); - if (!strcmp(tab, "config")) { - wprintf(" "); - } - else { - wprintf(""); - } - wprintf(_("Message expire policy")); - if (!strcmp(tab, "expire")) { - wprintf(" "); - } - else { - wprintf(""); - } - wprintf(_("Access controls")); - if (!strcmp(tab, "access")) { - wprintf(" "); - } - else { - wprintf(""); - } - wprintf(_("Sharing")); - if (!strcmp(tab, "sharing")) { - wprintf(" "); - } - else { - wprintf(""); - } - wprintf(_("Mailing list service")); - if (!strcmp(tab, "listserv")) { - wprintf(" 
\n"); - /** end tabbed dialog */ - - /** begin content of whatever tab is open now */ - wprintf("
" - "\n" - "
\n"); - - if (!strcmp(tab, "admin")) { - wprintf(""); - } - - if (!strcmp(tab, "config")) { - wprintf("
\n"); - - wprintf("
  • "); - wprintf(_("Name of room: ")); - wprintf("\n", - er_name, - (sizeof(er_name)-1) - ); - - wprintf("
  • "); - wprintf(_("Resides on floor: ")); - wprintf("\n"); - - wprintf("
  • "); - wprintf(_("Type of room:")); - wprintf("
      \n"); - - wprintf("
    • "); - wprintf(_("Public room")); - wprintf("\n"); - - wprintf("
    • "); - wprintf(_("Private - guess name")); - - wprintf("\n
    • "); - wprintf(_("Private - require password:")); - wprintf("\n\n", - er_password); - - wprintf("
    • "); - wprintf(_("Private - invitation only")); - - wprintf("\n
    • "); - wprintf(_("If private, cause current users to forget room")); - - wprintf("\n
    \n"); - - wprintf("
  • "); - wprintf(_("Preferred users only")); - - wprintf("\n
  • "); - wprintf(_("Read-only room")); - - /** directory stuff */ - wprintf("\n
  • "); - wprintf(_("File directory room")); - - wprintf("\n
    • "); - wprintf(_("Directory name: ")); - wprintf("\n", - er_dirname); - - wprintf("
    • "); - wprintf(_("Uploading allowed")); - - wprintf("\n
    • "); - wprintf(_("Downloading allowed")); - - wprintf("\n
    • "); - wprintf(_("Visible directory")); - wprintf("
    \n"); - - /** end of directory stuff */ - - wprintf("
  • "); - wprintf(_("Network shared room")); - - wprintf("\n
  • "); - wprintf(_("Permanent (does not auto-purge)")); - - /** start of anon options */ - - wprintf("\n
  • "); - wprintf(_("Anonymous messages")); - wprintf("
      \n"); - - wprintf("
    • "); - wprintf(_("No anonymous messages")); - - wprintf("\n
    • "); - wprintf(_("All messages are anonymous")); - - wprintf("\n
    • "); - wprintf(_("Prompt user when entering messages")); - wprintf("
    \n"); - - /* end of anon options */ - - wprintf("
  • "); - wprintf(_("Room aide: ")); - serv_puts("GETA"); - serv_getln(buf, sizeof buf); - if (buf[0] != '2') { - wprintf("%s\n", &buf[4]); - } else { - extract_token(er_roomaide, &buf[4], 0, '|', sizeof er_roomaide); - wprintf("\n", er_roomaide); - } - - wprintf("
\n"); - wprintf("\n" - "" - " " - "" - "
\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
" - "" - "" - "\n" - "" - "
"); - wprintf(_("Shared with")); - wprintf(""); - wprintf(_("Not shared with")); - wprintf("
\n"); - - wprintf("\n"); - - for (i=0; i 0) { - wprintf("" - "\n", node); - - wprintf(""); - - wprintf("\n"); - } - } - - wprintf("
"); - wprintf(_("Remote node name")); - wprintf(""); - wprintf(_("Remote room name")); - wprintf(""); - wprintf(_("Actions")); - wprintf("
%s"); - if (strlen(remote_room) > 0) { - escputs(remote_room); - } - wprintf(""); - - wprintf(" 0) { - wprintf("|"); - urlescputs(remote_room); - } - wprintf("\">"); - wprintf("\n"); - wprintf("\n"); - wprintf("", _("Unshare")); - wprintf("
\n"); - wprintf("
\n"); - wprintf("\n"); - - for (i=0; i 0) { - wprintf("
" - "
\n"); - } - } - - wprintf("
"); - wprintf(_("Remote node name")); - wprintf(""); - wprintf(_("Remote room name")); - wprintf(""); - wprintf(_("Actions")); - wprintf("
"); - escputs(node); - wprintf("" - "" - ""); - wprintf(""); - wprintf("\n"); - wprintf("\n"); - wprintf("", _("Share")); - wprintf("
\n"); - wprintf("

\n" - "%s
  • ", _("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. " - "
  • If the remote room name is blank, it is assumed " - "that the room name is identical on the remote node." - "
  • If the remote room name is different, the remote " - "node must also configure the name of the room here." - "

\n" - )); - - } - - /** Mailing list management */ - if (!strcmp(tab, "listserv")) { - - wprintf("
" - "" - "
"); - - wprintf(_("The contents of this room are being " - "mailed as individual messages " - "to the following list recipients:" - "

\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(" "); - wprintf(_("(remove)")); - wprintf("
"); - } - } - wprintf("
\n" - "\n" - "\n"); - wprintf("\n"); - wprintf("", _("Add")); - wprintf("
\n"); - - wprintf("
\n"); - - wprintf(_("The contents of this room are being " - "mailed in digest form " - "to the following list recipients:" - "

\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(" "); - wprintf(_("(remove)")); - wprintf("
"); - } - } - wprintf("
\n" - "\n" - "\n"); - wprintf("\n"); - wprintf("", _("Add")); - wprintf("
\n"); - - wprintf("

\n"); - - if (self_service(999) == 1) { - wprintf(_("This room is configured to allow " - "self-service subscribe/unsubscribe requests.")); - wprintf(""); - wprintf(_("Click to disable.")); - wprintf("
\n"); - wprintf(_("The URL for subscribe/unsubscribe is: ")); - wprintf("%s://%s/listsub
\n", - (is_https ? "https" : "http"), - WC->http_host); - } - else { - wprintf(_("This room is not configured to allow " - "self-service subscribe/unsubscribe requests.")); - wprintf(" "); - wprintf(_("Click to enable.")); - wprintf("
\n"); - } - - - wprintf("
\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("
\n"); - wprintf("\n"); - wprintf("\n"); - - if (WC->axlevel >= 6) { - wprintf("\n"); - wprintf("\n"); - - wprintf("
"); - wprintf(_("Message expire policy for this room")); - wprintf("
("); - escputs(WC->wc_roomname); - wprintf(")
"); - wprintf("", - ((roompolicy == 0) ? "CHECKED" : "") ); - wprintf(_("Use the default policy for this floor")); - wprintf("
\n"); - wprintf("", - ((roompolicy == 1) ? "CHECKED" : "") ); - wprintf(_("Never automatically expire messages")); - wprintf("
\n"); - wprintf("", - ((roompolicy == 2) ? "CHECKED" : "") ); - wprintf(_("Expire by message count")); - wprintf("
\n"); - wprintf("", - ((roompolicy == 3) ? "CHECKED" : "") ); - wprintf(_("Expire by message age")); - wprintf("
"); - wprintf(_("Number of messages or days: ")); - wprintf("", roomvalue); - wprintf("

"); - wprintf(_("Message expire policy for this floor")); - wprintf("
("); - escputs(floorlist[WC->wc_floor]); - wprintf(")
"); - wprintf("", - ((floorpolicy == 0) ? "CHECKED" : "") ); - wprintf(_("Use the system default")); - wprintf("
\n"); - wprintf("", - ((floorpolicy == 1) ? "CHECKED" : "") ); - wprintf(_("Never automatically expire messages")); - wprintf("
\n"); - wprintf("", - ((floorpolicy == 2) ? "CHECKED" : "") ); - wprintf(_("Expire by message count")); - wprintf("
\n"); - wprintf("", - ((floorpolicy == 3) ? "CHECKED" : "") ); - wprintf(_("Expire by message age")); - wprintf("
"); - wprintf(_("Number of messages or days: ")); - wprintf("", - floorvalue); - } - - wprintf("
\n"); - wprintf("

\n"); - wprintf("", _("Save changes")); - wprintf(" "); - wprintf("", _("Cancel")); - wprintf("
\n" - "\n" - "
\n" - ); - - } - - /** Mailing list management */ - if (!strcmp(tab, "access")) { - display_whok(); - } - - /** end content of whatever tab is open now */ - wprintf("
\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, - _("User %s kicked out of room %s.\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, - _("User %s invited to room %s.\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("
"); - 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("

"); - - wprintf("
\n"); - wprintf("\n"); - wprintf("
\n"); - - wprintf("", _("Kick")); - wprintf("
\n"); - - wprintf("
"); - wprintf(_("To grant another user access to this room, enter the " - "user name in the box below and click 'Invite'.")); - wprintf("

"); - - wprintf("
\n"); - wprintf("\n"); - wprintf(_("Invite:")); - wprintf(" "); - wprintf("
\n" - "" - "" - "
\n", _("Invite")); - - wprintf("
\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("
\n" - "
" - ""); - wprintf(_("Create a new room")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("
" - "
\n"); - - wprintf("
\n"); - - wprintf("
  • "); - wprintf(_("Name of room: ")); - wprintf("\n"); - - wprintf("
  • "); - wprintf(_("Resides on floor: ")); - load_floorlist(); - wprintf("\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("
  • "); - wprintf(_("Default view for room: ")); - wprintf("\n"); - - wprintf("
  • "); - wprintf(_("Type of room:")); - wprintf("
      \n"); - - wprintf("
    • "); - wprintf(_("Public (automatically appears to everyone)")); - - wprintf("\n
    • "); - wprintf(_("Private - hidden (accessible to anyone who knows its name)")); - - wprintf("\n
    • "); - wprintf(_("Private - require password: ")); - wprintf("\n"); - - wprintf("
    • "); - wprintf(_("Private - invitation only")); - - wprintf("\n
    • "); - wprintf(_("Personal (mailbox for you only)")); - - wprintf("\n
    \n"); - - wprintf("
    \n"); - wprintf("", _("Create new room")); - wprintf(" "); - wprintf("", _("Cancel")); - wprintf("
    \n"); - wprintf("
  • \n
    "); - serv_printf("MESG roomaccess"); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') { - fmout("CENTER"); - } - wprintf("
\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("
\n" - "
" - ""); - wprintf(_("Go to a hidden room")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("
" - "
\n"); - - wprintf("
\n"); - wprintf("
"); - 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

"); - - wprintf("
\n"); - - wprintf("\n" - "
"); - wprintf(_("Enter room name:")); - wprintf("" - "\n", rname); - - if (req_pass) { - wprintf("
"); - wprintf(_("Enter room password:")); - wprintf(""); - wprintf("\n"); - } - wprintf("

\n"); - - wprintf("" - " " - "", - _("Go there"), - _("Cancel") - ); - wprintf("
\n"); - wprintf("
\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("
\n"); - wprintf("
"); - wprintf(""); - wprintf(_("Zap (forget/unsubscribe) the current room")); - wprintf("\n"); - wprintf("
\n"); - wprintf("
\n
\n"); - - wprintf(_("If you select this option, %s will " - "disappear from your room list. Is this what you wish " - "to do?
\n"), WC->wc_roomname); - - wprintf("
\n"); - wprintf("", _("Zap this room")); - wprintf(" "); - wprintf("", _("Cancel")); - wprintf("
\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; iwc_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("
Loading folder list...
\n"); - - /** include NanoTree */ - wprintf("\n"); - - /** initialize NanoTree */ - wprintf("\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("" - "
"); - - levels = 0; - oldlevels = 0; - for (i=0; i 0) ) { - /* End inner box */ - do_template("endbox"); - - ++num_boxes; - if ((num_boxes % boxes_per_column) == 0) { - ++current_column; - if (current_column < columns) { - wprintf("\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(""); - } - else { - wprintf(""); - } - if (fold[i].hasnewmsgs) { - wprintf(""); - } - else { - wprintf(""); - } - extract_token(buf, fold[i].name, levels-1, '|', sizeof buf); - escputs(buf); - wprintf(""); - if (fold[i].selectable) { - wprintf(""); - } - else { - wprintf(""); - } - if (!strcasecmp(fold[i].name, "My Folders|Mail")) { - wprintf(" (INBOX)"); - } - wprintf("
\n"); - } - } - /** End the final inner box */ - do_template("endbox"); - - wprintf("
\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 0) ) { - /** End inner box */ - wprintf("
\n"); - wprintf("
\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("" - "%s
\n", floordiv_id, floordivtitle); - wprintf("
", - floordiv_id, - (!strcasecmp(floordiv_id, WC->floordiv_expanded) ? "block" : "none") - ); - } - - oldlevels = levels; - - if (levels > 1) { - wprintf("
", 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(""); - wprintf("\"\" ", icon); - } - else { - wprintf(""); - } - if (fold[i].hasnewmsgs) { - wprintf(""); - } - else { - wprintf(""); - } - extract_token(buf, fold[i].name, levels-1, '|', sizeof buf); - escputs(buf); - if (!strcasecmp(fold[i].name, "My Folders|Mail")) { - wprintf(" (INBOX)"); - } - wprintf(""); - if (fold[i].selectable) { - wprintf(""); - } - else { - wprintf(""); - } - wprintf("
"); - wprintf("
\n"); /** roomdiv */ - } - } - wprintf("
\n"); /** floordiv */ - - - /** BEGIN: The old invisible pixel trick, to get our JavaScript to initialize */ - wprintf(" 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 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\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("
\n" - "\n"); - - /** offer the ability to switch views */ - wprintf("
" - "" - ); - if (!strcasecmp(listviewpref, "rooms")) { - wprintf(_("Room list")); - } - if (!strcasecmp(listviewpref, "folders")) { - wprintf(_("Folder list")); - } - if (!strcasecmp(listviewpref, "table")) { - wprintf(_("Room list")); - } - wprintf("
\n" - "
"); - offer_start_page(); - wprintf("
\n"); - wprintf("
\n" - "
\n" - "
\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, "
\n"); - serv_printf("SPEX floor|%d|%d", atoi(bstr("floorpolicy")), atoi(bstr("floorvalue"))); - serv_getln(buf, sizeof buf); - strcat(WC->ImportantMessage, &buf[4]); - } - - display_editroom(); -} - - -/*@}*/ diff --git a/webcit/rss.c b/webcit/rss.c deleted file mode 100644 index 6a962559a..000000000 --- a/webcit/rss.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup RssRooms Generate some RSS for our rooms. - * \ingroup WebcitHttpServerRSS - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" - - -time_t if_modified_since; /**< the last modified stamp */ - -/** - * \brief view rss Config menu - * \param reply_to the original author - * \param subject the subject of the feed - */ -void display_rss_control(char *reply_to, char *subject) -{ - wprintf("

\n"); - wprintf("[%s] \n", _("Reply")); - wprintf("[%s]\n", _("Email")); - wprintf("

\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("\n"); - wprintf("\n"); - wprintf(" \n"); - wprintf(" %s - %s\n", WC->wc_roomname, serv_info.serv_humannode); - wprintf(" %s://%s:%d/dotgoto?room=", (is_https ? "https" : "http"), WC->http_host, PORT_NUM); - escputs(roomname); - wprintf("\n"); - wprintf(" "); - /** 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("\n"); - if (now) { - wprintf(" %s\n", date); - } - wprintf(" %s\n", SERVER); - wprintf(" http://blogs.law.harvard.edu/tech/rss\n"); - wprintf(" 30\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(" \n"); - if (subj[0]) { - wprintf(" %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("\n"); - if (now) { - wprintf(" %s\n", date); - } - wprintf(" %s\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(" "); - wprintf("\n"); - break; - } - if (intext == 1 && isspace(buf[0])) { - wprintf("
"); - } - intext = 1; - if (bq == 0 && !strncmp(buf, " >", 2)) { - wprintf("
"); - bq = 1; - } else if (bq == 1 && strncmp(buf, " >", 2)) { - wprintf("
"); - bq = 0; - } - url(buf); - escputs(buf); - wprintf("\n"); - } - display_rss_control(from, subj); - wprintf("]]>
\n"); - } - /** Boring old 80-column fixed format text gets handled this way... */ - else if (!strcasecmp(content_type, "text/plain")) { - wprintf(" 0) && (isspace(buf[strlen(buf) - 1]))) - buf[strlen(buf) - 1] = 0; - if ((bq == 0) && - ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) { - wprintf("
"); - bq = 1; - } else if ((bq == 1) && - (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) { - wprintf("
"); - bq = 0; - } - wprintf(""); - url(buf); - escputs(buf); - wprintf("
\n"); - } - display_rss_control(from, subj); - wprintf("]]>
\n"); - } - /** HTML is fun, but we've got to strip it first */ - else if (!strcasecmp(content_type, "text/html")) { - wprintf(" \n"); - } - -ENDBODY: - wprintf("
\n"); -ENDITEM: - now = 0L; - } - - /** Do RSS footer */ - wprintf("
\n"); - wprintf("
\n"); - wDumpContent(0); -} - - -/*@}*/ diff --git a/webcit/serv_func.c b/webcit/serv_func.c deleted file mode 100644 index 05574c266..000000000 --- a/webcit/serv_func.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup ServFuncs Handles various types of data transfer operations with the Citadel service. - * \ingroup CitadelCommunitacion - */ - -/*@{*/ -#include "webcit.h" -#include "webserver.h" - -struct serv_info serv_info; /**< our connection data to the server */ - -/** - * \brief get info about the server we've connected to - * \param browser_host the citadell we want to connect to - * \param user_agent which browser uses our client? - */ -void get_serv_info(char *browser_host, char *user_agent) -{ - char buf[SIZ]; - int a; - - /** Tell the server what kind of client is connecting */ - serv_printf("IDEN %d|%d|%d|%s|%s", - DEVELOPER_ID, - CLIENT_ID, - CLIENT_VERSION, - user_agent, - browser_host - ); - serv_getln(buf, sizeof buf); - - /** Tell the server what kind of richtext we prefer */ - serv_puts("MSGP text/html|text/plain"); - serv_getln(buf, sizeof buf); - -#ifdef WEBCIT_WITH_CALENDAR_SERVICE - /** - * Tell the server that when we save a calendar event, we - * want invitations to be generated by the Citadel server - * instead of by the client. - */ - serv_puts("ICAL sgi|1"); - serv_getln(buf, sizeof buf); -#endif - - /** Now ask the server to tell us a little bit about itself... */ - serv_puts("INFO"); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') - return; - - a = 0; - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - switch (a) { - case 0: - serv_info.serv_pid = atoi(buf); - WC->ctdl_pid = serv_info.serv_pid; - break; - case 1: - safestrncpy(serv_info.serv_nodename, buf, sizeof serv_info.serv_nodename); - break; - case 2: - safestrncpy(serv_info.serv_humannode, buf, sizeof serv_info.serv_humannode); - break; - case 3: - safestrncpy(serv_info.serv_fqdn, buf, sizeof serv_info.serv_fqdn); - break; - case 4: - safestrncpy(serv_info.serv_software, buf, sizeof serv_info.serv_software); - break; - case 5: - serv_info.serv_rev_level = atoi(buf); - break; - case 6: - safestrncpy(serv_info.serv_bbs_city, buf, sizeof serv_info.serv_bbs_city); - break; - case 7: - safestrncpy(serv_info.serv_sysadm, buf, sizeof serv_info.serv_sysadm); - break; - case 9: - safestrncpy(serv_info.serv_moreprompt, buf, sizeof serv_info.serv_moreprompt); - break; - case 14: - serv_info.serv_supports_ldap = atoi(buf); - break; - case 15: - serv_info.serv_newuser_disabled = atoi(buf); - break; - } - ++a; - } -} - - - -/** - * \brief Read Citadel variformat text and spit it out as HTML. - * \param align html align string - */ -void fmout(char *align) -{ - int intext = 0; - int bq = 0; - char buf[SIZ]; - - wprintf("
\n", align); - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - - if ((intext == 1) && (isspace(buf[0]))) { - wprintf("
"); - } - intext = 1; - - /** - * Quoted text should be displayed in italics and in a - * different colour. This code understands Citadel-style - * " >" quotes and will convert to
tags. - */ - if ((bq == 0) && (!strncmp(buf, " >", 2))) { - wprintf("
"); - bq = 1; - } else if ((bq == 1) && (strncmp(buf, " >", 2))) { - wprintf("
"); - 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(""); - } - wprintf("

\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("
"); - } - intext = 1; - - /** - * Quoted text should be displayed in italics and in a - * different colour. This code understands Citadel-style - * " >" quotes and will convert to
tags. - */ - if ((bq == 0) && (!strncmp(buf, " >", 2))) { - wprintf("
"); - bq = 1; - } else if ((bq == 1) && (strncmp(buf, " >", 2))) { - wprintf("
"); - bq = 0; - } - if ((bq == 1) && (!strncmp(buf, " >", 2))) { - strcpy(buf, &buf[2]); - } - - msgescputs(buf); - } - if (bq == 1) { - wprintf(""); - } -} - - - - -/** - * \brief Transmit message text (in memory) to the server. - * - * \param ptr Pointer to the message being transmitted - */ -void text_to_server(char *ptr) -{ - char buf[256]; - int ch, a, pos; - - pos = 0; - buf[0] = 0; - - while (ptr[pos] != 0) { - ch = ptr[pos++]; - if (ch == 10) { - while ( (isspace(buf[strlen(buf) - 1])) - && (strlen(buf) > 1) ) - buf[strlen(buf) - 1] = 0; - serv_puts(buf); - buf[0] = 0; - if (ptr[pos] != 0) strcat(buf, " "); - } else { - a = strlen(buf); - buf[a + 1] = 0; - buf[a] = ch; - if ((ch == 32) && (strlen(buf) > 200)) { - buf[a] = 0; - serv_puts(buf); - buf[0] = 0; - } - if (strlen(buf) > 250) { - serv_puts(buf); - buf[0] = 0; - } - } - } - serv_puts(buf); -} - - -/** - * \brief Transmit message text (in memory) to the server, - * converting to Quoted-Printable encoding as we go. - * - * \param ptr Pointer to the message being transmitted - */ -void text_to_server_qp(char *ptr) -{ - char buf[256]; - int ch, pos; - int output_len = 0; - - pos = 0; - buf[0] = 0; - output_len = 0; - - while (ptr[pos] != 0) { - ch = ptr[pos++]; - - if (ch == 13) { - /* ignore carriage returns */ - } - else if (ch == 10) { - /* hard line break */ - if (output_len > 0) { - if (isspace(buf[output_len-1])) { - sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]); - output_len += 2; - } - } - buf[output_len++] = 0; - serv_puts(buf); - output_len = 0; - } - else if (ch == 9) { - buf[output_len++] = ch; - } - else if ( (ch >= 32) && (ch <= 60) ) { - buf[output_len++] = ch; - } - else if ( (ch >= 62) && (ch <= 126) ) { - buf[output_len++] = ch; - } - else { - sprintf(&buf[output_len], "=%02X", ch); - output_len += 3; - } - - if (output_len > 72) { - /* soft line break */ - if (isspace(buf[output_len-1])) { - sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]); - output_len += 2; - } - buf[output_len++] = '='; - buf[output_len++] = 0; - serv_puts(buf); - output_len = 0; - } - } - - /* end of data - transmit anything that's left */ - if (output_len > 0) { - if (isspace(buf[output_len-1])) { - sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]); - output_len += 2; - } - buf[output_len++] = 0; - serv_puts(buf); - output_len = 0; - } -} - - - - -/** - * \brief translate server message output to text - * (used for editing room info files and such) - */ -void server_to_text() -{ - char buf[SIZ]; - - int count = 0; - - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - if ((buf[0] == 32) && (count > 0)) { - wprintf("\n"); - } - wprintf("%s", buf); - ++count; - } -} - - - -/** - * Read binary data from server into memory using a series of - * server READ commands. - * \param buffer the output buffer - * \param total_len the maximal length of buffer - */ -void read_server_binary(char *buffer, size_t total_len) { - char buf[SIZ]; - size_t bytes = 0; - size_t thisblock = 0; - - memset(buffer, 0, total_len); - while (bytes < total_len) { - thisblock = 4095; - if ((total_len - bytes) < thisblock) { - thisblock = total_len - bytes; - if (thisblock == 0) return; - } - serv_printf("READ %d|%d", (int)bytes, (int)thisblock); - serv_getln(buf, sizeof buf); - if (buf[0] == '6') { - thisblock = (size_t)atoi(&buf[4]); - if (!WC->connected) return; - serv_read(&buffer[bytes], thisblock); - bytes += thisblock; - } - else { - lprintf(3, "Error: %s\n", &buf[4]); - return; - } - } -} - - -/** - * \brief Read text from server, appending to a string buffer until the - * usual 000 terminator is found. Caller is responsible for freeing - * the returned pointer. - */ -char *read_server_text(void) { - char *text = NULL; - size_t bytes_allocated = 0; - size_t bytes_read = 0; - int linelen; - char buf[SIZ]; - - text = malloc(SIZ); - if (text == NULL) { - return(NULL); - } - text[0] = 0; - bytes_allocated = SIZ; - - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - linelen = strlen(buf); - buf[linelen] = '\n'; - buf[linelen+1] = 0; - ++linelen; - - if ((bytes_read + linelen) >= (bytes_allocated - 2)) { - bytes_allocated = 2 * bytes_allocated; - text = realloc(text, bytes_allocated); - } - - strcpy(&text[bytes_read], buf); - bytes_read += linelen; - } - - return(text); -} - - - -/*@}*/ diff --git a/webcit/setup.c b/webcit/setup.c deleted file mode 100644 index f17e7ac01..000000000 --- a/webcit/setup.c +++ /dev/null @@ -1,683 +0,0 @@ -/* - * $Id$ - * - * WebCit setup utility - * - * (This is basically just an install wizard. It's not required.) - * - */ - - -#include "webcit.h" -#include "webserver.h" - - -#ifdef HAVE_NEWT -#include -#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%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 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://:/"); - - /* set an invalid setup type */ - setup_type = (-1); - - /* Check to see if we're running the web installer */ - if (getenv("CITADEL_INSTALLER") != NULL) { - using_web_installer = 1; - } - - /* parse command line args */ - for (a = 0; a < argc; ++a) { - if (!strncmp(argv[a], "-u", 2)) { - strcpy(aaa, argv[a]); - strcpy(aaa, &aaa[2]); - setup_type = atoi(aaa); - } - if (!strcmp(argv[a], "-i")) { - info_only = 1; - } - if (!strcmp(argv[a], "-q")) { - setup_type = UI_SILENT; - } - } - - - /* If a setup type was not specified, try to determine automatically - * the best one to use out of all available types. - */ - if (setup_type < 0) { - setup_type = discover_ui(); - } - if (info_only == 1) { - important_message("WebCit Setup", "Welcome to WebCit setup"); - cleanup(0); - } - - /* If we're on something BSDish then we don't have inittab */ - if (access("/etc/inittab", F_OK)) { - important_message("Not running SysV style init", - "WebCit Setup can only run on systems that use /etc/inittab.\n" - "Please manually configure your startup scripts to run WebCit\n" - "when the system is booted.\n"); - cleanup(0); - } - - /* Get started in a valid setup directory. */ - strcpy(setup_directory, WEBCITDIR); - if ( (using_web_installer) && (getenv("WEBCIT") != NULL) ) { - strcpy(setup_directory, getenv("WEBCIT")); - } - else { - set_value("In what directory is WebCit installed?", - setup_directory); - } - if (chdir(setup_directory) != 0) { - important_message("WebCit Setup", - "The directory you specified does not exist."); - cleanup(errno); - } - - /* See if we need to shut down the WebCit service. */ - for (a=0; a<=3; ++a) { - progress("Shutting down the WebCit service...", a, 3); - if (a == 0) shutdown_service(); - sleep(1); - } - - /* Now begin. */ - switch (setup_type) { - - case UI_TEXT: - printf("\n\n\n" - " *** WebCit setup program ***\n\n"); - break; - - } - - check_inittab_entry(); /* Check /etc/inittab */ - - /* See if we can start the WebCit service. */ - if (strlen(init_entry) > 0) { - for (a=0; a<=3; ++a) { - progress("Starting the WebCit service...", a, 3); - if (a == 0) start_the_service(); - sleep(1); - } - sprintf(aaa, - "Setup is finished. You may now log in.\n" - "Point your web browser at %s\n", suggested_url); - important_message("Setup finished", aaa); - } - else { - important_message("Setup finished", - "Setup is finished. You may now start the server."); - } - - cleanup(0); - return 0; -} diff --git a/webcit/setup_wizard.c b/webcit/setup_wizard.c deleted file mode 100644 index bc2b2277a..000000000 --- a/webcit/setup_wizard.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * $Id$ - * - * First-time setup wizard - */ - -#include "webcit.h" - - -/* - */ -void do_setup_wizard(void) -{ - char *step; - FILE *fp; - - step = bstr("step"); - - if (!strcasecmp(step, "Finish")) { - fp = fopen(wizard_filename, "w"); - if (fp != NULL) { - fprintf(fp, "%d\n", serv_info.serv_rev_level); - fclose(fp); - } - do_welcome(); - return; - } - - output_headers(1, 1, 2, 0, 0, 0); - - wprintf("
\n"); - wprintf("
"); - wprintf("\""); - wprintf(" First time setup"); - wprintf(""); - wprintf("
\n"); - wprintf("
\n" - "
\n"); - - wprintf("
" - "
\n" - ); - - wprintf("
" - "This is where the setup wizard will be placed.
\n" - "For now, just click Finish.

\n" - ); - - wprintf("\n"); - wprintf("\n"); - - wprintf("
\n"); - wDumpContent(1); -} - - diff --git a/webcit/siteconfig.c b/webcit/siteconfig.c deleted file mode 100644 index 17a7c3cd4..000000000 --- a/webcit/siteconfig.c +++ /dev/null @@ -1,650 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup AdminConfig Administrative screen for site-wide configuration - * \ingroup CitadelConfig - */ -/*@{*/ - -#include "webcit.h" - -/** - * \brief display all configuration items - */ -void display_siteconfig(void) -{ - char buf[SIZ]; - int i, j; - - char general[SIZ]; - char access[SIZ]; - char network[SIZ]; - char tuning[SIZ]; - char directory[SIZ]; - char purger[SIZ]; - char idxjnl[SIZ]; - - /** expire policy settings */ - int sitepolicy = 0; - int sitevalue = 0; - int mboxpolicy = 0; - int mboxvalue = 0; - - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n" - "
" - ""); - wprintf(_("Site configuration")); - wprintf("" - "
\n" - "
\n
\n" - ); - - serv_printf("CONF get"); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - wprintf("
"); - wprintf(""); - wprintf(_("Error")); - wprintf("\n"); - wprintf("

\n"); - wprintf("%s
\n", &buf[4]); - wDumpContent(1); - return; - } - - wprintf("
" - "
"); - - char *tabnames[] = { - _("General"), - _("Access"), - _("Network"), - _("Tuning"), - _("Directory"), - _("Auto-purger"), - _("Indexing/Journaling") - }; - - sprintf(general, "

%s

", - _("General site configuration items") - ); - - sprintf(access, "

%s

", - _("Access controls and site policy settings") - ); - - sprintf(network, "

%s

%s

", - _("Network services"), - _("Changes made on this screen will not take effect " - "until you restart the Citadel server.") - ); - - sprintf(tuning, "

%s

", - _("Advanced server fine-tuning controls") - ); - - sprintf(directory, "

%s

%s

", - _("Configure the LDAP connector for Citadel"), - _("Changes made on this screen will not take effect " - "until you restart the Citadel server.") - ); - - sprintf(purger, "

%s

%s

", - _("Configure automatic expiry of old messages"), - _("These settings may be overridden on a per-floor or per-room basis.") - ); - - sprintf(idxjnl, "

%s

%s

", - _("Indexing and Journaling"), - _("Warning: these facilities are resource intensive.") - ); - - - wprintf("\n"); - - i = 0; - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - switch (i++) { - case 0: - sprintf(&general[strlen(general)], "\n"); - break; - case 1: - sprintf(&general[strlen(general)], "\n"); - break; - case 2: - sprintf(&general[strlen(general)], "\n"); - break; - case 3: - sprintf(&general[strlen(general)], "\n"); - break; - case 4: - sprintf(&access[strlen(access)], "\n"); - break; - case 5: - sprintf(&tuning[strlen(tuning)], "\n"); - break; - case 6: - sprintf(&access[strlen(access)], "\n"); - break; - case 7: - sprintf(&access[strlen(access)], "\n"); - break; - case 8: - sprintf(&access[strlen(access)], "\n"); - break; - case 9: - sprintf(&access[strlen(access)], "\n"); - break; - case 10: - sprintf(&general[strlen(general)], "\n"); - break; - case 11: - sprintf(&access[strlen(access)], "\n"); - break; - case 12: - sprintf(&general[strlen(general)], "\n"); - break; - case 13: - sprintf(&general[strlen(general)], "\n"); - break; - case 14: - sprintf(&tuning[strlen(tuning)], "\n"); - break; - case 16: - sprintf(&tuning[strlen(tuning)], "\n"); - break; - case 17: - sprintf(&tuning[strlen(tuning)], "\n"); - break; - case 18: - sprintf(&access[strlen(access)], "\n"); - break; - case 19: - sprintf(&access[strlen(access)], "\n"); - break; - case 20: - sprintf(&tuning[strlen(tuning)], "\n"); - break; - case 21: - sprintf(&tuning[strlen(tuning)], "\n"); - break; - case 22: - sprintf(&tuning[strlen(tuning)], "\n"); - break; - case 23: - sprintf(&network[strlen(network)], "\n"); - break; - case 24: - sprintf(&network[strlen(network)], "\n"); - break; - case 25: /* note: reverse bool */ - sprintf(&network[strlen(network)], "\n"); - break; - case 26: - sprintf(&access[strlen(access)], "\n"); - break; - case 27: - sprintf(&network[strlen(network)], "\n"); - break; - case 28: - sprintf(&network[strlen(network)], "\n"); - break; - case 29: - sprintf(&access[strlen(access)], "\n"); - break; - case 31: - sprintf(&purger[strlen(purger)], "\n"); - break; - case 32: - sprintf(&directory[strlen(directory)], "\n"); - break; - case 33: - sprintf(&directory[strlen(directory)], "\n"); - break; - case 34: - sprintf(&directory[strlen(directory)], "\n"); - break; - case 35: - sprintf(&directory[strlen(directory)], "\n"); - break; - case 36: - sprintf(&directory[strlen(directory)], "\n"); - break; - case 37: - sprintf(&network[strlen(network)], "\n"); - break; - case 38: - sprintf(&network[strlen(network)], "\n"); - break; - case 39: - sprintf(&network[strlen(network)], "\n"); - break; - case 40: - sprintf(&network[strlen(network)], "\n"); - break; - case 41: - sprintf(&network[strlen(network)], "\n"); - break; - case 42: - sprintf(&idxjnl[strlen(idxjnl)], "\n"); - break; - case 43: - sprintf(&tuning[strlen(tuning)], "\n"); - break; - case 44: - sprintf(&network[strlen(network)], "\n"); - break; - case 45: - sprintf(&network[strlen(network)], "\n"); - break; - case 46: - sprintf(&idxjnl[strlen(idxjnl)], "\n"); - break; - case 47: - sprintf(&idxjnl[strlen(idxjnl)], "\n"); - break; - case 48: - sprintf(&idxjnl[strlen(idxjnl)], "\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)], "\n"); - - sprintf(&purger[strlen(purger)], "\n"); - - sprintf(&purger[strlen(purger)], "\n"); - - sprintf(&purger[strlen(purger)], "\n"); - - sprintf(&purger[strlen(purger)], "\n"); - - - sprintf(&general[strlen(general)], "
"); - sprintf(&general[strlen(general)], _("Node name")); - sprintf(&general[strlen(general)], ""); - sprintf(&general[strlen(general)], "", buf); - sprintf(&general[strlen(general)], "
"); - sprintf(&general[strlen(general)], _("Fully qualified domain name")); - sprintf(&general[strlen(general)], ""); - sprintf(&general[strlen(general)], "", buf); - sprintf(&general[strlen(general)], "
"); - sprintf(&general[strlen(general)], _("Human-readable node name")); - sprintf(&general[strlen(general)], ""); - sprintf(&general[strlen(general)], "", buf); - sprintf(&general[strlen(general)], "
"); - sprintf(&general[strlen(general)], _("Telephone number")); - sprintf(&general[strlen(general)], ""); - sprintf(&general[strlen(general)], "", buf); - sprintf(&general[strlen(general)], "
"); - sprintf(&access[strlen(access)], _("Automatically grant room-aide status to users who create private rooms")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "", - ((atoi(buf) != 0) ? "checked" : "")); - sprintf(&access[strlen(access)], "
"); - sprintf(&tuning[strlen(tuning)], _("Server connection idle timeout (in seconds)")); - sprintf(&tuning[strlen(tuning)], ""); - sprintf(&tuning[strlen(tuning)], "", buf); - sprintf(&tuning[strlen(tuning)], "
"); - sprintf(&access[strlen(access)], _("Initial access level for new users")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "
"); - sprintf(&access[strlen(access)], _("Require registration for new users")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "", - ((atoi(buf) != 0) ? "checked" : "")); - sprintf(&access[strlen(access)], "
"); - sprintf(&access[strlen(access)], _("Quarantine messages from problem users")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "", - ((atoi(buf) != 0) ? "checked" : "")); - sprintf(&access[strlen(access)], "
"); - sprintf(&access[strlen(access)], _("Name of quarantine room")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "", buf); - sprintf(&access[strlen(access)], "
"); - sprintf(&general[strlen(general)], _("Paginator prompt (for text mode clients)")); - sprintf(&general[strlen(general)], ""); - sprintf(&general[strlen(general)], "", buf); - sprintf(&general[strlen(general)], "
"); - sprintf(&access[strlen(access)], _("Restrict access to Internet mail")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "", - ((atoi(buf) != 0) ? "checked" : "")); - sprintf(&access[strlen(access)], "
"); - sprintf(&general[strlen(general)], _("Geographic location of this system")); - sprintf(&general[strlen(general)], ""); - sprintf(&general[strlen(general)], "", buf); - sprintf(&general[strlen(general)], "
"); - sprintf(&general[strlen(general)], _("Name of system administrator")); - sprintf(&general[strlen(general)], ""); - sprintf(&general[strlen(general)], "", buf); - sprintf(&general[strlen(general)], "
"); - sprintf(&tuning[strlen(tuning)], _("Maximum concurrent sessions (0 = no limit)")); - sprintf(&tuning[strlen(tuning)], ""); - sprintf(&tuning[strlen(tuning)], "", buf); - sprintf(&tuning[strlen(tuning)], "
"); - sprintf(&tuning[strlen(tuning)], _("Default user purge time (days)")); - sprintf(&tuning[strlen(tuning)], ""); - sprintf(&tuning[strlen(tuning)], "", buf); - sprintf(&tuning[strlen(tuning)], "
"); - sprintf(&tuning[strlen(tuning)], _("Default room purge time (days)")); - sprintf(&tuning[strlen(tuning)], ""); - sprintf(&tuning[strlen(tuning)], "", buf); - sprintf(&tuning[strlen(tuning)], "
"); - sprintf(&access[strlen(access)], _("Name of room to log pages")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "", buf); - sprintf(&access[strlen(access)], "
"); - sprintf(&access[strlen(access)], _("Access level required to create rooms")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "
"); - sprintf(&tuning[strlen(tuning)], _("Maximum message length")); - sprintf(&tuning[strlen(tuning)], ""); - sprintf(&tuning[strlen(tuning)], "", buf); - sprintf(&tuning[strlen(tuning)], "
"); - sprintf(&tuning[strlen(tuning)], _("Minimum number of worker threads")); - sprintf(&tuning[strlen(tuning)], ""); - sprintf(&tuning[strlen(tuning)], "", buf); - sprintf(&tuning[strlen(tuning)], "
"); - sprintf(&tuning[strlen(tuning)], _("Maximum number of worker threads")); - sprintf(&tuning[strlen(tuning)], ""); - sprintf(&tuning[strlen(tuning)], "", buf); - sprintf(&tuning[strlen(tuning)], "
"); - sprintf(&network[strlen(network)], _("POP3 listener port (-1 to disable)")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", buf); - sprintf(&network[strlen(network)], "
"); - sprintf(&network[strlen(network)], _("SMTP MTA port (-1 to disable)")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", buf); - sprintf(&network[strlen(network)], "
"); - sprintf(&network[strlen(network)], _("Correct forged From: lines during authenticated SMTP")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", - ((atoi(buf) == 0) ? "CHECKED" : "")); - sprintf(&network[strlen(network)], "
"); - sprintf(&access[strlen(access)], _("Allow aides to zap (forget) rooms")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "", - ((atoi(buf) != 0) ? "CHECKED" : "")); - sprintf(&access[strlen(access)], "
"); - sprintf(&network[strlen(network)], _("IMAP listener port (-1 to disable)")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", buf); - sprintf(&network[strlen(network)], "
"); - sprintf(&network[strlen(network)], _("Network run frequency (in seconds)")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", buf); - sprintf(&network[strlen(network)], "
"); - sprintf(&access[strlen(access)], _("Disable self-service user account creation")); - sprintf(&access[strlen(access)], ""); - sprintf(&access[strlen(access)], "", - ((atoi(buf) != 0) ? "CHECKED" : "")); - sprintf(&access[strlen(access)], "
"); - sprintf(&purger[strlen(purger)], _("Hour to run database auto-purge")); - sprintf(&purger[strlen(purger)], ""); - sprintf(&purger[strlen(purger)], ""); - sprintf(&purger[strlen(purger)], "
"); - sprintf(&directory[strlen(directory)], _("Host name of LDAP server (blank to disable)")); - sprintf(&directory[strlen(directory)], ""); - sprintf(&directory[strlen(directory)], "", buf); - sprintf(&directory[strlen(directory)], "
"); - sprintf(&directory[strlen(directory)], _("Port number of LDAP server (blank to disable)")); - sprintf(&directory[strlen(directory)], ""); - sprintf(&directory[strlen(directory)], "", atoi(buf)); - sprintf(&directory[strlen(directory)], "
"); - sprintf(&directory[strlen(directory)], _("Base DN")); - sprintf(&directory[strlen(directory)], ""); - sprintf(&directory[strlen(directory)], "", buf); - sprintf(&directory[strlen(directory)], "
"); - sprintf(&directory[strlen(directory)], _("Bind DN")); - sprintf(&directory[strlen(directory)], ""); - sprintf(&directory[strlen(directory)], "", buf); - sprintf(&directory[strlen(directory)], "
"); - sprintf(&directory[strlen(directory)], _("Password for bind DN")); - sprintf(&directory[strlen(directory)], ""); - sprintf(&directory[strlen(directory)], "", - buf); - sprintf(&directory[strlen(directory)], "
"); - sprintf(&network[strlen(network)], _("Server IP address (0.0.0.0 for 'any')")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", buf); - sprintf(&network[strlen(network)], "
"); - sprintf(&network[strlen(network)], _("SMTP MSA port (-1 to disable)")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", buf); - sprintf(&network[strlen(network)], "
"); - sprintf(&network[strlen(network)], _("IMAP over SSL port (-1 to disable)")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", buf); - sprintf(&network[strlen(network)], "
"); - sprintf(&network[strlen(network)], _("POP3 over SSL port (-1 to disable)")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", buf); - sprintf(&network[strlen(network)], "
"); - sprintf(&network[strlen(network)], _("SMTP over SSL port (-1 to disable)")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", buf); - sprintf(&network[strlen(network)], "
"); - sprintf(&idxjnl[strlen(idxjnl)], _("Enable full text index")); - sprintf(&idxjnl[strlen(idxjnl)], ""); - sprintf(&idxjnl[strlen(idxjnl)], "", - ((atoi(buf) != 0) ? "CHECKED" : "")); - sprintf(&idxjnl[strlen(idxjnl)], "
"); - sprintf(&tuning[strlen(tuning)], _("Automatically delete committed database logs")); - sprintf(&tuning[strlen(tuning)], ""); - sprintf(&tuning[strlen(tuning)], "", - ((atoi(buf) != 0) ? "CHECKED" : "")); - sprintf(&tuning[strlen(tuning)], "
"); - sprintf(&network[strlen(network)], _("Instantly expunge deleted messages in IMAP")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", - ((atoi(buf) != 0) ? "CHECKED" : "")); - sprintf(&network[strlen(network)], "
"); - sprintf(&network[strlen(network)], _("Allow unauthenticated SMTP clients to spoof this site's domains")); - sprintf(&network[strlen(network)], ""); - sprintf(&network[strlen(network)], "", - ((atoi(buf) != 0) ? "CHECKED" : "")); - sprintf(&network[strlen(network)], "
"); - sprintf(&idxjnl[strlen(idxjnl)], _("Perform journaling of email messages")); - sprintf(&idxjnl[strlen(idxjnl)], ""); - sprintf(&idxjnl[strlen(idxjnl)], "", - ((atoi(buf) != 0) ? "CHECKED" : "")); - sprintf(&idxjnl[strlen(idxjnl)], "
"); - sprintf(&idxjnl[strlen(idxjnl)], _("Perform journaling of non-email messages")); - sprintf(&idxjnl[strlen(idxjnl)], ""); - sprintf(&idxjnl[strlen(idxjnl)], "", - ((atoi(buf) != 0) ? "CHECKED" : "")); - sprintf(&idxjnl[strlen(idxjnl)], "
"); - sprintf(&idxjnl[strlen(idxjnl)], _("Email destination of journalized messages")); - sprintf(&idxjnl[strlen(idxjnl)], ""); - sprintf(&idxjnl[strlen(idxjnl)], "", buf); - sprintf(&idxjnl[strlen(idxjnl)], "

"); - sprintf(&purger[strlen(purger)], _("Default message expire policy for public rooms")); - sprintf(&purger[strlen(purger)], ""); - sprintf(&purger[strlen(purger)], "", - ((sitepolicy == 1) ? "CHECKED" : "") ); - sprintf(&purger[strlen(purger)], _("Never automatically expire messages")); - sprintf(&purger[strlen(purger)], "
\n"); - sprintf(&purger[strlen(purger)], "", - ((sitepolicy == 2) ? "CHECKED" : "") ); - sprintf(&purger[strlen(purger)], _("Expire by message count")); - sprintf(&purger[strlen(purger)], "
\n"); - sprintf(&purger[strlen(purger)], "", - ((sitepolicy == 3) ? "CHECKED" : "") ); - sprintf(&purger[strlen(purger)], _("Expire by message age")); - sprintf(&purger[strlen(purger)], "
"); - sprintf(&purger[strlen(purger)], _("Number of messages or days: ")); - sprintf(&purger[strlen(purger)], "", sitevalue); - sprintf(&purger[strlen(purger)], "

"); - sprintf(&purger[strlen(purger)], _("Default message expire policy for private mailboxes")); - sprintf(&purger[strlen(purger)], ""); - sprintf(&purger[strlen(purger)], "", - ((mboxpolicy == 0) ? "CHECKED" : "") ); - sprintf(&purger[strlen(purger)], _("Same policy as public rooms")); - sprintf(&purger[strlen(purger)], "
\n"); - sprintf(&purger[strlen(purger)], "", - ((mboxpolicy == 1) ? "CHECKED" : "") ); - sprintf(&purger[strlen(purger)], _("Never automatically expire messages")); - sprintf(&purger[strlen(purger)], "
\n"); - sprintf(&purger[strlen(purger)], "", - ((mboxpolicy == 2) ? "CHECKED" : "") ); - sprintf(&purger[strlen(purger)], _("Expire by message count")); - sprintf(&purger[strlen(purger)], "
\n"); - sprintf(&purger[strlen(purger)], "", - ((mboxpolicy == 3) ? "CHECKED" : "") ); - sprintf(&purger[strlen(purger)], _("Expire by message age")); - sprintf(&purger[strlen(purger)], "
"); - sprintf(&purger[strlen(purger)], _("Number of messages or days: ")); - sprintf(&purger[strlen(purger)], "", mboxvalue); - sprintf(&purger[strlen(purger)], "

"); - sprintf(&access[strlen(access)], "
"); - sprintf(&network[strlen(network)], ""); - sprintf(&tuning[strlen(tuning)], ""); - sprintf(&directory[strlen(directory)], ""); - sprintf(&purger[strlen(purger)], ""); - sprintf(&idxjnl[strlen(idxjnl)], ""); - - 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("

"); - wprintf("", _("Save changes")); - wprintf(" "); - wprintf("\n", _("Cancel")); - wprintf("
\n"); - wprintf("
\n"); - wDumpContent(1); -} - -/** - * parse siteconfig changes - */ -void siteconfig(void) -{ - char buf[256]; - - if (strlen(bstr("ok_button")) == 0) { - display_aide_menu(); - return; - } - serv_printf("CONF set"); - serv_getln(buf, sizeof buf); - if (buf[0] != '4') { - safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage); - display_aide_menu(); - return; - } - serv_printf("%s", bstr("c_nodename")); - serv_printf("%s", bstr("c_fqdn")); - serv_printf("%s", bstr("c_humannode")); - serv_printf("%s", bstr("c_phonenum")); - serv_printf("%s", ((!strcasecmp(bstr("c_creataide"), "yes") ? "1" : "0"))); - serv_printf("%s", bstr("c_sleeping")); - serv_printf("%s", bstr("c_initax")); - serv_printf("%s", ((!strcasecmp(bstr("c_regiscall"), "yes") ? "1" : "0"))); - serv_printf("%s", ((!strcasecmp(bstr("c_twitdetect"), "yes") ? "1" : "0"))); - serv_printf("%s", bstr("c_twitroom")); - serv_printf("%s", bstr("c_moreprompt")); - serv_printf("%s", ((!strcasecmp(bstr("c_restrict"), "yes") ? "1" : "0"))); - serv_printf("%s", bstr("c_bbs_city")); - serv_printf("%s", bstr("c_sysadm")); - serv_printf("%s", bstr("c_maxsessions")); - serv_printf(""); /* placeholder - this field is not in use */ - serv_printf("%s", bstr("c_userpurge")); - serv_printf("%s", bstr("c_roompurge")); - serv_printf("%s", bstr("c_logpages")); - serv_printf("%s", bstr("c_createax")); - serv_printf("%s", bstr("c_maxmsglen")); - serv_printf("%s", bstr("c_min_workers")); - serv_printf("%s", bstr("c_max_workers")); - serv_printf("%s", bstr("c_pop3_port")); - serv_printf("%s", bstr("c_smtp_port")); - serv_printf("%s", ((!strcasecmp(bstr("c_rfc822_strict_from"), "yes") ? "0" : "1"))); /* note: reverse bool */ - serv_printf("%s", ((!strcasecmp(bstr("c_aide_zap"), "yes") ? "1" : "0"))); - serv_printf("%s", bstr("c_imap_port")); - serv_printf("%s", bstr("c_net_freq")); - serv_printf("%s", ((!strcasecmp(bstr("c_disable_newu"), "yes") ? "1" : "0"))); - serv_printf("1"); /* placeholder - this field is not in use */ - serv_printf("%s", bstr("c_purge_hour")); - serv_printf("%s", bstr("c_ldap_host")); - serv_printf("%s", bstr("c_ldap_port")); - serv_printf("%s", bstr("c_ldap_base_dn")); - serv_printf("%s", bstr("c_ldap_bind_dn")); - serv_printf("%s", bstr("c_ldap_bind_pw")); - serv_printf("%s", bstr("c_ip_addr")); - serv_printf("%s", bstr("c_msa_port")); - serv_printf("%s", bstr("c_imaps_port")); - serv_printf("%s", bstr("c_pop3s_port")); - serv_printf("%s", bstr("c_smtps_port")); - serv_printf("%s", ((!strcasecmp(bstr("c_enable_fulltext"), "yes") ? "1" : "0"))); - serv_printf("%s", ((!strcasecmp(bstr("c_auto_cull"), "yes") ? "1" : "0"))); - serv_printf("%s", ((!strcasecmp(bstr("c_instant_expunge"), "yes") ? "1" : "0"))); - serv_printf("%s", ((!strcasecmp(bstr("c_allow_spoofing"), "yes") ? "1" : "0"))); - serv_printf("%s", ((!strcasecmp(bstr("c_journal_email"), "yes") ? "1" : "0"))); - serv_printf("%s", ((!strcasecmp(bstr("c_journal_pubmsgs"), "yes") ? "1" : "0"))); - serv_printf("%s", bstr("c_journal_dest")); - serv_printf("000"); - - serv_printf("SPEX site|%d|%d", atoi(bstr("sitepolicy")), atoi(bstr("sitevalue"))); - serv_getln(buf, sizeof buf); - serv_printf("SPEX mailboxes|%d|%d", atoi(bstr("mboxpolicy")), atoi(bstr("mboxvalue"))); - serv_getln(buf, sizeof buf); - - safestrncpy(WC->ImportantMessage, _("Your system configuration has been updated."), - sizeof WC->ImportantMessage); - display_aide_menu(); -} - - -/*@}*/ diff --git a/webcit/snprintf.c b/webcit/snprintf.c deleted file mode 100644 index 66e9701e2..000000000 --- a/webcit/snprintf.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup SnprintfReplacement modified from Sten Gunterberg's BUGTRAQ post of 22 Jul 1997 - * --nathan bryant - * \ingroup tools - */ -/*@{*/ -/** - * \brief Replacements for snprintf() and vsnprintf() - * - * Use it only if you have the "spare" cycles needed to effectively - * do every snprintf operation twice! Why is that? Because everything - * is first vfprintf()'d to /dev/null to determine the number of bytes. - * Perhaps a bit slow for demanding applications on slow machines, - * no problem for a fast machine with some spare cycles. - * - * You don't have a /dev/null? Every Linux contains one for free! - * - * Because the format string is never even looked at, all current and - * possible future printf-conversions should be handled just fine. - * - * Written July 1997 by Sten Gunterberg (gunterberg@ergon.ch) - */ - -#include "webcit.h" -#include "webserver.h" - -/** - * \brief is it needed???? - * \param fmt the formatstring? - * \param argp how many params? - */ -static int needed(const char *fmt, va_list argp) -{ - static FILE *sink = NULL; - - /** - * ok, there's a small race here that could result in the sink being - * opened more than once if we're threaded, but I'd rather ignore it than - * spend cycles synchronizing :-) */ - - if (sink == NULL) { - if ((sink = fopen("/dev/null", "w")) == NULL) { - perror("/dev/null"); - exit(1); - } - } - return vfprintf(sink, fmt, argp); -} - -/** - * \brief vsnprintf wrapper - * \param buf the output charbuffer - * \param max maximal size of the buffer - * \param fmt the formatstring (see man printf) - * \param argp the variable argument list - */ -int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp) -{ - char *p; - int size; - - if ((p = malloc(needed(fmt, argp) + 1)) == NULL) { - lprintf(1, "vsnprintf: malloc failed, aborting\n"); - abort(); - } - if ((size = vsprintf(p, fmt, argp)) >= max) - size = -1; - - strncpy(buf, p, max); - buf[max - 1] = 0; - free(p); - return size; -} - -/** - * \brief snprintf wrapper - * \param buf the output charbuffer - * \param max maximal size of the buffer - * \param fmt the formatstring (see man printf) - * \param ... the variable argument list - */ -int snprintf(char *buf, size_t max, const char *fmt,...) -{ - va_list argp; - int bytes; - - va_start(argp, fmt); - bytes = vsnprintf(buf, max, fmt, argp); - va_end(argp); - - return bytes; -} - - - -/*@}*/ diff --git a/webcit/src/auth.c b/webcit/src/auth.c new file mode 100644 index 000000000..0f594e163 --- /dev/null +++ b/webcit/src/auth.c @@ -0,0 +1,562 @@ +/* + * $Id$ + */ +/** + * + * \defgroup WebcitAuth WebcitAuth; Handles authentication of users to a Citadel server. + * \ingroup CitadelConfig + */ + +/*@{*/ +#include "webcit.h" + + + +/** + * \brief user states + * the plain text states of a user. filled in at \ function TODO initialize_ax_defs() + * due to NLS + */ +char *axdefs[7]; + +void initialize_axdefs(void) { + axdefs[0] = _("Deleted"); /*!0: an erased user */ + axdefs[1] = _("New User"); /*!1: a new user */ + axdefs[2] = _("Problem User"); /*!2: a trouble maker */ + axdefs[3] = _("Local User"); /*!3: user with normal privileges */ + axdefs[4] = _("Network User"); /*!4: a user that may access network resources */ + axdefs[5] = _("Preferred User");/*!5: a moderator */ + axdefs[6] = _("Aide"); /*!6: chief */ +} + + + + +/** + * \brief Display the login screen + * \param mesg The error message if last attempt failed. + */ +void display_login(char *mesg) +{ + char buf[SIZ]; + + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n"); + + if (mesg != NULL) if (strlen(mesg) > 0) { + stresc(buf, mesg, 0, 0); + svprintf("mesg", WCS_STRING, "%s", buf); + } + + svprintf("LOGIN_INSTRUCTIONS", WCS_STRING, + _("
    " + "
  • If you already have an account on %s, " + "enter your user name and password and click "Login." " + "
  • If you are a new user, enter the name and password " + "you wish to use, " + "and click "New User." " + "
  • Please log off properly when finished. " + "
  • You must use a browser that supports frames and " + "cookies. " + "
  • 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.
    " + "
"), + 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, "
"); + svprintf("NEWUSER_BUTTON_POST", WCS_STRING, "
"); + } + 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; ilogged_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("
"); + 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("
"); + wprintf(_("Log in again")); + wprintf("   " + ""); + wprintf(_("Close window")); + wprintf("
\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("
\n" + "
" + ""); + wprintf(_("Validate new users")); + wprintf("
\n
\n
\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("%s
\n", &buf[4]); + } + } + } + + /** Now see if any more users require validation. */ + serv_puts("GNUR"); + serv_getln(buf, sizeof buf); + if (buf[0] == '2') { + wprintf(""); + wprintf(_("No users require validation at this time.")); + wprintf("
\n"); + wDumpContent(1); + return; + } + if (buf[0] != '3') { + wprintf("%s
\n", &buf[4]); + wDumpContent(1); + return; + } + + wprintf("
" + "
\n"); + wprintf("
"); + + 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

%s

", + buf, &cmd[4]); + if (a == 2) + wprintf("PW: %s
\n", buf); + if (a == 3) + wprintf("%s
\n", buf); + if (a == 4) + wprintf("%s
\n", buf); + if (a == 5) + wprintf("%s, ", buf); + if (a == 6) + wprintf("%s ", buf); + if (a == 7) + wprintf("%s
\n", buf); + if (a == 8) + wprintf("%s
\n", buf); + if (a == 9) + wprintf(_("Current access level: %d (%s)\n"), + atoi(buf), axdefs[atoi(buf)]); + } while (strcmp(buf, "000")); + } else { + wprintf("

%s

%s
\n", user, &cmd[4]); + } + + wprintf("
"); + wprintf(_("Select access level for this user:")); + wprintf("
\n"); + for (a = 0; a <= 6; ++a) { + wprintf("%s   \n", + a, axdefs[a]); + } + wprintf("
\n"); + + wprintf("
\n"); + wprintf("
\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("
\n" + "
" + ""); + wprintf(_("Change your password")); + wprintf("" + "
\n" + "
\n
\n" + ); + + if (strlen(WC->ImportantMessage) > 0) { + do_template("beginbox_nt"); + wprintf("" + "%s
\n", WC->ImportantMessage); + do_template("endbox"); + safestrncpy(WC->ImportantMessage, "", sizeof WC->ImportantMessage); + } + + wprintf("
" + "
\n"); + + wprintf("

"); + serv_puts("MESG changepw"); + serv_getln(buf, sizeof buf); + if (buf[0] == '1') { + fmout("CENTER"); + } + + wprintf("
\n"); + wprintf("
" + "" + "\n"); + wprintf("\n"); + wprintf("\n"); + wprintf("\n"); + + wprintf("
"); + wprintf(_("Enter new password:")); + wprintf("
"); + wprintf(_("Enter it again to confirm:")); + wprintf("

\n"); + wprintf("", _("Change password")); + wprintf(" "); + wprintf("\n", _("Cancel")); + wprintf("
\n"); + wprintf("
\n"); + wDumpContent(1); +} + +/** + * \brief change password + * if passwords match, propagate it to citserver. + */ +void changepw(void) +{ + char buf[SIZ]; + char newpass1[32], newpass2[32]; + + if (strlen(bstr("change_action")) == 0) { + safestrncpy(WC->ImportantMessage, + _("Cancelled. Password was not changed."), + sizeof WC->ImportantMessage); + display_main_menu(); + return; + } + + safestrncpy(newpass1, bstr("newpass1"), sizeof newpass1); + safestrncpy(newpass2, bstr("newpass2"), sizeof newpass2); + + if (strcasecmp(newpass1, newpass2)) { + safestrncpy(WC->ImportantMessage, + _("They don't match. Password was not changed."), + sizeof WC->ImportantMessage); + display_changepw(); + return; + } + + if (strlen(newpass1) == 0) { + safestrncpy(WC->ImportantMessage, + _("Blank passwords are not allowed."), + sizeof WC->ImportantMessage); + display_changepw(); + return; + } + + serv_printf("SETP %s", newpass1); + serv_getln(buf, sizeof buf); + sprintf(WC->ImportantMessage, "%s", &buf[4]); + if (buf[0] == '2') { + safestrncpy(WC->wc_password, buf, sizeof WC->wc_password); + display_main_menu(); + } + else { + display_changepw(); + } +} + + + +/** @} */ diff --git a/webcit/src/autocompletion.c b/webcit/src/autocompletion.c new file mode 100644 index 000000000..0b9373ea2 --- /dev/null +++ b/webcit/src/autocompletion.c @@ -0,0 +1,49 @@ +/* + * $Id$ + *//** + * \defgroup AjaxAutoCompletion ajax-powered autocompletion... + * \ingroup ClientPower + */ + +/*@{*/ +#include "webcit.h" + +/** + * \brief Recipient autocompletion results + * \param partial the account to search for ?????? + */ +void recp_autocomplete(char *partial) { + char buf[1024]; + char name[128]; + + output_headers(0, 0, 0, 0, 0, 0); + + wprintf("Content-type: text/html\r\n" + "Server: %s\r\n" + "Connection: close\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-store\r\n", + SERVER); + begin_burst(); + + wprintf("
    "); + + 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("
  • "); + escputs(name); + wprintf("
  • "); + } + } + + wprintf("
"); + + wprintf("\r\n\r\n"); + wDumpContent(0); +} + + +/** @} */ diff --git a/webcit/src/availability.c b/webcit/src/availability.c new file mode 100644 index 000000000..0c1198a26 --- /dev/null +++ b/webcit/src/availability.c @@ -0,0 +1,262 @@ +/* + * $Id$ + */ +/** + * + * \defgroup CalendarAv Check attendee availability for scheduling a meeting. + * \ingroup Calendaring + */ +/*@{*/ + + +#include "webcit.h" +#include "webserver.h" + +/** only available if we have calendaring */ +#ifdef WEBCIT_WITH_CALENDAR_SERVICE + + + +/** + * \brief verify users avaiability + * Utility function to fetch a VFREEBUSY type of thing for + * any specified user. + * \param who string of the user to search + */ +icalcomponent *get_freebusy_for_user(char *who) { + char buf[SIZ]; + char *serialized_fb = NULL; + icalcomponent *fb = NULL; + + serv_printf("ICAL freebusy|%s", who); + serv_getln(buf, sizeof buf); + if (buf[0] == '1') { + serialized_fb = read_server_text(); + } + + if (serialized_fb == NULL) { + return NULL; + } + + fb = icalcomponent_new_from_string(serialized_fb); + free(serialized_fb); + if (fb == NULL) { + return NULL; + } + + return(fb); +} + + + + +/** + * \brief Check if dates are overlapping + * Check to see if two events overlap. + * (This function is used in both Citadel and WebCit. If you change it in + * one place, change it in the other. Better yet, put it in a library.) + * \param t1start date one start + * \param t1end date one end + * \param t2start date one start + * \param t2end date two end + * \returns nonzero if they do. + */ +int ical_ctdl_is_overlap( + struct icaltimetype t1start, + struct icaltimetype t1end, + struct icaltimetype t2start, + struct icaltimetype t2end +) { + + if (icaltime_is_null_time(t1start)) return(0); + if (icaltime_is_null_time(t2start)) return(0); + + /** First, check for all-day events */ + if (t1start.is_date) { + if (!icaltime_compare_date_only(t1start, t2start)) { + return(1); + } + if (!icaltime_is_null_time(t2end)) { + if (!icaltime_compare_date_only(t1start, t2end)) { + return(1); + } + } + } + + if (t2start.is_date) { + if (!icaltime_compare_date_only(t2start, t1start)) { + return(1); + } + if (!icaltime_is_null_time(t1end)) { + if (!icaltime_compare_date_only(t2start, t1end)) { + return(1); + } + } + } + + /** Now check for overlaps using date *and* time. */ + + /** First, bail out if either event 1 or event 2 is missing end time. */ + if (icaltime_is_null_time(t1end)) return(0); + if (icaltime_is_null_time(t2end)) return(0); + + /** If event 1 ends before event 2 starts, we're in the clear. */ + if (icaltime_compare(t1end, t2start) <= 0) return(0); + + /** If event 2 ends before event 1 starts, we're also ok. */ + if (icaltime_compare(t2end, t1start) <= 0) return(0); + + /** Otherwise, they overlap. */ + return(1); +} + + + +/* + * \brief dig availability on citserver + * Back end function for check_attendee_availability() + * This one checks an individual attendee against a supplied + * event start and end time. All these fields have already been + * broken out. + * \param attendee_string name of the attendee + * \param event_start starttime of the event to check + * \param event_end endtime of the event to check + * \return The result is placed in 'annotation'. + */ +void check_individual_attendee(char *attendee_string, + struct icaltimetype event_start, + struct icaltimetype event_end, + char *annotation) { + + icalcomponent *fbc = NULL; + icalcomponent *fb = NULL; + icalproperty *thisfb = NULL; + struct icalperiodtype period; + + /** + * Set to 'unknown' right from the beginning. Unless we learn + * something else, that's what we'll go with. + */ + strcpy(annotation, _("availability unknown")); + + fbc = get_freebusy_for_user(attendee_string); + if (fbc == NULL) { + return; + } + + /** + * Make sure we're looking at a VFREEBUSY by itself. What we're probably + * looking at initially is a VFREEBUSY encapsulated in a VCALENDAR. + */ + if (icalcomponent_isa(fbc) == ICAL_VCALENDAR_COMPONENT) { + fb = icalcomponent_get_first_component(fbc, ICAL_VFREEBUSY_COMPONENT); + } + else if (icalcomponent_isa(fbc) == ICAL_VFREEBUSY_COMPONENT) { + fb = fbc; + } + + /** Iterate through all FREEBUSY's looking for conflicts. */ + if (fb != NULL) { + + strcpy(annotation, _("free")); + + for (thisfb = icalcomponent_get_first_property(fb, ICAL_FREEBUSY_PROPERTY); + thisfb != NULL; + thisfb = icalcomponent_get_next_property(fb, ICAL_FREEBUSY_PROPERTY) ) { + + /** Do the check */ + period = icalproperty_get_freebusy(thisfb); + if (ical_ctdl_is_overlap(period.start, period.end, + event_start, event_end)) { + strcpy(annotation, _("BUSY")); + } + + } + } + + icalcomponent_free(fbc); +} + + + + +/** + * \brief check attendees availability + * Check the availability of all attendees for an event (when possible) + * and annotate accordingly. + * \param vevent the event which should be compared with attendees calendar + */ +void check_attendee_availability(icalcomponent *vevent) { + icalproperty *attendee = NULL; + icalproperty *dtstart_p = NULL; + icalproperty *dtend_p = NULL; + struct icaltimetype dtstart_t; + struct icaltimetype dtend_t; + char attendee_string[SIZ]; + char annotated_attendee_string[SIZ]; + char annotation[SIZ]; + + if (vevent == NULL) { + return; + } + + /** + * If we're looking at a fully encapsulated VCALENDAR + * rather than a VEVENT component, attempt to use the first + * relevant VEVENT subcomponent. If there is none, the + * NULL returned by icalcomponent_get_first_component() will + * tell the next iteration of this function to create a + * new one. + */ + if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) { + check_attendee_availability( + icalcomponent_get_first_component( + vevent, ICAL_VEVENT_COMPONENT + ) + ); + return; + } + + ical_dezonify(vevent); /**< Convert everything to UTC */ + + /** + * Learn the start and end times. + */ + dtstart_p = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY); + if (dtstart_p != NULL) dtstart_t = icalproperty_get_dtstart(dtstart_p); + + dtend_p = icalcomponent_get_first_property(vevent, ICAL_DTEND_PROPERTY); + if (dtend_p != NULL) dtend_t = icalproperty_get_dtend(dtend_p); + + /** + * Iterate through attendees. + */ + for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); + attendee != NULL; + attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) { + + strcpy(attendee_string, icalproperty_get_attendee(attendee)); + if (!strncasecmp(attendee_string, "MAILTO:", 7)) { + + /** screen name or email address */ + strcpy(attendee_string, &attendee_string[7]); + striplt(attendee_string); + + check_individual_attendee(attendee_string, + dtstart_t, dtend_t, + annotation); + + /** Replace the attendee name with an annotated one. */ + snprintf(annotated_attendee_string, sizeof annotated_attendee_string, + "MAILTO:%s (%s)", attendee_string, annotation); + icalproperty_set_attendee(attendee, annotated_attendee_string); + + } + } + +} + + +#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ + +/** @} */ diff --git a/webcit/src/calendar.c b/webcit/src/calendar.c new file mode 100644 index 000000000..5353dfa67 --- /dev/null +++ b/webcit/src/calendar.c @@ -0,0 +1,1008 @@ +/* + * $Id$ + */ +/** + * \defgroup calav Functions which handle calendar objects and their processing/display. + * \ingroup Calendaring + */ +/* @{ */ + +#include "webcit.h" +#include "webserver.h" + +#ifndef WEBCIT_WITH_CALENDAR_SERVICE + +/** + * \brief get around non existing types + * Handler stubs for builds with no calendar library available + * \param part_source dummy pointer to the source + * \param msgnum number of the mesage in the db + * \param cal_partnum number of the calendar part + */ +void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) { + + wprintf(_("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.
\n") + ); + +} + +/** + * \brief say we can't display calendar items + * \param msgnum number of the mesage in our db + */ +void display_calendar(long msgnum) { + wprintf(_("" + "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." + "
\n")); +} + +/** + * \brief say we can't display task items + * \param msgnum number of the mesage in our db + */ +void display_task(long msgnum) { + wprintf(_("" + "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." + "
\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("
\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("\n"); + break; + case ICAL_METHOD_REPLY: + wprintf("\n"); + break; + case ICAL_METHOD_PUBLISH: + wprintf("\n"); + break; + default: + wprintf("\n"); + break; + } + } + + p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY); + if (p != NULL) { + wprintf("\n"); + } + + p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY); + if (p != NULL) { + wprintf("\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("", d_str); + } + else { + tt = icaltime_as_timet(t); + fmt_date(buf, tt, 0); + wprintf("", 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("", buf); + } + + } + + p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY); + if (p != NULL) { + wprintf("\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("\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("\n"); + } + } + lprintf(9, "...done.\n"); + + /** Display the Accept/Decline buttons */ + wprintf("" + "\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("\n", + _("Click Update 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("
\n" + "" + "  " + ""); + wprintf(_("Meeting invitation")); + wprintf("
\n" + "" + "  " + ""); + wprintf(_("Attendee's reply to your invitation")); + wprintf("
\n" + "" + "  " + ""); + wprintf(_("Published event")); + wprintf("
"); + wprintf(_("This is an unknown type of calendar item.")); + wprintf("
"); + wprintf(_("Summary:")); + wprintf(""); + escputs((char *)icalproperty_get_comment(p)); + wprintf("
"); + wprintf(_("Location:")); + wprintf(""); + escputs((char *)icalproperty_get_comment(p)); + wprintf("
"); + wprintf(_("Date:")); + wprintf("%s
"); + wprintf(_("Starting date/time:")); + wprintf("%s
"); + wprintf(_("Ending date/time:")); + wprintf("%s
"); + wprintf(_("Description:")); + wprintf(""); + escputs((char *)icalproperty_get_comment(p)); + wprintf("
"); + wprintf(_("Attendee:")); + wprintf(""); + 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("
%s", + (is_update ? + _("Update:") : + _("CONFLICT:") + ) + ); + escputs(conflict_message); + wprintf("
%s" + "%s" + " | " + "%s" + " | " + "%s" + "
" + "%s" + "" + "%s" + " | " + "%s" + "" + "
\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("
\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("
\n"); + wprintf("
" + ""); + wprintf(_("Respond to meeting request")); + wprintf("" + "
\n" + ); + wprintf("
\n
\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("
" + "" + "" + ); + 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 not been entered into your calendar.") + ); + } + wprintf(" "); + wprintf(_("A reply has been sent to the meeting organizer.")); + wprintf("
\n"); + } else { + wprintf("" + "%s\n", &buf[4]); + } + + wprintf("wc_roomname); + wprintf("\">
"); + wprintf(_("Return to messages")); + wprintf("

\n"); + + wDumpContent(1); +} + + + +/** + * \brief Handle an incoming RSVP + */ +void handle_rsvp(void) { + char buf[SIZ]; + + output_headers(1, 1, 2, 0, 0, 0); + + wprintf("
\n"); + wprintf("
" + ""); + wprintf(_("Update your calendar with this RSVP")); + wprintf("" + "
\n" + "
\n
\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("
" + "" + "" + ); + 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 not been updated.") + ); + } + wprintf("
\n" + ); + } else { + wprintf("" + "%s\n", &buf[4]); + } + + wprintf("wc_roomname); + wprintf("\">
"); + wprintf(_("Return to messages")); + wprintf("

\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("
\n" + "" + "" + "
"); + wprintf(_("Edit task")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("
" + "
"); + + wprintf("
\n"); + wprintf("\n", + msgnum); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + wprintf("
"); + wprintf(_("Summary:")); + wprintf("" + "
"); + wprintf(_("Start date:")); + wprintf(""); + 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("
"); + wprintf(_("Due date:")); + wprintf(""); + 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("
"); + wprintf(_("Description:")); + wprintf(""); + wprintf("
\n"); + + wprintf("
" + "" + "  " + "\n" + "  " + "\n" + "
\n", + _("Save"), + _("Delete"), + _("Cancel") + ); + + wprintf("
\n"); + + wprintf("
\n"); + wDumpContent(1); + + if (created_new_vtodo) { + icalcomponent_free(vtodo); + } +} + +/* + * \brief Save an edited task + * \param supplied_vtodo the task to save + * \param msgnum number of the mesage in our db + */ +void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { + char buf[SIZ]; + int delete_existing = 0; + icalproperty *prop; + icalcomponent *vtodo, *encaps; + int created_new_vtodo = 0; + int i; + int sequence = 0; + struct icaltimetype t; + + if (supplied_vtodo != NULL) { + vtodo = supplied_vtodo; + /** + * If we're looking at a fully encapsulated VCALENDAR + * rather than a VTODO component, attempt to use the first + * relevant VTODO subcomponent. If there is none, the + * NULL returned by icalcomponent_get_first_component() will + * tell the next iteration of this function to create a + * new one. + */ + if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) { + save_individual_task( + icalcomponent_get_first_component( + vtodo, ICAL_VTODO_COMPONENT + ), msgnum + ); + return; + } + } + else { + vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); + created_new_vtodo = 1; + } + + if (strlen(bstr("save_button")) > 0) { + + /** Replace values in the component with ones from the form */ + + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_SUMMARY_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + icalcomponent_add_property(vtodo, + icalproperty_new_summary(bstr("summary"))); + + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_DESCRIPTION_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + icalcomponent_add_property(vtodo, + icalproperty_new_description(bstr("description"))); + + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_DTSTART_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + icaltime_from_webform(&t, "dtstart"); + icalcomponent_add_property(vtodo, + icalproperty_new_dtstart(t) + ); + + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_DUE_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + icaltime_from_webform(&t, "due"); + icalcomponent_add_property(vtodo, + icalproperty_new_due(t) + ); + + /** Give this task a UID if it doesn't have one. */ + lprintf(9, "Give this task a UID if it doesn't have one.\n"); + if (icalcomponent_get_first_property(vtodo, + ICAL_UID_PROPERTY) == NULL) { + generate_uuid(buf); + icalcomponent_add_property(vtodo, + icalproperty_new_uid(buf) + ); + } + + /** Increment the sequence ID */ + lprintf(9, "Increment the sequence ID\n"); + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) { + i = icalproperty_get_sequence(prop); + lprintf(9, "Sequence was %d\n", i); + if (i > sequence) sequence = i; + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + ++sequence; + lprintf(9, "New sequence is %d. Adding...\n", sequence); + icalcomponent_add_property(vtodo, + icalproperty_new_sequence(sequence) + ); + + /** + * Encapsulate event into full VCALENDAR component. Clone it first, + * for two reasons: one, it's easier to just free the whole thing + * when we're done instead of unbundling, but more importantly, we + * can't encapsulate something that may already be encapsulated + * somewhere else. + */ + lprintf(9, "Encapsulating into full VCALENDAR component\n"); + encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo)); + + /* Serialize it and save it to the message base */ + serv_puts("ENT0 1|||4"); + serv_getln(buf, sizeof buf); + if (buf[0] == '4') { + serv_puts("Content-type: text/calendar"); + serv_puts(""); + serv_puts(icalcomponent_as_ical_string(encaps)); + serv_puts("000"); + + /** + * Probably not necessary; the server will see the UID + * of the object and delete the old one anyway, but + * just in case... + */ + delete_existing = 1; + } + icalcomponent_free(encaps); + } + + /** + * If the user clicked 'Delete' then explicitly delete the message. + */ + if (strlen(bstr("delete_button")) > 0) { + delete_existing = 1; + } + + if ( (delete_existing) && (msgnum > 0L) ) { + serv_printf("DELE %ld", atol(bstr("msgnum"))); + serv_getln(buf, sizeof buf); + } + + if (created_new_vtodo) { + icalcomponent_free(vtodo); + } + + /** Go back to the task list */ + readloop("readfwd"); +} + + + +/** + * \brief generic item handler + * Code common to all display handlers. Given a message number and a MIME + * type, we load the message and hunt for that MIME type. If found, we load + * the relevant part, deserialize it into a libical component, filter it for + * the requested object type, and feed it to the specified handler. + * \param mimetype mimetyp of our object + * \param which_kind sort of ical type + * \param msgnum number of the mesage in our db + * \param callback a funcion \todo + * + */ +void display_using_handler(long msgnum, + char *mimetype, + icalcomponent_kind which_kind, + void (*callback)(icalcomponent *, long) + ) { + char buf[SIZ]; + char mime_partnum[SIZ]; + char mime_filename[SIZ]; + char mime_content_type[SIZ]; + char mime_disposition[SIZ]; + int mime_length; + char relevant_partnum[SIZ]; + char *relevant_source = NULL; + icalcomponent *cal, *c; + + sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */ + serv_puts(buf); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') return; + + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + if (!strncasecmp(buf, "part=", 5)) { + extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename); + extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum); + extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition); + extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type); + mime_length = extract_int(&buf[5], 5); + + if (!strcasecmp(mime_content_type, "text/calendar")) { + strcpy(relevant_partnum, mime_partnum); + } + + } + } + + if (strlen(relevant_partnum) > 0) { + relevant_source = load_mimepart(msgnum, relevant_partnum); + if (relevant_source != NULL) { + + cal = icalcomponent_new_from_string(relevant_source); + if (cal != NULL) { + + ical_dezonify(cal); + + /** Simple components of desired type */ + if (icalcomponent_isa(cal) == which_kind) { + callback(cal, msgnum); + } + + /** Subcomponents of desired type */ + for (c = icalcomponent_get_first_component(cal, + which_kind); + (c != 0); + c = icalcomponent_get_next_component(cal, + which_kind)) { + callback(c, msgnum); + } + icalcomponent_free(cal); + } + free(relevant_source); + } + } + +} + +/** + * \brief display whole calendar + * \param msgnum number of the mesage in our db + */ +void display_calendar(long msgnum) { + display_using_handler(msgnum, "text/calendar", + ICAL_VEVENT_COMPONENT, + display_individual_cal); +} + +/** + * \brief display whole taksview + * \param msgnum number of the mesage in our db + */ +void display_task(long msgnum) { + display_using_handler(msgnum, "text/calendar", + ICAL_VTODO_COMPONENT, + display_individual_cal); +} + +/** + * \brief display the editor component for a task + */ +void display_edit_task(void) { + long msgnum = 0L; + + /** Force change the room if we have to */ + if (strlen(bstr("taskrm")) > 0) { + gotoroom(bstr("taskrm")); + } + + msgnum = atol(bstr("msgnum")); + if (msgnum > 0L) { + /** existing task */ + display_using_handler(msgnum, "text/calendar", + ICAL_VTODO_COMPONENT, + display_edit_individual_task); + } + else { + /** new task */ + display_edit_individual_task(NULL, 0L); + } +} + +/** + *\brief save an edited task + */ +void save_task(void) { + long msgnum = 0L; + + msgnum = atol(bstr("msgnum")); + if (msgnum > 0L) { + display_using_handler(msgnum, "text/calendar", + ICAL_VTODO_COMPONENT, + save_individual_task); + } + else { + save_individual_task(NULL, 0L); + } +} + +/** + * \brief display the editor component for an event + */ +void display_edit_event(void) { + long msgnum = 0L; + + msgnum = atol(bstr("msgnum")); + if (msgnum > 0L) { + /* existing event */ + display_using_handler(msgnum, "text/calendar", + ICAL_VEVENT_COMPONENT, + display_edit_individual_event); + } + else { + /* new event */ + display_edit_individual_event(NULL, 0L); + } +} + +/** + * \brief save an edited event + */ +void save_event(void) { + long msgnum = 0L; + + msgnum = atol(bstr("msgnum")); + + if (msgnum > 0L) { + display_using_handler(msgnum, "text/calendar", + ICAL_VEVENT_COMPONENT, + save_individual_event); + } + else { + save_individual_event(NULL, 0L); + } +} + + + + + +/** + * \brief freebusy display (for client software) + * \param req dunno. ????? + */ +void do_freebusy(char *req) { + char who[SIZ]; + char buf[SIZ]; + char *fb; + + extract_token(who, req, 1, ' ', sizeof who); + if (!strncasecmp(who, "/freebusy/", 10)) { + strcpy(who, &who[10]); + } + unescape_input(who); + + if ( (!strcasecmp(&who[strlen(who)-4], ".vcf")) + || (!strcasecmp(&who[strlen(who)-4], ".ifb")) + || (!strcasecmp(&who[strlen(who)-4], ".vfb")) ) { + who[strlen(who)-4] = 0; + } + + lprintf(9, "freebusy requested for <%s>\n", who); + serv_printf("ICAL freebusy|%s", who); + serv_getln(buf, sizeof buf); + + if (buf[0] != '1') { + wprintf("HTTP/1.1 404 %s\n", &buf[4]); + output_headers(0, 0, 0, 0, 0, 0); + wprintf("Content-Type: text/plain\r\n"); + wprintf("\r\n"); + wprintf("%s\n", &buf[4]); + return; + } + + fb = read_server_text(); + http_transmit_thing(fb, strlen(fb), "text/calendar", 0); + free(fb); +} + + + +#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ + + +/*@}*/ diff --git a/webcit/src/calendar_tools.c b/webcit/src/calendar_tools.c new file mode 100644 index 000000000..d62cb8296 --- /dev/null +++ b/webcit/src/calendar_tools.c @@ -0,0 +1,302 @@ +/* + * $Id$ + */ +/** + * \defgroup MiscCal Miscellaneous functions which handle calendar components. + * \ingroup Calendaring + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" + +/** Hour strings */ +char *hourname[] = { + "12am", "1am", "2am", "3am", "4am", "5am", "6am", + "7am", "8am", "9am", "10am", "11am", "12pm", + "1pm", "2pm", "3pm", "4pm", "5pm", "6pm", + "7pm", "8pm", "9pm", "10pm", "11pm" +}; + +#ifdef WEBCIT_WITH_CALENDAR_SERVICE + +/** + * \brief display and edit date/time + * The display_icaltimetype_as_webform() and icaltime_from_webform() functions + * handle the display and editing of date/time properties in web pages. The + * first one converts an icaltimetype into valid HTML markup -- a series of form + * fields for editing the date and time. When the user submits the form, the + * results can be fed back into the second function, which turns it back into + * an icaltimetype. The "prefix" string required by both functions is prepended + * to all field names. This allows a form to contain more than one date/time + * property (for example, a start and end time) by ensuring the field names are + * unique within the form. + * + * \todo NOTE: These functions assume that the icaltimetype being edited is in UTC, and + * will convert to/from local time for editing. "local" in this case is assumed + * to be the time zone in which the WebCit server is running. A future improvement + * might be to allow the user to specify his/her timezone. + * \param t the time we want to parse + * \param prefix ???? \todo + */ + + +void display_icaltimetype_as_webform(struct icaltimetype *t, char *prefix) { + int i; + time_t now; + struct tm tm_now; + int this_year; + time_t tt; + struct tm tm; + const int span = 10; + int all_day_event = 0; + time_t monthselect_time; + struct tm monthselect_tm; + char monthselect_str[32]; + char calhourformat[16]; + + get_preference("calhourformat", calhourformat, sizeof calhourformat); + + now = time(NULL); + localtime_r(&now, &tm_now); + this_year = tm_now.tm_year + 1900; + + if (t == NULL) return; + if (t->is_date) all_day_event = 1; + tt = icaltime_as_timet(*t); + if (all_day_event) { + gmtime_r(&tt, &tm); + } + else { + localtime_r(&tt, &tm); + } + + wprintf(_("Month: ")); + wprintf("\n"); + + wprintf(_("Day: ")); + wprintf("\n"); + + wprintf(_("Year: ")); + wprintf("\n"); + + wprintf(_("Hour: ")); + wprintf("\n"); + + wprintf(_("Minute: ")); + wprintf("\n"); +} + +/** + *\brief Get time from form + * get the time back from the user and convert it into internal structs. + * \param t our time element + * \param prefix whats that\todo ???? + */ +void icaltime_from_webform(struct icaltimetype *t, char *prefix) { + char vname[32]; + time_t tt; + struct tm tm; + struct icaltimetype t2; + + tt = time(NULL); + localtime_r(&tt, &tm); + + sprintf(vname, "%s_month", prefix); tm.tm_mon = atoi(bstr(vname)) - 1; + sprintf(vname, "%s_day", prefix); tm.tm_mday = atoi(bstr(vname)); + sprintf(vname, "%s_year", prefix); tm.tm_year = atoi(bstr(vname)) - 1900; + sprintf(vname, "%s_hour", prefix); tm.tm_hour = atoi(bstr(vname)); + sprintf(vname, "%s_minute", prefix); tm.tm_min = atoi(bstr(vname)); + + tt = mktime(&tm); + t2 = icaltime_from_timet(tt, 0); + memcpy(t, &t2, sizeof(struct icaltimetype)); +} + +/** + *\brief Get time from form + * get the time back from the user and convert it into internal structs. + * \param t our time element + * \param prefix whats that\todo ???? + */ + +void icaltime_from_webform_dateonly(struct icaltimetype *t, char *prefix) { + char vname[32]; + + memset(t, 0, sizeof(struct icaltimetype)); + + sprintf(vname, "%s_month", prefix); t->month = atoi(bstr(vname)); + sprintf(vname, "%s_day", prefix); t->day = atoi(bstr(vname)); + sprintf(vname, "%s_year", prefix); t->year = atoi(bstr(vname)); + t->is_utc = 1; + t->is_date = 1; +} + + +/** + * \brief Render PAPSTAT + * Render a PARTSTAT parameter as a string (and put it in parentheses) + * \param buf the string to put it to + * \param attendee the attendee to textify + */ +void partstat_as_string(char *buf, icalproperty *attendee) { + icalparameter *partstat_param; + icalparameter_partstat partstat; + + strcpy(buf, _("(status unknown)")); + + partstat_param = icalproperty_get_first_parameter( + attendee, + ICAL_PARTSTAT_PARAMETER + ); + if (partstat_param == NULL) { + return; + } + + partstat = icalparameter_get_partstat(partstat_param); + switch(partstat) { + case ICAL_PARTSTAT_X: + strcpy(buf, "(x)"); + break; + case ICAL_PARTSTAT_NEEDSACTION: + strcpy(buf, _("(needs action)")); + break; + case ICAL_PARTSTAT_ACCEPTED: + strcpy(buf, _("(accepted)")); + break; + case ICAL_PARTSTAT_DECLINED: + strcpy(buf, _("(declined)")); + break; + case ICAL_PARTSTAT_TENTATIVE: + strcpy(buf, _("(tenative)")); + break; + case ICAL_PARTSTAT_DELEGATED: + strcpy(buf, _("(delegated)")); + break; + case ICAL_PARTSTAT_COMPLETED: + strcpy(buf, _("(completed)")); + break; + case ICAL_PARTSTAT_INPROCESS: + strcpy(buf, _("(in process)")); + break; + case ICAL_PARTSTAT_NONE: + strcpy(buf, _("(none)")); + break; + } +} + + +/** + * \brief embedd + * Utility function to encapsulate a subcomponent into a full VCALENDAR + * \param subcomp the component to encapsulate + * \returns the meta object ??? + */ +icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp) { + icalcomponent *encaps; + + /* lprintf(9, "ical_encapsulate_subcomponent() called\n"); */ + + if (subcomp == NULL) { + lprintf(3, "ERROR: called with NULL argument!\n"); + return NULL; + } + + /** + * If we're already looking at a full VCALENDAR component, + * don't bother ... just return itself. + */ + if (icalcomponent_isa(subcomp) == ICAL_VCALENDAR_COMPONENT) { + return subcomp; + } + + /** Encapsulate the VEVENT component into a complete VCALENDAR */ + encaps = icalcomponent_new(ICAL_VCALENDAR_COMPONENT); + if (encaps == NULL) { + lprintf(3, "%s:%d: Error - could not allocate component!\n", + __FILE__, __LINE__); + return NULL; + } + + /** Set the Product ID */ + icalcomponent_add_property(encaps, icalproperty_new_prodid(PRODID)); + + /** Set the Version Number */ + icalcomponent_add_property(encaps, icalproperty_new_version("2.0")); + + /** Encapsulate the subcomponent inside */ + /* lprintf(9, "Doing the encapsulation\n"); */ + icalcomponent_add_component(encaps, subcomp); + + /** Convert all timestamps to UTC so we don't have to deal with + * stupid VTIMEZONE crap. + */ + ical_dezonify(encaps); + + /** Return the object we just created. */ + return(encaps); +} + + + + +#endif +/*@}*/ diff --git a/webcit/src/calendar_view.c b/webcit/src/calendar_view.c new file mode 100644 index 000000000..dde6f2169 --- /dev/null +++ b/webcit/src/calendar_view.c @@ -0,0 +1,984 @@ +/* + * $Id$ + */ +/** + * \defgroup CalHtmlHandles Handles the HTML display of calendar items. + * \ingroup Calendaring + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" + +#ifndef WEBCIT_WITH_CALENDAR_SERVICE + +/**\brief stub for non-libical builds */ +void do_calendar_view(void) { + wprintf("
"); + wprintf(_("The calendar view is not available.")); + wprintf("

\n"); +} + +/**\brief stub for non-libical builds */ +void do_tasks_view(void) { + wprintf("
"); + wprintf(_("The tasks view is not available.")); + wprintf("

\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("


\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("" + "
" + ); + } + + wprintf("" + "", + WC->disp_cal[i].cal_msgnum, + bstr("calview"), + bstr("year"), + bstr("month"), + bstr("day") + ); + escputs((char *) + icalproperty_get_comment(p)); + wprintf("
\n"); + + if (all_day_event) { + wprintf("
"); + } + + } + + } + + + } + } +} + + +/** + * \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("%i:%2i" + "" + "", + 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("" + "%s%s", + 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("
" + "
\n"); + + wprintf("\n"); + + wprintf("
"); + + localtime_r(&previous_month, &tm); + wprintf("", + (int)(tm.tm_year)+1900, tm.tm_mon + 1); + wprintf("\n"); + + wc_strftime(colheader_label, sizeof colheader_label, "%B", &starting_tm); + wprintf("  " + "" + "%s %d" + "" + "  ", colheader_label, year); + + localtime_r(&next_month, &tm); + wprintf("", + (int)(tm.tm_year)+1900, tm.tm_mon + 1); + wprintf("\n"); + + wprintf("
\n"); + + /** Inner table (the real one) */ + wprintf(""); + 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("\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(""); + } + + wprintf(""); + + /** After displaying Saturday, end the row */ + if ((i % 7) == 6) { + wprintf("\n"); + } + + thetime += (time_t)86400; /** ahead 24 hours */ + } + + wprintf("
" + "%s", colheader_label); + + } + wprintf("
", + ((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("" + "%d
", + 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("
" /** end of inner table */ + "
" /** end of outer table */ + "
\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("
" + "
\n"); + + wprintf("\n"); + + wprintf("
"); + + localtime_r(&previous_month, &tm); + wprintf("", + (int)(tm.tm_year)+1900, tm.tm_mon + 1); + wprintf("\n"); + + wc_strftime(month_label, sizeof month_label, "%B", &tm); + wprintf("  " + "" + "%s %d" + "" + "  ", month_label, year); + + localtime_r(&next_month, &tm); + wprintf("", + (int)(tm.tm_year)+1900, tm.tm_mon + 1); + wprintf("\n"); + + wprintf("
\n"); + + /** Inner table (the real one) */ + wprintf(""); + wprintf("\n"); + wprintf("
\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("" + " \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("\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("
%s %s
%s%s%s%s
%s,%i." + "
\n"); + } + + thetime += (time_t)86400; /** ahead 24 hours */ + } + + wprintf("
" /** end of inner table */ + "
" /** end of outer table */ + "
\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("
week view FIXME

\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("


\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("" + "
" + ); + } + + wprintf("" + "", + WC->disp_cal[i].cal_msgnum, + year, month, day + ); + escputs((char *) + icalproperty_get_comment(p)); + wprintf("
\n"); + + if (all_day_event) { + wprintf("
"); + } + } + + } + + + } + } +} + + +/** + * \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("
" + "
\n"); + + /** Inner table (the real one) */ + wprintf("\n"); + + /** Innermost table (contains hours etc.) */ + wprintf("" + ); + + wprintf(""); /** end stuff-on-the-right */ + + + + wprintf("
" + "\n"); + + /** Display events before 8:00 (hour=-1 is all-day events) */ + wprintf("" + "" + "\n"); + + /** Now the middle of the day... */ + for (hour = daystart; hour <= dayend; ++hour) { /* could do HEIGHT=xx */ + wprintf("\n"); + } + + /** Display events after 5:00... */ + wprintf("" + "" + "\n"); + + + wprintf("
"); + for (hour = (-1); hour <= (daystart-1); ++hour) { + calendar_day_view_display_events(year, month, day, hour); + } + wprintf("
"); + wprintf("", + year, month, day, hour + ); + + if (!strcasecmp(calhourformat, "24")) { + wprintf("%2d:00 ", hour); + } + else { + wprintf("%d:00%s ", + (hour <= 12 ? hour : hour-12), + (hour < 12 ? "am" : "pm") + ); + } + + wprintf(""); + + /* put the data here, stupid */ + calendar_day_view_display_events(year, month, day, hour); + + wprintf("
"); + for (hour = (dayend+1); hour <= 23; ++hour) { + calendar_day_view_display_events(year, month, day, hour); + } + wprintf("
" /* end of innermost table */ + "
"); /* begin stuff-on-the-right */ + + + /** Begin todays-date-with-left-and-right-arrows */ + wprintf("\n"); + wprintf(""); + + /** Left arrow */ + wprintf(""); + + /** 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, + "", + &d_tm + ); + wprintf("%s", d_str); + + /** Right arrow */ + wprintf(""); + + wprintf("
"); + wprintf("", + yesterday.year, yesterday.month, yesterday.day); + wprintf(""); + wprintf("" + "%B
" + "%d
" + "%Y
" + "
"); + wprintf("", + tomorrow.year, tomorrow.month, tomorrow.day); + wprintf("\n"); + wprintf("
\n"); + /** End todays-date-with-left-and-right-arrows */ + + /** \todo In the future we might want to put a month-o-matic here */ + + wprintf("\n"); + + wprintf("
" /** end of inner table */ + "
" /** 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)
\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("
" + "\n\n" + "\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("\n"); + + due = get_task_due_date(WC->disp_cal[i].cal); + fmt_date(buf, due, 0); + wprintf("\n", buf); + } + + wprintf("
"); + wprintf(_("Name of task")); + wprintf(""); + wprintf(_("Date due")); + wprintf("
", + (bg ? "DDDDDD" : "FFFFFF") + ); + + p = icalcomponent_get_first_property(WC->disp_cal[i].cal, + ICAL_SUMMARY_PROPERTY); + wprintf("disp_cal[i].cal_msgnum ); + urlescputs(WC->wc_roomname); + wprintf("\">"); + wprintf(" "); + if (p != NULL) { + escputs((char *)icalproperty_get_comment(p)); + } + wprintf("\n"); + wprintf("%s
\n"); + + /** Free the list */ + free_calendar_buffer(); + +} + +#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ + +/** @} */ diff --git a/webcit/src/context_loop.c b/webcit/src/context_loop.c new file mode 100644 index 000000000..e276383f7 --- /dev/null +++ b/webcit/src/context_loop.c @@ -0,0 +1,493 @@ +/* + * $Id$ + */ +/** + * \defgroup WebServerII some of the webserver stuff. + * This is the other half of the webserver. It handles the task of hooking + * up HTTP requests with the sessions they belong to, using HTTP cookies to + * keep track of things. If the HTTP request doesn't belong to any currently + * active session, a new session is started. + * \ingroup WebcitHttpServer + * + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" + +/** Only one thread may manipulate SessionList at a time... */ +pthread_mutex_t SessionListMutex; + +struct wcsession *SessionList = NULL; /**< our sessions ????*/ + +pthread_key_t MyConKey; /**< TSD key for MySession() */ + + +/** + * \brief free the memory used for viewing atachments + * \param sess the session object to destroy + */ +void free_attachments(struct wcsession *sess) { + struct wc_attachment *att; + + while (sess->first_attachment != NULL) { + att = sess->first_attachment; + sess->first_attachment = sess->first_attachment->next; + free(att->data); + free(att); + } +} + +/** + * \brief what?????? + */ +void do_housekeeping(void) +{ + struct wcsession *sptr, *ss; + struct wcsession *sessions_to_kill = NULL; + int num_sessions = 0; + static int num_threads = MIN_WORKER_THREADS; + + /** + * Lock the session list, moving any candidates for euthanasia into + * a separate list. + */ + pthread_mutex_lock(&SessionListMutex); + num_sessions = 0; + for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { + ++num_sessions; + + /** Kill idle sessions */ + if ((time(NULL) - (sptr->lastreq)) > + (time_t) WEBCIT_TIMEOUT) { + sptr->killthis = 1; + } + + /** Remove sessions flagged for kill */ + if (sptr->killthis) { + + /** remove session from linked list */ + if (sptr == SessionList) { + SessionList = SessionList->next; + } + else for (ss=SessionList;ss!=NULL;ss=ss->next) { + if (ss->next == sptr) { + ss->next = ss->next->next; + } + } + + sptr->next = sessions_to_kill; + sessions_to_kill = sptr; + } + } + pthread_mutex_unlock(&SessionListMutex); + + /** + * Now free up and destroy the culled sessions. + */ + while (sessions_to_kill != NULL) { + lprintf(3, "Destroying session %d\n", sessions_to_kill->wc_session); + pthread_mutex_lock(&sessions_to_kill->SessionMutex); + close(sessions_to_kill->serv_sock); + close(sessions_to_kill->chat_sock); + if (sessions_to_kill->preferences != NULL) { + free(sessions_to_kill->preferences); + } + if (sessions_to_kill->cache_fold != NULL) { + free(sessions_to_kill->cache_fold); + } + free_attachments(sessions_to_kill); + free_march_list(sessions_to_kill); + pthread_mutex_unlock(&sessions_to_kill->SessionMutex); + sptr = sessions_to_kill->next; + free(sessions_to_kill); + sessions_to_kill = sptr; + --num_sessions; + } + + /** + * If there are more sessions than threads, then we should spawn + * more threads ... up to a predefined maximum. + */ + while ( (num_sessions > num_threads) + && (num_threads <= MAX_WORKER_THREADS) ) { + spawn_another_worker_thread(); + ++num_threads; + lprintf(3, "There are %d sessions and %d threads active.\n", + num_sessions, num_threads); + } +} + + +/** + * \brief Wake up occasionally and clean house + */ +void housekeeping_loop(void) +{ + while (1) { + sleeeeeeeeeep(HOUSEKEEPING); + do_housekeeping(); + } +} + + +/** + * \brief Create a Session id + * Generate a unique WebCit session ID (which is not the same thing as the + * Citadel session ID). + * + * \todo FIXME ... ensure that session number is truly unique + * + */ +int GenerateSessionID(void) +{ + static int seq = (-1); + + if (seq < 0) { + seq = (int) time(NULL); + } + + return ++seq; +} + + +/** + * \brief Collapse multiple cookies on one line + * \param sock a socket? + * \param buf some bunch of chars? + * \param hold hold what? + */ +int req_gets(int sock, char *buf, char *hold) +{ + int a; + + if (strlen(hold) == 0) { + strcpy(buf, ""); + a = client_getln(sock, buf, SIZ); + if (a<1) return(-1); + } else { + safestrncpy(buf, hold, SIZ); + } + strcpy(hold, ""); + + if (!strncasecmp(buf, "Cookie: ", 8)) { + for (a = 0; a < strlen(buf); ++a) + if (buf[a] == ';') { + sprintf(hold, "Cookie: %s", &buf[a + 1]); + buf[a] = 0; + while (isspace(hold[8])) + strcpy(&hold[8], &hold[9]); + return(0); + } + } + + return(0); +} + +/** + * \brief close some fd for some reason??? + * \param fd the fd to close?????? + * lingering_close() a`la Apache. see + * http://www.apache.org/docs/misc/fin_wait_2.html for rationale + */ + +int lingering_close(int fd) +{ + char buf[SIZ]; + int i; + fd_set set; + struct timeval tv, start; + + gettimeofday(&start, NULL); + shutdown(fd, 1); + do { + do { + gettimeofday(&tv, NULL); + tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec); + tv.tv_usec = start.tv_usec - tv.tv_usec; + if (tv.tv_usec < 0) { + tv.tv_sec--; + tv.tv_usec += 1000000; + } + FD_ZERO(&set); + FD_SET(fd, &set); + i = select(fd + 1, &set, NULL, NULL, &tv); + } while (i == -1 && errno == EINTR); + + if (i <= 0) + break; + + i = read(fd, buf, sizeof buf); + } while (i != 0 && (i != -1 || errno == EINTR)); + + return close(fd); +} + + + +/** + * \brief sanity requests + * Check for bogus requests coming from brain-dead Windows boxes. + * + * \param http_cmd The HTTP request to check + */ +int is_bogus(char *http_cmd) { + char *url; + + url = strstr(http_cmd, " "); + if (url == NULL) return(1); + ++url; + + /** Worms and trojans and viruses, oh my! */ + if (!strncasecmp(url, "/scripts/root.exe", 17)) return(2); + if (!strncasecmp(url, "/c/winnt", 8)) return(2); + if (!strncasecmp(url, "/MSADC/", 7)) return(2); + + /** Broken Microsoft DAV implementation */ + if (!strncasecmp(url, "/_vti", 5)) return(3); + + return(0); /* probably ok */ +} + + + +/** + * \brief handle one request + * This loop gets called once for every HTTP connection made to WebCit. At + * this entry point we have an HTTP socket with a browser allegedly on the + * other end, but we have not yet bound to a WebCit session. + * + * The job of this function is to locate the correct session and bind to it, + * or create a session if necessary and bind to it, then run the WebCit + * transaction loop. Afterwards, we unbind from the session. When this + * function returns, the worker thread is then free to handle another + * transaction. + * \param sock the socket we will put our answer to + */ +void context_loop(int sock) +{ + struct httprequest *req = NULL; + struct httprequest *last = NULL; + struct httprequest *hptr; + char buf[SIZ], hold[SIZ]; + int desired_session = 0; + int got_cookie = 0; + int gzip_ok = 0; + struct wcsession *TheSession, *sptr; + char httpauth_string[1024]; + char httpauth_user[1024]; + char httpauth_pass[1024]; + char accept_language[256]; + char *ptr = NULL; + int session_is_new = 0; + + strcpy(httpauth_string, ""); + strcpy(httpauth_user, DEFAULT_HTTPAUTH_USER); + strcpy(httpauth_pass, DEFAULT_HTTPAUTH_PASS); + + /** + * Find out what it is that the web browser is asking for + */ + memset(hold, 0, sizeof(hold)); + do { + if (req_gets(sock, buf, hold) < 0) return; + + /** + * Can we compress? + */ + if (!strncasecmp(buf, "Accept-encoding:", 16)) { + if (strstr(&buf[16], "gzip")) { + gzip_ok = 1; + } + } + + /** + * Browser-based sessions use cookies for session authentication + */ + if (!strncasecmp(buf, "Cookie: webcit=", 15)) { + cookie_to_stuff(&buf[15], &desired_session, + NULL, 0, NULL, 0, NULL, 0); + got_cookie = 1; + } + + /** + * GroupDAV-based sessions use HTTP authentication + */ + if (!strncasecmp(buf, "Authorization: Basic ", 21)) { + CtdlDecodeBase64(httpauth_string, &buf[21], strlen(&buf[21])); + extract_token(httpauth_user, httpauth_string, 0, ':', sizeof httpauth_user); + extract_token(httpauth_pass, httpauth_string, 1, ':', sizeof httpauth_pass); + } + + if (!strncasecmp(buf, "If-Modified-Since: ", 19)) { + if_modified_since = httpdate_to_timestamp(&buf[19]); + } + + if (!strncasecmp(buf, "Accept-Language: ", 17)) { + safestrncpy(accept_language, &buf[17], sizeof accept_language); + } + + /** + * Read in the request + */ + hptr = (struct httprequest *) + malloc(sizeof(struct httprequest)); + if (req == NULL) + req = hptr; + else + last->next = hptr; + hptr->next = NULL; + last = hptr; + + safestrncpy(hptr->line, buf, sizeof hptr->line); + + } while (strlen(buf) > 0); + + /** + * If the request is prefixed by "/webcit" then chop that off. This + * allows a front end web server to forward all /webcit requests to us + * while still using the same web server port for other things. + */ + + ptr = strstr(req->line, " /webcit "); /*< Handle "/webcit" */ + if (ptr != NULL) { + strcpy(ptr+2, ptr+8); + } + + ptr = strstr(req->line, " /webcit"); /*< Handle "/webcit/" */ + if (ptr != NULL) { + strcpy(ptr+1, ptr+8); + } + + /** Begin parsing the request. */ + + safestrncpy(buf, req->line, sizeof buf); + lprintf(5, "HTTP: %s\n", buf); + + /** Check for bogus requests */ + if (is_bogus(buf)) { + strcpy(req->line, "GET /404 HTTP/1.1"); + strcpy(buf, "GET /404 HTTP/1.1"); + } + + /** + * Strip out the method, leaving the URL up front... + */ + remove_token(buf, 0, ' '); + if (buf[1]==' ') buf[1]=0; + + /** + * While we're at it, gracefully handle requests for the + * robots.txt and favicon.ico files. + */ + if (!strncasecmp(buf, "/robots.txt", 11)) { + strcpy(req->line, "GET /static/robots.txt" + "?force_close_session=yes HTTP/1.1"); + } + else if (!strncasecmp(buf, "/favicon.ico", 12)) { + strcpy(req->line, "GET /static/favicon.ico"); + } + + /** + * These are the URL's which may be executed without a + * session cookie already set. If it's not one of these, + * force the session to close because cookies are + * probably disabled on the client browser. + */ + else if ( (strcmp(buf, "/")) + && (strncasecmp(buf, "/listsub", 8)) + && (strncasecmp(buf, "/freebusy", 9)) + && (strncasecmp(buf, "/do_logout", 10)) + && (strncasecmp(buf, "/groupdav", 9)) + && (strncasecmp(buf, "/static", 7)) + && (strncasecmp(buf, "/rss", 4)) + && (strncasecmp(buf, "/404", 4)) + && (got_cookie == 0)) { + strcpy(req->line, "GET /static/nocookies.html" + "?force_close_session=yes HTTP/1.1"); + } + + /** + * See if there's an existing session open with the desired ID or user/pass + */ + TheSession = NULL; + + if (TheSession == NULL) { + pthread_mutex_lock(&SessionListMutex); + for (sptr = SessionList; sptr != NULL; sptr = sptr->next) { + + /** If HTTP-AUTH, look for a session with matching credentials */ + if ( (strlen(httpauth_user) > 0) + &&(!strcasecmp(sptr->httpauth_user, httpauth_user)) + &&(!strcasecmp(sptr->httpauth_pass, httpauth_pass)) ) { + TheSession = sptr; + } + + /** If cookie-session, look for a session with matching session ID */ + if ( (desired_session != 0) && (sptr->wc_session == desired_session)) { + TheSession = sptr; + } + + } + pthread_mutex_unlock(&SessionListMutex); + } + + /** + * Create a new session if we have to + */ + if (TheSession == NULL) { + lprintf(3, "Creating a new session\n"); + TheSession = (struct wcsession *) + malloc(sizeof(struct wcsession)); + memset(TheSession, 0, sizeof(struct wcsession)); + TheSession->serv_sock = (-1); + TheSession->chat_sock = (-1); + TheSession->wc_session = GenerateSessionID(); + strcpy(TheSession->httpauth_user, httpauth_user); + strcpy(TheSession->httpauth_pass, httpauth_pass); + pthread_mutex_init(&TheSession->SessionMutex, NULL); + pthread_mutex_lock(&SessionListMutex); + TheSession->next = SessionList; + SessionList = TheSession; + pthread_mutex_unlock(&SessionListMutex); + session_is_new = 1; + } + + /** + * A future improvement might be to check the session integrity + * at this point before continuing. + */ + + /** + * Bind to the session and perform the transaction + */ + pthread_mutex_lock(&TheSession->SessionMutex); /*< bind */ + pthread_setspecific(MyConKey, (void *)TheSession); + TheSession->http_sock = sock; + TheSession->lastreq = time(NULL); /*< log */ + TheSession->gzip_ok = gzip_ok; +#ifdef ENABLE_NLS + if (session_is_new) { + httplang_to_locale(accept_language); + } + go_selected_language(); /*< set locale */ +#endif + session_loop(req); /*< do transaction */ +#ifdef ENABLE_NLS + stop_selected_language(); /*< unset locale */ +#endif + pthread_mutex_unlock(&TheSession->SessionMutex); /*< unbind */ + + /** Free the request buffer */ + while (req != NULL) { + hptr = req->next; + free(req); + req = hptr; + } + + /** + * Free up any session-local substitution variables which + * were set during this transaction + */ + clear_local_substs(); +} +/*@}*/ diff --git a/webcit/src/cookie_conversion.c b/webcit/src/cookie_conversion.c new file mode 100644 index 000000000..24e29ce74 --- /dev/null +++ b/webcit/src/cookie_conversion.c @@ -0,0 +1,98 @@ +/* + * $Id$ + */ +/** + * \defgroup CookieConversion Grep Cookies + * Utility functions which convert the HTTP cookie format we use to and + * from user/password/room strings. + * + * \ingroup WebcitHttpServer + */ +/*@{*/ +#include "webcit.h" + + +#define TRUE 1 /**< for sure? */ +#define FALSE 0 /**< nope. */ + +typedef unsigned char byte; /**< Byte type */ + +/** + * \brief find cookie + * Pack all session info into one easy-to-digest cookie. Healthy and delicious! + * \param cookie cookie string to create??? + * \param session the session we want to convert into a cookie + * \param user the user to be associated with the cookie + * \param pass his passphrase + * \param room the room he wants to enter + */ +void stuff_to_cookie(char *cookie, int session, + char *user, char *pass, char *room) +{ + char buf[SIZ]; + int i; + + sprintf(buf, "%d|%s|%s|%s|", session, user, pass, room); + strcpy(cookie, ""); + for (i=0; i 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; ireq_info->subject); + X509_set_subject_name(cer, req->req_info->subject); + X509_gmtime_adj(X509_get_notBefore(cer), 0); + X509_gmtime_adj(X509_get_notAfter(cer),(long)60*60*24*SIGN_DAYS); + + req_pkey = X509_REQ_get_pubkey(req); + X509_set_pubkey(cer, req_pkey); + EVP_PKEY_free(req_pkey); + + /** Sign the cert */ + if (!X509_sign(cer, pk, EVP_md5())) { + lprintf(3, "X509_sign(): error\n"); + } + else { + /** Write it to disk. */ + fp = fopen(CTDL_CER_PATH, "w"); + if (fp != NULL) { + chmod(CTDL_CER_PATH, 0600); + PEM_write_X509(fp, cer); + fclose(fp); + } + } + X509_free(cer); + } + } + + RSA_free(rsa); + } + } + + /** + * Now try to bind to the key and certificate. + * Note that we use SSL_CTX_use_certificate_chain_file() which allows + * the certificate file to contain intermediate certificates. + */ + SSL_CTX_use_certificate_chain_file(ssl_ctx, CTDL_CER_PATH); + SSL_CTX_use_PrivateKey_file(ssl_ctx, CTDL_KEY_PATH, SSL_FILETYPE_PEM); + if ( !SSL_CTX_check_private_key(ssl_ctx) ) { + lprintf(3, "Cannot install certificate: %s\n", + ERR_reason_error_string(ERR_get_error())); + } + +} + + +/** + * \brief starts SSL/TLS encryption for the current session. + * \param sock the socket connection + * \return Zero if the SSL/TLS handshake succeeded, non-zero otherwise. + */ +int starttls(int sock) { + int retval, bits, alg_bits; + SSL *newssl; + + pthread_setspecific(ThreadSSL, NULL); + + if (!ssl_ctx) { + return(1); + } + if (!(newssl = SSL_new(ssl_ctx))) { + lprintf(3, "SSL_new failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + return(2); + } + if (!(SSL_set_fd(newssl, sock))) { + lprintf(3, "SSL_set_fd failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + SSL_free(newssl); + return(3); + } + retval = SSL_accept(newssl); + if (retval < 1) { + /** + * Can't notify the client of an error here; they will + * discover the problem at the SSL layer and should + * revert to unencrypted communications. + */ + long errval; + + errval = SSL_get_error(newssl, retval); + lprintf(3, "SSL_accept failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + SSL_free(newssl); + newssl = NULL; + return(4); + } + BIO_set_close(newssl->rbio, BIO_NOCLOSE); + bits = + SSL_CIPHER_get_bits(SSL_get_current_cipher(newssl), + &alg_bits); + lprintf(5, "SSL/TLS using %s on %s (%d of %d bits)\n", + SSL_CIPHER_get_name(SSL_get_current_cipher(newssl)), + SSL_CIPHER_get_version(SSL_get_current_cipher(newssl)), + bits, alg_bits); + + pthread_setspecific(ThreadSSL, newssl); + return(0); +} + + + +/** + * \brief shuts down the TLS connection + * + * WARNING: This may make your session vulnerable to a known plaintext + * attack in the current implmentation. + */ +void endtls(void) +{ + if (THREADSSL == NULL) return; + + lprintf(5, "Ending SSL/TLS\n"); + SSL_shutdown(THREADSSL); + SSL_free(THREADSSL); + pthread_setspecific(ThreadSSL, NULL); +} + + +/** + * \brief callback for OpenSSL mutex locks + * \param mode which mode?????? + * \param n how many??? + * \param file which filename ??? + * \param line what line???? + */ +void ssl_lock(int mode, int n, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(SSLCritters[n]); + else + pthread_mutex_unlock(SSLCritters[n]); +} + +/** + * \brief Send binary data to the client encrypted. + * \param buf chars to send to the client + * \param nbytes how many chars + */ +void client_write_ssl(char *buf, int nbytes) +{ + int retval; + int nremain; + char junk[1]; + + if (THREADSSL == NULL) return; + + nremain = nbytes; + + while (nremain > 0) { + if (SSL_want_write(THREADSSL)) { + if ((SSL_read(THREADSSL, junk, 0)) < 1) { + lprintf(9, "SSL_read in client_write: %s\n", + ERR_reason_error_string(ERR_get_error())); + } + } + retval = SSL_write(THREADSSL, &buf[nbytes - nremain], nremain); + if (retval < 1) { + long errval; + + errval = SSL_get_error(THREADSSL, retval); + if (errval == SSL_ERROR_WANT_READ || + errval == SSL_ERROR_WANT_WRITE) { + sleep(1); + continue; + } + lprintf(9, "SSL_write got error %ld, ret %d\n", errval, retval); + if (retval == -1) { + lprintf(9, "errno is %d\n", errno); + } + endtls(); + return; + } + nremain -= retval; + } +} + + +/** + * \brief read data from the encrypted layer. + * \param buf charbuffer to read to + * \param bytes how many + * \param timeout how long should we wait? + * \returns what??? + */ +int client_read_ssl(char *buf, int bytes, int timeout) +{ +#if 0 + fd_set rfds; + struct timeval tv; + int retval; + int s; +#endif + int len, rlen; + char junk[1]; + + if (THREADSSL == NULL) return(0); + + len = 0; + while (len < bytes) { +#if 0 + /** + * This code is disabled because we don't need it when + * using blocking reads (which we are). -IO + */ + FD_ZERO(&rfds); + s = BIO_get_fd(THREADSSL->rbio, NULL); + FD_SET(s, &rfds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + + retval = select(s + 1, &rfds, NULL, NULL, &tv); + + if (FD_ISSET(s, &rfds) == 0) { + return (0); + } + +#endif + if (SSL_want_read(THREADSSL)) { + if ((SSL_write(THREADSSL, junk, 0)) < 1) { + lprintf(9, "SSL_write in client_read: %s\n", ERR_reason_error_string(ERR_get_error())); + } + } + rlen = SSL_read(THREADSSL, &buf[len], bytes - len); + if (rlen < 1) { + long errval; + + errval = SSL_get_error(THREADSSL, rlen); + if (errval == SSL_ERROR_WANT_READ || + errval == SSL_ERROR_WANT_WRITE) { + sleep(1); + continue; + } + lprintf(9, "SSL_read got error %ld\n", errval); + endtls(); + return (0); + } + len += rlen; + } + return (1); +} + + +#endif /* HAVE_OPENSSL */ +/*@}*/ diff --git a/webcit/src/doxygen_groups.c b/webcit/src/doxygen_groups.c new file mode 100644 index 000000000..3fed3e629 --- /dev/null +++ b/webcit/src/doxygen_groups.c @@ -0,0 +1,109 @@ +/* + * ok, hacky, but gets us nice groups. so we define sub parts to join from other + * files here. NO CODE IN HERE! This is comment shouldn't appear in doxygen. + * we have: + * CitadelConfig; WebcitDisplayItems; WebcitHttpServer; WebcitHttpServerGDav; + * ClientPower; Calendaring; MenuInfrastructure; CitadelCommunitacion; + * VCards + * WebcitHttpServerRSS; tools; + */ + + +/** + * \defgroup CitadelConfig Configuration Mechanisms + * \brief Functions about configuring citadel / webcit + */ + +/*@{*/ +/*@}*/ + +/** + * \defgroup tools Utility Functions + * \brief Functions that aren't related to webcit topics + */ + +/*@{*/ +/*@}*/ + + +/** + * \defgroup WebcitDisplayItems Display some mime types through webcit + * \brief Functions that format mime types into HTML to the user + */ + +/*@{*/ +/*@}*/ + +/** + * \defgroup WebcitHttpServer the Webserver part + * \brief Functions that run the HTTP-Deamon + */ + +/*@{*/ +/*@}*/ + +/** + * \defgroup WebcitHttpServerGDav Groupdav Mechanisms + * \ingroup WebcitHttpServer + * \brief Functions that handle groupdav requests + */ +/*@{*/ +/*@}*/ + + +/** + * \defgroup WebcitHttpServerRSS RSS Mechanisms + * \ingroup WebcitHttpServer + * \brief Functions that handle RSS requests + */ + +/*@{*/ +/*@}*/ + +/** + * \defgroup ClientPower Client powered Functionality + * \brief Functions that spawn things on the webbrowser + */ + +/*@{*/ +/*@}*/ + +/** + * \defgroup Calendaring Calendaring background + * \brief Functions that make the Business-logic of the calendaring items + * \ingroup WebcitDisplayItems + */ + +/*@{*/ +/*@}*/ + +/** + * \defgroup VCards showing / editing VCards + * \brief Functions that make the Business-logic of the vcard stuff + * \ingroup WebcitDisplayItems + */ + +/*@{*/ +/*@}*/ + +/** + * \defgroup MenuInfrastructure Things that guide you through the webcit parts + * \brief Functions that display menues, trees etc. to connect the parts of the + * ui to a whole thing + * \ingroup WebcitDisplayItems + */ + +/*@{*/ +/*@}*/ + +/** + * \defgroup CitadelCommunitacion Talk to the citadel server + * \brief Functions that talk to the citadel server and process reviewed entities + * \ingroup WebcitDisplayItems + */ + +/*@{*/ +/*@}*/ + + + diff --git a/webcit/src/event.c b/webcit/src/event.c new file mode 100644 index 000000000..94fac68db --- /dev/null +++ b/webcit/src/event.c @@ -0,0 +1,721 @@ +/* + * $Id$ + */ +/** + * \defgroup EditCal Editing calendar events. + * \ingroup Calendaring + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" + + +#ifdef WEBCIT_WITH_CALENDAR_SERVICE + +/** + * \brief Display an event by itself (for editing) + * \param supplied_vevent the event to edit + * \param msgnum reference on the citserver + */ +void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum) { + icalcomponent *vevent; + icalproperty *p; + icalvalue *v; + struct icaltimetype t_start, t_end; + time_t now; + struct tm tm_now; + int created_new_vevent = 0; + icalproperty *organizer = NULL; + char organizer_string[SIZ]; + icalproperty *attendee = NULL; + char attendee_string[SIZ]; + char buf[SIZ]; + int organizer_is_me = 0; + int i; + int sequence = 0; + + now = time(NULL); + strcpy(organizer_string, ""); + strcpy(attendee_string, ""); + + if (supplied_vevent != NULL) { + vevent = supplied_vevent; + /** + * If we're looking at a fully encapsulated VCALENDAR + * rather than a VEVENT component, attempt to use the first + * relevant VEVENT subcomponent. If there is none, the + * NULL returned by icalcomponent_get_first_component() will + * tell the next iteration of this function to create a + * new one. + */ + if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) { + display_edit_individual_event( + icalcomponent_get_first_component( + vevent, ICAL_VEVENT_COMPONENT + ), msgnum + ); + return; + } + } + else { + vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT); + created_new_vevent = 1; + } + + /** Learn the sequence */ + p = icalcomponent_get_first_property(vevent, ICAL_SEQUENCE_PROPERTY); + if (p != NULL) { + sequence = icalproperty_get_sequence(p); + } + + /** Begin output */ + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n" + "
" + ""); + wprintf(_("Add or edit an event")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("\n" + ); + + + wprintf("
" + "
\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("
\n"); + wprintf("SEQUENCE == %d
\n", sequence); + *************************************************************/ + + wprintf("
\n"); + + wprintf("\n", + msgnum); + wprintf("\n", + bstr("calview")); + wprintf("\n", + bstr("year")); + wprintf("\n", + bstr("month")); + wprintf("\n", + bstr("day")); + + /** Put it in a borderless table so it lines up nicely */ + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\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("\n"); + + wprintf(""); + + /** + * 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("\n"); + + /** Transparency */ + wprintf("\n"); + + /** Attendees */ + wprintf("\n"); + + /** Done with properties. */ + wprintf("
"); + wprintf(_("Summary")); + wprintf("\n" + "
"); + wprintf(_("Location")); + wprintf("\n" + "
"); + wprintf(_("Start")); + wprintf("\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("%s", + (t_start.is_date ? "CHECKED" : "" ), + _("All day event") + ); + + wprintf("
"); + wprintf(_("End")); + wprintf("\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("
"); + wprintf(_("Notes")); + wprintf("\n" + "
"); + wprintf(_("Organizer")); + wprintf(""); + escputs(organizer_string); + if (organizer_is_me) { + wprintf(" "); + wprintf(_("(you are the organizer)")); + wprintf("\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(""); + + wprintf("
"); + wprintf(_("Show time as:")); + wprintf(""); + + 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(""); + wprintf(_("Free")); + wprintf("  "); + + wprintf(""); + wprintf(_("Busy")); + + wprintf("
"); + wprintf(_("Attendees")); + wprintf("
" + ""); + wprintf(_("(One per line)")); + wprintf("
" + "
\n
" + "" + "  " + "\n" + "  " + "\n" + "  " + "\n" + "
\n", + _("Save"), + _("Delete"), + _("Check attendee availability"), + _("Cancel") + ); + + wprintf("
\n"); + + wprintf("
\n"); + wprintf("\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 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 0) ) { + serv_puts("ENT0 1|||4|||1|"); + serv_getln(buf, sizeof buf); + if (buf[0] == '8') { + serv_puts("Content-type: text/calendar"); + serv_puts(""); + serv_puts(icalcomponent_as_ical_string(encaps)); + serv_puts("000"); + } + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + lprintf(9, "ENT0 REPLY: %s\n", buf); + } + icalcomponent_free(encaps); + } + + /** Or, check attendee availability if the user asked for that. */ + if ( (encaps != NULL) && (strlen(bstr("check_button")) > 0) ) { + + /** Call this function, which does the real work */ + check_attendee_availability(encaps); + + /** This displays the form again, with our annotations */ + display_edit_individual_event(encaps, msgnum); + + icalcomponent_free(encaps); + } + + } + + /** + * If the user clicked 'Delete' then delete it. + */ + lprintf(9, "Checking to see if we have to delete an old event\n"); + if ( (strlen(bstr("delete_button")) > 0) && (msgnum > 0L) ) { + serv_printf("DELE %ld", atol(bstr("msgnum"))); + serv_getln(buf, sizeof buf); + } + + if (created_new_vevent) { + icalcomponent_free(vevent); + } + + /** If this was a save or deelete, go back to the calendar view. */ + if (strlen(bstr("check_button")) == 0) { + readloop("readfwd"); + } +} + + +#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ + +/*@}*/ diff --git a/webcit/src/floors.c b/webcit/src/floors.c new file mode 100644 index 000000000..d91928540 --- /dev/null +++ b/webcit/src/floors.c @@ -0,0 +1,198 @@ +/* + * $Id$ + */ +/** + * \defgroup AdminFloor Administrative screens for floor maintenance + * \ingroup CitadelConfig + */ +/*@{*/ + +#include "webcit.h" +#include "webserver.h" + + + + +/** + * \brief Display floor config + * Display floor configuration. If prepend_html is not NULL, its contents + * will be displayed at the top of the screen. + * \param prepend_html pagetitle to prepend + */ +void display_floorconfig(char *prepend_html) +{ + char buf[SIZ]; + + int floornum; + char floorname[SIZ]; + int refcount; + + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n" + "
" + ""); + wprintf(_("Add/change/delete floors")); + wprintf("" + "
\n" + "
\n
\n" + ); + + if (prepend_html != NULL) { + wprintf("
"); + client_write(prepend_html, strlen(prepend_html)); + wprintf("

\n"); + } + + serv_printf("LFLR"); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') { + wprintf("
"); + wprintf(""); + wprintf(_("Error")); + wprintf("\n"); + wprintf("
\n"); + wprintf("%s
\n", &buf[4]); + wDumpContent(1); + return; + } + + wprintf("
" + "\n" + "\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(""); + + wprintf("", _("Change name")); + + wprintf("\n", refcount); + + wprintf("", _("Change CSS")); + + wprintf("\n"); + } + + wprintf("" + "" + "\n", _("Create new floor")); + + wprintf("
"); + wprintf(_("Floor number")); + wprintf(""); + wprintf(_("Floor name")); + wprintf(""); + wprintf(_("Number of rooms")); + wprintf(""); + wprintf(_("Floor CSS")); + wprintf("
%d", floornum); + if (refcount == 0) { + wprintf("" + "" + "", floornum); + wprintf(_("(delete floor)")); + wprintf("
"); + } + wprintf("" + "", floornum); + wprintf(_("(edit graphic)")); + wprintf("
"); + wprintf("
" + "
" + "" + "\n", + floornum, floorname); + wprintf("" + "
%d" + "
" + "" + "\n", + floornum, floorname); + wprintf("" + "
 
" + "\n" + "" + "
 
\n"); + wDumpContent(1); +} + + +/** + * \brief delete the actual floor + */ +void delete_floor(void) { + int floornum; + char buf[SIZ]; + char message[SIZ]; + + floornum = atoi(bstr("floornum")); + + serv_printf("KFLR %d|1", floornum); + serv_getln(buf, sizeof buf); + + if (buf[0] == '2') { + sprintf(message, _("Floor has been deleted.")); + } + else { + sprintf(message, "%s", &buf[4]); + } + + display_floorconfig(message); +} + +/** + * \brief tart creating a new floor + */ +void create_floor(void) { + char buf[SIZ]; + char message[SIZ]; + char floorname[SIZ]; + + strcpy(floorname, bstr("floorname")); + + serv_printf("CFLR %s|1", floorname); + serv_getln(buf, sizeof buf); + + if (buf[0] == '2') { + sprintf(message, _("New floor has been created.")); + } else { + sprintf(message, "%s", &buf[4]); + } + + display_floorconfig(message); +} + +/** + * \brief rename this floor + */ +void rename_floor(void) { + int floornum; + char buf[SIZ]; + char message[SIZ]; + char floorname[SIZ]; + + floornum = atoi(bstr("floornum")); + strcpy(floorname, bstr("floorname")); + + serv_printf("EFLR %d|%s", floornum, floorname); + serv_getln(buf, sizeof buf); + + sprintf(message, "%s", &buf[4]); + + display_floorconfig(message); +} + + +/*@}*/ diff --git a/webcit/src/fmt_date.c b/webcit/src/fmt_date.c new file mode 100644 index 000000000..2c7c9f6fd --- /dev/null +++ b/webcit/src/fmt_date.c @@ -0,0 +1,225 @@ +/* + * $Id$ + */ +/** + * \defgroup FormatDates Miscellaneous routines formating dates + * \ingroup Calendaring + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" + +typedef unsigned char byte; /**< a byte. */ + +#define FALSE 0 /**< no. */ +#define TRUE 1 /**< yes. */ + +/** + * \brief Wrapper around strftime() or strftime_l() + * depending upon how our build is configured. + * + * \param s String target buffer + * \param max Maximum size of string target buffer + * \param format strftime() format + * \param tm Input date/time + */ +size_t wc_strftime(char *s, size_t max, const char *format, const struct tm *tm) +{ +#ifdef ENABLE_NLS + if (wc_locales[WC->selected_language] == NULL) { + return strftime(s, max, format, tm); + } + else { + return strftime_l(s, max, format, tm, wc_locales[WC->selected_language]); + } +#else + return strftime(s, max, format, tm); +#endif +} + + +/** + * \brief Format a date/time stamp for output + * \param buf the output buffer + * \param thetime time to convert to string + * \param brief do we want compact view????? + */ +void fmt_date(char *buf, time_t thetime, int brief) +{ + struct tm tm; + struct tm today_tm; + time_t today_timet; + int hour; + char calhourformat[16]; + + get_preference("calhourformat", calhourformat, sizeof calhourformat); + + today_timet = time(NULL); + localtime_r(&today_timet, &today_tm); + + localtime_r(&thetime, &tm); + hour = tm.tm_hour; + if (hour == 0) + hour = 12; + else if (hour > 12) + hour = hour - 12; + + buf[0] = 0; + + if (brief) { + + /** If date == today, show only the time */ + if ((tm.tm_year == today_tm.tm_year) + &&(tm.tm_mon == today_tm.tm_mon) + &&(tm.tm_mday == today_tm.tm_mday)) { + wc_strftime(buf, 32, "%l:%M%p", &tm); + } + /** Otherwise, for messages up to 6 months old, show the + * month and day, and the time */ + else if (today_timet - thetime < 15552000) { + wc_strftime(buf, 32, "%b %d %l:%M%p", &tm); + } + /** older than 6 months, show only the date */ + else { + wc_strftime(buf, 32, "%b %d %Y", &tm); + } + } + else { + wc_strftime(buf, 32, "%c", &tm); + } +} + + +/** + * \brief Format TIME ONLY for output + * \param buf the output buffer + * \param thetime time to format into buf + */ +void fmt_time(char *buf, time_t thetime) +{ + struct tm *tm; + int hour; + char calhourformat[16]; + + get_preference("calhourformat", calhourformat, sizeof calhourformat); + + buf[0] = 0; + tm = localtime(&thetime); + hour = tm->tm_hour; + if (hour == 0) + hour = 12; + else if (hour > 12) + hour = hour - 12; + + if (!strcasecmp(calhourformat, "24")) { + sprintf(buf, "%2d:%02d", + tm->tm_hour, tm->tm_min + ); + } + else { + sprintf(buf, "%d:%02d%s", + hour, tm->tm_min, ((tm->tm_hour > 12) ? "pm" : "am") + ); + } +} + + + + +/** + * \brief Break down the timestamp used in HTTP headers + * Should read rfc1123 and rfc850 dates OK + * \todo FIXME won't read asctime + * Doesn't understand timezone, but we only should be using GMT/UTC anyway + * \param buf time to parse + * \return the time found in buf + */ +time_t httpdate_to_timestamp(char *buf) +{ + time_t t = 0; + struct tm tt; + char *c; + char tz[256]; + + /** Skip day of week, to number */ + for (c = buf; *c != ' '; c++) + ; + c++; + + /* Get day of month */ + tt.tm_mday = atoi(c); + for (; *c != ' ' && *c != '-'; c++); + c++; + + /** Get month */ + switch (*c) { + case 'A': /** April, August */ + tt.tm_mon = (c[1] == 'p') ? 3 : 7; + break; + case 'D': /** December */ + tt.tm_mon = 11; + break; + case 'F': /** February */ + tt.tm_mon = 1; + break; + case 'M': /** March, May */ + tt.tm_mon = (c[2] == 'r') ? 2 : 4; + break; + case 'J': /** January, June, July */ + tt.tm_mon = (c[2] == 'n') ? ((c[1] == 'a') ? 0 : 5) : 6; + break; + case 'N': /** November */ + tt.tm_mon = 10; + break; + case 'O': /** October */ + tt.tm_mon = 9; + break; + case 'S': /** September */ + tt.tm_mon = 8; + break; + default: + return 42; + break; /** NOTREACHED */ + } + c += 4; + + tt.tm_year = 0; + /** Get year */ + tt.tm_year = atoi(c); + for (; *c != ' '; c++); + c++; + if (tt.tm_year >= 1900) + tt.tm_year -= 1900; + + /** Get hour */ + tt.tm_hour = atoi(c); + for (; *c != ':'; c++); + c++; + + /** Get minute */ + tt.tm_min = atoi(c); + for (; *c != ':'; c++); + c++; + + /** Get second */ + tt.tm_sec = atoi(c); + for (; *c && *c != ' '; c++); + + /** Got everything; let's go */ + /** First, change to UTC */ + if (getenv("TZ")) + sprintf(tz, "TZ=%s", getenv("TZ")); + else + strcpy(tz, "TZ="); + putenv("TZ=UTC"); + tzset(); + t = mktime(&tt); + putenv(tz); + tzset(); + return t; +} + + + + +/*@}*/ diff --git a/webcit/src/gettext.c b/webcit/src/gettext.c new file mode 100644 index 000000000..ddee2f873 --- /dev/null +++ b/webcit/src/gettext.c @@ -0,0 +1,299 @@ +/* + * $Id + */ +/** + * \defgroup LocaleHeaderParser Parse the browser http locale headers and set the NLS stuff. + * \ingroup WebcitHttpServer + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" + +#ifdef ENABLE_NLS + +#define NUM_LANGS 5 /**< how many different locales do we know? */ +#define SEARCH_LANG 20 /**< how many langs should we parse? */ + +/** actual supported locales */ +char *AvailLang[NUM_LANGS] = { + "C", + "en_US", + "de_DE", + "it_IT", + "en_GB" +}; + +locale_t wc_locales[NUM_LANGS]; /**< here we keep the parsed stuff */ + +/** Keep information about one locale */ +typedef struct _lang_pref{ + char lang[16]; /**< the language locale string */ + char region[16]; /**< the region locale string */ + long priority; /**< which priority does it have */ + int availability; /**< do we know it? */ + int selectedlang; /**< is this the selected language? */ +} LangStruct; + +/* \brief parse browser locale header + * seems as most browsers just do a one after coma value even if more than 10 locales are available. Sample strings: + * opera: + * Accept-Language: sq;q=1.0,de;q=0.9,as;q=0.8,ar;q=0.7,bn;q=0.6,zh-cn;q=0.5,kn;q=0.4,ch;q=0.3,fo;q=0.2,gn;q=0.1,ce;q=0.1,ie;q=0.1 + * Firefox + * Accept-Language: 'de-de,en-us;q=0.7,en;q=0.3' + * Accept-Language: de,en-ph;q=0.8,en-us;q=0.5,de-at;q=0.3 + * Accept-Language: de,en-us;q=0.9,it;q=0.9,de-de;q=0.8,en-ph;q=0.7,de-at;q=0.7,zh-cn;q=0.6,cy;q=0.5,ar-om;q=0.5,en-tt;q=0.4,xh;q=0.3,nl-be;q=0.3,cs;q=0.2,sv;q=0.1,tk;q=0.1 + * \param LocaleString the string from the browser http headers + */ + +void httplang_to_locale(char *LocaleString) +{ + LangStruct wanted_locales[SEARCH_LANG]; + LangStruct *ls; + + int i = 0; + int j = 0; + size_t len = strlen(LocaleString); + long prio; + int av; + int nBest; + int nParts; + char *search = (char *) malloc(len); + + memcpy(search, LocaleString, len); + search[len] = '\0'; + nParts=num_tokens(search,','); + for (i=0; ((ipriority=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; jregion[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; jlang[0], AvailLang[j]); + if ((result<0)&&(resultavailability)){ + 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; ((iavailability<=0)&& + (avavailability)&& + (priopriority)&& + (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\n"); + + for (i=0; i < NUM_LANGS; ++i) { + wprintf("\n", + ((WC->selected_language == i) ? "selected" : ""), + AvailLang[i], + AvailLang[i] + ); + } + + wprintf("\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; iselected_language = i; + } + } +} + +/** + * \brief Activate the selected language for this session. + */ +void go_selected_language(void) { + if (WC->selected_language < 0) return; + uselocale(wc_locales[WC->selected_language]); /** switch locales */ + textdomain(textdomain(NULL)); /** clear the cache */ +} + +/** + * \brief Deactivate the selected language for this session. + */ +void stop_selected_language(void) { + uselocale(LC_GLOBAL_LOCALE); /** switch locales */ + textdomain(textdomain(NULL)); /** clear the cache */ +} + + +/** + * \brief Create a locale_t for each available language + */ +void initialize_locales(void) { + int i; + locale_t Empty_Locale; + char buf[32]; + + /* create default locale */ + Empty_Locale = newlocale(LC_ALL_MASK, NULL, NULL); + + for (i = 0; i < NUM_LANGS; ++i) { + if (i == 0) { + sprintf(buf, "%s", AvailLang[i]); // locale 0 (C) is ascii, not utf-8 + } + else { + sprintf(buf, "%s.UTF8", AvailLang[i]); + } + wc_locales[i] = newlocale( + (LC_MESSAGES_MASK|LC_TIME_MASK), + buf, + (((i > 0) && (wc_locales[0] != NULL)) ? wc_locales[0] : Empty_Locale) + ); + if (wc_locales[i] == NULL) { + lprintf(1, "Error configuring locale for %s: %s\n", + buf, + strerror(errno) + ); + } + else { + lprintf(3, "Configured available locale: %s\n", buf); + } + } +} + + +#else /* ENABLE_NLS */ +/** \brief dummy for non NLS enabled systems */ +void offer_languages(void) { + wprintf("English (US)"); +} + +/** \brief dummy for non NLS enabled systems */ +void set_selected_language(char *lang) { +} + +/** \brief dummy for non NLS enabled systems */ +void go_selected_language(void) { +} + +/** \brief dummy for non NLS enabled systems */ +void stop_selected_language(void) { +} + +#endif /* ENABLE_NLS */ + + +/*@}*/ diff --git a/webcit/src/graphics.c b/webcit/src/graphics.c new file mode 100644 index 000000000..00f256b98 --- /dev/null +++ b/webcit/src/graphics.c @@ -0,0 +1,116 @@ +/* + * $Id$ + * + * Handles HTTP upload of graphics files into the system. + * \ingroup WebcitHttpServer + */ + +#include "webcit.h" + +void display_graphics_upload(char *description, char *check_cmd, char *uplurl) +{ + char buf[SIZ]; + + serv_puts(check_cmd); + serv_getln(buf, sizeof buf); + if (buf[0] != '2') { + strcpy(WC->ImportantMessage, &buf[4]); + display_main_menu(); + return; + } + output_headers(1, 1, 0, 0, 0, 0); + + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n" + "
" + ""); + wprintf(_("Image upload")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("
" + "
\n"); + + wprintf("
\n"); + + wprintf("
\n", uplurl); + + 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("

\n"); + + wprintf(_("Please select a file to upload:")); + wprintf("

\n"); + wprintf("\n"); + wprintf("

"); + wprintf("\n", _("Upload")); + wprintf(" "); + wprintf("\n", _("Reset form")); + wprintf(" "); + wprintf("\n", _("Cancel")); + wprintf("
\n"); + wprintf("
\n"); + wprintf("
\n"); + wDumpContent(1); +} + +void do_graphics_upload(char *upl_cmd) +{ + char buf[SIZ]; + int bytes_remaining; + int pos = 0; + int thisblock; + + if (strlen(bstr("cancel_button")) > 0) { + strcpy(WC->ImportantMessage, + _("Graphics upload has been cancelled.")); + display_main_menu(); + return; + } + + if (WC->upload_length == 0) { + strcpy(WC->ImportantMessage, + _("You didn't upload a file.")); + display_main_menu(); + return; + } + serv_puts(upl_cmd); + serv_getln(buf, sizeof buf); + if (buf[0] != '2') { + strcpy(WC->ImportantMessage, &buf[4]); + display_main_menu(); + return; + } + bytes_remaining = WC->upload_length; + while (bytes_remaining) { + thisblock = ((bytes_remaining > 4096) ? 4096 : bytes_remaining); + serv_printf("WRIT %d", thisblock); + serv_getln(buf, sizeof buf); + if (buf[0] != '7') { + strcpy(WC->ImportantMessage, &buf[4]); + serv_puts("UCLS 0"); + serv_getln(buf, sizeof buf); + display_main_menu(); + return; + } + thisblock = extract_int(&buf[4], 0); + serv_write(&WC->upload[pos], thisblock); + pos = pos + thisblock; + bytes_remaining = bytes_remaining - thisblock; + } + + serv_puts("UCLS 1"); + serv_getln(buf, sizeof buf); + if (buf[0] != 'x') { + display_success(&buf[4]); + return; + } +} diff --git a/webcit/src/groupdav.h b/webcit/src/groupdav.h new file mode 100644 index 000000000..2a933ad8e --- /dev/null +++ b/webcit/src/groupdav.h @@ -0,0 +1,14 @@ +/* $Id$ */ + +void groupdav_common_headers(void); +void groupdav_main(struct httprequest *, char *, int, char *); +void groupdav_get(char *); +void groupdav_put(char *, char *, char *, char *, int); +void groupdav_delete(char *, char *); +void groupdav_propfind(char *, int, char *, char *); +void groupdav_options(char *); +long locate_message_by_uid(char *); +void groupdav_folder_list(void); +void euid_escapize(char *, char *); +void euid_unescapize(char *, char *); +void groupdav_identify_host(void); diff --git a/webcit/src/groupdav_delete.c b/webcit/src/groupdav_delete.c new file mode 100644 index 000000000..2d44b8fc1 --- /dev/null +++ b/webcit/src/groupdav_delete.c @@ -0,0 +1,90 @@ +/* + * $Id$ + * + * Handles GroupDAV DELETE requests. + * + */ + +#include "webcit.h" +#include "webserver.h" +#include "groupdav.h" + + +/* + * The pathname is always going to be /groupdav/room_name/euid + */ +void groupdav_delete(char *dav_pathname, char *dav_ifmatch) { + char dav_roomname[SIZ]; + char dav_uid[SIZ]; + long dav_msgnum = (-1); + char buf[SIZ]; + int n = 0; + + /* First, break off the "/groupdav/" prefix */ + remove_token(dav_pathname, 0, '/'); + remove_token(dav_pathname, 0, '/'); + + /* Now extract the message euid */ + n = num_tokens(dav_pathname, '/'); + extract_token(dav_uid, dav_pathname, n-1, '/', sizeof dav_uid); + remove_token(dav_pathname, n-1, '/'); + + /* What's left is the room name. Remove trailing slashes. */ + if (dav_pathname[strlen(dav_pathname)-1] == '/') { + dav_pathname[strlen(dav_pathname)-1] = 0; + } + strcpy(dav_roomname, dav_pathname); + + /* Go to the correct room. */ + if (strcasecmp(WC->wc_roomname, dav_roomname)) { + gotoroom(dav_roomname); + } + if (strcasecmp(WC->wc_roomname, dav_roomname)) { + wprintf("HTTP/1.1 404 not found\r\n"); + groupdav_common_headers(); + wprintf("Content-Length: 0\r\n\r\n"); + return; + } + + dav_msgnum = locate_message_by_uid(dav_uid); + + /* + * If no item exists with the requested uid ... simple error. + */ + if (dav_msgnum < 0L) { + wprintf("HTTP/1.1 404 Not Found\r\n"); + groupdav_common_headers(); + wprintf("Content-Length: 0\r\n\r\n"); + return; + } + + /* + * It's there ... check the ETag and make sure it matches + * the message number. + */ + if (strlen(dav_ifmatch) > 0) { + if (atol(dav_ifmatch) != dav_msgnum) { + wprintf("HTTP/1.1 412 Precondition Failed\r\n"); + groupdav_common_headers(); + wprintf("Content-Length: 0\r\n\r\n"); + return; + } + } + + /* + * Ok, attempt to delete the item. + */ + serv_printf("DELE %ld", dav_msgnum); + serv_getln(buf, sizeof buf); + if (buf[0] == '2') { + wprintf("HTTP/1.1 204 No Content\r\n"); /* success */ + groupdav_common_headers(); + wprintf("Content-Length: 0\r\n\r\n"); + } + else { + wprintf("HTTP/1.1 403 Forbidden\r\n"); /* access denied */ + groupdav_common_headers(); + wprintf("Content-Length: 0\r\n\r\n"); + } + return; +} diff --git a/webcit/src/groupdav_get.c b/webcit/src/groupdav_get.c new file mode 100644 index 000000000..d773fe29b --- /dev/null +++ b/webcit/src/groupdav_get.c @@ -0,0 +1,143 @@ +/* + * $Id$ + * + * Handles GroupDAV GET requests. + * + */ + +#include "webcit.h" +#include "webserver.h" +#include "groupdav.h" + + +/* + * Fetch the entire contents of the room as one big ics file. + * This is for "webcal://" type access. + */ +void groupdav_get_big_ics(void) { + char buf[1024]; + + serv_puts("ICAL getics"); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') { + wprintf("HTTP/1.1 404 not found\r\n"); + groupdav_common_headers(); + wprintf( + "Content-Type: text/plain\r\n" + "\r\n" + "%s\r\n", + &buf[4] + ); + return; + } + + wprintf("HTTP/1.1 200 OK\r\n"); + groupdav_common_headers(); + wprintf("Content-type: text/calendar; charset=UTF-8\r\n"); + begin_burst(); + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + wprintf("%s\r\n", buf); + } + end_burst(); +} + + +/* + * The pathname is always going to take one of two formats: + * /groupdav/room_name/euid (GroupDAV) + * /groupdav/room_name (webcal) + */ +void groupdav_get(char *dav_pathname) { + char dav_roomname[1024]; + char dav_uid[1024]; + long dav_msgnum = (-1); + char buf[1024]; + int in_body = 0; + int found_content_type = 0; + + if (num_tokens(dav_pathname, '/') < 3) { + wprintf("HTTP/1.1 404 not found\r\n"); + groupdav_common_headers(); + wprintf( + "Content-Type: text/plain\r\n" + "\r\n" + "The object you requested was not found.\r\n" + ); + return; + } + + extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname); + extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid); + if ((!strcasecmp(dav_uid, "ics")) || (!strcasecmp(dav_uid, "calendar.ics"))) { + strcpy(dav_uid, ""); + } + + /* Go to the correct room. */ + if (strcasecmp(WC->wc_roomname, dav_roomname)) { + gotoroom(dav_roomname); + } + if (strcasecmp(WC->wc_roomname, dav_roomname)) { + wprintf("HTTP/1.1 404 not found\r\n"); + groupdav_common_headers(); + wprintf( + "Content-Type: text/plain\r\n" + "\r\n" + "There is no folder called \"%s\" on this server.\r\n", + dav_roomname + ); + return; + } + + /** GET on the collection itself returns an ICS of the entire collection. + */ + if (!strcasecmp(dav_uid, "")) { + groupdav_get_big_ics(); + return; + } + + dav_msgnum = locate_message_by_uid(dav_uid); + serv_printf("MSG2 %ld", dav_msgnum); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') { + wprintf("HTTP/1.1 404 not found\r\n"); + groupdav_common_headers(); + wprintf( + "Content-Type: text/plain\r\n" + "\r\n" + "Object \"%s\" was not found in the \"%s\" folder.\r\n", + dav_uid, + dav_roomname + ); + return; + } + + wprintf("HTTP/1.1 200 OK\r\n"); + groupdav_common_headers(); + wprintf("etag: \"%ld\"\r\n", dav_msgnum); + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + if (in_body) { + wprintf("%s\r\n", buf); + } + else if (!strncasecmp(buf, "Date: ", 6)) { + wprintf("%s\r\n", buf); + } + else if (!strncasecmp(buf, "Content-type: ", 14)) { + wprintf("%s", buf); + if (bmstrcasestr(buf, "charset=")) { + wprintf("%s\r\n", buf); + } + else { + wprintf("%s;charset=UTF-8\r\n", buf); + } + found_content_type = 1; + } + else if ((strlen(buf) == 0) && (in_body == 0)) { + if (!found_content_type) { + wprintf("Content-type: text/plain\r\n"); + } + in_body = 1; + begin_burst(); + } + } + end_burst(); +} diff --git a/webcit/src/groupdav_main.c b/webcit/src/groupdav_main.c new file mode 100644 index 000000000..ca31fe21e --- /dev/null +++ b/webcit/src/groupdav_main.c @@ -0,0 +1,246 @@ +/* + * $Id$ + * + * Entry point for GroupDAV functions + * + */ + +#include "webcit.h" +#include "webserver.h" +#include "groupdav.h" + + +/* + * Output HTTP headers which are common to all requests. + * + * Please observe that we don't use the usual output_headers() + * and wDumpContent() functions in the GroupDAV subsystem, so we + * do our own header stuff here. + * + */ +void groupdav_common_headers(void) { + wprintf( + "Server: %s / %s\r\n" + "Connection: close\r\n", + SERVER, serv_info.serv_software + ); +} + + + +/* + * string conversion function + */ +void euid_escapize(char *target, char *source) { + int i; + int target_length = 0; + + strcpy(target, ""); + for (i=0; iline, 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; ihttp_host) > 0) { + wprintf("%s://%s", + (is_https ? "https" : "http"), + WC->http_host); + } +} diff --git a/webcit/src/groupdav_options.c b/webcit/src/groupdav_options.c new file mode 100644 index 000000000..b92b79478 --- /dev/null +++ b/webcit/src/groupdav_options.c @@ -0,0 +1,97 @@ +/* + * $Id$ + * + * Handles DAV OPTIONS requests (experimental -- not required by GroupDAV) + * + */ + +#include "webcit.h" +#include "webserver.h" +#include "groupdav.h" + +/* + * The pathname is always going to be /groupdav/room_name/msg_num + */ +void groupdav_options(char *dav_pathname) { + char dav_roomname[256]; + char dav_uid[256]; + long dav_msgnum = (-1); + char datestring[256]; + time_t now; + + now = time(NULL); + http_datestring(datestring, sizeof datestring, now); + + extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname); + extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid); + + /* + * If the room name is blank, the client is doing a top-level OPTIONS. + */ + if (strlen(dav_roomname) == 0) { + wprintf("HTTP/1.1 200 OK\r\n"); + groupdav_common_headers(); + wprintf("Date: %s\r\n", datestring); + wprintf("DAV: 1\r\n"); + wprintf("Allow: OPTIONS, PROPFIND\r\n"); + wprintf("\r\n"); + return; + } + + /* Go to the correct room. */ + if (strcasecmp(WC->wc_roomname, dav_roomname)) { + gotoroom(dav_roomname); + } + + if (strcasecmp(WC->wc_roomname, dav_roomname)) { + wprintf("HTTP/1.1 404 not found\r\n"); + groupdav_common_headers(); + wprintf("Date: %s\r\n", datestring); + wprintf( + "Content-Type: text/plain\r\n" + "\r\n" + "There is no folder called \"%s\" on this server.\r\n", + dav_roomname + ); + return; + } + + /* If dav_uid is non-empty, client is requesting an OPTIONS on + * a specific item in the room. + */ + if (strlen(dav_uid) > 0) { + + dav_msgnum = locate_message_by_uid(dav_uid); + if (dav_msgnum < 0) { + wprintf("HTTP/1.1 404 not found\r\n"); + groupdav_common_headers(); + wprintf( + "Content-Type: text/plain\r\n" + "\r\n" + "Object \"%s\" was not found in the \"%s\" folder.\r\n", + dav_uid, + dav_roomname + ); + return; + } + + wprintf("HTTP/1.1 200 OK\r\n"); + groupdav_common_headers(); + wprintf("Date: %s\r\n", datestring); + wprintf("DAV: 1\r\n"); + wprintf("Allow: OPTIONS, PROPFIND, GET, PUT, DELETE\r\n"); + wprintf("\r\n"); + return; + } + + /* + * We got to this point, which means that the client is requesting + * an OPTIONS on the room itself. + */ + wprintf("HTTP/1.1 200 OK\r\n"); + groupdav_common_headers(); + wprintf("Date: %s\r\n", datestring); + wprintf("DAV: 1\r\n"); + wprintf("Allow: OPTIONS, PROPFIND, GET, PUT\r\n"); + wprintf("\r\n"); +} diff --git a/webcit/src/groupdav_propfind.c b/webcit/src/groupdav_propfind.c new file mode 100644 index 000000000..8e6154d61 --- /dev/null +++ b/webcit/src/groupdav_propfind.c @@ -0,0 +1,438 @@ +/* + * $Id$ + * + * Handles GroupDAV PROPFIND requests. + * + * A few notes about our XML output: + * + * --> Yes, we are spewing tags directly instead of using an XML library. + * If you would like to rewrite this using libxml2, code it up and submit + * a patch. Whining will be summarily ignored. + * + * --> XML is deliberately output with no whitespace/newlines between tags. + * This makes it difficult to read, but we have discovered clients which + * crash when you try to pretty it up. + * + */ + +#include "webcit.h" +#include "webserver.h" +#include "groupdav.h" + +/* + * Given an encoded UID, translate that to an unencoded Citadel EUID and + * then search for it in the current room. Return a message number or -1 + * if not found. + * + */ +long locate_message_by_uid(char *uid) { + char buf[256]; + char decoded_uid[1024]; + long retval = (-1L); + + /* Decode the uid */ + euid_unescapize(decoded_uid, uid); + +/************** THE NEW WAY ***********************/ + serv_printf("EUID %s", decoded_uid); + serv_getln(buf, sizeof buf); + if (buf[0] == '2') { + retval = atol(&buf[4]); + } +/***************************************************/ + +/************** THE OLD WAY *********************** + serv_puts("MSGS ALL|0|1"); + serv_getln(buf, sizeof buf); + if (buf[0] == '8') { + serv_printf("exti|%s", decoded_uid); + serv_puts("000"); + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + retval = atol(buf); + } + } + ***************************************************/ + + return(retval); +} + + + +/* + * List rooms (or "collections" in DAV terminology) which contain + * interesting groupware objects. + */ +void groupdav_collection_list(char *dav_pathname, int dav_depth) +{ + char buf[256]; + char roomname[256]; + int view; + char datestring[256]; + time_t now; + time_t mtime; + int is_groupware_collection = 0; + int starting_point = 1; /**< 0 for /, 1 for /groupdav/ */ + + if (!strcmp(dav_pathname, "/")) { + starting_point = 0; + } + else if (!strcasecmp(dav_pathname, "/groupdav")) { + starting_point = 1; + } + else if (!strcasecmp(dav_pathname, "/groupdav/")) { + starting_point = 1; + } + else if ( (!strncasecmp(dav_pathname, "/groupdav/", 10)) && (strlen(dav_pathname) > 10) ) { + starting_point = 2; + } + + now = time(NULL); + http_datestring(datestring, sizeof datestring, now); + + /** + * Be rude. Completely ignore the XML request and simply send them + * everything we know about. Let the client sort it out. + */ + wprintf("HTTP/1.0 207 Multi-Status\r\n"); + groupdav_common_headers(); + wprintf("Date: %s\r\n", datestring); + wprintf("Content-type: text/xml\r\n"); + wprintf("Content-encoding: identity\r\n"); + + begin_burst(); + + wprintf("" + "" + ); + + /** + * If the client is requesting the root, show a root node. + */ + if (starting_point == 0) { + wprintf(""); + wprintf(""); + groupdav_identify_host(); + wprintf("/"); + wprintf(""); + wprintf(""); + wprintf("HTTP/1.1 200 OK"); + wprintf(""); + wprintf("/"); + wprintf(""); + wprintf(""); + escputs(datestring); + wprintf(""); + wprintf(""); + wprintf(""); + wprintf(""); + } + + /** + * If the client is requesting "/groupdav", show a /groupdav subdirectory. + */ + if ((starting_point + dav_depth) >= 1) { + wprintf(""); + wprintf(""); + groupdav_identify_host(); + wprintf("/groupdav"); + wprintf(""); + wprintf(""); + wprintf("HTTP/1.1 200 OK"); + wprintf(""); + wprintf("GroupDAV"); + wprintf(""); + wprintf(""); + escputs(datestring); + wprintf(""); + wprintf(""); + wprintf(""); + wprintf(""); + } + + /** + * 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(""); + + wprintf(""); + groupdav_identify_host(); + wprintf("/groupdav/"); + urlescputs(roomname); + wprintf("/"); + + wprintf(""); + wprintf("HTTP/1.1 200 OK"); + wprintf(""); + wprintf(""); + escputs(roomname); + wprintf(""); + wprintf(""); + + switch(view) { + case VIEW_CALENDAR: + wprintf(""); + break; + case VIEW_TASKS: + wprintf(""); + break; + case VIEW_ADDRESSBOOK: + wprintf(""); + break; + } + + wprintf(""); + wprintf(""); + escputs(datestring); + wprintf(""); + wprintf(""); + wprintf(""); + wprintf(""); + } + } + wprintf("\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("" + "" + ); + + wprintf(""); + + wprintf(""); + groupdav_identify_host(); + wprintf("/groupdav/"); + urlescputs(WC->wc_roomname); + euid_escapize(encoded_uid, dav_uid); + wprintf("/%s", encoded_uid); + wprintf(""); + wprintf(""); + wprintf("HTTP/1.1 200 OK"); + wprintf("\"%ld\"", dav_msgnum); + wprintf(""); + + wprintf("\n"); + wprintf("\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("" + "" + ); + + + /** Transmit the collection resource (FIXME check depth and starting point) */ + wprintf(""); + + wprintf(""); + groupdav_identify_host(); + wprintf("/groupdav/"); + urlescputs(WC->wc_roomname); + wprintf(""); + + wprintf(""); + wprintf("HTTP/1.1 200 OK"); + wprintf(""); + wprintf(""); + escputs(WC->wc_roomname); + wprintf(""); + wprintf(""); + + switch(WC->wc_default_view) { + case VIEW_CALENDAR: + wprintf(""); + break; + case VIEW_TASKS: + wprintf(""); + break; + case VIEW_ADDRESSBOOK: + wprintf(""); + break; + } + + wprintf(""); + /* FIXME get the mtime + wprintf(""); + escputs(datestring); + wprintf(""); + */ + wprintf(""); + wprintf(""); + wprintf(""); + + /** 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"); + wprintf(""); + groupdav_identify_host(); + wprintf("/groupdav/"); + urlescputs(WC->wc_roomname); + euid_escapize(encoded_uid, uid); + wprintf("/%s", encoded_uid); + wprintf(""); + wprintf(""); + wprintf("HTTP/1.1 200 OK"); + wprintf(""); + wprintf("\"%ld\"", msgs[i]); + wprintf(""); + wprintf(""); + wprintf(""); + } + } + + wprintf("\n"); + end_burst(); + + if (msgs != NULL) { + free(msgs); + } +} diff --git a/webcit/src/groupdav_put.c b/webcit/src/groupdav_put.c new file mode 100644 index 000000000..21eaefbb8 --- /dev/null +++ b/webcit/src/groupdav_put.c @@ -0,0 +1,205 @@ +/* + * $Id$ + * + * Handles GroupDAV PUT requests. + * + */ + +#include "webcit.h" +#include "webserver.h" +#include "groupdav.h" + + +/* + * This function is for uploading an ENTIRE calendar, not just one + * component. This would be for webcal:// 'publish' operations, not + * for GroupDAV. + */ +void groupdav_put_bigics(char *dav_content, int dav_content_length) +{ + char buf[1024]; + + serv_puts("ICAL putics"); + serv_getln(buf, sizeof buf); + if (buf[0] != '4') { + wprintf("HTTP/1.1 502 Bad Gateway\r\n"); + groupdav_common_headers(); + wprintf("Content-type: text/plain\r\n" + "\r\n" + "%s\r\n", &buf[4] + ); + return; + } + + serv_write(dav_content, dav_content_length); + serv_printf("\n000"); + + /* Report success and not much else. */ + wprintf("HTTP/1.1 204 No Content\r\n"); + lprintf(9, "HTTP/1.1 204 No Content\r\n"); + groupdav_common_headers(); + wprintf("Content-Length: 0\r\n\r\n"); +} + + + +/* + * The pathname is always going to take one of two formats: + * /groupdav/room_name/euid (GroupDAV) + * /groupdav/room_name (webcal) + */ +void groupdav_put(char *dav_pathname, char *dav_ifmatch, + char *dav_content_type, char *dav_content, + int dav_content_length +) { + char dav_roomname[1024]; + char dav_uid[1024]; + long new_msgnum = (-2L); + long old_msgnum = (-1L); + char buf[SIZ]; + int n = 0; + + if (num_tokens(dav_pathname, '/') < 3) { + wprintf("HTTP/1.1 404 not found\r\n"); + groupdav_common_headers(); + wprintf( + "Content-Type: text/plain\r\n" + "\r\n" + "The object you requested was not found.\r\n" + ); + return; + } + + extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname); + extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid); + if ((!strcasecmp(dav_uid, "ics")) || (!strcasecmp(dav_uid, "calendar.ics"))) { + strcpy(dav_uid, ""); + } + + /* Go to the correct room. */ + if (strcasecmp(WC->wc_roomname, dav_roomname)) { + gotoroom(dav_roomname); + } + if (strcasecmp(WC->wc_roomname, dav_roomname)) { + wprintf("HTTP/1.1 404 not found\r\n"); + groupdav_common_headers(); + wprintf( + "Content-Type: text/plain\r\n" + "\r\n" + "There is no folder called \"%s\" on this server.\r\n", + dav_roomname + ); + return; + } + + /* + * If an HTTP If-Match: header is present, the client is attempting + * to replace an existing item. We have to check to see if the + * message number associated with the supplied uid matches what the + * client is expecting. If not, the server probably contains a newer + * version, so we fail... + */ + if (strlen(dav_ifmatch) > 0) { + lprintf(9, "dav_ifmatch: %s\n", dav_ifmatch); + old_msgnum = locate_message_by_uid(dav_uid); + lprintf(9, "old_msgnum: %ld\n", old_msgnum); + if (atol(dav_ifmatch) != old_msgnum) { + wprintf("HTTP/1.1 412 Precondition Failed\r\n"); + lprintf(9, "HTTP/1.1 412 Precondition Failed (ifmatch=%ld, old_msgnum=%ld)\r\n", + atol(dav_ifmatch), old_msgnum); + groupdav_common_headers(); + wprintf("Content-Length: 0\r\n\r\n"); + return; + } + } + + /** PUT on the collection itself uploads an ICS of the entire collection. + */ + if (!strcasecmp(dav_uid, "")) { + groupdav_put_bigics(dav_content, dav_content_length); + return; + } + + /* + * We are cleared for upload! We use the new calling syntax for ENT0 + * which allows a confirmation to be sent back to us. That's how we + * extract the message ID. + */ + serv_puts("ENT0 1|||4|||1|"); + serv_getln(buf, sizeof buf); + if (buf[0] != '8') { + wprintf("HTTP/1.1 502 Bad Gateway\r\n"); + groupdav_common_headers(); + wprintf("Content-type: text/plain\r\n" + "\r\n" + "%s\r\n", &buf[4] + ); + return; + } + + /* Send the content to the Citadel server */ + serv_printf("Content-type: %s\n\n", dav_content_type); + serv_puts(dav_content); + serv_puts("\n000"); + + /* Fetch the reply from the Citadel server */ + n = 0; + strcpy(dav_uid, ""); + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + switch(n++) { + case 0: new_msgnum = atol(buf); + break; + case 1: lprintf(9, "new_msgnum=%ld (%s)\n", new_msgnum, buf); + break; + case 2: strcpy(dav_uid, buf); + break; + default: + break; + } + } + + /* Tell the client what happened. */ + + /* Citadel failed in some way? */ + if (new_msgnum < 0L) { + wprintf("HTTP/1.1 502 Bad Gateway\r\n"); + groupdav_common_headers(); + wprintf("Content-type: text/plain\r\n" + "\r\n" + "new_msgnum is %ld\r\n" + "\r\n", new_msgnum + ); + return; + } + + /* We created this item for the first time. */ + if (old_msgnum < 0L) { + wprintf("HTTP/1.1 201 Created\r\n"); + lprintf(9, "HTTP/1.1 201 Created\r\n"); + groupdav_common_headers(); + wprintf("etag: \"%ld\"\r\n", new_msgnum); + wprintf("Content-Length: 0\r\n"); + wprintf("Location: "); + groupdav_identify_host(); + wprintf("/groupdav/"); + urlescputs(dav_roomname); + wprintf("/%s\r\n", dav_uid); + wprintf("\r\n"); + return; + } + + /* We modified an existing item. */ + wprintf("HTTP/1.1 204 No Content\r\n"); + lprintf(9, "HTTP/1.1 204 No Content\r\n"); + groupdav_common_headers(); + wprintf("etag: \"%ld\"\r\n", new_msgnum); + wprintf("Content-Length: 0\r\n\r\n"); + + /* The item we replaced has probably already been deleted by + * the Citadel server, but we'll do this anyway, just in case. + */ + serv_printf("DELE %ld", old_msgnum); + serv_getln(buf, sizeof buf); + + return; +} diff --git a/webcit/src/html2html.c b/webcit/src/html2html.c new file mode 100644 index 000000000..ca8804c1a --- /dev/null +++ b/webcit/src/html2html.c @@ -0,0 +1,374 @@ +/* + * $Id$ + */ +/** + * \defgroup HTML2HTML Output an HTML message, modifying it slightly to make sure it plays nice + * with the rest of our web framework. + * \ingroup WebcitHttpServer + */ +/*@{*/ +#include "webcit.h" +#include "vcard.h" +#include "webserver.h" + + +/** + * \brief Strip surrounding single or double quotes from a string. + * + * \param s String to be stripped. + */ +void stripquotes(char *s) +{ + int len; + + if (!s) return; + + len = strlen(s); + if (len < 2) return; + + if ( ( (s[0] == '\"') && (s[len-1] == '\"') ) || ( (s[0] == '\'') && (s[len-1] == '\'') ) ) { + s[len-1] = 0; + strcpy(s, &s[1]); + } +} + + +/** + * \brief Check to see if a META tag has overridden the declared MIME character set. + * + * \param charset Character set name (left unchanged if we don't do anything) + * \param meta_http_equiv Content of the "http-equiv" portion of the META tag + * \param meta_content Content of the "content" portion of the META tag + */ +void extract_charset_from_meta(char *charset, char *meta_http_equiv, char *meta_content) +{ + char *ptr; + char buf[64]; + + if (!charset) return; + if (!meta_http_equiv) return; + if (!meta_content) return; + + + if (strcasecmp(meta_http_equiv, "Content-type")) return; + + ptr = strchr(meta_content, ';'); + if (!ptr) return; + + safestrncpy(buf, ++ptr, sizeof buf); + striplt(buf); + if (!strncasecmp(buf, "charset=", 8)) { + strcpy(charset, &buf[8]); + } +} + + + +/** + * \brief Sanitize and enhance an HTML message for display. + * Also convert weird character sets to UTF-8 if necessary. + * + * \param supplied_charset the input charset as declared in the MIME headers + */ +void output_html(char *supplied_charset, int treat_as_wiki) { + char buf[SIZ]; + char *msg; + char *ptr; + char *msgstart; + char *msgend; + char *converted_msg; + int buffer_length = 1; + int line_length = 0; + int content_length = 0; + int output_length = 0; + char new_window[SIZ]; + int brak = 0; + int alevel = 0; + int i; + int linklen; + char charset[128]; +#ifdef HAVE_ICONV + iconv_t ic = (iconv_t)(-1) ; + char *ibuf; /**< Buffer of characters to be converted */ + char *obuf; /**< Buffer for converted characters */ + size_t ibuflen; /**< Length of input buffer */ + size_t obuflen; /**< Length of output buffer */ + char *osav; /**< Saved pointer to output buffer */ +#endif + + safestrncpy(charset, supplied_charset, sizeof charset); + msg = strdup(""); + sprintf(new_window, ""); + wprintf(_("realloc() error! couldn't get %d bytes: %s"), + buffer_length + 1, + strerror(errno)); + wprintf("

\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 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, "
'))) + ) { + /* 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, "') + ||(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"); + output_length += 2; + for (i=0; i"); + 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, "", 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("

\n"); + + /** Now give back the memory */ + free(converted_msg); + free(msg); +} + +/*@}*/ diff --git a/webcit/src/http_datestring.c b/webcit/src/http_datestring.c new file mode 100644 index 000000000..33f578464 --- /dev/null +++ b/webcit/src/http_datestring.c @@ -0,0 +1,67 @@ +/* + * $Id$ + */ +/** + * \defgroup HTTPDateTime Function to generate HTTP-compliant textual time/date stamp + * (This module was lifted directly from the Citadel server source) + * + * \ingroup WebcitHttpServer + */ +/*@{*/ +#include "webcit.h" + +/** HTTP Months - do not translate - these are not for human consumption */ +static char *httpdate_months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +/** HTTP Weekdays - do not translate - these are not for human consumption */ +static char *httpdate_weekdays[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + + +/** + * \brief Supplied with a unix timestamp, generate a textual time/date stamp + * \param buf the return buffer + * \param n the size of the buffer + * \param xtime the time to format as string + */ +void http_datestring(char *buf, size_t n, time_t xtime) { + struct tm t; + + long offset; + char offsign; + + localtime_r(&xtime, &t); + + /** Convert "seconds west of GMT" to "hours/minutes offset" */ +#ifdef HAVE_STRUCT_TM_TM_GMTOFF + offset = t.tm_gmtoff; +#else + offset = timezone; +#endif + if (offset > 0) { + offsign = '+'; + } + else { + offset = 0L - offset; + offsign = '-'; + } + offset = ( (offset / 3600) * 100 ) + ( offset % 60 ); + + snprintf(buf, n, "%s, %02d %s %04d %02d:%02d:%02d %c%04ld", + httpdate_weekdays[t.tm_wday], + t.tm_mday, + httpdate_months[t.tm_mon], + t.tm_year + 1900, + t.tm_hour, + t.tm_min, + t.tm_sec, + offsign, offset + ); +} + + +/*@}*/ diff --git a/webcit/src/ical_dezonify.c b/webcit/src/ical_dezonify.c new file mode 100644 index 000000000..c2042fd5f --- /dev/null +++ b/webcit/src/ical_dezonify.c @@ -0,0 +1,171 @@ +/* + * $Id$ + */ +/** + * \defgroup IcalDezonify normalize ical dates to UTC + * Function to go through an ical component set and convert all non-UTC + * date/time properties to UTC. It also strips out any VTIMEZONE + * subcomponents afterwards, because they're irrelevant. + * + * Everything here will work on both a fully encapsulated VCALENDAR component + * or any type of subcomponent. + * + * \ingroup Calendaring + */ +/*@{*/ + +#include "webcit.h" +#include "webserver.h" + + +#ifdef WEBCIT_WITH_CALENDAR_SERVICE + + +/** + * \brief Back end function for ical_dezonify() + * + * We supply this with the master component, the relevant component, + * and the property (which will be a DTSTART, DTEND, etc.) + * which we want to convert to UTC. + * \param cal dunno ??? + * \param rcal dunno ??? + * \param prop dunno ??? + */ +void ical_dezonify_backend(icalcomponent *cal, + icalcomponent *rcal, + icalproperty *prop) { + + icaltimezone *t = NULL; + icalparameter *param; + const char *tzid; + struct icaltimetype TheTime; + + /** Give me nothing and I will give you nothing in return. */ + if (cal == NULL) return; + + /** Hunt for a TZID parameter in this property. */ + param = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER); + + /** Get the stringish name of this TZID. */ + if (param != NULL) { + tzid = icalparameter_get_tzid(param); + + /** Convert it to an icaltimezone type. */ + if (tzid != NULL) { + t = icalcomponent_get_timezone(cal, tzid); + } + + } + + /** Now we know the timezone. Convert to UTC. */ + + if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) { + TheTime = icalproperty_get_dtstart(prop); + } + else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) { + TheTime = icalproperty_get_dtend(prop); + } + else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) { + TheTime = icalproperty_get_due(prop); + } + else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) { + TheTime = icalproperty_get_exdate(prop); + } + else { + return; + } + + /** Do the conversion. */ + if (t != NULL) { + icaltimezone_convert_time(&TheTime, + t, + icaltimezone_get_utc_timezone() + ); + } + TheTime.is_utc = 1; + icalproperty_remove_parameter_by_kind(prop, ICAL_TZID_PARAMETER); + + /** Now add the converted property back in. */ + if (icalproperty_isa(prop) == ICAL_DTSTART_PROPERTY) { + icalproperty_set_dtstart(prop, TheTime); + } + else if (icalproperty_isa(prop) == ICAL_DTEND_PROPERTY) { + icalproperty_set_dtend(prop, TheTime); + } + else if (icalproperty_isa(prop) == ICAL_DUE_PROPERTY) { + icalproperty_set_due(prop, TheTime); + } + else if (icalproperty_isa(prop) == ICAL_EXDATE_PROPERTY) { + icalproperty_set_exdate(prop, TheTime); + } +} + + +/** + * \brief Recursive portion of ical_dezonify() + * \param cal dunno ??? + * \param rcal dunno ??? + */ +void ical_dezonify_recur(icalcomponent *cal, icalcomponent *rcal) { + icalcomponent *c; + icalproperty *p; + + /** + * Recurse through all subcomponents *except* VTIMEZONE ones. + */ + for (c=icalcomponent_get_first_component( + rcal, ICAL_ANY_COMPONENT); + c != NULL; + c = icalcomponent_get_next_component( + rcal, ICAL_ANY_COMPONENT) + ) { + if (icalcomponent_isa(c) != ICAL_VTIMEZONE_COMPONENT) { + ical_dezonify_recur(cal, c); + } + } + + /** + * Now look for DTSTART and DTEND properties + */ + for (p=icalcomponent_get_first_property( + rcal, ICAL_ANY_PROPERTY); + p != NULL; + p = icalcomponent_get_next_property( + rcal, ICAL_ANY_PROPERTY) + ) { + if ( + (icalproperty_isa(p) == ICAL_DTSTART_PROPERTY) + || (icalproperty_isa(p) == ICAL_DTEND_PROPERTY) + || (icalproperty_isa(p) == ICAL_DUE_PROPERTY) + || (icalproperty_isa(p) == ICAL_EXDATE_PROPERTY) + ) { + ical_dezonify_backend(cal, rcal, p); + } + } +} + + +/** + * \brief Convert all DTSTART and DTEND properties in all subcomponents to UTC. + * This function will search any VTIMEZONE subcomponents to learn the + * relevant timezone information. + * \param cal item to process + */ +void ical_dezonify(icalcomponent *cal) { + icalcomponent *vt = NULL; + + /** Convert all times to UTC */ + ical_dezonify_recur(cal, cal); + + /** Strip out VTIMEZONE subcomponents -- we don't need them anymore */ + while (vt = icalcomponent_get_first_component( + cal, ICAL_VTIMEZONE_COMPONENT), vt != NULL) { + icalcomponent_remove_component(cal, vt); + icalcomponent_free(vt); + } + +} + + +#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ +/*@}*/ diff --git a/webcit/src/iconbar.c b/webcit/src/iconbar.c new file mode 100644 index 000000000..bb9a4e417 --- /dev/null +++ b/webcit/src/iconbar.c @@ -0,0 +1,774 @@ +/* + * $Id$ + */ +/** + * \defgroup IconBar Displays and customizes the iconbar. + * \ingroup MenuInfrastructure + */ +/*@{*/ +#include "webcit.h" + + +/** Values for ib_displayas */ +#define IB_PICTEXT 0 /**< picture and text */ +#define IB_PICONLY 1 /**< just a picture */ +#define IB_TEXTONLY 2 /**< just text */ + + +/** + * \brief draw the icon bar????? + */ +void do_selected_iconbar(void) { + if (WC->current_iconbar == current_iconbar_roomlist) { + do_iconbar_roomlist(); + } + else { + do_iconbar(); + } +} + +/** + * \brief draw the icon bar??? + */ +void do_iconbar(void) { + char iconbar[SIZ]; + char buf[SIZ]; + char key[SIZ], value[SIZ]; + int i; + + WC->current_iconbar = current_iconbar_menu; + + /** + * The initialized values of these variables also happen to + * specify the default values for users who haven't customized + * their iconbars. These should probably be set in a master + * configuration somewhere. + */ + int ib_displayas = 0; /**< pictures and text, pictures, text */ + int ib_logo = 0; /**< Site logo */ + int ib_summary = 1; /**< Summary page icon */ + int ib_inbox = 1; /**< Inbox icon */ + int ib_calendar = 1; /**< Calendar icon */ + int ib_contacts = 1; /**< Contacts icon */ + int ib_notes = 1; /**< Notes icon */ + int ib_tasks = 1; /**< Tasks icon */ + int ib_rooms = 1; /**< Rooms icon */ + int ib_users = 1; /**< Users icon */ + int ib_chat = 1; /**< Chat icon */ + int ib_advanced = 1; /**< Advanced Options icon */ + int ib_citadel = 1; /**< 'Powered by Citadel' logo */ + /* + */ + + get_preference("iconbar", iconbar, sizeof iconbar); + for (i=0; i\n" + "
\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\n" + "
\n"); + + /** embed the room list */ + list_all_rooms_by_floor("iconbar"); + + wprintf("
\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\n" + "
" + ""); + wprintf(_("Customize the icon bar")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("
" + "
"); + + wprintf("
\n"); + + wprintf("
"); + wprintf(_("Display icons as:")); + wprintf(" "); + for (i=0; i<=2; ++i) { + 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("

\n"); + + wprintf(_("Select the icons you would like to see displayed " + "in the 'icon bar' menu on the left side of the " + "screen.")); + wprintf("

\n"); + + wprintf("\n"); + + wprintf("\n", + ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), + (ib_logo ? "CHECKED" : ""), + _("Site logo"), + _("An icon describing this site") + ); + + wprintf("\n", + ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), + (ib_summary ? "CHECKED" : ""), + _("Summary"), + _("Your summary page") + ); + + wprintf("\n", + ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), + (ib_inbox ? "CHECKED" : ""), + _("Mail (inbox)"), + _("A shortcut to your email Inbox") + ); + + wprintf("\n", + ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), + (ib_contacts ? "CHECKED" : ""), + _("Contacts"), + _("Your personal address book") + ); + + wprintf("\n", + ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), + (ib_notes ? "CHECKED" : ""), + _("Notes"), + _("Your personal notes") + ); + +#ifdef WEBCIT_WITH_CALENDAR_SERVICE + wprintf("\n", + ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), + (ib_calendar ? "CHECKED" : ""), + _("Calendar"), + _("A shortcut to your personal calendar") + ); + + wprintf("\n", + ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), + (ib_tasks ? "CHECKED" : ""), + _("Tasks"), + _("A shortcut to your personal task list") + ); +#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ + + wprintf("\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("\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("\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("\n", + ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), + (ib_advanced ? "CHECKED" : ""), + _("Advanced options"), + _("Access to the complete menu of Citadel functions.") + + ); + + wprintf("\n", + ((bar = 1 - bar), (bar ? "\"#CCCCCC\"" : "\"#FFFFFF\"")), + (ib_citadel ? "CHECKED" : ""), + _("Citadel logo"), + _("Displays the 'Powered by Citadel' icon") + ); + + wprintf("
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "
" + "" + "" + "\" \"" + "" + "%s
" + "%s" + "

\n" + "
" + "" + " " + "" + "
\n", + _("Save changes"), + _("Cancel") + ); + + wprintf("
\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( + "
" + "" + " "); + wprintf(_("Your icon bar has been updated. Please select any of its " + "choices to continue.")); + wprintf("
\n"); + wDumpContent(2); +} + + + +/*@}*/ diff --git a/webcit/src/inetconf.c b/webcit/src/inetconf.c new file mode 100644 index 000000000..85f98153b --- /dev/null +++ b/webcit/src/inetconf.c @@ -0,0 +1,202 @@ +/* + * $Id$ + */ +/** + * \defgroup InetCfg Functions which handle Internet domain configuration etc. + * \ingroup CitadelConfig + */ +/*@{*/ +#include "webcit.h" + + +/** + * \brief display the inet config dialog + */ +void display_inetconf(void) +{ + char buf[SIZ]; + char ename[SIZ]; + char etype[SIZ]; + int i; + int which; + + enum { + ic_localhost, + ic_directory, + ic_gwdom, + ic_smarthost, + ic_rbl, + ic_spamass, + ic_max + }; + char *ic_spec[ic_max]; + char *ic_misc; + char *ic_keyword[ic_max]; + char *ic_boxtitle[ic_max]; + char *ic_desc[ic_max]; + + ic_keyword[0] = _("localhost"); + ic_keyword[1] = _("directory"); + ic_keyword[2] = _("gatewaydomain"); + ic_keyword[3] = _("smarthost"); + ic_keyword[4] = _("rbl"); + ic_keyword[5] = _("spamassassin"); + + ic_boxtitle[0] = _("Local host aliases"); + ic_boxtitle[1] = _("Directory domains"); + ic_boxtitle[2] = _("Gateway domains"); + ic_boxtitle[3] = _("Smart hosts"); + ic_boxtitle[4] = _("RBL hosts"); + ic_boxtitle[5] = _("SpamAssassin hosts"); + + ic_desc[0] = _("(domains for which this host receives mail)"); + ic_desc[1] = _("(domains mapped with the Global Address Book)"); + ic_desc[2] = _("(domains whose subdomains match Citadel hosts)"); + ic_desc[3] = _("(if present, forward all outbound mail to one of these hosts)"); + ic_desc[4] = _("(hosts running a Realtime Blackhole List)"); + ic_desc[5] = _("(hosts running the SpamAssassin service)"); + + for (i=0; i= 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("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Internet configuration")); + wprintf("\n"); + wprintf("
\n"); + wprintf("
\n
\n"); + + wprintf("
" + "
\n"); + for (which=0; which"); + } + svprintf("BOXTITLE", WCS_STRING, ic_boxtitle[which]); + do_template("beginbox"); + wprintf(""); + escputs(ic_desc[which]); + wprintf("
"); + wprintf("\n"); + if (strlen(ic_spec[which]) > 0) { + for (i=0; i\n"); + } + } + wprintf("\n" + "
"); + extract_token(buf, ic_spec[which], i, '\n', sizeof buf); + escputs(buf); + wprintf("" + "", + _("Delete this entry?")); + wprintf(""); + wprintf(_("(Delete)")); + wprintf("
" + "" + "", ic_keyword[which]); + wprintf("" + "" + "
\n"); + do_template("endbox"); + } + wprintf("
\n"); + wDumpContent(1); + + for (i=0; iImportantMessage, _("%s has been deleted."), ename); + } + else { + if (strlen(newconfig) > 0) strcat(newconfig, "\n"); + strcat(newconfig, buf); + } + } + + serv_printf("CONF PUTSYS|application/x-citadel-internet-config"); + serv_getln(buf, SIZ); + if (buf[0] == '4') { + serv_puts(newconfig); + if (!strcasecmp(bstr("oper"), "add")) { + serv_printf("%s|%s", bstr("ename"), bstr("etype") ); + sprintf(WC->ImportantMessage, "%s added.", bstr("ename")); + } + serv_puts("000"); + } + + display_inetconf(); + + free(buf); + free(ename); + free(etype); + free(newconfig); +} + + + +/*@}*/ diff --git a/webcit/src/listsub.c b/webcit/src/listsub.c new file mode 100644 index 000000000..3f7e9cf53 --- /dev/null +++ b/webcit/src/listsub.c @@ -0,0 +1,234 @@ +/* + * $Id$ + */ +/** + * \defgroup ListSubForms Web forms for handling mailing list subscribe/unsubscribe requests. + * \ingroup WebcitDisplayItems + */ + +/*@{*/ +#include "webcit.h" + + + +/** + * \brief List subscription handling + */ +void do_listsub(void) +{ + char cmd[256]; + char room[256]; + char token[256]; + char email[256]; + char subtype[256]; + char escaped_email[256]; + char escaped_room[256]; + + char buf[SIZ]; + int self; + char sroom[SIZ]; + + strcpy(WC->wc_fullname, ""); + strcpy(WC->wc_username, ""); + strcpy(WC->wc_password, ""); + strcpy(WC->wc_roomname, ""); + + output_headers(1, 0, 0, 1, 1, 0); + begin_burst(); + + wprintf("\n" + "\n" + "\n" + "\n" + ); + wprintf(_("List subscription")); + wprintf("\n"); + + strcpy(cmd, bstr("cmd")); + strcpy(room, bstr("room")); + strcpy(token, bstr("token")); + strcpy(email, bstr("email")); + strcpy(subtype, bstr("subtype")); + + wprintf("
" + "
" + ""); + wprintf(_("List subscribe/unsubscribe")); + wprintf("

\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("

"); + wprintf(_("Confirmation request sent")); + wprintf("

"); + wprintf(_("You are subscribing %s" + " to the %s 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.

" + "Please click on the link which is being " + "e-mailed to you and your subscription will " + "be confirmed.
\n"), + escaped_email, escaped_room); + wprintf("%s
\n", _("Go back...")); + } + else { + wprintf("ERROR: %s" + "

\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("

Confirmation request sent

" + "You are unsubscribing "); + escputs(email); + wprintf(" 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.

" + "Please click on the link which is being " + "e-mailed to you and your unsubscription will " + "be confirmed.
\n" + "Back...
\n" + ); + } + else { + wprintf("ERROR: %s" + "

\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("

Confirmation successful!

"); + } + else { + wprintf("

Confirmation failed.

" + "This could mean one of two things:
    \n" + "
  • You waited too long to confirm your " + "subscribe/unsubscribe request (the " + "confirmation link is only valid for three " + "days)\n
  • You have already " + "successfully confirmed your " + "subscribe/unsubscribe request and are " + "attempting to do it again.
\n" + "The error returned by the server was: " + ); + } + wprintf("%s

\n", &buf[4]); + } + + /** + * Any other (invalid) command causes the form to be displayed + */ + else { +FORM: wprintf("
\n" + "\n" + ); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("
Name of list" + "" + "
Your e-mail address" + "
" + "(If subscribing) preferred format: " + "One message at a time  " + "Digest format  " + "
\n" + "\n" + "\n" + "
\n" + ); + + wprintf("
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.
\n" + ); + + } + + wprintf("\n"); + wDumpContent(0); + end_webcit_session(); +} + + + +/*@}*/ diff --git a/webcit/src/locate_host.c b/webcit/src/locate_host.c new file mode 100644 index 000000000..7b5869907 --- /dev/null +++ b/webcit/src/locate_host.c @@ -0,0 +1,44 @@ +/* + * $Id$ + */ +/** + * \defgroup Hostlookup Examine a socket and determine the name/address of the originating host. + * \ingroup WebcitHttpServer + */ +/*@{*/ + +#include "webcit.h" + +/** + * \brief get a hostname + * \todo buffersize? + * \param tbuf the returnbuffer + * \param client_socket the sock fd where the client is connected + */ +void locate_host(char *tbuf, int client_socket) +{ + struct sockaddr_in cs; + struct hostent *ch; + socklen_t len; + char *i; + int a1, a2, a3, a4; + + len = sizeof(cs); + if (getpeername(client_socket, (struct sockaddr *) &cs, &len) < 0) { + strcpy(tbuf, ""); + return; + } + if ((ch = gethostbyaddr((char *) &cs.sin_addr, sizeof(cs.sin_addr), + AF_INET)) == NULL) { + i = (char *) &cs.sin_addr; + a1 = ((*i++) & 0xff); + a2 = ((*i++) & 0xff); + a3 = ((*i++) & 0xff); + a4 = ((*i++) & 0xff); + sprintf(tbuf, "%d.%d.%d.%d", a1, a2, a3, a4); + return; + } + safestrncpy(tbuf, ch->h_name, 64); +} + +/*@}*/ diff --git a/webcit/src/mainmenu.c b/webcit/src/mainmenu.c new file mode 100644 index 000000000..3f66e6d58 --- /dev/null +++ b/webcit/src/mainmenu.c @@ -0,0 +1,400 @@ +/* + * $Id$ + */ +/** + * \defgroup DispAdvancedMenu Displays the "advanced" (main) menu. + * \ingroup MenuInfrastructure + * + */ +/*@{*/ +#include "webcit.h" + +/** + * \brief The Main Menu + */ +void display_main_menu(void) +{ + output_headers(1, 1, 1, 0, 0, 0); + + wprintf("
" + "" + "" + "
\n"); + + svprintf("BOXTITLE", WCS_STRING, _("Basic commands")); + do_template("beginbox"); + + wprintf("\n" + "" + "" + "
"); /**< start of first column */ + + wprintf(""); + wprintf(_("List known rooms")); + wprintf("
"); + wprintf(_("Where can I go from here?")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Goto next room")); + wprintf("
" + ""); + wprintf(_("...with unread messages")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Skip to next room")); + wprintf("
" + ""); + wprintf(_("(come back here later)")); + wprintf("\n"); + + if ((strlen(WC->ugname) > 0) && (strcasecmp(WC->ugname, WC->wc_roomname))) { + wprintf("
" + "" + ""); + wprintf(_("Ungoto")); + wprintf("
" + ""); + wprintf(_("(oops! Back to %s)"), WC->ugname); + wprintf("\n"); + } + + wprintf("
\n"); /* start of second column */ + + wprintf("" + ""); + wprintf(_("Read new messages")); + wprintf("
" + ""); + wprintf(_("...in this room")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Read all messages")); + wprintf("
" + ""); + wprintf(_("...old and new")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Enter a message")); + wprintf("
" + ""); + wprintf(_("(post in this room)")); + wprintf("\n"); + + wprintf("
"); /* start of third column */ + + wprintf("" + ""); + wprintf(_("Summary page")); + wprintf("
" + ""); + wprintf(_("Summary of my account")); + wprintf("
\n"); + + wprintf("\n" + ""); + wprintf(_("User list")); + wprintf("
" + ""); + wprintf(_("(all registered users)")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Log off")); + wprintf("
" + ""); + wprintf(_("Bye!")); + wprintf("\n"); + + wprintf("
\n"); + do_template("endbox"); + + wprintf("
"); + + svprintf("BOXTITLE", WCS_STRING, _("Your info")); + do_template("beginbox"); + + wprintf("" + ""); + wprintf(_("Change your preferences and settings")); + wprintf("
\n"); + + wprintf("
" + ""); + wprintf(_("Update your contact information")); + wprintf("
\n"); + + wprintf("
" + ""); + wprintf(_("Change your password")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Enter your 'bio'")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Edit your online photo")); + wprintf("\n"); + + do_template("endbox"); + + wprintf("
"); + + svprintf("BOXTITLE", WCS_STRING, _("Advanced room commands")); + do_template("beginbox"); + + if ((WC->axlevel >= 6) || (WC->is_room_aide)) { + wprintf("" + ""); + wprintf(_("Edit or delete this room")); + wprintf("
\n"); + } + + wprintf("" + ""); + wprintf(_("Go to a 'hidden' room")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Create a new room")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Zap (forget) this room (%s)"), WC->wc_roomname); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("List all forgotten rooms")); + wprintf("\n"); + + do_template("endbox"); + + wprintf("
"); + wDumpContent(2); +} + + +/** + * \brief System administration menu + */ +void display_aide_menu(void) +{ + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n" + "
" + ""); + wprintf(_("System Administration Menu")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("
" + "
"); + + svprintf("BOXTITLE", WCS_STRING, _("Global Configuration")); + do_template("beginbox"); + + wprintf("" + ""); + wprintf(_("Edit site-wide configuration")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Domain names and Internet mail configuration")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Configure replication with other Citadel servers")); + wprintf("\n"); + + do_template("endbox"); + + wprintf("
"); + + svprintf("BOXTITLE", WCS_STRING, _("User account management")); + do_template("beginbox"); + + wprintf("" + ""); + wprintf(_("Add, change, delete user accounts")); + wprintf("
\n"); + + wprintf("" + ""); + wprintf(_("Validate new users")); + wprintf("
\n"); + + do_template("endbox"); + + svprintf("BOXTITLE", WCS_STRING, _("Rooms and Floors")); + do_template("beginbox"); + + wprintf("" + ""); + wprintf(_("Add, change, or delete floors")); + wprintf("\n"); + + do_template("endbox"); + + wprintf("
"); + 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("
\n" + "
" + ""); + wprintf(_("Enter a server command")); + wprintf("
\n" + "
\n
\n" + ); + + wprintf("
" + "
\n"); + + wprintf("
"); + 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("
\n"); + + wprintf("
\n"); + + wprintf(_("Enter command:")); + wprintf("

\n"); + + wprintf(_("Command input (if requesting SEND_LISTING transfer mode):")); + wprintf("

\n"); + + wprintf(""); + wprintf(_("Detected host header is %s://%s"), (is_https ? "https" : "http"), WC->http_host); + wprintf("\n"); + wprintf("", _("Send command")); + wprintf(" "); + wprintf("
\n", _("Cancel")); + + wprintf("
\n"); + wprintf("
\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("
Command:"); + escputs(bstr("g_cmd")); + wprintf("
Result:"); + escputs(buf); + wprintf("

\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("
\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("
"); + wprintf("Enter another command
\n"); + wprintf("Return to menu\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("\n" + "\n" + "MenuBar\n" + "\n" + "\n"); + do_template("background"); + } + + do_template("menubar"); + + if (as_single_page) { + wDumpContent(2); + } + + +} + + +/*@}*/ diff --git a/webcit/src/messages.c b/webcit/src/messages.c new file mode 100644 index 000000000..1be863959 --- /dev/null +++ b/webcit/src/messages.c @@ -0,0 +1,3183 @@ +/* + * $Id$ + */ +/** + * \defgroup MsgDisp Functions which deal with the fetching and displaying of messages. + * \ingroup WebcitDisplayItems + * + */ +/*@{*/ +#include "webcit.h" +#include "vcard.h" +#include "webserver.h" +#include "groupdav.h" + +#define SUBJ_COL_WIDTH_PCT 50 /**< Mailbox view column width */ +#define SENDER_COL_WIDTH_PCT 30 /**< Mailbox view column width */ +#define DATE_PLUS_BUTTONS_WIDTH_PCT 20 /**< Mailbox view column width */ + +/** + * Address book entry (keep it short and sweet, it's just a quickie lookup + * which we can use to get to the real meat and bones later) + */ +struct addrbookent { + char ab_name[64]; /**< name string */ + long ab_msgnum; /**< message number of address book entry */ +}; + + + +#ifdef HAVE_ICONV + +/** + * \brief Wrapper around iconv_open() + * Our version adds aliases for non-standard Microsoft charsets + * such as 'MS950', aliasing them to names like 'CP950' + * + * \param tocode Target encoding + * \param fromcode Source encoding + */ +iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode) +{ + iconv_t ic = (iconv_t)(-1) ; + ic = iconv_open(tocode, fromcode); + if (ic == (iconv_t)(-1) ) { + char alias_fromcode[64]; + if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) { + safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode); + alias_fromcode[0] = 'C'; + alias_fromcode[1] = 'P'; + ic = iconv_open(tocode, alias_fromcode); + } + } + return(ic); +} + + +/** + * \brief Handle subjects with RFC2047 encoding + * such as: + * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?= + * \param buf the stringbuffer to process + */ +void utf8ify_rfc822_string(char *buf) { + char *start, *end; + char newbuf[1024]; + char charset[128]; + char encoding[16]; + char istr[1024]; + iconv_t ic = (iconv_t)(-1) ; + char *ibuf; /**< Buffer of characters to be converted */ + char *obuf; /**< Buffer for converted characters */ + size_t ibuflen; /**< Length of input buffer */ + size_t obuflen; /**< Length of output buffer */ + char *isav; /**< Saved pointer to input buffer */ + char *osav; /**< Saved pointer to output buffer */ + int passes = 0; + int i; + int illegal_non_rfc2047_encoding = 0; + + /** Sometimes, badly formed messages contain strings which were simply + * written out directly in some foreign character set instead of + * using RFC2047 encoding. This is illegal but we will attempt to + * handle it anyway by converting from a user-specified default + * charset to UTF-8 if we see any nonprintable characters. + */ + for (i=0; i 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 126)) { + need_to_encode = 1; + } + } + + if (!need_to_encode) { + safestrncpy(target, source, maxlen); + return; + } + + strcpy(target, "=?UTF-8?Q?"); + for (i=0; i 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"); + 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(""); + return; + } + + wprintf("
"); + 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; jprop[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, "
"); + strcat(mailto, + ""); + + strcat(mailto, "\">"); + stresc(&mailto[strlen(mailto)], thisvalue, 1, 1); + strcat(mailto, ""); + } + else if (!strcasecmp(firsttoken, "tel")) { + if (strlen(phone) > 0) strcat(phone, "
"); + strcat(phone, thisvalue); + for (j=0; j
\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("\n"); + } + ***/ + } + + free(thisname); + free(thisvalue); + } + + if (pass == 1) { + wprintf("" + "\n"); + + if (strlen(phone) > 0) { + wprintf("\n", phone); + } + if (strlen(mailto) > 0) { + wprintf("\n", mailto); + } + } + + } + + wprintf("
"); + wprintf(_("Address:")); + wprintf(""); + for (j=0; j 0) { + escputs(buf); + if (j<3) wprintf("
"); + else wprintf(" "); + } + } + wprintf("
"); + escputs(thisname); + wprintf(""); + escputs(thisvalue); + wprintf("
" + "" + ""); + escputs(fullname); + wprintf(""); + if (strlen(title) > 0) { + wprintf("
"); + escputs(title); + wprintf("
"); + } + if (strlen(org) > 0) { + wprintf("
"); + escputs(org); + wprintf("
"); + } + wprintf("
"); + wprintf(_("Telephone:")); + wprintf("%s
"); + wprintf(_("E-mail:")); + wprintf("%s
\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(""); + wprintf(_("ERROR:")); + wprintf(" %s
\n", &buf[4]); + return; + } + + /** begin everythingamundo table */ + if (!printable_view) { + wprintf("
\n"); + wprintf("
\n"); + } + + /** begin message header table */ + wprintf("\n"); + + /** start msg buttons */ + if (!printable_view) { + wprintf(""); + } + + wprintf("
\n"); + + wprintf(""); + strcpy(m_subject, ""); + strcpy(m_cc, ""); + + while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) { + if (!strcmp(buf, "000")) { + wprintf(""); + wprintf(_("unexpected end of message")); + wprintf("

\n"); + wprintf("
\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(""); + escputs(from); + wprintf(" "); + } + 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), + "", + 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), + "\n" + "%s (%s, %d bytes) [ " + "%s" + " | " + "%s" + " ]
\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(""); +#ifdef HAVE_ICONV + utf8ify_rfc822_string(m_cc); + utf8ify_rfc822_string(m_subject); +#endif + if (strlen(m_cc) > 0) { + wprintf("
" + ""); + wprintf(_("CC:")); + wprintf(" "); + escputs(m_cc); + wprintf(""); + } + if (strlen(m_subject) > 0) { + wprintf("
" + ""); + wprintf(_("Subject:")); + wprintf(" "); + escputs(m_subject); + wprintf(""); + } + wprintf("
\n"); + + /** Reply */ + if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { + wprintf("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] ", _("Reply")); + } + + /** ReplyQuoted */ + if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { + if (!WC->is_mailbox) { + wprintf("[%s] ", _("ReplyQuoted")); + } + } + + /** ReplyAll */ + if (WC->wc_view == VIEW_MAILBOX) { + wprintf("[%s] ", _("ReplyAll")); + } + + /** Forward */ + if (WC->wc_view == VIEW_MAILBOX) { + wprintf("[%s] ", _("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("[%s] ", + msgnum, _("Move")); + + /** Delete */ + wprintf("" + "[%s] ", msgnum, _("Delete this message?"), _("Delete") + ); + } + + /** Headers */ + wprintf("" + "[%s]", msgnum, msgnum, _("Headers")); + + + /** Print */ + wprintf("" + "[%s]", msgnum, msgnum, _("Print")); + + wprintf("
\n"); + + /** Begin body */ + wprintf("
"); + + /** + * Learn the content type + */ + strcpy(mime_content_type, "text/plain"); + while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) { + if (!strcmp(buf, "000")) { + wprintf(""); + wprintf(_("unexpected end of message")); + wprintf("

\n"); + goto ENDBODY; + } + if (!strncasecmp(buf, "Content-type: ", 14)) { + safestrncpy(mime_content_type, &buf[14], + sizeof(mime_content_type)); + for (i=0; i 0) && (isspace(buf[strlen(buf) - 1]))) + buf[strlen(buf) - 1] = 0; + if ((bq == 0) && + ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) { + wprintf("
"); + bq = 1; + } else if ((bq == 1) && + (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) { + wprintf("
"); + bq = 0; + } + wprintf(""); + url(buf); + escputs(buf); + wprintf("
\n"); + } + wprintf("
"); + } + + 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("
\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"); + read_message(msgnum, 1, buf); + wprintf(""); + } + } + + + /** 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("", + msgnum, vcard_partnum); + wprintf("[%s]", _("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("
\n"); + + /** end everythingamundo table */ + if (!printable_view) { + wprintf("
\n"); + wprintf("

\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\n" + "Printable view\n" + "\n" + ); + + read_message(msgnum, 1, ""); + + wprintf("\n\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
", &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("
"); + +#ifdef HAVE_ICONV + utf8ify_rfc822_string(m_subject); +#endif + if (strlen(m_subject) > 0) { + wprintf(_("Subject:")); + wprintf(" "); + msgescputs(m_subject); + wprintf("
"); + } + + /** + * Begin body + */ + wprintf("
"); + } + + /** + * 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 0) && (isspace(buf[strlen(buf) - 1]))) + buf[strlen(buf) - 1] = 0; + if ((bq == 0) && + ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) { + wprintf("
"); + bq = 1; + } else if ((bq == 1) && + (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) { + wprintf("
"); + bq = 0; + } + wprintf(""); + url(buf); + msgescputs(buf); + wprintf("
"); + } + wprintf("
"); + } + + /** 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; ilength = 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("", + WC->summ[num].msgnum, + (WC->summ[num].is_new ? "bold" : "normal"), + WC->summ[num].msgnum + ); + + wprintf("", SUBJ_COL_WIDTH_PCT); + escputs(WC->summ[num].subj); + wprintf(""); + + wprintf("", SENDER_COL_WIDTH_PCT); + escputs(WC->summ[num].from); + wprintf(""); + + wprintf("", DATE_PLUS_BUTTONS_WIDTH_PCT); + fmt_date(datebuf, WC->summ[num].date, 1); /* brief */ + escputs(datebuf); + wprintf(""); + + wprintf("\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("", + msgnum, vcard_partnum); + wprintf("[%s]", _("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; iab_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("


"); + wprintf(_("This address book is empty.")); + wprintf("
\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("", i); + } + else { + wprintf(""); + } + 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("\n"); + } + else { + wprintf("\n"); + } + } + wprintf("
\n"); + + wprintf("\n" + ); + + for (i=0; i 0) { + wprintf("\n"); + } + bg = 1 - bg; + wprintf("", + (bg ? "DDDDDD" : "FFFFFF") + ); + } + + wprintf("\n"); + ++displayed; + } + } + + wprintf("
"); + + wprintf("", bstr("alpha")); + vcard_n_prettyize(addrbook[i].ab_name); + escputs(addrbook[i].ab_name); + wprintf("
\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(""); + if (!strcmp(oper, "readnew")) { + wprintf(_("No new messages.")); + } else if (!strcmp(oper, "readold")) { + wprintf(_("No old messages.")); + } else { + wprintf(_("No messages here.")); + } + wprintf("\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 = "" ; + } + else if (!strcasecmp(sortby, "rsubject")) { + subjsort_button = "" ; + } + else { + subjsort_button = "" ; + } + + if (!strcasecmp(sortby, "sender")) { + sendsort_button = "" ; + } + else if (!strcasecmp(sortby, "rsender")) { + sendsort_button = "" ; + } + else { + sendsort_button = "" ; + } + + if (!strcasecmp(sortby, "date")) { + datesort_button = "" ; + } + else if (!strcasecmp(sortby, "rdate")) { + datesort_button = "" ; + } + else { + datesort_button = "" ; + } + + if (is_summary) { + wprintf("
\n"); /** end of 'content' div */ + + wprintf("\n" + ); + + /** note that Date and Delete are now in the same column */ + wprintf("
" + "
" + "" + "" + ); + wprintf("" + "" + "" + "\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("
%s %s%s %s%s %s" + " " + "" + "
\n"); + + wprintf("
" + + "
\n" + + "" + ); + } + + if (is_notes) { + wprintf("
%s
\n", _("Click on any note to edit it.")); + wprintf("
\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) * + (maxmsgsmsgarr[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" + "\n"); /**< end of 'fix_scrollbar_bug' div */ + wprintf(""); /**< end of 'message_list' div */ + + /** Here's the grab-it-to-resize-the-message-list widget */ + wprintf("
" + "
" + "
" + "
\n" + ); + + wprintf("
"); /**< 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("
"); + wprintf(_("Reading #"), lowest_displayed, highest_displayed); + + wprintf(" "); + wprintf(_("of %d messages."), nummsgs); + + /** forward/reverse */ + wprintf(" 
\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
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("\r\n"); + text_to_server_qp(bstr("msgtext")); /** Transmit message in quoted-printable encoding */ + serv_puts("\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("
\n"); + embed_room_banner(NULL, navbar_none); + wprintf("
\n"); + wprintf("
\n" + "
" + "
"); + + /** 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("%s
\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("%s
\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)], _(" from ")); + 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)], _(" to ")); + stresc(&buf[strlen(buf)], bstr("recp"), 1, 1); + } + */ + + strcat(&buf[strlen(buf)], _(" in ")); + stresc(&buf[strlen(buf)], WC->wc_roomname, 1, 1); + + /** begin message entry screen */ + wprintf("
\n"); + wprintf("\n", now); + if (WC->wc_view == VIEW_WIKI) { + wprintf("\n", bstr("wikipage")); + } + wprintf("\n", bstr("return_to")); + + wprintf("\""); + wprintf("%s\n", buf); /** header bar */ + if (WC->room_flags & QR_ANONOPT) { + wprintf(" " + "", + (is_anonymous ? "checked" : "") + ); + wprintf("Anonymous"); + } + wprintf("
\n"); /** header bar */ + + wprintf("\n"); + if (recipient_required) { + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + + /** Initialize the autocomplete ajax helpers (found in wclib.js) */ + wprintf(" \n" + ); + } + + wprintf("
"); + wprintf(""); + wprintf(_("To:")); + wprintf(""); + wprintf("" + ""); + wprintf("
"); + wprintf("
"); + wprintf(""); + wprintf(_("CC:")); + wprintf(""); + wprintf("" + ""); + wprintf("
"); + wprintf("
"); + wprintf(""); + wprintf(_("BCC:")); + wprintf(""); + wprintf("" + ""); + wprintf("
"); + wprintf("
"); + wprintf(""); + wprintf(_("Subject (optional):")); + wprintf(""); + wprintf("" + "\n"); + + wprintf(" " + "\n", _("Cancel")); + wprintf("
\n"); + + wprintf("
"); + + wprintf(""); + wprintf("

\n"); + + /** + * The following script embeds the TinyMCE richedit control, and automatically + * transforms the textarea into a richedit textarea. + */ + wprintf( + "\n" + "\n" + ); + + + /** Enumerate any attachments which are already in place... */ + wprintf(" "); + wprintf(_("Attachments:")); + wprintf(" "); + wprintf(""); + + /** Now offer the ability to attach additional files... */ + wprintf("   "); + wprintf(_("Attach file:")); + wprintf(" \n  " + "\n", _("Add")); + + /** Seth asked for these to be at the top *and* bottom... */ + wprintf(" " + "\n", _("Cancel")); + + /** Make sure we only insert our signature once */ + if (strcmp(bstr("sig_inserted"), "yes")) { + wprintf("\n"); + } + + wprintf("
\n"); + + wprintf("
\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("%s
\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("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Confirm move of message")); + wprintf("\n"); + wprintf("
\n"); + wprintf("
\n
\n"); + + wprintf("
"); + + wprintf(_("Move this message to:")); + wprintf("
\n"); + + wprintf("
\n"); + wprintf("\n", bstr("msgid")); + + wprintf("\n"); + wprintf("
\n"); + + wprintf("", _("Move")); + wprintf(" "); + wprintf("", _("Cancel")); + wprintf("
\n"); + + wprintf("
\n"); + wDumpContent(1); +} + + +/** + * \brief move a message to another folder + */ +void move_msg(void) +{ + long msgid; + char buf[SIZ]; + + msgid = atol(bstr("msgid")); + + if (strlen(bstr("move_button")) > 0) { + sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room")); + serv_puts(buf); + serv_getln(buf, sizeof buf); + sprintf(WC->ImportantMessage, "%s", &buf[4]); + } else { + sprintf(WC->ImportantMessage, (_("The message was not moved."))); + } + + readloop("readnew"); + +} + + +/*@}*/ diff --git a/webcit/src/mime_parser.c b/webcit/src/mime_parser.c new file mode 100644 index 000000000..02edd1c58 --- /dev/null +++ b/webcit/src/mime_parser.c @@ -0,0 +1,662 @@ +/* + * $Id$ + */ +/** + * \defgroup MIME This is the MIME parser for Citadel. + * + * Copyright (c) 1998-2005 by Art Cancro + * This code is distributed under the terms of the GNU General Public License. + * \ingroup WebcitHttpServer + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" +#include "mime_parser.h" + +/** + * \brief get mime key + * \param target where to put the mime buffer at??? + * \param source where to extract the mimetype from + * \param key what??? + */ +void extract_key(char *target, char *source, char *key) +{ + int a, b; + + strcpy(target, source); + for (a = 0; a < strlen(target); ++a) { + if ((!strncasecmp(&target[a], key, strlen(key))) + && (target[a + strlen(key)] == '=')) { + strcpy(target, &target[a + strlen(key) + 1]); + if (target[0] == 34) + strcpy(target, &target[1]); + for (b = 0; b < strlen(target); ++b) + if (target[b] == 34) + target[b] = 0; + return; + } + } + strcpy(target, ""); +} + + +/** + * \brief For non-multipart messages, we need to generate a quickie partnum of "1" + * to return to callback functions. Some callbacks demand it. + * \param supplied_partnum partnum to convert + * \return the converted num + */ +char *fixed_partnum(char *supplied_partnum) { + if (supplied_partnum == NULL) return "1"; + if (strlen(supplied_partnum)==0) return "1"; + return supplied_partnum; +} + + + +/** + * \brief Convert "quoted-printable" to binary. Returns number of bytes decoded. + * \param decoded the buffer with the decoded output + * \param encoded the encoded string to decode + * \param sourcelen length of the decoded buffer + */ +int CtdlDecodeQuotedPrintable(char *decoded, char *encoded, int sourcelen) { + char buf[SIZ]; + int buf_length = 0; + int soft_line_break = 0; + unsigned int ch; + int decoded_length = 0; + int i; + + decoded[0] = 0; + decoded_length = 0; + buf[0] = 0; + buf_length = 0; + + for (i = 0; i < sourcelen; ++i) { + + buf[buf_length++] = encoded[i]; + + if ( (encoded[i] == '\n') + || (encoded[i] == 0) + || (i == (sourcelen-1)) ) { + buf[buf_length++] = 0; + + /*** begin -- process one line ***/ + + if (buf[strlen(buf)-1] == '\n') { + buf[strlen(buf)-1] = 0; + } + if (buf[strlen(buf)-1] == '\r') { + buf[strlen(buf)-1] = 0; + } + while (isspace(buf[strlen(buf)-1])) { + buf[strlen(buf)-1] = 0; + } + soft_line_break = 0; + + while (strlen(buf) > 0) { + if (!strcmp(buf, "=")) { + soft_line_break = 1; + strcpy(buf, ""); + } else if ((strlen(buf)>=3) && (buf[0]=='=')) { + sscanf(&buf[1], "%02x", &ch); + decoded[decoded_length++] = ch; + strcpy(buf, &buf[3]); + } else { + decoded[decoded_length++] = buf[0]; + strcpy(buf, &buf[1]); + } + } + if (soft_line_break == 0) { + decoded[decoded_length++] = '\r'; + decoded[decoded_length++] = '\n'; + } + buf_length = 0; + /*** end -- process one line ***/ + } + } + + decoded[decoded_length++] = 0; + return(decoded_length); +} + +/** + * \brief fully decode a message + * Given a message or message-part body and a length, handle any necessary + * decoding and pass the request up the stack. + * \param partnum todo ????? + * \param part_start todo + * \param length todo + * \param content_type todo + * \param charset todo + * \param encoding todo + * \param disposition todo + * \param name todo + * \param filename todo + * \param CallBack todo + * \param PreMultiPartCallBack todo + * \param PostMultiPartCallBack todo + * \param userdata todo + * \param dont_decode todo + */ +void mime_decode(char *partnum, + char *part_start, size_t length, + char *content_type, char *charset, char *encoding, + char *disposition, + char *name, char *filename, + void (*CallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + void (*PreMultiPartCallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + void (*PostMultiPartCallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + void *userdata, + int dont_decode +) +{ + + char *decoded; + size_t bytes_decoded = 0; + + /* Some encodings aren't really encodings */ + if (!strcasecmp(encoding, "7bit")) + strcpy(encoding, ""); + if (!strcasecmp(encoding, "8bit")) + strcpy(encoding, ""); + if (!strcasecmp(encoding, "binary")) + strcpy(encoding, ""); + + /* If this part is not encoded, send as-is */ + if ( (strlen(encoding) == 0) || (dont_decode)) { + if (CallBack != NULL) { + CallBack(name, filename, fixed_partnum(partnum), + disposition, part_start, + content_type, charset, length, encoding, userdata); + } + return; + } + + if ((strcasecmp(encoding, "base64")) + && (strcasecmp(encoding, "quoted-printable"))) { + return; + } + /** + * Allocate a buffer for the decoded data. The output buffer is the + * same size as the input buffer; this assumes that the decoded data + * will never be larger than the encoded data. This is a safe + * assumption with base64, uuencode, and quoted-printable. + */ + decoded = malloc(length+2048); + if (decoded == NULL) { + return; + } + + if (!strcasecmp(encoding, "base64")) { + bytes_decoded = CtdlDecodeBase64(decoded, part_start, length); + } + else if (!strcasecmp(encoding, "quoted-printable")) { + bytes_decoded = CtdlDecodeQuotedPrintable(decoded, + part_start, length); + } + + if (bytes_decoded > 0) if (CallBack != NULL) { + CallBack(name, filename, fixed_partnum(partnum), + disposition, decoded, + content_type, charset, bytes_decoded, "binary", userdata); + } + + free(decoded); +} + +/** + * \brief Break out the components of a multipart message + * (This function expects to be fed HEADERS + CONTENT) + * Note: NULL can be supplied as content_end; in this case, the message is + * considered to have ended when the parser encounters a 0x00 byte. + * \param partnum todo + * \param content_start todo ????? + * \param content_end todo + * \param CallBack todo + * \param PreMultiPartCallBack + * \param PostMultiPartCallBack + * \param userdata todo + * \param dont_decode todo + */ +void the_mime_parser(char *partnum, + char *content_start, char *content_end, + void (*CallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + void (*PreMultiPartCallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + void (*PostMultiPartCallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + void *userdata, + int dont_decode +) +{ + + char *ptr; + char *srch = NULL; + char *part_start, *part_end = NULL; + char buf[SIZ]; + char *header; + char *boundary; + char *startary; + size_t startary_len = 0; + char *endary; + char *next_boundary; + char *content_type; + char *charset; + size_t content_length; + char *encoding; + char *disposition; + char *name = NULL; + char *content_type_name; + char *content_disposition_name; + char *filename; + int is_multipart; + int part_seq = 0; + int i; + size_t length; + char nested_partnum[SIZ]; + + ptr = content_start; + content_length = 0; + + boundary = malloc(SIZ); + memset(boundary, 0, SIZ); + + startary = malloc(SIZ); + memset(startary, 0, SIZ); + + endary = malloc(SIZ); + memset(endary, 0, SIZ); + + header = malloc(SIZ); + memset(header, 0, SIZ); + + content_type = malloc(SIZ); + memset(content_type, 0, SIZ); + + charset = malloc(SIZ); + memset(charset, 0, SIZ); + + encoding = malloc(SIZ); + memset(encoding, 0, SIZ); + + content_type_name = malloc(SIZ); + memset(content_type_name, 0, SIZ); + + content_disposition_name = malloc(SIZ); + memset(content_disposition_name, 0, SIZ); + + filename = malloc(SIZ); + memset(filename, 0, SIZ); + + disposition = malloc(SIZ); + memset(disposition, 0, SIZ); + + /** If the caller didn't supply an endpointer, generate one by measure */ + if (content_end == NULL) { + content_end = &content_start[strlen(content_start)]; + } + + /** Learn interesting things from the headers */ + strcpy(header, ""); + do { + ptr = memreadline(ptr, buf, SIZ); + if (ptr >= content_end) { + goto end_parser; + } + + for (i = 0; i < strlen(buf); ++i) { + if (isspace(buf[i])) { + buf[i] = ' '; + } + } + + if (!isspace(buf[0])) { + if (!strncasecmp(header, "Content-type: ", 14)) { + strcpy(content_type, &header[14]); + extract_key(content_type_name, content_type, "name"); + extract_key(charset, content_type, "charset"); + /** Deal with weird headers */ + if (strchr(content_type, ' ')) + *(strchr(content_type, ' ')) = '\0'; + if (strchr(content_type, ';')) + *(strchr(content_type, ';')) = '\0'; + } + if (!strncasecmp(header, "Content-Disposition: ", 21)) { + strcpy(disposition, &header[21]); + extract_key(content_disposition_name, disposition, "name"); + extract_key(filename, disposition, "filename"); + } + if (!strncasecmp(header, "Content-length: ", 16)) { + content_length = (size_t) atol(&header[16]); + } + if (!strncasecmp(header, + "Content-transfer-encoding: ", 27)) + strcpy(encoding, &header[27]); + if (strlen(boundary) == 0) + extract_key(boundary, header, "boundary"); + strcpy(header, ""); + } + if ((strlen(header) + strlen(buf) + 2) < SIZ) + strcat(header, buf); + } while ((strlen(buf) > 0) && (*ptr != 0)); + + if (strchr(disposition, ';')) + *(strchr(disposition, ';')) = '\0'; + striplt(disposition); + if (strchr(content_type, ';')) + *(strchr(content_type, ';')) = '\0'; + striplt(content_type); + + if (strlen(boundary) > 0) { + is_multipart = 1; + } else { + is_multipart = 0; + } + + /** If this is a multipart message, then recursively process it */ + part_start = NULL; + if (is_multipart) { + + /** Tell the client about this message's multipartedness */ + if (PreMultiPartCallBack != NULL) { + PreMultiPartCallBack("", "", partnum, "", + NULL, content_type, charset, + 0, encoding, userdata); + } + + /** Figure out where the boundaries are */ + snprintf(startary, SIZ, "--%s", boundary); + snprintf(endary, SIZ, "--%s--", boundary); + startary_len = strlen(startary); + + part_start = NULL; + do { + next_boundary = NULL; + for (srch=ptr; srch 0) { + snprintf(nested_partnum, + sizeof nested_partnum, + "%s.%d", partnum, + ++part_seq); + } + else { + snprintf(nested_partnum, + sizeof nested_partnum, + "%d", ++part_seq); + } + the_mime_parser(nested_partnum, + part_start, part_end, + CallBack, + PreMultiPartCallBack, + PostMultiPartCallBack, + userdata, + dont_decode); + } + + if (next_boundary != NULL) { + /** + * If we pass out of scope, don't attempt to + * read past the end boundary. */ + if (!strcmp(next_boundary, endary)) { + ptr = content_end; + } + else { + /** Set up for the next part. */ + part_start = strstr(next_boundary, "\n"); + ++part_start; + ptr = part_start; + } + } + else { + /** Invalid end of multipart. Bail out! */ + ptr = content_end; + } + } while ( (ptr < content_end) && (next_boundary != NULL) ); + + if (PostMultiPartCallBack != NULL) { + PostMultiPartCallBack("", "", partnum, "", NULL, + content_type, charset, 0, encoding, userdata); + } + goto end_parser; + } + + /** If it's not a multipart message, then do something with it */ + if (!is_multipart) { + part_start = ptr; + length = 0; + while (ptr < content_end) { + ++ptr; + ++length; + } + part_end = content_end; + /** fix an off-by-one error */ + --part_end; + --length; + + /** Truncate if the header told us to */ + if ( (content_length > 0) && (length > content_length) ) { + length = content_length; + } + + /** + * Sometimes the "name" field is tacked on to Content-type, + * and sometimes it's tacked on to Content-disposition. Use + * whichever one we have. + */ + if (strlen(content_disposition_name) > strlen(content_type_name)) { + name = content_disposition_name; + } + else { + name = content_type_name; + } + + /* + lprintf(9, "mime_decode part=%s, len=%d, type=%s, charset=%s, encoding=%s\n", + partnum, length, content_type, charset, encoding); + */ + + /** + * Ok, we've got a non-multipart part here, so do something with it. + */ + mime_decode(partnum, + part_start, length, + content_type, charset, encoding, disposition, + name, filename, + CallBack, NULL, NULL, + userdata, dont_decode + ); + + /** + * Now if it's an encapsulated message/rfc822 then we have to recurse into it + */ + if (!strcasecmp(content_type, "message/rfc822")) { + + if (PreMultiPartCallBack != NULL) { + PreMultiPartCallBack("", "", partnum, "", + NULL, content_type, charset, + 0, encoding, userdata); + } + if (CallBack != NULL) { + if (strlen(partnum) > 0) { + snprintf(nested_partnum, + sizeof nested_partnum, + "%s.%d", partnum, + ++part_seq); + } + else { + snprintf(nested_partnum, + sizeof nested_partnum, + "%d", ++part_seq); + } + the_mime_parser(nested_partnum, + part_start, part_end, + CallBack, + PreMultiPartCallBack, + PostMultiPartCallBack, + userdata, + dont_decode + ); + } + if (PostMultiPartCallBack != NULL) { + PostMultiPartCallBack("", "", partnum, "", NULL, + content_type, charset, 0, encoding, userdata); + } + + + } + + } + +end_parser: /** free the buffers! end the oppression!! */ + free(boundary); + free(startary); + free(endary); + free(header); + free(content_type); + free(charset); + free(encoding); + free(content_type_name); + free(content_disposition_name); + free(filename); + free(disposition); +} + + + +/** + * \brief Entry point for the MIME parser. + * (This function expects to be fed HEADERS + CONTENT) + * Note: NULL can be supplied as content_end; in this case, the message is + * considered to have ended when the parser encounters a 0x00 byte. + * \param content_start todo ????????? + * \param content_end todo + * \param CallBack todo + * \param PreMultiPartCallBack todo + * \param PostMultiPartCallBack todo + * \param userdata todo + * \param dont_decode todo + */ +void mime_parser(char *content_start, + char *content_end, + + void (*CallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + + void (*PreMultiPartCallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + + void (*PostMultiPartCallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + + void *userdata, + int dont_decode +) +{ + + the_mime_parser("", content_start, content_end, + CallBack, + PreMultiPartCallBack, + PostMultiPartCallBack, + userdata, dont_decode); +} + + + +/*@}*/ diff --git a/webcit/src/mime_parser.h b/webcit/src/mime_parser.h new file mode 100644 index 000000000..b82cd6884 --- /dev/null +++ b/webcit/src/mime_parser.h @@ -0,0 +1,57 @@ +/* + * $Id$ + * + */ + +/* + * Here's a bunch of stupid magic to make the MIME parser portable between + * Citadel and WebCit. + */ +#ifndef SIZ +#define SIZ 4096 +#endif + + +/* + * Declarations for functions in the parser + */ + +void extract_key(char *target, char *source, char *key); + +void mime_parser(char *content_start, char *content_end, + void (*CallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + void (*PreMultiPartCallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + void (*PostMultiPartCallBack) + (char *cbname, + char *cbfilename, + char *cbpartnum, + char *cbdisp, + void *cbcontent, + char *cbtype, + char *cbcharset, + size_t cblength, + char *cbencoding, + void *cbuserdata), + void *userdata, + int dont_decode + ); diff --git a/webcit/src/netconf.c b/webcit/src/netconf.c new file mode 100644 index 000000000..f3802961a --- /dev/null +++ b/webcit/src/netconf.c @@ -0,0 +1,320 @@ +/* + * $Id$ + */ +/** + * \defgroup NetShareConf Functions which handle network and sharing configuration. + * + * \ingroup CitadelConfig + */ +/*@{*/ +#include "webcit.h" + +/** + * \brief edit a network node + */ +void edit_node(void) { + char buf[SIZ]; + char node[SIZ]; + char cnode[SIZ]; + FILE *fp; + + if (strlen(bstr("ok_button")) > 0) { + strcpy(node, bstr("node") ); + fp = tmpfile(); + if (fp != NULL) { + serv_puts("CONF getsys|application/x-citadel-ignet-config"); + serv_getln(buf, sizeof buf); + if (buf[0] == '1') { + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + extract_token(cnode, buf, 0, '|', sizeof cnode); + if (strcasecmp(node, cnode)) { + fprintf(fp, "%s\n", buf); + } + } + fprintf(fp, "%s|%s|%s|%s\n", + bstr("node"), + bstr("secret"), + bstr("host"), + bstr("port") ); + } + rewind(fp); + + serv_puts("CONF putsys|application/x-citadel-ignet-config"); + serv_getln(buf, sizeof buf); + if (buf[0] == '4') { + while (fgets(buf, sizeof buf, fp) != NULL) { + buf[strlen(buf)-1] = 0; + serv_puts(buf); + } + serv_puts("000"); + } + fclose(fp); + } + } + + display_netconf(); +} + + +/** + * \brief add a node + */ +void display_add_node(void) +{ + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Add a new node")); + wprintf(""); + wprintf("
\n"); + wprintf("
\n
\n"); + + wprintf("
\n"); + wprintf("
\n"); + wprintf("", _("Node name")); + wprintf("\n"); + wprintf("", _("Shared secret")); + wprintf("\n"); + wprintf("", _("Host or IP address")); + wprintf("\n"); + wprintf("", _("Port number")); + wprintf("\n"); + wprintf("
%s
%s
%s
%s

"); + wprintf("", _("Add node")); + wprintf(" "); + wprintf("", _("Cancel")); + wprintf("
\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("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Edit node configuration for ")); + escputs(node); + wprintf("\n"); + wprintf("
\n"); + wprintf("
\n
\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("
\n"); + wprintf("
\n"); + wprintf(""); + wprintf("\n", cnode); + wprintf(""); + wprintf("\n", csecret); + wprintf(""); + wprintf("\n", chost); + wprintf(""); + wprintf("\n", cport); + wprintf("
"); + wprintf(_("Node name")); + wprintf("
"); + wprintf(_("Shared secret")); + wprintf("
"); + wprintf(_("Host or IP address")); + wprintf("
"); + wprintf(_("Port number")); + wprintf("

"); + wprintf("", + _("Save changes")); + wprintf(" "); + wprintf("", + _("Cancel")); + wprintf("
\n"); + } + + } + } + + else { /** command error getting configuration */ + wprintf("%s
\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("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Network configuration")); + wprintf("\n"); + wprintf("
\n"); + wprintf("
\n
\n"); + + wprintf("
"); + wprintf(""); + wprintf(_("Add a new node")); + wprintf("
\n"); + wprintf("
"); + + wprintf("
"); + wprintf(""); + wprintf(_("Currently configured nodes")); + wprintf("\n"); + wprintf("
\n"); + serv_puts("CONF getsys|application/x-citadel-ignet-config"); + serv_getln(buf, sizeof buf); + if (buf[0] == '1') { + wprintf("
\n"); + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + extract_token(node, buf, 0, '|', sizeof node); + wprintf(""); + wprintf(""); + wprintf(""); + wprintf("\n"); + } + wprintf("
"); + escputs(node); + wprintf(""); + wprintf(_("(Edit)")); + wprintf(""); + wprintf(_("(Delete)")); + wprintf("
\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("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Confirm delete")); + wprintf("\n"); + wprintf("
\n"); + wprintf("
\n
\n"); + + strcpy(node, bstr("node")); + wprintf("
"); + wprintf(_("Are you sure you want to delete ")); + wprintf(""); + escputs(node); + wprintf("?
\n"); + wprintf(""); + wprintf(_("Yes")); + wprintf("   "); + wprintf(""); + wprintf(_("No")); + wprintf("
\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(""); + wprintf(_("Back to menu")); + wprintf("\n"); + wDumpContent(1); + } else { + strcpy(WC->ImportantMessage, &buf[4]); + display_netconf(); + } + } +} + + +/*@}*/ diff --git a/webcit/src/notes.c b/webcit/src/notes.c new file mode 100644 index 000000000..137480a52 --- /dev/null +++ b/webcit/src/notes.c @@ -0,0 +1,134 @@ +/* + * $Id$ + */ +/** + * \defgroup StickyNotes Functions which handle "sticky notes" + * \ingroup WebcitDisplayItems + */ +/*@{*/ +#include "webcit.h" +#include "groupdav.h" +#include "webserver.h" + +/** + * \brief display sticky notes + * \param msgnum the citadel mesage number + */ +void display_note(long msgnum) +{ + char buf[SIZ]; + char notetext[SIZ]; + char display_notetext[SIZ]; + char eid[128]; + int in_text = 0; + int i; + + wprintf("\n"); + + serv_printf("MSG0 %ld", msgnum); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') { + wprintf("%s
\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 0) { + wprintf("%s
\n", eid, display_notetext); + } + else { + wprintf("%s
\n", msgnum, display_notetext); + } + + /** Offer in-place editing. */ + if (strlen(eid) > 0) { + wprintf("\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\n" + "
" + ""); + wprintf(_("Send instant message")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("
" + "
\n"); + + wprintf(_("Send an instant message to: ")); + escputs(recp); + wprintf("
\n"); + + wprintf("
\n"); + + wprintf("
\n"); + + wprintf("\n"); + + wprintf("\n"); + + wprintf(_("Enter message text:")); + wprintf("
"); + + wprintf("\n"); + + wprintf("

\n"); + + wprintf("", _("Send message")); + wprintf("
\n", _("Cancel")); + + wprintf("\n"); + wprintf("
\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("
\n" + "
" + ""); + wprintf(_("Add or edit an event")); + wprintf("" + "
\n" + "
\n
\n" + ); + + strcpy(recp, bstr("recp")); + strcpy(closewin, bstr("closewin")); + + if (strlen(bstr("send_button")) == 0) { + wprintf(""); + wprintf(_("Message was not sent.")); + wprintf("
\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(""); + wprintf(_("Message has been sent to ")); + escputs(recp); + wprintf(".
\n"); + } + else { + wprintf("%s
\n", &buf[4]); + } + } + + if (!strcasecmp(closewin, "yes")) { + wprintf("
"); + wprintf(_("[ close window ]")); + wprintf("
\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("\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("" + ); + } + } + + /** Then schedule it to happen again a minute from now if the user is idle. */ + wprintf(" " + ); +} + + + +/** + * \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("\n" + "\n" + "\n" + "\n" + + "\n" + ); + + if (setup_chat_socket() != 0) { + wprintf(_("An error occurred while setting up the chat socket.")); + wprintf("\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("\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("last_chat_user)) { + wprintf("" + "
" + ); + + } + + wprintf(""); + + wprintf("
"); + + if (!strcasecmp(cl_user, ":")) { + wprintf(""); + } + + if (strcasecmp(cl_user, WC->last_chat_user)) { + wprintf(""); + + if (!strcasecmp(cl_user, WC->wc_fullname)) { + wprintf(""); + } + else { + wprintf(""); + } + jsescputs(cl_user); + + wprintf(": "); + } + else { + wprintf("   "); + } + jsescputs(cl_text); + if (!strcasecmp(cl_user, ":")) { + wprintf(""); + } + + wprintf("
"); + wprintf("'); \n"); + + strcpy(WC->last_chat_user, cl_user); + } + } + + wprintf("parent.chat_transcript.scrollTo(999999,999999);\">\n"); + } + + free(output_data); + + wprintf("\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("" + "" + ); + + 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("\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("
\n"); + wprintf("\n", SIZ-10); + wprintf("
"); + wprintf("\n", _("Send")); + wprintf("\n", _("Help")); + wprintf("\n", _("List users")); + wprintf("\n", _("Exit")); + wprintf("
\n"); + + wprintf("\n"); + wDumpContent(0); +} + +/*@}*/ diff --git a/webcit/src/preferences.c b/webcit/src/preferences.c new file mode 100644 index 000000000..a21468c63 --- /dev/null +++ b/webcit/src/preferences.c @@ -0,0 +1,431 @@ +/* + * $Id$ + */ +/** + * \defgroup ManagePrefs Manage user preferences with a little help from the Citadel server. + * \ingroup CitadelConfig + * + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" +#include "groupdav.h" + + +/** + * \brief display preferences dialog + */ +void load_preferences(void) { + char buf[SIZ]; + long msgnum = 0L; + + serv_printf("GOTO %s", USERCONFIGROOM); + serv_getln(buf, sizeof buf); + if (buf[0] != '2') return; + + serv_puts("MSGS ALL|0|1"); + serv_getln(buf, sizeof buf); + if (buf[0] == '8') { + serv_puts("subj|__ WebCit Preferences __"); + serv_puts("000"); + } + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + msgnum = atol(buf); + } + + if (msgnum > 0L) { + serv_printf("MSG0 %ld", msgnum); + serv_getln(buf, sizeof buf); + if (buf[0] == '1') { + while (serv_getln(buf, sizeof buf), + (strcmp(buf, "text") && strcmp(buf, "000"))) { + } + if (!strcmp(buf, "text")) { + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + if (WC->preferences == NULL) { + WC->preferences = malloc(SIZ); + strcpy(WC->preferences, ""); + } + else { + WC->preferences = realloc( + WC->preferences, + strlen(WC->preferences) + +SIZ + ); + } + strcat(WC->preferences, buf); + strcat(WC->preferences, "\n"); + } + } + } + } + + /** Go back to the room we're supposed to be in */ + serv_printf("GOTO %s", WC->wc_roomname); + serv_getln(buf, sizeof buf); +} + +/** + * \brief Goto the user's configuration room, creating it if necessary. + * \return 0 on success or nonzero upon failure. + */ +int goto_config_room(void) { + char buf[SIZ]; + + serv_printf("GOTO %s", USERCONFIGROOM); + serv_getln(buf, sizeof buf); + if (buf[0] != '2') { /* try to create the config room if not there */ + serv_printf("CRE8 1|%s|4|0", USERCONFIGROOM); + serv_getln(buf, sizeof buf); + serv_printf("GOTO %s", USERCONFIGROOM); + serv_getln(buf, sizeof buf); + if (buf[0] != '2') return(1); + } + return(0); +} + +/** + * \brief save the modifications + */ +void save_preferences(void) { + char buf[SIZ]; + long msgnum = 0L; + + if (goto_config_room() != 0) return; /* oh well. */ + serv_puts("MSGS ALL|0|1"); + serv_getln(buf, sizeof buf); + if (buf[0] == '8') { + serv_puts("subj|__ WebCit Preferences __"); + serv_puts("000"); + } + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + msgnum = atol(buf); + } + + if (msgnum > 0L) { + serv_printf("DELE %ld", msgnum); + serv_getln(buf, sizeof buf); + } + + serv_printf("ENT0 1||0|1|__ WebCit Preferences __|"); + serv_getln(buf, sizeof buf); + if (buf[0] == '4') { + serv_puts(WC->preferences); + serv_puts(""); + serv_puts("000"); + } + + /** Go back to the room we're supposed to be in */ + serv_printf("GOTO %s", WC->wc_roomname); + serv_getln(buf, sizeof buf); +} + +/** + * \brief query the actual setting of key in the citadel database + * \param key config key to query + * \param value value to the key to get + * \param value_len length of the value string + */ +void get_preference(char *key, char *value, size_t value_len) { + int num_prefs; + int i; + char buf[SIZ]; + char thiskey[SIZ]; + + strcpy(value, ""); + + num_prefs = num_tokens(WC->preferences, '\n'); + for (i=0; ipreferences, 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; ipreferences, 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("
\n"); + wprintf("
"); + wprintf("\""); + wprintf(" "); + wprintf(_("Preferences and settings")); + wprintf(""); + offer_start_page(); + wprintf("
\n"); + wprintf("
\n" + "
\n"); + + wprintf("
" + "
\n"); + + /** begin form */ + wprintf("
\n" + "
\n" + "\n"); + + /** + * Room list view + */ + get_preference("roomlistview", buf, sizeof buf); + wprintf("\n"); + + /** + * Calendar hour format + */ + get_preference("calhourformat", calhourformat, sizeof calhourformat); + if (calhourformat[0] == 0) strcpy(calhourformat, "12"); + wprintf("\n"); + + /** + * Calendar day view -- day start time + */ + get_preference("daystart", buf, sizeof buf); + if (buf[0] == 0) strcpy(buf, "8"); + wprintf("\n"); + + /** + * Calendar day view -- day end time + */ + get_preference("dayend", buf, sizeof buf); + if (buf[0] == 0) strcpy(buf, "17"); + wprintf("\n"); + + /** + * Signature + */ + get_preference("use_sig", buf, sizeof buf); + if (buf[0] == 0) strcpy(buf, "no"); + wprintf("\n"); + + wprintf(" " + ); + + /** 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(""); + + /** submit buttons */ + wprintf("
"); + wprintf(_("Room list view")); + wprintf(""); + + wprintf(""); + wprintf(_("Tree (folders) view")); + wprintf("
\n"); + + wprintf(""); + wprintf(_("Table (rooms) view")); + wprintf("
\n"); + + wprintf("
"); + wprintf(_("Calendar hour format")); + wprintf(""); + + wprintf(""); + wprintf(_("12 hour (am/pm)")); + wprintf("
\n"); + + wprintf(""); + wprintf(_("24 hour")); + wprintf("
\n"); + + wprintf("
"); + wprintf(_("Calendar day view begins at:")); + wprintf(""); + + wprintf("\n"); + wprintf("
"); + wprintf(_("Calendar day view ends at:")); + wprintf(""); + + wprintf("\n"); + wprintf("
"); + wprintf(_("Attach signature to email messages?")); + wprintf(""); + + wprintf(" " + ); + + wprintf(""); + wprintf(_("No signature")); + wprintf("
\n"); + + wprintf(""); + wprintf(_("Use this signature:")); + wprintf("
" + "
" + "
" + ); + + wprintf("
\n"); + + wprintf("
"); + wprintf(_("Default character set for email headers:")); + wprintf(""); + wprintf("", buf); + wprintf("
\n" + "" + " " + "\n", + _("Change"), + _("Cancel") + ); + + /** end form */ + wprintf("
\n"); + wprintf("
\n"); + wDumpContent(1); +} + +/** + * \brief Commit new preferences and settings + */ +void set_preferences(void) +{ + char ebuf[300]; + + if (strlen(bstr("change_button")) == 0) { + safestrncpy(WC->ImportantMessage, + _("Cancelled. No settings were changed."), + sizeof WC->ImportantMessage); + display_main_menu(); + return; + } + + /** + * Set the last argument to 1 only for the final setting, so + * we don't send the prefs file to the server repeatedly + */ + set_preference("roomlistview", bstr("roomlistview"), 0); + set_preference("calhourformat", bstr("calhourformat"), 0); + set_preference("use_sig", bstr("use_sig"), 0); + set_preference("daystart", bstr("daystart"), 0); + set_preference("dayend", bstr("dayend"), 0); + set_preference("default_header_charset", bstr("default_header_charset"), 0); + + euid_escapize(ebuf, bstr("signature")); + set_preference("signature", ebuf, 1); + + display_main_menu(); +} + + +/*@}*/ diff --git a/webcit/src/roomops.c b/webcit/src/roomops.c new file mode 100644 index 000000000..a7993a062 --- /dev/null +++ b/webcit/src/roomops.c @@ -0,0 +1,3052 @@ +/* + * $Id$ + */ +/** + * \defgroup RoomOps Lots of different room-related operations. + * \ingroup CitadelCommunitacion + */ +/*@{*/ +#include "webcit.h" + +char floorlist[128][SIZ]; /**< list of our floor names */ + +char *viewdefs[8]; /**< the different kinds of available views */ + +/** + * \brief initialize the viewdefs with localized strings + */ +void initialize_viewdefs(void) { + viewdefs[0] = _("Bulletin Board"); + viewdefs[1] = _("Mail Folder"); + viewdefs[2] = _("Address Book"); + viewdefs[3] = _("Calendar"); + viewdefs[4] = _("Task List"); + viewdefs[5] = _("Notes List"); + viewdefs[6] = _("Wiki"); + viewdefs[7] = _("Calendar List"); +} + +/** + * \brief Determine which views are allowed as the default for creating a new room. + * + * \param which_view The view ID being queried. + */ +int is_view_allowed_as_default(int which_view) +{ + switch(which_view) { + case VIEW_BBS: return(1); + case VIEW_MAILBOX: return(1); + case VIEW_ADDRESSBOOK: return(1); + case VIEW_CALENDAR: return(1); + case VIEW_TASKS: return(1); + case VIEW_NOTES: return(1); + case VIEW_WIKI: return(0); /**< because it isn't finished yet */ + case VIEW_CALBRIEF: return(0); + default: return(0); /**< should never get here */ + } +} + + +/** + * \brief load the list of floors + */ +void load_floorlist(void) +{ + int a; + char buf[SIZ]; + + for (a = 0; a < 128; ++a) + floorlist[a][0] = 0; + + serv_puts("LFLR"); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') { + strcpy(floorlist[0], "Main Floor"); + return; + } + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + extract_token(floorlist[extract_int(buf, 0)], buf, 1, '|', sizeof floorlist[0]); + } +} + + +/** + * \brief Free a session's march list + * + * \param wcf Pointer to session being cleared + */ +void free_march_list(struct wcsession *wcf) +{ + struct march *mptr; + + while (wcf->march != NULL) { + mptr = wcf->march->next; + free(wcf->march); + wcf->march = mptr; + } + +} + + + +/** + * \brief remove a room from the march list + */ +void remove_march(char *aaa) +{ + struct march *mptr, *mptr2; + + if (WC->march == NULL) + return; + + if (!strcasecmp(WC->march->march_name, aaa)) { + mptr = WC->march->next; + free(WC->march); + WC->march = mptr; + return; + } + mptr2 = WC->march; + for (mptr = WC->march; mptr != NULL; mptr = mptr->next) { + if (!strcasecmp(mptr->march_name, aaa)) { + mptr2->next = mptr->next; + free(mptr); + mptr = mptr2; + } else { + mptr2 = mptr; + } + } +} + + + + +/** + * \brief display rooms in tree structure??? + * \param rp the roomlist to build a tree from + */ +void room_tree_list(struct roomlisting *rp) +{ + char rmname[64]; + int f; + + if (rp == NULL) { + return; + } + + room_tree_list(rp->lnext); + + strcpy(rmname, rp->rlname); + f = rp->rlflags; + + wprintf(""); + 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(" \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("

\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("wc_roomname); + wprintf("\">"); + serv_puts("CLOS"); + serv_getln(buf, sizeof buf); + } + else if (WC->wc_view == VIEW_ADDRESSBOOK) { + wprintf("" + ); + } + else if ( (WC->wc_view == VIEW_CALENDAR) || (WC->wc_view == VIEW_CALBRIEF) ) { + wprintf("" + ); + } + else if (WC->wc_view == VIEW_TASKS) { + wprintf("" + ); + } + else if (WC->wc_view == VIEW_NOTES) { + wprintf("" + ); + } + else if (WC->wc_view == VIEW_MAILBOX) { + wprintf("" + ); + } + else { + wprintf("" + ); + } + +} + + + +/** + * \brief Display the current view and offer an option to change it + */ +void embed_view_o_matic(void) { + int i; + + wprintf("
\n" + ""); + wprintf(_("View as:")); + wprintf(" " + "
\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("\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("
\n" + "\n"); + + + if (navbar_style == navbar_default) wprintf( + "\n", _("Ungoto") + ); + + if ( (navbar_style == navbar_default) && (WC->wc_view == VIEW_BBS) ) { + wprintf( + "\n", _("Read new messages") + ); + } + + if (navbar_style == navbar_default) { + switch(WC->wc_view) { + case VIEW_ADDRESSBOOK: + wprintf( + "\n", _("View contacts") + ); + break; + case VIEW_CALENDAR: + wprintf( + "\n", _("Day view") + ); + wprintf( + "\n", _("Month view") + ); + break; + case VIEW_CALBRIEF: + wprintf( + "\n", _("Calendar list") + ); + break; + case VIEW_TASKS: + wprintf( + "\n", _("View tasks") + ); + break; + case VIEW_NOTES: + wprintf( + "\n", _("View notes") + ); + break; + case VIEW_MAILBOX: + wprintf( + "\n", _("View message list") + ); + break; + case VIEW_WIKI: + wprintf( + "\n", _("Wiki home") + ); + break; + default: + wprintf( + "\n", _("Read all messages") + ); + break; + } + } + + if (navbar_style == navbar_default) { + switch(WC->wc_view) { + case VIEW_ADDRESSBOOK: + wprintf( + "\n", _("Add new contact") + ); + break; + case VIEW_CALENDAR: + case VIEW_CALBRIEF: + wprintf( + "\n", _("Add new event") + ); + break; + case VIEW_TASKS: + wprintf( + "\n", _("Add new task") + ); + break; + case VIEW_NOTES: + wprintf( + "\n", _("Add new note") + ); + break; + case VIEW_WIKI: + safestrncpy(buf, bstr("page"), sizeof buf); + str_wiki_index(buf); + wprintf( + "\n", buf, _("Edit this page") + ); + break; + default: + wprintf( + "\n", _("Enter a message") + ); + break; + } + } + + if (navbar_style == navbar_default) wprintf( + "\n", + _("Leave all messages marked as unread, go to next room with unread messages"), + _("Skip this room") + ); + + if (navbar_style == navbar_default) wprintf( + "\n", + _("Mark all messages as read, go to next room with unread messages"), + _("Goto next room") + ); + + wprintf("
" + "" + "" + "%s" + "" + "" + "" + "%s" + "" + "" + "" + "" + "%s" + "" + "" + "" + "" + "%s" + "" + "" + "" + "" + "%s" + "" + "" + "" + "" + "%s" + "" + "" + "" + "" + "%s" + "" + "" + "" + "" + "%s" + "" + "" + "" + "" + "%s" + "" + "" + "" + "" + "%s" + "" + "" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "%s" + "" + "" + "" + "%s" + "" + "" + "" + "%s" + "
\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 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("
" + "
" + "" + "" + "\n"); + + if (!strcmp(tab, "admin")) { + wprintf("\n"); + } + else { + wprintf("\n"); + } + + wprintf("\n"); + + if (!strcmp(tab, "config")) { + wprintf("\n"); + } + else { + wprintf("\n"); + } + + wprintf("\n"); + + if (!strcmp(tab, "expire")) { + wprintf("\n"); + } + else { + wprintf("\n"); + } + + wprintf("\n"); + + if (!strcmp(tab, "access")) { + wprintf("\n"); + } + else { + wprintf("\n"); + } + + wprintf("\n"); + + if (!strcmp(tab, "sharing")) { + wprintf("\n"); + } + else { + wprintf("\n"); + } + + wprintf("\n"); + + if (!strcmp(tab, "listserv")) { + wprintf("\n"); + } + else { + wprintf("\n"); + } + + wprintf("\n"); + + wprintf("
 "); + } + else { + wprintf(""); + } + wprintf(_("Administration")); + if (!strcmp(tab, "admin")) { + wprintf(" "); + } + else { + wprintf(""); + } + wprintf(_("Configuration")); + if (!strcmp(tab, "config")) { + wprintf(" "); + } + else { + wprintf(""); + } + wprintf(_("Message expire policy")); + if (!strcmp(tab, "expire")) { + wprintf(" "); + } + else { + wprintf(""); + } + wprintf(_("Access controls")); + if (!strcmp(tab, "access")) { + wprintf(" "); + } + else { + wprintf(""); + } + wprintf(_("Sharing")); + if (!strcmp(tab, "sharing")) { + wprintf(" "); + } + else { + wprintf(""); + } + wprintf(_("Mailing list service")); + if (!strcmp(tab, "listserv")) { + wprintf(" 
\n"); + /** end tabbed dialog */ + + /** begin content of whatever tab is open now */ + wprintf("
" + "\n" + "
\n"); + + if (!strcmp(tab, "admin")) { + wprintf(""); + } + + if (!strcmp(tab, "config")) { + wprintf("
\n"); + + wprintf("
  • "); + wprintf(_("Name of room: ")); + wprintf("\n", + er_name, + (sizeof(er_name)-1) + ); + + wprintf("
  • "); + wprintf(_("Resides on floor: ")); + wprintf("\n"); + + wprintf("
  • "); + wprintf(_("Type of room:")); + wprintf("
      \n"); + + wprintf("
    • "); + wprintf(_("Public room")); + wprintf("\n"); + + wprintf("
    • "); + wprintf(_("Private - guess name")); + + wprintf("\n
    • "); + wprintf(_("Private - require password:")); + wprintf("\n\n", + er_password); + + wprintf("
    • "); + wprintf(_("Private - invitation only")); + + wprintf("\n
    • "); + wprintf(_("If private, cause current users to forget room")); + + wprintf("\n
    \n"); + + wprintf("
  • "); + wprintf(_("Preferred users only")); + + wprintf("\n
  • "); + wprintf(_("Read-only room")); + + /** directory stuff */ + wprintf("\n
  • "); + wprintf(_("File directory room")); + + wprintf("\n
    • "); + wprintf(_("Directory name: ")); + wprintf("\n", + er_dirname); + + wprintf("
    • "); + wprintf(_("Uploading allowed")); + + wprintf("\n
    • "); + wprintf(_("Downloading allowed")); + + wprintf("\n
    • "); + wprintf(_("Visible directory")); + wprintf("
    \n"); + + /** end of directory stuff */ + + wprintf("
  • "); + wprintf(_("Network shared room")); + + wprintf("\n
  • "); + wprintf(_("Permanent (does not auto-purge)")); + + /** start of anon options */ + + wprintf("\n
  • "); + wprintf(_("Anonymous messages")); + wprintf("
      \n"); + + wprintf("
    • "); + wprintf(_("No anonymous messages")); + + wprintf("\n
    • "); + wprintf(_("All messages are anonymous")); + + wprintf("\n
    • "); + wprintf(_("Prompt user when entering messages")); + wprintf("
    \n"); + + /* end of anon options */ + + wprintf("
  • "); + wprintf(_("Room aide: ")); + serv_puts("GETA"); + serv_getln(buf, sizeof buf); + if (buf[0] != '2') { + wprintf("%s\n", &buf[4]); + } else { + extract_token(er_roomaide, &buf[4], 0, '|', sizeof er_roomaide); + wprintf("\n", er_roomaide); + } + + wprintf("
\n"); + wprintf("\n" + "" + " " + "" + "
\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
" + "" + "" + "\n" + "" + "
"); + wprintf(_("Shared with")); + wprintf(""); + wprintf(_("Not shared with")); + wprintf("
\n"); + + wprintf("\n"); + + for (i=0; i 0) { + wprintf("" + "\n", node); + + wprintf(""); + + wprintf("\n"); + } + } + + wprintf("
"); + wprintf(_("Remote node name")); + wprintf(""); + wprintf(_("Remote room name")); + wprintf(""); + wprintf(_("Actions")); + wprintf("
%s"); + if (strlen(remote_room) > 0) { + escputs(remote_room); + } + wprintf(""); + + wprintf(" 0) { + wprintf("|"); + urlescputs(remote_room); + } + wprintf("\">"); + wprintf("\n"); + wprintf("\n"); + wprintf("", _("Unshare")); + wprintf("
\n"); + wprintf("
\n"); + wprintf("\n"); + + for (i=0; i 0) { + wprintf("
" + "
\n"); + } + } + + wprintf("
"); + wprintf(_("Remote node name")); + wprintf(""); + wprintf(_("Remote room name")); + wprintf(""); + wprintf(_("Actions")); + wprintf("
"); + escputs(node); + wprintf("" + "" + ""); + wprintf(""); + wprintf("\n"); + wprintf("\n"); + wprintf("", _("Share")); + wprintf("
\n"); + wprintf("

\n" + "%s
  • ", _("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. " + "
  • If the remote room name is blank, it is assumed " + "that the room name is identical on the remote node." + "
  • If the remote room name is different, the remote " + "node must also configure the name of the room here." + "

\n" + )); + + } + + /** Mailing list management */ + if (!strcmp(tab, "listserv")) { + + wprintf("
" + "" + "
"); + + wprintf(_("The contents of this room are being " + "mailed as individual messages " + "to the following list recipients:" + "

\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(" "); + wprintf(_("(remove)")); + wprintf("
"); + } + } + wprintf("
\n" + "\n" + "\n"); + wprintf("\n"); + wprintf("", _("Add")); + wprintf("
\n"); + + wprintf("
\n"); + + wprintf(_("The contents of this room are being " + "mailed in digest form " + "to the following list recipients:" + "

\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(" "); + wprintf(_("(remove)")); + wprintf("
"); + } + } + wprintf("
\n" + "\n" + "\n"); + wprintf("\n"); + wprintf("", _("Add")); + wprintf("
\n"); + + wprintf("

\n"); + + if (self_service(999) == 1) { + wprintf(_("This room is configured to allow " + "self-service subscribe/unsubscribe requests.")); + wprintf(""); + wprintf(_("Click to disable.")); + wprintf("
\n"); + wprintf(_("The URL for subscribe/unsubscribe is: ")); + wprintf("%s://%s/listsub
\n", + (is_https ? "https" : "http"), + WC->http_host); + } + else { + wprintf(_("This room is not configured to allow " + "self-service subscribe/unsubscribe requests.")); + wprintf(" "); + wprintf(_("Click to enable.")); + wprintf("
\n"); + } + + + wprintf("
\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("
\n"); + wprintf("\n"); + wprintf("\n"); + + if (WC->axlevel >= 6) { + wprintf("\n"); + wprintf("\n"); + + wprintf("
"); + wprintf(_("Message expire policy for this room")); + wprintf("
("); + escputs(WC->wc_roomname); + wprintf(")
"); + wprintf("", + ((roompolicy == 0) ? "CHECKED" : "") ); + wprintf(_("Use the default policy for this floor")); + wprintf("
\n"); + wprintf("", + ((roompolicy == 1) ? "CHECKED" : "") ); + wprintf(_("Never automatically expire messages")); + wprintf("
\n"); + wprintf("", + ((roompolicy == 2) ? "CHECKED" : "") ); + wprintf(_("Expire by message count")); + wprintf("
\n"); + wprintf("", + ((roompolicy == 3) ? "CHECKED" : "") ); + wprintf(_("Expire by message age")); + wprintf("
"); + wprintf(_("Number of messages or days: ")); + wprintf("", roomvalue); + wprintf("

"); + wprintf(_("Message expire policy for this floor")); + wprintf("
("); + escputs(floorlist[WC->wc_floor]); + wprintf(")
"); + wprintf("", + ((floorpolicy == 0) ? "CHECKED" : "") ); + wprintf(_("Use the system default")); + wprintf("
\n"); + wprintf("", + ((floorpolicy == 1) ? "CHECKED" : "") ); + wprintf(_("Never automatically expire messages")); + wprintf("
\n"); + wprintf("", + ((floorpolicy == 2) ? "CHECKED" : "") ); + wprintf(_("Expire by message count")); + wprintf("
\n"); + wprintf("", + ((floorpolicy == 3) ? "CHECKED" : "") ); + wprintf(_("Expire by message age")); + wprintf("
"); + wprintf(_("Number of messages or days: ")); + wprintf("", + floorvalue); + } + + wprintf("
\n"); + wprintf("

\n"); + wprintf("", _("Save changes")); + wprintf(" "); + wprintf("", _("Cancel")); + wprintf("
\n" + "\n" + "
\n" + ); + + } + + /** Mailing list management */ + if (!strcmp(tab, "access")) { + display_whok(); + } + + /** end content of whatever tab is open now */ + wprintf("
\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, + _("User %s kicked out of room %s.\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, + _("User %s invited to room %s.\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("
"); + 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("

"); + + wprintf("
\n"); + wprintf("\n"); + wprintf("
\n"); + + wprintf("", _("Kick")); + wprintf("
\n"); + + wprintf("
"); + wprintf(_("To grant another user access to this room, enter the " + "user name in the box below and click 'Invite'.")); + wprintf("

"); + + wprintf("
\n"); + wprintf("\n"); + wprintf(_("Invite:")); + wprintf(" "); + wprintf("
\n" + "" + "" + "
\n", _("Invite")); + + wprintf("
\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("
\n" + "
" + ""); + wprintf(_("Create a new room")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("
" + "
\n"); + + wprintf("
\n"); + + wprintf("
  • "); + wprintf(_("Name of room: ")); + wprintf("\n"); + + wprintf("
  • "); + wprintf(_("Resides on floor: ")); + load_floorlist(); + wprintf("\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("
  • "); + wprintf(_("Default view for room: ")); + wprintf("\n"); + + wprintf("
  • "); + wprintf(_("Type of room:")); + wprintf("
      \n"); + + wprintf("
    • "); + wprintf(_("Public (automatically appears to everyone)")); + + wprintf("\n
    • "); + wprintf(_("Private - hidden (accessible to anyone who knows its name)")); + + wprintf("\n
    • "); + wprintf(_("Private - require password: ")); + wprintf("\n"); + + wprintf("
    • "); + wprintf(_("Private - invitation only")); + + wprintf("\n
    • "); + wprintf(_("Personal (mailbox for you only)")); + + wprintf("\n
    \n"); + + wprintf("
    \n"); + wprintf("", _("Create new room")); + wprintf(" "); + wprintf("", _("Cancel")); + wprintf("
    \n"); + wprintf("
  • \n
    "); + serv_printf("MESG roomaccess"); + serv_getln(buf, sizeof buf); + if (buf[0] == '1') { + fmout("CENTER"); + } + wprintf("
\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("
\n" + "
" + ""); + wprintf(_("Go to a hidden room")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("
" + "
\n"); + + wprintf("
\n"); + wprintf("
"); + 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

"); + + wprintf("
\n"); + + wprintf("\n" + "
"); + wprintf(_("Enter room name:")); + wprintf("" + "\n", rname); + + if (req_pass) { + wprintf("
"); + wprintf(_("Enter room password:")); + wprintf(""); + wprintf("\n"); + } + wprintf("

\n"); + + wprintf("" + " " + "", + _("Go there"), + _("Cancel") + ); + wprintf("
\n"); + wprintf("
\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("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Zap (forget/unsubscribe) the current room")); + wprintf("\n"); + wprintf("
\n"); + wprintf("
\n
\n"); + + wprintf(_("If you select this option, %s will " + "disappear from your room list. Is this what you wish " + "to do?
\n"), WC->wc_roomname); + + wprintf("
\n"); + wprintf("", _("Zap this room")); + wprintf(" "); + wprintf("", _("Cancel")); + wprintf("
\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; iwc_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("
Loading folder list...
\n"); + + /** include NanoTree */ + wprintf("\n"); + + /** initialize NanoTree */ + wprintf("\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("" + "
"); + + levels = 0; + oldlevels = 0; + for (i=0; i 0) ) { + /* End inner box */ + do_template("endbox"); + + ++num_boxes; + if ((num_boxes % boxes_per_column) == 0) { + ++current_column; + if (current_column < columns) { + wprintf("\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(""); + } + else { + wprintf(""); + } + if (fold[i].hasnewmsgs) { + wprintf(""); + } + else { + wprintf(""); + } + extract_token(buf, fold[i].name, levels-1, '|', sizeof buf); + escputs(buf); + wprintf(""); + if (fold[i].selectable) { + wprintf(""); + } + else { + wprintf(""); + } + if (!strcasecmp(fold[i].name, "My Folders|Mail")) { + wprintf(" (INBOX)"); + } + wprintf("
\n"); + } + } + /** End the final inner box */ + do_template("endbox"); + + wprintf("
\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 0) ) { + /** End inner box */ + wprintf("
\n"); + wprintf("
\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("" + "%s
\n", floordiv_id, floordivtitle); + wprintf("
", + floordiv_id, + (!strcasecmp(floordiv_id, WC->floordiv_expanded) ? "block" : "none") + ); + } + + oldlevels = levels; + + if (levels > 1) { + wprintf("
", 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(""); + wprintf("\"\" ", icon); + } + else { + wprintf(""); + } + if (fold[i].hasnewmsgs) { + wprintf(""); + } + else { + wprintf(""); + } + extract_token(buf, fold[i].name, levels-1, '|', sizeof buf); + escputs(buf); + if (!strcasecmp(fold[i].name, "My Folders|Mail")) { + wprintf(" (INBOX)"); + } + wprintf(""); + if (fold[i].selectable) { + wprintf(""); + } + else { + wprintf(""); + } + wprintf("
"); + wprintf("
\n"); /** roomdiv */ + } + } + wprintf("
\n"); /** floordiv */ + + + /** BEGIN: The old invisible pixel trick, to get our JavaScript to initialize */ + wprintf(" 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 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\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("
\n" + "\n"); + + /** offer the ability to switch views */ + wprintf("
" + "" + ); + if (!strcasecmp(listviewpref, "rooms")) { + wprintf(_("Room list")); + } + if (!strcasecmp(listviewpref, "folders")) { + wprintf(_("Folder list")); + } + if (!strcasecmp(listviewpref, "table")) { + wprintf(_("Room list")); + } + wprintf("
\n" + "
"); + offer_start_page(); + wprintf("
\n"); + wprintf("
\n" + "
\n" + "
\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, "
\n"); + serv_printf("SPEX floor|%d|%d", atoi(bstr("floorpolicy")), atoi(bstr("floorvalue"))); + serv_getln(buf, sizeof buf); + strcat(WC->ImportantMessage, &buf[4]); + } + + display_editroom(); +} + + +/*@}*/ diff --git a/webcit/src/rss.c b/webcit/src/rss.c new file mode 100644 index 000000000..6a962559a --- /dev/null +++ b/webcit/src/rss.c @@ -0,0 +1,349 @@ +/* + * $Id$ + */ +/** + * \defgroup RssRooms Generate some RSS for our rooms. + * \ingroup WebcitHttpServerRSS + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" + + +time_t if_modified_since; /**< the last modified stamp */ + +/** + * \brief view rss Config menu + * \param reply_to the original author + * \param subject the subject of the feed + */ +void display_rss_control(char *reply_to, char *subject) +{ + wprintf("

\n"); + wprintf("[%s] \n", _("Reply")); + wprintf("[%s]\n", _("Email")); + wprintf("

\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("\n"); + wprintf("\n"); + wprintf(" \n"); + wprintf(" %s - %s\n", WC->wc_roomname, serv_info.serv_humannode); + wprintf(" %s://%s:%d/dotgoto?room=", (is_https ? "https" : "http"), WC->http_host, PORT_NUM); + escputs(roomname); + wprintf("\n"); + wprintf(" "); + /** 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("\n"); + if (now) { + wprintf(" %s\n", date); + } + wprintf(" %s\n", SERVER); + wprintf(" http://blogs.law.harvard.edu/tech/rss\n"); + wprintf(" 30\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(" \n"); + if (subj[0]) { + wprintf(" %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("\n"); + if (now) { + wprintf(" %s\n", date); + } + wprintf(" %s\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(" "); + wprintf("\n"); + break; + } + if (intext == 1 && isspace(buf[0])) { + wprintf("
"); + } + intext = 1; + if (bq == 0 && !strncmp(buf, " >", 2)) { + wprintf("
"); + bq = 1; + } else if (bq == 1 && strncmp(buf, " >", 2)) { + wprintf("
"); + bq = 0; + } + url(buf); + escputs(buf); + wprintf("\n"); + } + display_rss_control(from, subj); + wprintf("]]>
\n"); + } + /** Boring old 80-column fixed format text gets handled this way... */ + else if (!strcasecmp(content_type, "text/plain")) { + wprintf(" 0) && (isspace(buf[strlen(buf) - 1]))) + buf[strlen(buf) - 1] = 0; + if ((bq == 0) && + ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) { + wprintf("
"); + bq = 1; + } else if ((bq == 1) && + (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) { + wprintf("
"); + bq = 0; + } + wprintf(""); + url(buf); + escputs(buf); + wprintf("
\n"); + } + display_rss_control(from, subj); + wprintf("]]>
\n"); + } + /** HTML is fun, but we've got to strip it first */ + else if (!strcasecmp(content_type, "text/html")) { + wprintf(" \n"); + } + +ENDBODY: + wprintf("
\n"); +ENDITEM: + now = 0L; + } + + /** Do RSS footer */ + wprintf("
\n"); + wprintf("
\n"); + wDumpContent(0); +} + + +/*@}*/ diff --git a/webcit/src/serv_func.c b/webcit/src/serv_func.c new file mode 100644 index 000000000..05574c266 --- /dev/null +++ b/webcit/src/serv_func.c @@ -0,0 +1,401 @@ +/* + * $Id$ + */ +/** + * \defgroup ServFuncs Handles various types of data transfer operations with the Citadel service. + * \ingroup CitadelCommunitacion + */ + +/*@{*/ +#include "webcit.h" +#include "webserver.h" + +struct serv_info serv_info; /**< our connection data to the server */ + +/** + * \brief get info about the server we've connected to + * \param browser_host the citadell we want to connect to + * \param user_agent which browser uses our client? + */ +void get_serv_info(char *browser_host, char *user_agent) +{ + char buf[SIZ]; + int a; + + /** Tell the server what kind of client is connecting */ + serv_printf("IDEN %d|%d|%d|%s|%s", + DEVELOPER_ID, + CLIENT_ID, + CLIENT_VERSION, + user_agent, + browser_host + ); + serv_getln(buf, sizeof buf); + + /** Tell the server what kind of richtext we prefer */ + serv_puts("MSGP text/html|text/plain"); + serv_getln(buf, sizeof buf); + +#ifdef WEBCIT_WITH_CALENDAR_SERVICE + /** + * Tell the server that when we save a calendar event, we + * want invitations to be generated by the Citadel server + * instead of by the client. + */ + serv_puts("ICAL sgi|1"); + serv_getln(buf, sizeof buf); +#endif + + /** Now ask the server to tell us a little bit about itself... */ + serv_puts("INFO"); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') + return; + + a = 0; + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + switch (a) { + case 0: + serv_info.serv_pid = atoi(buf); + WC->ctdl_pid = serv_info.serv_pid; + break; + case 1: + safestrncpy(serv_info.serv_nodename, buf, sizeof serv_info.serv_nodename); + break; + case 2: + safestrncpy(serv_info.serv_humannode, buf, sizeof serv_info.serv_humannode); + break; + case 3: + safestrncpy(serv_info.serv_fqdn, buf, sizeof serv_info.serv_fqdn); + break; + case 4: + safestrncpy(serv_info.serv_software, buf, sizeof serv_info.serv_software); + break; + case 5: + serv_info.serv_rev_level = atoi(buf); + break; + case 6: + safestrncpy(serv_info.serv_bbs_city, buf, sizeof serv_info.serv_bbs_city); + break; + case 7: + safestrncpy(serv_info.serv_sysadm, buf, sizeof serv_info.serv_sysadm); + break; + case 9: + safestrncpy(serv_info.serv_moreprompt, buf, sizeof serv_info.serv_moreprompt); + break; + case 14: + serv_info.serv_supports_ldap = atoi(buf); + break; + case 15: + serv_info.serv_newuser_disabled = atoi(buf); + break; + } + ++a; + } +} + + + +/** + * \brief Read Citadel variformat text and spit it out as HTML. + * \param align html align string + */ +void fmout(char *align) +{ + int intext = 0; + int bq = 0; + char buf[SIZ]; + + wprintf("
\n", align); + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + + if ((intext == 1) && (isspace(buf[0]))) { + wprintf("
"); + } + intext = 1; + + /** + * Quoted text should be displayed in italics and in a + * different colour. This code understands Citadel-style + * " >" quotes and will convert to
tags. + */ + if ((bq == 0) && (!strncmp(buf, " >", 2))) { + wprintf("
"); + bq = 1; + } else if ((bq == 1) && (strncmp(buf, " >", 2))) { + wprintf("
"); + 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(""); + } + wprintf("

\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("
"); + } + intext = 1; + + /** + * Quoted text should be displayed in italics and in a + * different colour. This code understands Citadel-style + * " >" quotes and will convert to
tags. + */ + if ((bq == 0) && (!strncmp(buf, " >", 2))) { + wprintf("
"); + bq = 1; + } else if ((bq == 1) && (strncmp(buf, " >", 2))) { + wprintf("
"); + bq = 0; + } + if ((bq == 1) && (!strncmp(buf, " >", 2))) { + strcpy(buf, &buf[2]); + } + + msgescputs(buf); + } + if (bq == 1) { + wprintf(""); + } +} + + + + +/** + * \brief Transmit message text (in memory) to the server. + * + * \param ptr Pointer to the message being transmitted + */ +void text_to_server(char *ptr) +{ + char buf[256]; + int ch, a, pos; + + pos = 0; + buf[0] = 0; + + while (ptr[pos] != 0) { + ch = ptr[pos++]; + if (ch == 10) { + while ( (isspace(buf[strlen(buf) - 1])) + && (strlen(buf) > 1) ) + buf[strlen(buf) - 1] = 0; + serv_puts(buf); + buf[0] = 0; + if (ptr[pos] != 0) strcat(buf, " "); + } else { + a = strlen(buf); + buf[a + 1] = 0; + buf[a] = ch; + if ((ch == 32) && (strlen(buf) > 200)) { + buf[a] = 0; + serv_puts(buf); + buf[0] = 0; + } + if (strlen(buf) > 250) { + serv_puts(buf); + buf[0] = 0; + } + } + } + serv_puts(buf); +} + + +/** + * \brief Transmit message text (in memory) to the server, + * converting to Quoted-Printable encoding as we go. + * + * \param ptr Pointer to the message being transmitted + */ +void text_to_server_qp(char *ptr) +{ + char buf[256]; + int ch, pos; + int output_len = 0; + + pos = 0; + buf[0] = 0; + output_len = 0; + + while (ptr[pos] != 0) { + ch = ptr[pos++]; + + if (ch == 13) { + /* ignore carriage returns */ + } + else if (ch == 10) { + /* hard line break */ + if (output_len > 0) { + if (isspace(buf[output_len-1])) { + sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]); + output_len += 2; + } + } + buf[output_len++] = 0; + serv_puts(buf); + output_len = 0; + } + else if (ch == 9) { + buf[output_len++] = ch; + } + else if ( (ch >= 32) && (ch <= 60) ) { + buf[output_len++] = ch; + } + else if ( (ch >= 62) && (ch <= 126) ) { + buf[output_len++] = ch; + } + else { + sprintf(&buf[output_len], "=%02X", ch); + output_len += 3; + } + + if (output_len > 72) { + /* soft line break */ + if (isspace(buf[output_len-1])) { + sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]); + output_len += 2; + } + buf[output_len++] = '='; + buf[output_len++] = 0; + serv_puts(buf); + output_len = 0; + } + } + + /* end of data - transmit anything that's left */ + if (output_len > 0) { + if (isspace(buf[output_len-1])) { + sprintf(&buf[output_len-1], "=%02X", buf[output_len-1]); + output_len += 2; + } + buf[output_len++] = 0; + serv_puts(buf); + output_len = 0; + } +} + + + + +/** + * \brief translate server message output to text + * (used for editing room info files and such) + */ +void server_to_text() +{ + char buf[SIZ]; + + int count = 0; + + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + if ((buf[0] == 32) && (count > 0)) { + wprintf("\n"); + } + wprintf("%s", buf); + ++count; + } +} + + + +/** + * Read binary data from server into memory using a series of + * server READ commands. + * \param buffer the output buffer + * \param total_len the maximal length of buffer + */ +void read_server_binary(char *buffer, size_t total_len) { + char buf[SIZ]; + size_t bytes = 0; + size_t thisblock = 0; + + memset(buffer, 0, total_len); + while (bytes < total_len) { + thisblock = 4095; + if ((total_len - bytes) < thisblock) { + thisblock = total_len - bytes; + if (thisblock == 0) return; + } + serv_printf("READ %d|%d", (int)bytes, (int)thisblock); + serv_getln(buf, sizeof buf); + if (buf[0] == '6') { + thisblock = (size_t)atoi(&buf[4]); + if (!WC->connected) return; + serv_read(&buffer[bytes], thisblock); + bytes += thisblock; + } + else { + lprintf(3, "Error: %s\n", &buf[4]); + return; + } + } +} + + +/** + * \brief Read text from server, appending to a string buffer until the + * usual 000 terminator is found. Caller is responsible for freeing + * the returned pointer. + */ +char *read_server_text(void) { + char *text = NULL; + size_t bytes_allocated = 0; + size_t bytes_read = 0; + int linelen; + char buf[SIZ]; + + text = malloc(SIZ); + if (text == NULL) { + return(NULL); + } + text[0] = 0; + bytes_allocated = SIZ; + + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + linelen = strlen(buf); + buf[linelen] = '\n'; + buf[linelen+1] = 0; + ++linelen; + + if ((bytes_read + linelen) >= (bytes_allocated - 2)) { + bytes_allocated = 2 * bytes_allocated; + text = realloc(text, bytes_allocated); + } + + strcpy(&text[bytes_read], buf); + bytes_read += linelen; + } + + return(text); +} + + + +/*@}*/ diff --git a/webcit/src/setup.c b/webcit/src/setup.c new file mode 100644 index 000000000..f17e7ac01 --- /dev/null +++ b/webcit/src/setup.c @@ -0,0 +1,683 @@ +/* + * $Id$ + * + * WebCit setup utility + * + * (This is basically just an install wizard. It's not required.) + * + */ + + +#include "webcit.h" +#include "webserver.h" + + +#ifdef HAVE_NEWT +#include +#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%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 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://:/"); + + /* set an invalid setup type */ + setup_type = (-1); + + /* Check to see if we're running the web installer */ + if (getenv("CITADEL_INSTALLER") != NULL) { + using_web_installer = 1; + } + + /* parse command line args */ + for (a = 0; a < argc; ++a) { + if (!strncmp(argv[a], "-u", 2)) { + strcpy(aaa, argv[a]); + strcpy(aaa, &aaa[2]); + setup_type = atoi(aaa); + } + if (!strcmp(argv[a], "-i")) { + info_only = 1; + } + if (!strcmp(argv[a], "-q")) { + setup_type = UI_SILENT; + } + } + + + /* If a setup type was not specified, try to determine automatically + * the best one to use out of all available types. + */ + if (setup_type < 0) { + setup_type = discover_ui(); + } + if (info_only == 1) { + important_message("WebCit Setup", "Welcome to WebCit setup"); + cleanup(0); + } + + /* If we're on something BSDish then we don't have inittab */ + if (access("/etc/inittab", F_OK)) { + important_message("Not running SysV style init", + "WebCit Setup can only run on systems that use /etc/inittab.\n" + "Please manually configure your startup scripts to run WebCit\n" + "when the system is booted.\n"); + cleanup(0); + } + + /* Get started in a valid setup directory. */ + strcpy(setup_directory, WEBCITDIR); + if ( (using_web_installer) && (getenv("WEBCIT") != NULL) ) { + strcpy(setup_directory, getenv("WEBCIT")); + } + else { + set_value("In what directory is WebCit installed?", + setup_directory); + } + if (chdir(setup_directory) != 0) { + important_message("WebCit Setup", + "The directory you specified does not exist."); + cleanup(errno); + } + + /* See if we need to shut down the WebCit service. */ + for (a=0; a<=3; ++a) { + progress("Shutting down the WebCit service...", a, 3); + if (a == 0) shutdown_service(); + sleep(1); + } + + /* Now begin. */ + switch (setup_type) { + + case UI_TEXT: + printf("\n\n\n" + " *** WebCit setup program ***\n\n"); + break; + + } + + check_inittab_entry(); /* Check /etc/inittab */ + + /* See if we can start the WebCit service. */ + if (strlen(init_entry) > 0) { + for (a=0; a<=3; ++a) { + progress("Starting the WebCit service...", a, 3); + if (a == 0) start_the_service(); + sleep(1); + } + sprintf(aaa, + "Setup is finished. You may now log in.\n" + "Point your web browser at %s\n", suggested_url); + important_message("Setup finished", aaa); + } + else { + important_message("Setup finished", + "Setup is finished. You may now start the server."); + } + + cleanup(0); + return 0; +} diff --git a/webcit/src/setup_wizard.c b/webcit/src/setup_wizard.c new file mode 100644 index 000000000..bc2b2277a --- /dev/null +++ b/webcit/src/setup_wizard.c @@ -0,0 +1,56 @@ +/* + * $Id$ + * + * First-time setup wizard + */ + +#include "webcit.h" + + +/* + */ +void do_setup_wizard(void) +{ + char *step; + FILE *fp; + + step = bstr("step"); + + if (!strcasecmp(step, "Finish")) { + fp = fopen(wizard_filename, "w"); + if (fp != NULL) { + fprintf(fp, "%d\n", serv_info.serv_rev_level); + fclose(fp); + } + do_welcome(); + return; + } + + output_headers(1, 1, 2, 0, 0, 0); + + wprintf("
\n"); + wprintf("
"); + wprintf("\""); + wprintf(" First time setup"); + wprintf(""); + wprintf("
\n"); + wprintf("
\n" + "
\n"); + + wprintf("
" + "
\n" + ); + + wprintf("
" + "This is where the setup wizard will be placed.
\n" + "For now, just click Finish.

\n" + ); + + wprintf("\n"); + wprintf("\n"); + + wprintf("
\n"); + wDumpContent(1); +} + + diff --git a/webcit/src/siteconfig.c b/webcit/src/siteconfig.c new file mode 100644 index 000000000..17a7c3cd4 --- /dev/null +++ b/webcit/src/siteconfig.c @@ -0,0 +1,650 @@ +/* + * $Id$ + */ +/** + * \defgroup AdminConfig Administrative screen for site-wide configuration + * \ingroup CitadelConfig + */ +/*@{*/ + +#include "webcit.h" + +/** + * \brief display all configuration items + */ +void display_siteconfig(void) +{ + char buf[SIZ]; + int i, j; + + char general[SIZ]; + char access[SIZ]; + char network[SIZ]; + char tuning[SIZ]; + char directory[SIZ]; + char purger[SIZ]; + char idxjnl[SIZ]; + + /** expire policy settings */ + int sitepolicy = 0; + int sitevalue = 0; + int mboxpolicy = 0; + int mboxvalue = 0; + + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n" + "
" + ""); + wprintf(_("Site configuration")); + wprintf("" + "
\n" + "
\n
\n" + ); + + serv_printf("CONF get"); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') { + wprintf("
"); + wprintf(""); + wprintf(_("Error")); + wprintf("\n"); + wprintf("

\n"); + wprintf("%s
\n", &buf[4]); + wDumpContent(1); + return; + } + + wprintf("
" + "
"); + + char *tabnames[] = { + _("General"), + _("Access"), + _("Network"), + _("Tuning"), + _("Directory"), + _("Auto-purger"), + _("Indexing/Journaling") + }; + + sprintf(general, "

%s

", + _("General site configuration items") + ); + + sprintf(access, "

%s

", + _("Access controls and site policy settings") + ); + + sprintf(network, "

%s

%s

", + _("Network services"), + _("Changes made on this screen will not take effect " + "until you restart the Citadel server.") + ); + + sprintf(tuning, "

%s

", + _("Advanced server fine-tuning controls") + ); + + sprintf(directory, "

%s

%s

", + _("Configure the LDAP connector for Citadel"), + _("Changes made on this screen will not take effect " + "until you restart the Citadel server.") + ); + + sprintf(purger, "

%s

%s

", + _("Configure automatic expiry of old messages"), + _("These settings may be overridden on a per-floor or per-room basis.") + ); + + sprintf(idxjnl, "

%s

%s

", + _("Indexing and Journaling"), + _("Warning: these facilities are resource intensive.") + ); + + + wprintf("\n"); + + i = 0; + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + switch (i++) { + case 0: + sprintf(&general[strlen(general)], "\n"); + break; + case 1: + sprintf(&general[strlen(general)], "\n"); + break; + case 2: + sprintf(&general[strlen(general)], "\n"); + break; + case 3: + sprintf(&general[strlen(general)], "\n"); + break; + case 4: + sprintf(&access[strlen(access)], "\n"); + break; + case 5: + sprintf(&tuning[strlen(tuning)], "\n"); + break; + case 6: + sprintf(&access[strlen(access)], "\n"); + break; + case 7: + sprintf(&access[strlen(access)], "\n"); + break; + case 8: + sprintf(&access[strlen(access)], "\n"); + break; + case 9: + sprintf(&access[strlen(access)], "\n"); + break; + case 10: + sprintf(&general[strlen(general)], "\n"); + break; + case 11: + sprintf(&access[strlen(access)], "\n"); + break; + case 12: + sprintf(&general[strlen(general)], "\n"); + break; + case 13: + sprintf(&general[strlen(general)], "\n"); + break; + case 14: + sprintf(&tuning[strlen(tuning)], "\n"); + break; + case 16: + sprintf(&tuning[strlen(tuning)], "\n"); + break; + case 17: + sprintf(&tuning[strlen(tuning)], "\n"); + break; + case 18: + sprintf(&access[strlen(access)], "\n"); + break; + case 19: + sprintf(&access[strlen(access)], "\n"); + break; + case 20: + sprintf(&tuning[strlen(tuning)], "\n"); + break; + case 21: + sprintf(&tuning[strlen(tuning)], "\n"); + break; + case 22: + sprintf(&tuning[strlen(tuning)], "\n"); + break; + case 23: + sprintf(&network[strlen(network)], "\n"); + break; + case 24: + sprintf(&network[strlen(network)], "\n"); + break; + case 25: /* note: reverse bool */ + sprintf(&network[strlen(network)], "\n"); + break; + case 26: + sprintf(&access[strlen(access)], "\n"); + break; + case 27: + sprintf(&network[strlen(network)], "\n"); + break; + case 28: + sprintf(&network[strlen(network)], "\n"); + break; + case 29: + sprintf(&access[strlen(access)], "\n"); + break; + case 31: + sprintf(&purger[strlen(purger)], "\n"); + break; + case 32: + sprintf(&directory[strlen(directory)], "\n"); + break; + case 33: + sprintf(&directory[strlen(directory)], "\n"); + break; + case 34: + sprintf(&directory[strlen(directory)], "\n"); + break; + case 35: + sprintf(&directory[strlen(directory)], "\n"); + break; + case 36: + sprintf(&directory[strlen(directory)], "\n"); + break; + case 37: + sprintf(&network[strlen(network)], "\n"); + break; + case 38: + sprintf(&network[strlen(network)], "\n"); + break; + case 39: + sprintf(&network[strlen(network)], "\n"); + break; + case 40: + sprintf(&network[strlen(network)], "\n"); + break; + case 41: + sprintf(&network[strlen(network)], "\n"); + break; + case 42: + sprintf(&idxjnl[strlen(idxjnl)], "\n"); + break; + case 43: + sprintf(&tuning[strlen(tuning)], "\n"); + break; + case 44: + sprintf(&network[strlen(network)], "\n"); + break; + case 45: + sprintf(&network[strlen(network)], "\n"); + break; + case 46: + sprintf(&idxjnl[strlen(idxjnl)], "\n"); + break; + case 47: + sprintf(&idxjnl[strlen(idxjnl)], "\n"); + break; + case 48: + sprintf(&idxjnl[strlen(idxjnl)], "\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)], "\n"); + + sprintf(&purger[strlen(purger)], "\n"); + + sprintf(&purger[strlen(purger)], "\n"); + + sprintf(&purger[strlen(purger)], "\n"); + + sprintf(&purger[strlen(purger)], "\n"); + + + sprintf(&general[strlen(general)], "
"); + sprintf(&general[strlen(general)], _("Node name")); + sprintf(&general[strlen(general)], ""); + sprintf(&general[strlen(general)], "", buf); + sprintf(&general[strlen(general)], "
"); + sprintf(&general[strlen(general)], _("Fully qualified domain name")); + sprintf(&general[strlen(general)], ""); + sprintf(&general[strlen(general)], "", buf); + sprintf(&general[strlen(general)], "
"); + sprintf(&general[strlen(general)], _("Human-readable node name")); + sprintf(&general[strlen(general)], ""); + sprintf(&general[strlen(general)], "", buf); + sprintf(&general[strlen(general)], "
"); + sprintf(&general[strlen(general)], _("Telephone number")); + sprintf(&general[strlen(general)], ""); + sprintf(&general[strlen(general)], "", buf); + sprintf(&general[strlen(general)], "
"); + sprintf(&access[strlen(access)], _("Automatically grant room-aide status to users who create private rooms")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "", + ((atoi(buf) != 0) ? "checked" : "")); + sprintf(&access[strlen(access)], "
"); + sprintf(&tuning[strlen(tuning)], _("Server connection idle timeout (in seconds)")); + sprintf(&tuning[strlen(tuning)], ""); + sprintf(&tuning[strlen(tuning)], "", buf); + sprintf(&tuning[strlen(tuning)], "
"); + sprintf(&access[strlen(access)], _("Initial access level for new users")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "
"); + sprintf(&access[strlen(access)], _("Require registration for new users")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "", + ((atoi(buf) != 0) ? "checked" : "")); + sprintf(&access[strlen(access)], "
"); + sprintf(&access[strlen(access)], _("Quarantine messages from problem users")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "", + ((atoi(buf) != 0) ? "checked" : "")); + sprintf(&access[strlen(access)], "
"); + sprintf(&access[strlen(access)], _("Name of quarantine room")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "", buf); + sprintf(&access[strlen(access)], "
"); + sprintf(&general[strlen(general)], _("Paginator prompt (for text mode clients)")); + sprintf(&general[strlen(general)], ""); + sprintf(&general[strlen(general)], "", buf); + sprintf(&general[strlen(general)], "
"); + sprintf(&access[strlen(access)], _("Restrict access to Internet mail")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "", + ((atoi(buf) != 0) ? "checked" : "")); + sprintf(&access[strlen(access)], "
"); + sprintf(&general[strlen(general)], _("Geographic location of this system")); + sprintf(&general[strlen(general)], ""); + sprintf(&general[strlen(general)], "", buf); + sprintf(&general[strlen(general)], "
"); + sprintf(&general[strlen(general)], _("Name of system administrator")); + sprintf(&general[strlen(general)], ""); + sprintf(&general[strlen(general)], "", buf); + sprintf(&general[strlen(general)], "
"); + sprintf(&tuning[strlen(tuning)], _("Maximum concurrent sessions (0 = no limit)")); + sprintf(&tuning[strlen(tuning)], ""); + sprintf(&tuning[strlen(tuning)], "", buf); + sprintf(&tuning[strlen(tuning)], "
"); + sprintf(&tuning[strlen(tuning)], _("Default user purge time (days)")); + sprintf(&tuning[strlen(tuning)], ""); + sprintf(&tuning[strlen(tuning)], "", buf); + sprintf(&tuning[strlen(tuning)], "
"); + sprintf(&tuning[strlen(tuning)], _("Default room purge time (days)")); + sprintf(&tuning[strlen(tuning)], ""); + sprintf(&tuning[strlen(tuning)], "", buf); + sprintf(&tuning[strlen(tuning)], "
"); + sprintf(&access[strlen(access)], _("Name of room to log pages")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "", buf); + sprintf(&access[strlen(access)], "
"); + sprintf(&access[strlen(access)], _("Access level required to create rooms")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "
"); + sprintf(&tuning[strlen(tuning)], _("Maximum message length")); + sprintf(&tuning[strlen(tuning)], ""); + sprintf(&tuning[strlen(tuning)], "", buf); + sprintf(&tuning[strlen(tuning)], "
"); + sprintf(&tuning[strlen(tuning)], _("Minimum number of worker threads")); + sprintf(&tuning[strlen(tuning)], ""); + sprintf(&tuning[strlen(tuning)], "", buf); + sprintf(&tuning[strlen(tuning)], "
"); + sprintf(&tuning[strlen(tuning)], _("Maximum number of worker threads")); + sprintf(&tuning[strlen(tuning)], ""); + sprintf(&tuning[strlen(tuning)], "", buf); + sprintf(&tuning[strlen(tuning)], "
"); + sprintf(&network[strlen(network)], _("POP3 listener port (-1 to disable)")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", buf); + sprintf(&network[strlen(network)], "
"); + sprintf(&network[strlen(network)], _("SMTP MTA port (-1 to disable)")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", buf); + sprintf(&network[strlen(network)], "
"); + sprintf(&network[strlen(network)], _("Correct forged From: lines during authenticated SMTP")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", + ((atoi(buf) == 0) ? "CHECKED" : "")); + sprintf(&network[strlen(network)], "
"); + sprintf(&access[strlen(access)], _("Allow aides to zap (forget) rooms")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "", + ((atoi(buf) != 0) ? "CHECKED" : "")); + sprintf(&access[strlen(access)], "
"); + sprintf(&network[strlen(network)], _("IMAP listener port (-1 to disable)")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", buf); + sprintf(&network[strlen(network)], "
"); + sprintf(&network[strlen(network)], _("Network run frequency (in seconds)")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", buf); + sprintf(&network[strlen(network)], "
"); + sprintf(&access[strlen(access)], _("Disable self-service user account creation")); + sprintf(&access[strlen(access)], ""); + sprintf(&access[strlen(access)], "", + ((atoi(buf) != 0) ? "CHECKED" : "")); + sprintf(&access[strlen(access)], "
"); + sprintf(&purger[strlen(purger)], _("Hour to run database auto-purge")); + sprintf(&purger[strlen(purger)], ""); + sprintf(&purger[strlen(purger)], ""); + sprintf(&purger[strlen(purger)], "
"); + sprintf(&directory[strlen(directory)], _("Host name of LDAP server (blank to disable)")); + sprintf(&directory[strlen(directory)], ""); + sprintf(&directory[strlen(directory)], "", buf); + sprintf(&directory[strlen(directory)], "
"); + sprintf(&directory[strlen(directory)], _("Port number of LDAP server (blank to disable)")); + sprintf(&directory[strlen(directory)], ""); + sprintf(&directory[strlen(directory)], "", atoi(buf)); + sprintf(&directory[strlen(directory)], "
"); + sprintf(&directory[strlen(directory)], _("Base DN")); + sprintf(&directory[strlen(directory)], ""); + sprintf(&directory[strlen(directory)], "", buf); + sprintf(&directory[strlen(directory)], "
"); + sprintf(&directory[strlen(directory)], _("Bind DN")); + sprintf(&directory[strlen(directory)], ""); + sprintf(&directory[strlen(directory)], "", buf); + sprintf(&directory[strlen(directory)], "
"); + sprintf(&directory[strlen(directory)], _("Password for bind DN")); + sprintf(&directory[strlen(directory)], ""); + sprintf(&directory[strlen(directory)], "", + buf); + sprintf(&directory[strlen(directory)], "
"); + sprintf(&network[strlen(network)], _("Server IP address (0.0.0.0 for 'any')")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", buf); + sprintf(&network[strlen(network)], "
"); + sprintf(&network[strlen(network)], _("SMTP MSA port (-1 to disable)")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", buf); + sprintf(&network[strlen(network)], "
"); + sprintf(&network[strlen(network)], _("IMAP over SSL port (-1 to disable)")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", buf); + sprintf(&network[strlen(network)], "
"); + sprintf(&network[strlen(network)], _("POP3 over SSL port (-1 to disable)")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", buf); + sprintf(&network[strlen(network)], "
"); + sprintf(&network[strlen(network)], _("SMTP over SSL port (-1 to disable)")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", buf); + sprintf(&network[strlen(network)], "
"); + sprintf(&idxjnl[strlen(idxjnl)], _("Enable full text index")); + sprintf(&idxjnl[strlen(idxjnl)], ""); + sprintf(&idxjnl[strlen(idxjnl)], "", + ((atoi(buf) != 0) ? "CHECKED" : "")); + sprintf(&idxjnl[strlen(idxjnl)], "
"); + sprintf(&tuning[strlen(tuning)], _("Automatically delete committed database logs")); + sprintf(&tuning[strlen(tuning)], ""); + sprintf(&tuning[strlen(tuning)], "", + ((atoi(buf) != 0) ? "CHECKED" : "")); + sprintf(&tuning[strlen(tuning)], "
"); + sprintf(&network[strlen(network)], _("Instantly expunge deleted messages in IMAP")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", + ((atoi(buf) != 0) ? "CHECKED" : "")); + sprintf(&network[strlen(network)], "
"); + sprintf(&network[strlen(network)], _("Allow unauthenticated SMTP clients to spoof this site's domains")); + sprintf(&network[strlen(network)], ""); + sprintf(&network[strlen(network)], "", + ((atoi(buf) != 0) ? "CHECKED" : "")); + sprintf(&network[strlen(network)], "
"); + sprintf(&idxjnl[strlen(idxjnl)], _("Perform journaling of email messages")); + sprintf(&idxjnl[strlen(idxjnl)], ""); + sprintf(&idxjnl[strlen(idxjnl)], "", + ((atoi(buf) != 0) ? "CHECKED" : "")); + sprintf(&idxjnl[strlen(idxjnl)], "
"); + sprintf(&idxjnl[strlen(idxjnl)], _("Perform journaling of non-email messages")); + sprintf(&idxjnl[strlen(idxjnl)], ""); + sprintf(&idxjnl[strlen(idxjnl)], "", + ((atoi(buf) != 0) ? "CHECKED" : "")); + sprintf(&idxjnl[strlen(idxjnl)], "
"); + sprintf(&idxjnl[strlen(idxjnl)], _("Email destination of journalized messages")); + sprintf(&idxjnl[strlen(idxjnl)], ""); + sprintf(&idxjnl[strlen(idxjnl)], "", buf); + sprintf(&idxjnl[strlen(idxjnl)], "

"); + sprintf(&purger[strlen(purger)], _("Default message expire policy for public rooms")); + sprintf(&purger[strlen(purger)], ""); + sprintf(&purger[strlen(purger)], "", + ((sitepolicy == 1) ? "CHECKED" : "") ); + sprintf(&purger[strlen(purger)], _("Never automatically expire messages")); + sprintf(&purger[strlen(purger)], "
\n"); + sprintf(&purger[strlen(purger)], "", + ((sitepolicy == 2) ? "CHECKED" : "") ); + sprintf(&purger[strlen(purger)], _("Expire by message count")); + sprintf(&purger[strlen(purger)], "
\n"); + sprintf(&purger[strlen(purger)], "", + ((sitepolicy == 3) ? "CHECKED" : "") ); + sprintf(&purger[strlen(purger)], _("Expire by message age")); + sprintf(&purger[strlen(purger)], "
"); + sprintf(&purger[strlen(purger)], _("Number of messages or days: ")); + sprintf(&purger[strlen(purger)], "", sitevalue); + sprintf(&purger[strlen(purger)], "

"); + sprintf(&purger[strlen(purger)], _("Default message expire policy for private mailboxes")); + sprintf(&purger[strlen(purger)], ""); + sprintf(&purger[strlen(purger)], "", + ((mboxpolicy == 0) ? "CHECKED" : "") ); + sprintf(&purger[strlen(purger)], _("Same policy as public rooms")); + sprintf(&purger[strlen(purger)], "
\n"); + sprintf(&purger[strlen(purger)], "", + ((mboxpolicy == 1) ? "CHECKED" : "") ); + sprintf(&purger[strlen(purger)], _("Never automatically expire messages")); + sprintf(&purger[strlen(purger)], "
\n"); + sprintf(&purger[strlen(purger)], "", + ((mboxpolicy == 2) ? "CHECKED" : "") ); + sprintf(&purger[strlen(purger)], _("Expire by message count")); + sprintf(&purger[strlen(purger)], "
\n"); + sprintf(&purger[strlen(purger)], "", + ((mboxpolicy == 3) ? "CHECKED" : "") ); + sprintf(&purger[strlen(purger)], _("Expire by message age")); + sprintf(&purger[strlen(purger)], "
"); + sprintf(&purger[strlen(purger)], _("Number of messages or days: ")); + sprintf(&purger[strlen(purger)], "", mboxvalue); + sprintf(&purger[strlen(purger)], "

"); + sprintf(&access[strlen(access)], "
"); + sprintf(&network[strlen(network)], ""); + sprintf(&tuning[strlen(tuning)], ""); + sprintf(&directory[strlen(directory)], ""); + sprintf(&purger[strlen(purger)], ""); + sprintf(&idxjnl[strlen(idxjnl)], ""); + + 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("

"); + wprintf("", _("Save changes")); + wprintf(" "); + wprintf("\n", _("Cancel")); + wprintf("
\n"); + wprintf("
\n"); + wDumpContent(1); +} + +/** + * parse siteconfig changes + */ +void siteconfig(void) +{ + char buf[256]; + + if (strlen(bstr("ok_button")) == 0) { + display_aide_menu(); + return; + } + serv_printf("CONF set"); + serv_getln(buf, sizeof buf); + if (buf[0] != '4') { + safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage); + display_aide_menu(); + return; + } + serv_printf("%s", bstr("c_nodename")); + serv_printf("%s", bstr("c_fqdn")); + serv_printf("%s", bstr("c_humannode")); + serv_printf("%s", bstr("c_phonenum")); + serv_printf("%s", ((!strcasecmp(bstr("c_creataide"), "yes") ? "1" : "0"))); + serv_printf("%s", bstr("c_sleeping")); + serv_printf("%s", bstr("c_initax")); + serv_printf("%s", ((!strcasecmp(bstr("c_regiscall"), "yes") ? "1" : "0"))); + serv_printf("%s", ((!strcasecmp(bstr("c_twitdetect"), "yes") ? "1" : "0"))); + serv_printf("%s", bstr("c_twitroom")); + serv_printf("%s", bstr("c_moreprompt")); + serv_printf("%s", ((!strcasecmp(bstr("c_restrict"), "yes") ? "1" : "0"))); + serv_printf("%s", bstr("c_bbs_city")); + serv_printf("%s", bstr("c_sysadm")); + serv_printf("%s", bstr("c_maxsessions")); + serv_printf(""); /* placeholder - this field is not in use */ + serv_printf("%s", bstr("c_userpurge")); + serv_printf("%s", bstr("c_roompurge")); + serv_printf("%s", bstr("c_logpages")); + serv_printf("%s", bstr("c_createax")); + serv_printf("%s", bstr("c_maxmsglen")); + serv_printf("%s", bstr("c_min_workers")); + serv_printf("%s", bstr("c_max_workers")); + serv_printf("%s", bstr("c_pop3_port")); + serv_printf("%s", bstr("c_smtp_port")); + serv_printf("%s", ((!strcasecmp(bstr("c_rfc822_strict_from"), "yes") ? "0" : "1"))); /* note: reverse bool */ + serv_printf("%s", ((!strcasecmp(bstr("c_aide_zap"), "yes") ? "1" : "0"))); + serv_printf("%s", bstr("c_imap_port")); + serv_printf("%s", bstr("c_net_freq")); + serv_printf("%s", ((!strcasecmp(bstr("c_disable_newu"), "yes") ? "1" : "0"))); + serv_printf("1"); /* placeholder - this field is not in use */ + serv_printf("%s", bstr("c_purge_hour")); + serv_printf("%s", bstr("c_ldap_host")); + serv_printf("%s", bstr("c_ldap_port")); + serv_printf("%s", bstr("c_ldap_base_dn")); + serv_printf("%s", bstr("c_ldap_bind_dn")); + serv_printf("%s", bstr("c_ldap_bind_pw")); + serv_printf("%s", bstr("c_ip_addr")); + serv_printf("%s", bstr("c_msa_port")); + serv_printf("%s", bstr("c_imaps_port")); + serv_printf("%s", bstr("c_pop3s_port")); + serv_printf("%s", bstr("c_smtps_port")); + serv_printf("%s", ((!strcasecmp(bstr("c_enable_fulltext"), "yes") ? "1" : "0"))); + serv_printf("%s", ((!strcasecmp(bstr("c_auto_cull"), "yes") ? "1" : "0"))); + serv_printf("%s", ((!strcasecmp(bstr("c_instant_expunge"), "yes") ? "1" : "0"))); + serv_printf("%s", ((!strcasecmp(bstr("c_allow_spoofing"), "yes") ? "1" : "0"))); + serv_printf("%s", ((!strcasecmp(bstr("c_journal_email"), "yes") ? "1" : "0"))); + serv_printf("%s", ((!strcasecmp(bstr("c_journal_pubmsgs"), "yes") ? "1" : "0"))); + serv_printf("%s", bstr("c_journal_dest")); + serv_printf("000"); + + serv_printf("SPEX site|%d|%d", atoi(bstr("sitepolicy")), atoi(bstr("sitevalue"))); + serv_getln(buf, sizeof buf); + serv_printf("SPEX mailboxes|%d|%d", atoi(bstr("mboxpolicy")), atoi(bstr("mboxvalue"))); + serv_getln(buf, sizeof buf); + + safestrncpy(WC->ImportantMessage, _("Your system configuration has been updated."), + sizeof WC->ImportantMessage); + display_aide_menu(); +} + + +/*@}*/ diff --git a/webcit/src/snprintf.c b/webcit/src/snprintf.c new file mode 100644 index 000000000..66e9701e2 --- /dev/null +++ b/webcit/src/snprintf.c @@ -0,0 +1,99 @@ +/* + * $Id$ + */ +/** + * \defgroup SnprintfReplacement modified from Sten Gunterberg's BUGTRAQ post of 22 Jul 1997 + * --nathan bryant + * \ingroup tools + */ +/*@{*/ +/** + * \brief Replacements for snprintf() and vsnprintf() + * + * Use it only if you have the "spare" cycles needed to effectively + * do every snprintf operation twice! Why is that? Because everything + * is first vfprintf()'d to /dev/null to determine the number of bytes. + * Perhaps a bit slow for demanding applications on slow machines, + * no problem for a fast machine with some spare cycles. + * + * You don't have a /dev/null? Every Linux contains one for free! + * + * Because the format string is never even looked at, all current and + * possible future printf-conversions should be handled just fine. + * + * Written July 1997 by Sten Gunterberg (gunterberg@ergon.ch) + */ + +#include "webcit.h" +#include "webserver.h" + +/** + * \brief is it needed???? + * \param fmt the formatstring? + * \param argp how many params? + */ +static int needed(const char *fmt, va_list argp) +{ + static FILE *sink = NULL; + + /** + * ok, there's a small race here that could result in the sink being + * opened more than once if we're threaded, but I'd rather ignore it than + * spend cycles synchronizing :-) */ + + if (sink == NULL) { + if ((sink = fopen("/dev/null", "w")) == NULL) { + perror("/dev/null"); + exit(1); + } + } + return vfprintf(sink, fmt, argp); +} + +/** + * \brief vsnprintf wrapper + * \param buf the output charbuffer + * \param max maximal size of the buffer + * \param fmt the formatstring (see man printf) + * \param argp the variable argument list + */ +int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp) +{ + char *p; + int size; + + if ((p = malloc(needed(fmt, argp) + 1)) == NULL) { + lprintf(1, "vsnprintf: malloc failed, aborting\n"); + abort(); + } + if ((size = vsprintf(p, fmt, argp)) >= max) + size = -1; + + strncpy(buf, p, max); + buf[max - 1] = 0; + free(p); + return size; +} + +/** + * \brief snprintf wrapper + * \param buf the output charbuffer + * \param max maximal size of the buffer + * \param fmt the formatstring (see man printf) + * \param ... the variable argument list + */ +int snprintf(char *buf, size_t max, const char *fmt,...) +{ + va_list argp; + int bytes; + + va_start(argp, fmt); + bytes = vsnprintf(buf, max, fmt, argp); + va_end(argp); + + return bytes; +} + + + +/*@}*/ diff --git a/webcit/src/subst.c b/webcit/src/subst.c new file mode 100644 index 000000000..435930698 --- /dev/null +++ b/webcit/src/subst.c @@ -0,0 +1,258 @@ +/* + * $Id$ + */ +/** + * \defgroup Subst Variable substitution type stuff + * \ingroup CitadelConfig + */ + +/*@{*/ + +#include "webcit.h" + + +/** + * \brief Clear out the list of substitution variables local to this session + */ +void clear_local_substs(void) { + struct wcsubst *ptr; + + while (WC->vars != NULL) { + ptr = WC->vars->next; + + if ((WC->vars->wcs_type == WCS_STRING) + || (WC->vars->wcs_type == WCS_SERVCMD)) { + free(WC->vars->wcs_value); + } + + free(WC->vars); + WC->vars = ptr; + } + + WC->vars = NULL; +} + + +/* + * \brief Add a substitution variable (local to this session) + * \param keyname the replacementstring to substitute + * \param keytype the kind of the key + * \param format the format string ala printf + * \param ... the arguments to substitute in the formatstring + */ +void svprintf(char *keyname, int keytype, const char *format,...) +{ + va_list arg_ptr; + char wbuf[SIZ]; + struct wcsubst *ptr = NULL; + struct wcsubst *scan; + + /** + * First scan through to see if we're doing a replacement of + * an existing key + */ + for (scan=WC->vars; scan!=NULL; scan=scan->next) { + if (!strcasecmp(scan->wcs_key, keyname)) { + ptr = scan; + free(ptr->wcs_value); + } + } + + /** Otherwise allocate a new one */ + if (ptr == NULL) { + ptr = (struct wcsubst *) malloc(sizeof(struct wcsubst)); + ptr->next = WC->vars; + safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key); + WC->vars = ptr; + } + + /** Format the string and save it */ + + va_start(arg_ptr, format); + vsnprintf(wbuf, sizeof wbuf, format, arg_ptr); + va_end(arg_ptr); + + ptr->wcs_type = keytype; + ptr->wcs_value = strdup(wbuf); +} + +/** + * \brief Add a substitution variable (local to this session) that does a callback + * \param keyname the keystring to substitute + * \param fcn_ptr the function callback to give the substitution string + */ +void svcallback(char *keyname, void (*fcn_ptr)() ) +{ + struct wcsubst *ptr; + + ptr = (struct wcsubst *) malloc(sizeof(struct wcsubst)); + ptr->next = WC->vars; + ptr->wcs_type = WCS_FUNCTION; + strcpy(ptr->wcs_key, keyname); + ptr->wcs_function = fcn_ptr; + WC->vars = ptr; +} + + + +/** + * \brief back end for print_value_of() ... does a server command + * \param servcmd server command to execute on the citadel server + */ +void pvo_do_cmd(char *servcmd) { + char buf[SIZ]; + + serv_puts(servcmd); + serv_getln(buf, sizeof buf); + + switch(buf[0]) { + case '2': + case '3': + case '5': + wprintf("%s\n", &buf[4]); + break; + case '1': + fmout("CENTER"); + break; + case '4': + wprintf("%s\n", &buf[4]); + serv_puts("000"); + break; + } +} + + + +/** + * \brief Print the value of a variable + * \param keyname get a key to print + */ +void print_value_of(char *keyname) { + struct wcsubst *ptr; + void *fcn(); + + if (keyname[0] == '=') { + do_template(&keyname[1]); + } + + if (!strcasecmp(keyname, "SERV_PID")) { + wprintf("%d", WC->ctdl_pid); + } + + else if (!strcasecmp(keyname, "SERV_NODENAME")) { + escputs(serv_info.serv_nodename); + } + + else if (!strcasecmp(keyname, "SERV_HUMANNODE")) { + escputs(serv_info.serv_humannode); + } + + else if (!strcasecmp(keyname, "SERV_FQDN")) { + escputs(serv_info.serv_fqdn); + } + + else if (!strcasecmp(keyname, "SERV_SOFTWARE")) { + escputs(serv_info.serv_software); + } + + else if (!strcasecmp(keyname, "SERV_REV_LEVEL")) { + wprintf("%d.%02d", + serv_info.serv_rev_level / 100, + serv_info.serv_rev_level % 100 + ); + } + + else if (!strcasecmp(keyname, "SERV_BBS_CITY")) { + escputs(serv_info.serv_bbs_city); + } + + else if (!strcasecmp(keyname, "CURRENT_USER")) { + escputs(WC->wc_fullname); + } + + else if (!strcasecmp(keyname, "CURRENT_ROOM")) { + escputs(WC->wc_roomname); + } + + /** Page-local variables */ + else for (ptr = WC->vars; ptr != NULL; ptr = ptr->next) { + if (!strcasecmp(ptr->wcs_key, keyname)) { + if (ptr->wcs_type == WCS_STRING) { + wprintf("%s", ptr->wcs_value); + } + else if (ptr->wcs_type == WCS_SERVCMD) { + pvo_do_cmd(ptr->wcs_value); + } + else if (ptr->wcs_type == WCS_FUNCTION) { + (*ptr->wcs_function) (); + } + } + } +} + + + +/** + * \brief Display a variable-substituted template + * \param templatename template file to load + */ +void do_template(void *templatename) { + char filename[PATH_MAX]; + FILE *fp; + char inbuf[1024]; + char outbuf[sizeof inbuf]; + char key[sizeof inbuf]; + int i, pos; + + strcpy(filename, "static/"); + strcat(filename, templatename); + if (WC->is_wap) + strcat(filename, ".wml"); + else + strcat(filename, ".html"); + + fp = fopen(filename, "r"); + if (fp == NULL) { + wprintf(_("ERROR: could not open template ")); + wprintf("'%s' - %s
\n", + templatename, strerror(errno)); + return; + } + + strcpy(inbuf, ""); + + while (fgets(inbuf, sizeof inbuf, fp) != NULL) { + strcpy(outbuf, ""); + + while (strlen(inbuf) > 0) { + pos = (-1); + for (i=strlen(inbuf); i>=0; --i) { + if ((inbuf[i]=='<')&&(inbuf[i+1]=='?')) pos = i; + } + if (pos < 0) { + wprintf("%s", inbuf); + strcpy(inbuf, ""); + } + else { + strncpy(outbuf, inbuf, pos); + outbuf[pos] = 0; + wprintf("%s", outbuf); + strcpy(inbuf, &inbuf[pos]); + pos = 1; + for (i=strlen(inbuf); i>=0; --i) { + if (inbuf[i]=='>') pos = i; + } + strncpy(key, &inbuf[2], pos-2); + key[pos-2] = 0; + print_value_of(key); + strcpy(inbuf, &inbuf[pos+1]); + } + } + } + + fclose(fp); +} + + + +/*@}*/ diff --git a/webcit/src/summary.c b/webcit/src/summary.c new file mode 100644 index 000000000..3d9940389 --- /dev/null +++ b/webcit/src/summary.c @@ -0,0 +1,288 @@ +/* + * $Id$ + */ +/** + * \defgroup SymaryFuncs Displays the "Summary Page" + * \ingroup WebcitDisplayItems + */ +/*@{*/ +#include "webcit.h" + +/** + * \brief Display today's date in a friendly format + */ +void output_date(void) { + struct tm tm; + time_t now; + char buf[128]; + + time(&now); + localtime_r(&now, &tm); + + wc_strftime(buf, 32, "%A, %x", &tm); + wprintf("%s", buf); +} + + + + +/** + * \brief Dummy section + */ +void dummy_section(void) { + svprintf("BOXTITLE", WCS_STRING, "(dummy 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("\n"); + for (i=0; i\n", + extract_int(&buf[4], 1), + extract_int(&buf[4], 2) + ); + } + } + wprintf("
"); + escputs(room); + wprintf("%d/%d
\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("
\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(""); + wprintf(_("(None)")); + wprintf("
\n"); + } + else { + for (i=0; imsgarr[i]); + } + } + + calendar_summary_view(); + +#else /* WEBCIT_WITH_CALENDAR_SERVICE */ + wprintf(""); + wprintf(_("(This server does not support task lists)")); + wprintf("\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(""); + wprintf(_("(Nothing)")); + wprintf("
\n"); + } + else { + for (i=0; imsgarr[i]); + } + calendar_summary_view(); + } + +#else /* WEBCIT_WITH_CALENDAR_SERVICE */ + wprintf(""); + wprintf(_("(This server does not support calendars)")); + wprintf("\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("
" + ""); + + /** + * Column One + */ + wprintf("
"); + wholist_section(); + + /** + * Column Two + */ + wprintf(""); + server_info_section(); + wprintf("
"); + tasks_section(); + + /** + * Column Three + */ + wprintf("
"); + new_messages_section(); + wprintf("
"); + calendar_section(); + + /** + * End of columns + */ + wprintf("
"); +} + + +/** + * \brief Display this user's summary page + */ +void summary(void) { + char title[256]; + + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n"); + wprintf("" + "
" + "" + ); + + snprintf(title, sizeof title, _("Summary page for %s"), WC->wc_fullname); + escputs(title); + wprintf("\n"); + wprintf(""); + output_date(); + wprintf("
"); + offer_start_page(); + wprintf("
\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("
\n
\n"); + summary_inner_div(); + wprintf("
\n"); + + wprintf( + " \n" + ); + + wDumpContent(1); +} + + +/*@}*/ diff --git a/webcit/src/sysmsgs.c b/webcit/src/sysmsgs.c new file mode 100644 index 000000000..7de399879 --- /dev/null +++ b/webcit/src/sysmsgs.c @@ -0,0 +1,106 @@ +/* + * $Id$ + */ +/** + * \defgroup ShowSysMsgs Editing of various text files on the Citadel server. + * \ingroup WebcitDisplayItems + */ +/*@{*/ +#include "webcit.h" + + +/** + * \brief display the form for editing something (room info, bio, etc) + * \param description the descriptive text for the box + * \param check_cmd command to check???? + * \param read_cmd read answer from citadel server??? + * \param save_cmd save comand to the citadel server?? + * \param with_room_banner should we bisplay a room banner? + */ +void display_edit(char *description, char *check_cmd, + char *read_cmd, char *save_cmd, int with_room_banner) +{ + char buf[SIZ]; + + serv_puts(check_cmd); + serv_getln(buf, sizeof buf); + + if (buf[0] != '2') { + safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage); + display_main_menu(); + return; + } + if (with_room_banner) { + output_headers(1, 1, 1, 0, 0, 0); + } + else { + output_headers(1, 1, 0, 0, 0, 0); + } + + svprintf("BOXTITLE", WCS_STRING, _("Edit %s"), description); + do_template("beginbox"); + + wprintf("
"); + 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("
"); + + wprintf("
\n", save_cmd); + wprintf("

\n"); + wprintf("", _("Save changes")); + wprintf(" "); + wprintf("
\n", _("Cancel")); + + wprintf("
\n"); + do_template("endbox"); + wDumpContent(1); +} + + +/** + * \brief save a screen which was displayed with display_edit() + * \param description the window title??? + * \param enter_cmd which command to enter at the citadel server??? + * \param regoto should we go to that room again after executing that command? + */ +void save_edit(char *description, char *enter_cmd, int regoto) +{ + char buf[SIZ]; + + if (strlen(bstr("save_button")) == 0) { + sprintf(WC->ImportantMessage, + _("Cancelled. %s was not saved."), + description); + display_main_menu(); + return; + } + serv_puts(enter_cmd); + serv_getln(buf, sizeof buf); + if (buf[0] != '4') { + safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage); + display_main_menu(); + return; + } + text_to_server(bstr("msgtext")); + serv_puts("000"); + + if (regoto) { + smart_goto(WC->wc_roomname); + } else { + sprintf(WC->ImportantMessage, + _("%s has been saved."), + description); + display_main_menu(); + return; + } +} + + +/*@}*/ diff --git a/webcit/src/tabs.c b/webcit/src/tabs.c new file mode 100644 index 000000000..5ecd53a5e --- /dev/null +++ b/webcit/src/tabs.c @@ -0,0 +1,81 @@ +/* + * $Id: $ + */ +/** + * \defgroup TabUtils Utility functions for creating tabbed dialogs + * \ingroup WebcitDisplayItems + */ +/*@{*/ +#include "webcit.h" + +/** + * \brief print tabbed dialog + * \param num_tabs how many tabs do we have? + * \param tabnames the headers of the tables + */ +void tabbed_dialog(int num_tabs, char *tabnames[]) { + int i; + + wprintf(" \n" + ); + + wprintf("" + "" + ); + + for (i=0; i" + "", + i, + ( (i==0) ? "ffffff" : "cccccc" ), + i + ); + wprintf("%s", tabnames[i]); + wprintf(""); + + wprintf("\n"); + } + + wprintf("
  
\n"); + wprintf("
"); +} + +/** + * \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("
", + 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("
\n"); + + if (tabnum == num_tabs-1) { + wprintf("
\n"); + } +} + + +/*@}*/ diff --git a/webcit/src/tcp_sockets.c b/webcit/src/tcp_sockets.c new file mode 100644 index 000000000..2d5d985d8 --- /dev/null +++ b/webcit/src/tcp_sockets.c @@ -0,0 +1,237 @@ +/* + * $Id$ + */ +/** + * \defgroup TcpSockets TCP client socket module for WebCit + * \ingroup CitadelCommunitacion + */ +/*@{*/ + +/* + * Uncomment this to log all communications with the Citadel server +#define SERV_TRACE 1 + */ + +#include "webcit.h" +#include "webserver.h" + +/** + * \brief register the timeout + * \param signum signalhandler number + * \return signals + */ +RETSIGTYPE timeout(int signum) +{ + lprintf(1, "Connection timed out.\n"); + exit(3); +} + + +/** + * \brief Connect a unix domain socket + * \param sockpath where to open a unix domain socket + */ +int uds_connectsock(char *sockpath) +{ + struct sockaddr_un addr; + int s; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, sockpath, sizeof addr.sun_path); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + lprintf(1, "Can't create socket: %s\n", + strerror(errno)); + return(-1); + } + + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + lprintf(1, "Can't connect: %s\n", + strerror(errno)); + close(s); + return(-1); + } + + return s; +} + + +/** + * \brief Connect a TCP/IP socket + * \param host the host to connect to + * \param service the service on the host to call + */ +int tcp_connectsock(char *host, char *service) +{ + struct hostent *phe; + struct servent *pse; + struct protoent *ppe; + struct sockaddr_in sin; + int s; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + + pse = getservbyname(service, "tcp"); + if (pse) { + sin.sin_port = pse->s_port; + } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) { + lprintf(1, "Can't get %s service entry\n", service); + return (-1); + } + phe = gethostbyname(host); + if (phe) { + memcpy(&sin.sin_addr, phe->h_addr, phe->h_length); + } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { + lprintf(1, "Can't get %s host entry: %s\n", + host, strerror(errno)); + return (-1); + } + if ((ppe = getprotobyname("tcp")) == 0) { + lprintf(1, "Can't get TCP protocol entry: %s\n", + strerror(errno)); + return (-1); + } + + s = socket(PF_INET, SOCK_STREAM, ppe->p_proto); + if (s < 0) { + lprintf(1, "Can't create socket: %s\n", strerror(errno)); + return (-1); + } + signal(SIGALRM, timeout); + alarm(30); + + if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + lprintf(1, "Can't connect to %s.%s: %s\n", + host, service, strerror(errno)); + close(s); + return (-1); + } + alarm(0); + signal(SIGALRM, SIG_IGN); + + return (s); +} + + + + +/** + * \brief Input binary data from socket + * \param buf the buffer to get the input to + * \param bytes the maximal number of bytes to read + */ +void serv_read(char *buf, int bytes) +{ + int len, rlen; + + len = 0; + while (len < bytes) { + rlen = read(WC->serv_sock, &buf[len], bytes - len); + if (rlen < 1) { + lprintf(1, "Server connection broken: %s\n", + strerror(errno)); + close(WC->serv_sock); + WC->serv_sock = (-1); + WC->connected = 0; + WC->logged_in = 0; + memset(buf, 0, bytes); + return; + } + len = len + rlen; + } +} + + +/** + * \brief input string from pipe + */ +void serv_getln(char *strbuf, int bufsize) +{ + int ch, len; + char buf[2]; + + len = 0; + strbuf[0] = 0; + do { + serv_read(&buf[0], 1); + ch = buf[0]; + if ((ch != 13) && (ch != 10)) { + strbuf[len++] = ch; + } + } while ((ch != 10) && (ch != 0) && (len < (bufsize-1))); + strbuf[len] = 0; +#ifdef SERV_TRACE + lprintf(9, "%3d>%s\n", WC->serv_sock, strbuf); +#endif +} + + + +/** + * \brief send binary to server + * \param buf the buffer to write to citadel server + * \param nbytes how many bytes to send to citadel server + */ +void serv_write(char *buf, int nbytes) +{ + int bytes_written = 0; + int retval; + while (bytes_written < nbytes) { + retval = write(WC->serv_sock, &buf[bytes_written], + nbytes - bytes_written); + if (retval < 1) { + lprintf(1, "Server connection broken: %s\n", + strerror(errno)); + close(WC->serv_sock); + WC->serv_sock = (-1); + WC->connected = 0; + WC->logged_in = 0; + return; + } + bytes_written = bytes_written + retval; + } +} + + +/** + * \brief send line to server + * \param string the line to send to the citadel server + */ +void serv_puts(char *string) +{ + char buf[SIZ]; + +#ifdef SERV_TRACE + lprintf(9, "%3d<%s\n", WC->serv_sock, string); +#endif + sprintf(buf, "%s\n", string); + serv_write(buf, strlen(buf)); +} + + +/** + * \brief convenience function to send stuff to the server + * \param format the formatstring + * \param ... the entities to insert into format + */ +void serv_printf(const char *format,...) +{ + va_list arg_ptr; + char buf[SIZ]; + + va_start(arg_ptr, format); + vsnprintf(buf, sizeof buf, format, arg_ptr); + va_end(arg_ptr); + + strcat(buf, "\n"); + serv_write(buf, strlen(buf)); +#ifdef SERV_TRACE + lprintf(9, "<%s", buf); +#endif +} + + +/*@}*/ diff --git a/webcit/src/tools.c b/webcit/src/tools.c new file mode 100644 index 000000000..0a0718381 --- /dev/null +++ b/webcit/src/tools.c @@ -0,0 +1,618 @@ +/* + * $Id$ + */ +/** + * \defgroup MiscRout Miscellaneous routines + * \ingroup tools + */ + +/*@{*/ +#include "webcit.h" +#include "webserver.h" + + +typedef unsigned char byte; /**< byte data type */ + +#define FALSE 0 /**< no. */ +#define TRUE 1 /**< yes. */ + +static byte dtable[256]; /**< base64 encode / decode table */ + +/** + * \brief sanitize strncopy. + * \param dest destination string + * \param src source string + * \param n length of source to copy + * \return result string + */ +char *safestrncpy(char *dest, const char *src, size_t n) +{ + if (dest == NULL || src == NULL) { + abort(); + } + strncpy(dest, src, n); + dest[n - 1] = 0; + return dest; +} + + + +/** + * \brief discover number of parameters/tokens in a string + * \param source string to inspect + * \param tok seperation token + * \return number of tokenized parts found + */ +int num_tokens(char *source, char tok) +{ + int a = 0; + int count = 1; + + if (source == NULL) + return (0); + for (a = 0; a < strlen(source); ++a) { + if (source[a] == tok) + ++count; + } + return (count); +} + +/** + * brief a string tokenizer + * \param dest destination string + * \param source the string to grab tokens from + * \param parmnum the n'th token to grab + * \param separator the tokenizer string + * \param maxlen the length of dest + */ +void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen) +{ + char *d; /* dest */ + const char *s; /* source */ + int count = 0; + int len = 0; + + dest[0] = 0; + + /* Locate desired parameter */ + s = source; + while (count < parmnum) { + /* End of string, bail! */ + if (!*s) { + s = NULL; + break; + } + if (*s == separator) { + count++; + } + s++; + } + if (!s) return; /* Parameter not found */ + + for (d = dest; *s && *s != separator && ++len 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= 2) { + extract_token(histr, setstr, 1, ':', sizeof histr); + if (!strcmp(histr, "*")) { + snprintf(histr, sizeof histr, "%ld", LONG_MAX); + } + } + else { + strcpy(histr, lostr); + } + lo = atol(lostr); + hi = atol(histr); + + if ((msgnum >= lo) && (msgnum <= hi)) return(1); + } + + return(0); +} + + + +/** + * \brief Strip a boundarized substring out of a string + * (for example, remove + * parentheses and anything inside them). + * + * This improved version can strip out *multiple* boundarized substrings. + * \param str the string to process + * \param leftboundary the boundary character on the left side of the target string + * \param rightboundary the boundary character on the right side of the target string + */ +void stripout(char *str, char leftboundary, char rightboundary) +{ + int a; + int lb = (-1); + int rb = (-1); + + do { + lb = (-1); + rb = (-1); + + for (a = 0; a < strlen(str); ++a) { + if (str[a] == leftboundary) + lb = a; + if (str[a] == rightboundary) + rb = a; + } + + if ((lb > 0) && (rb > lb)) { + strcpy(&str[lb - 1], &str[rb + 1]); + } + + } while ((lb > 0) && (rb > lb)); + +} + + + +/** + * \brief Replacement for sleep() that uses select() in order to avoid SIGALRM + * \param seconds how many seconds should we sleep? + */ +void sleeeeeeeeeep(int seconds) +{ + struct timeval tv; + + tv.tv_sec = seconds; + tv.tv_usec = 0; + select(0, NULL, NULL, NULL, &tv); +} + + + +/** + * \brief encode a string into base64 to for example tunnel it through mail transport + * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by + * John Walker, copied over from the Citadel server. + * \param dest encrypted string + * \param source the string to encrypt + * \param sourcelen the length of the source data (may contain string terminators) + */ + +void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen) +{ + int i, hiteof = FALSE; + int spos = 0; + int dpos = 0; + int thisline = 0; + + /** Fill dtable with character encodings. */ + + for (i = 0; i < 26; i++) { + dtable[i] = 'A' + i; + dtable[26 + i] = 'a' + i; + } + for (i = 0; i < 10; i++) { + dtable[52 + i] = '0' + i; + } + dtable[62] = '+'; + dtable[63] = '/'; + + while (!hiteof) { + byte igroup[3], ogroup[4]; + int c, n; + + igroup[0] = igroup[1] = igroup[2] = 0; + for (n = 0; n < 3; n++) { + if (spos >= sourcelen) { + hiteof = TRUE; + break; + } + c = source[spos++]; + igroup[n] = (byte) c; + } + if (n > 0) { + ogroup[0] = dtable[igroup[0] >> 2]; + ogroup[1] = + dtable[((igroup[0] & 3) << 4) | + (igroup[1] >> 4)]; + ogroup[2] = + dtable[((igroup[1] & 0xF) << 2) | + (igroup[2] >> 6)]; + ogroup[3] = dtable[igroup[2] & 0x3F]; + + /** + * Replace characters in output stream with "=" pad + * characters if fewer than three characters were + * read from the end of the input stream. + */ + + if (n < 3) { + ogroup[3] = '='; + if (n < 2) { + ogroup[2] = '='; + } + } + for (i = 0; i < 4; i++) { + dest[dpos++] = ogroup[i]; + dest[dpos] = 0; + } + thisline += 4; + if (thisline > 70) { + dest[dpos++] = '\r'; + dest[dpos++] = '\n'; + dest[dpos] = 0; + thisline = 0; + } + } + } + if (thisline > 70) { + dest[dpos++] = '\r'; + dest[dpos++] = '\n'; + dest[dpos] = 0; + thisline = 0; + } +} + + +/** + * \brief Convert base64-encoded to binary. + * It will stop after reading 'length' bytes. + * + * \param dest The destination buffer + * \param source The base64 data to be decoded. + * \param length The number of bytes to decode. + * \return The actual length of the decoded data. + */ +int CtdlDecodeBase64(char *dest, const char *source, size_t length) +{ + int i, c; + int dpos = 0; + int spos = 0; + + for (i = 0; i < 255; i++) { + dtable[i] = 0x80; + } + for (i = 'A'; i <= 'Z'; i++) { + dtable[i] = 0 + (i - 'A'); + } + for (i = 'a'; i <= 'z'; i++) { + dtable[i] = 26 + (i - 'a'); + } + for (i = '0'; i <= '9'; i++) { + dtable[i] = 52 + (i - '0'); + } + dtable['+'] = 62; + dtable['/'] = 63; + dtable['='] = 0; + + /**CONSTANTCONDITION*/ while (TRUE) { + byte a[4], b[4], o[3]; + + for (i = 0; i < 4; i++) { + if (spos >= length) { + return (dpos); + } + c = source[spos++]; + + if (c == 0) { + if (i > 0) { + return (dpos); + } + return (dpos); + } + if (dtable[c] & 0x80) { + /** Ignoring errors: discard invalid character */ + i--; + continue; + } + a[i] = (byte) c; + b[i] = (byte) dtable[c]; + } + o[0] = (b[0] << 2) | (b[1] >> 4); + o[1] = (b[1] << 4) | (b[2] >> 2); + o[2] = (b[2] << 6) | b[3]; + i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3); + if (i >= 1) + dest[dpos++] = o[0]; + if (i >= 2) + dest[dpos++] = o[1]; + if (i >= 3) + dest[dpos++] = o[2]; + dest[dpos] = 0; + if (i < 3) { + return (dpos); + } + } +} + + + +/** + * \brief Generate a new, globally unique UID parameter for a calendar etc. object + * + * \param buf String buffer into which our newly created UUID should be placed + */ +void generate_uuid(char *buf) { + static int seq = 0; + + sprintf(buf, "%s-%lx-%x-%x", + serv_info.serv_nodename, + (long)time(NULL), + getpid(), + (seq++) + ); +} + + +/** + * \brief Local replacement for controversial C library function that generates + * names for temporary files. Included to shut up compiler warnings. + * \todo return a fd to the file instead of the name for security reasons + * \param name the created filename + * \param len the length of the filename + */ +void CtdlMakeTempFileName(char *name, int len) { + int i = 0; + + while (i++, i < 100) { + snprintf(name, len, "/tmp/ctdl.%04x.%04x", + getpid(), + rand() + ); + if (!access(name, F_OK)) { + return; + } + } +} + + + +/* + * \brief case-insensitive substring search + * + * This uses the Boyer-Moore search algorithm and is therefore quite fast. + * The code is roughly based on the strstr() replacement from 'tin' written + * by Urs Jannsen. + * + * \param text String to be searched + * \param pattern String to search for + */ +char *bmstrcasestr(char *text, char *pattern) { + + register unsigned char *p, *t; + register int i, j, *delta; + register size_t p1; + int deltaspace[256]; + size_t textlen; + size_t patlen; + + textlen = strlen (text); + patlen = strlen (pattern); + + /* algorithm fails if pattern is empty */ + if ((p1 = patlen) == 0) + return (text); + + /* code below fails (whenever i is unsigned) if pattern too long */ + if (p1 > textlen) + return (NULL); + + /* set up deltas */ + delta = deltaspace; + for (i = 0; i <= 255; i++) + delta[i] = p1; + for (p = (unsigned char *) pattern, i = p1; --i > 0;) + delta[tolower(*p++)] = i; + + /* + * From now on, we want patlen - 1. + * In the loop below, p points to the end of the pattern, + * t points to the end of the text to be tested against the + * pattern, and i counts the amount of text remaining, not + * including the part to be tested. + */ + p1--; + p = (unsigned char *) pattern + p1; + t = (unsigned char *) text + p1; + i = textlen - patlen; + while(1) { + if (tolower(p[0]) == tolower(t[0])) { + if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) { + return ((char *)t - p1); + } + } + j = delta[tolower(t[0])]; + if (i < j) + break; + i -= j; + t += j; + } + return (NULL); +} + + + + + +/*@}*/ diff --git a/webcit/src/useredit.c b/webcit/src/useredit.c new file mode 100644 index 000000000..265f91587 --- /dev/null +++ b/webcit/src/useredit.c @@ -0,0 +1,500 @@ +/* + * $Id$ + */ +/** + * \defgroup AdminTasks Administrative screen to add/change/delete user accounts + * \ingroup CitadelConfig + * + */ +/*@{*/ + +#include "webcit.h" +#include "webserver.h" + + +/** + * \brief show a list of available users to edit them + * \param message the header message??? + * \param preselect which user should be selected in the browser + */ +void select_user_to_edit(char *message, char *preselect) +{ + char buf[SIZ]; + char username[SIZ]; + + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n"); + wprintf("" + "
" + "" + ""); + wprintf(_("Edit or delete users")); + wprintf("
\n" + "
\n
\n" + ); + + if (message != NULL) wprintf(message); + + wprintf("
\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("

"); + + wprintf("
\n"); + wprintf(_("New user: ")); + wprintf("
\n" + "" + "
\n", _("Create")); + + do_template("endbox"); + + wprintf("
"); + + 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("

"); + + wprintf("
" + "
\n"); + wprintf("
\n"); + + wprintf("", _("Edit configuration")); + wprintf("", _("Edit address book entry")); + wprintf("", _("Delete user"), _("Delete this user?")); + wprintf("
\n"); + do_template("endbox"); + + wprintf("
\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, + "" + "%s

\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, + "%s

\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, + "" + "%s

\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("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Edit user account: ")); + escputs(username); + wprintf("
\n"); + wprintf("
\n
\n"); + + wprintf("
" + "
\n"); + wprintf("
\n" + "\n"); + wprintf("\n" + "\n", + is_new, usernum); + + wprintf("\n", flags); + + wprintf("
"); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + + now = time(NULL); + wprintf(""); + + wprintf("\n"); + + wprintf("
"); + wprintf(_("Password")); + wprintf("" + "
"); + wprintf(_("Permission to send Internet mail")); + wprintf(""); + wprintf("
"); + wprintf(_("Number of logins")); + wprintf("" + "
"); + wprintf(_("Messages submitted")); + wprintf("" + "
"); + wprintf(_("Access level")); + wprintf("" + "
"); + wprintf(_("User ID number")); + wprintf("" + "
"); + wprintf(_("Date and time of last login")); + wprintf("" + "
"); + wprintf(_("Auto-purge after this many days")); + wprintf("" + "
\n"); + + wprintf("\n" + " " + "\n" + "

\n", _("Save changes"), _("Cancel")); + + wprintf("
\n"); + wprintf("
\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, + "" + "%s

\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, + "" + "%s

\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, + "" + "%s

\n", &buf[4]); + select_user_to_edit(error_message, NULL); + } + +} + + + +/*@}*/ diff --git a/webcit/src/userlist.c b/webcit/src/userlist.c new file mode 100644 index 000000000..cbd6c669a --- /dev/null +++ b/webcit/src/userlist.c @@ -0,0 +1,174 @@ +/* + * $Id$ + */ +/** + * \defgroup AccDisplay Display a list of all accounts on a Citadel system. + * \ingroup CitadelConfig + */ + +/*@{*/ +#include "webcit.h" + +/** + * \brief structure to keep namelists in + */ +struct namelist { + struct namelist *next; /**< next item of the linked list */ + char name[32]; /**< name of the userentry */ +}; + +/** + * \brief display the userlist + */ +void userlist(void) +{ + char buf[256]; + char fl[256]; + char title[256]; + struct tm tmbuf; + time_t lc; + struct namelist *bio = NULL; + struct namelist *bptr; + int has_bio; + int bg = 0; + + serv_puts("LBIO"); + serv_getln(buf, sizeof buf); + if (buf[0] == '1') + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + bptr = (struct namelist *) malloc(sizeof(struct namelist)); + bptr->next = bio; + strcpy(bptr->name, buf); + bio = bptr; + } + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n" + "
" + ""); + snprintf(title, sizeof title, _("User list for %s"), serv_info.serv_humannode); + escputs(title); + wprintf("" + "
\n" + "
\n
\n" + ); + + serv_puts("LIST"); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') { + wprintf("%s
\n", &buf[4]); + goto DONE; + } + + wprintf("
" + "" + "", + _("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("\n", + extract_long(buf, 4), extract_long(buf, 5)); + + } + wprintf("
\n"); + wprintf("
%s%s%s%s%s%s
", + (bg ? "DDDDDD" : "FFFFFF") + ); + if (has_bio) { + wprintf(""); + escputs(fl); + wprintf(""); + } else { + escputs(fl); + } + wprintf("%ld%d", + 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("%ld%5ld
\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("
\n" + "" + "" + "
"); + wprintf(_("User profile")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("
" + "
\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("
"); + if (have_pic == 1) { + wprintf(""); + } + wprintf("

%s

\n", who); + serv_printf("RBIO %s", who); + serv_getln(buf, sizeof buf); + if (buf[0] == '1') { + fmout("JUSTIFY"); + } + wprintf("
" + "  "); + snprintf(buf, sizeof buf, _("Click here to send an instant message to %s"), who); + escputs(buf); + wprintf("\n"); + + wprintf("
\n"); + wDumpContent(1); +} + + +/*@}*/ diff --git a/webcit/src/vcard.c b/webcit/src/vcard.c new file mode 100644 index 000000000..d236b00c5 --- /dev/null +++ b/webcit/src/vcard.c @@ -0,0 +1,376 @@ +/* + * $Id$ + * Copyright (C) 1999-2006 by Art Cancro + * This code is freely redistributable under the terms of the GNU General + * Public License. All other rights reserved. + */ +/** + * \defgroup VCardMain vCard data type implementation for the Citadel system. + * \ingroup VCards + */ +/*@{*/ +#include "webcit.h" +#include "webserver.h" +#include "vcard.h" + +/** + * \brief Constructor (empty vCard) + * \return an empty vcard + */ +struct vCard *vcard_new() { + struct vCard *v; + + v = (struct vCard *) malloc(sizeof(struct vCard)); + if (v == NULL) return v; + + v->magic = CTDL_VCARD_MAGIC; + v->numprops = 0; + v->prop = NULL; + + return v; +} + +/** + * \brief Remove the "charset=" attribute from a vCard property name + * + * \param strbuf The property name string to be stripped + */ +void remove_charset_attribute(char *strbuf) +{ + int i, t; + char compare[256]; + + t = num_tokens(strbuf, ';'); + for (i=0; i0) { + 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; iprop[i].value); ++i) { + if ( (v->prop[i].value[j] < 32) || (v->prop[i].value[j] > 126) ) { + is_utf8 = 1; + } + } + strcat(ser, v->prop[i].name); + if (is_utf8) { + strcat(ser, ";charset=UTF-8"); + } + strcat(ser, ":"); + strcat(ser, v->prop[i].value); + strcat(ser, "\r\n"); + } + } + strcat(ser, "end:vcard\r\n"); + + return ser; +} + + + +/* + * \brief Convert FN (Friendly Name) into N (Name) + * + * \param vname Supplied friendly-name + * \param n Target buffer to store Name + * \param vname_size Size of buffer + */ +void vcard_fn_to_n(char *vname, char *n, size_t vname_size) { + char lastname[256]; + char firstname[256]; + char middlename[256]; + char honorific_prefixes[256]; + char honorific_suffixes[256]; + char buf[256]; + + safestrncpy(buf, n, sizeof buf); + + /* Try to intelligently convert the screen name to a + * fully expanded vCard name based on the number of + * words in the name + */ + safestrncpy(lastname, "", sizeof lastname); + safestrncpy(firstname, "", sizeof firstname); + safestrncpy(middlename, "", sizeof middlename); + safestrncpy(honorific_prefixes, "", sizeof honorific_prefixes); + safestrncpy(honorific_suffixes, "", sizeof honorific_suffixes); + + /* Honorific suffixes */ + if (num_tokens(buf, ',') > 1) { + extract_token(honorific_suffixes, buf, (num_tokens(buf, ' ') - 1), ',', + sizeof honorific_suffixes); + remove_token(buf, (num_tokens(buf, ',') - 1), ','); + } + + /* Find a last name */ + extract_token(lastname, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof lastname); + remove_token(buf, (num_tokens(buf, ' ') - 1), ' '); + + /* Find honorific prefixes */ + if (num_tokens(buf, ' ') > 2) { + extract_token(honorific_prefixes, buf, 0, ' ', sizeof honorific_prefixes); + remove_token(buf, 0, ' '); + } + + /* Find a middle name */ + if (num_tokens(buf, ' ') > 1) { + extract_token(middlename, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof middlename); + remove_token(buf, (num_tokens(buf, ' ') - 1), ' '); + } + + /* Anything left is probably the first name */ + safestrncpy(firstname, buf, sizeof firstname); + striplt(firstname); + + /* Compose the structured name */ + snprintf(vname, vname_size, "%s;%s;%s;%s;%s", lastname, firstname, middlename, + honorific_prefixes, honorific_suffixes); +} + + + + + + +/*@}*/ diff --git a/webcit/src/vcard.h b/webcit/src/vcard.h new file mode 100644 index 000000000..ac30d4298 --- /dev/null +++ b/webcit/src/vcard.h @@ -0,0 +1,38 @@ +/* + * $Id$ + * Copyright (C) 1999 by Art Cancro + * This code is freely redistributable under the terms of the GNU General + * Public License. All other rights reserved. + */ +/** + * \defgroup VcardHeader vCard implementation for Citadel + * \ingroup VCards + * + */ + +/*@{ */ +#define CTDL_VCARD_MAGIC 0xa1f9 /**< magic byte vcards start with??? */ + +/** + * \brief This data structure represents a vCard object currently in memory. + */ +struct vCard { + int magic; /**< the Magic Byte */ + int numprops; /**< number of properties this vcard will have */ + struct vCardProp { + char *name; /**< Keyname of the property */ + char *value; /**< value of the property */ + } *prop; /**< Vcard Property. Linked list??? */ +}; + + +struct vCard *vcard_new(void); +struct vCard *vcard_load(char *); +void vcard_free(struct vCard *); +void vcard_set_prop(struct vCard *v, char *name, char *value, int append); +char *vcard_get_prop(struct vCard *v, char *propname, int is_partial, + int instance, int return_propname); +char *vcard_serialize(struct vCard *); + + +/*@}*/ diff --git a/webcit/src/vcard_edit.c b/webcit/src/vcard_edit.c new file mode 100644 index 000000000..2c78477e1 --- /dev/null +++ b/webcit/src/vcard_edit.c @@ -0,0 +1,426 @@ +/* + * $Id$ + */ +/** + * \defgroup vCardEdit Handles on-screen editing of vCard objects. + * \ingroup VCards + */ +/*@{*/ +#include "webcit.h" +#include "vcard.h" + +/** + * \brief Edit the vCard component of a MIME message. + * Supply the message number + * and MIME part number to fetch. Or, specify -1 for the message number + * to start with a blank card. + * \param msgnum number of the item on the citadel server + * \param partnum what??? + * \param return_to where to go back in the browser after edit ???? + */ +void do_edit_vcard(long msgnum, char *partnum, char *return_to) { + char buf[SIZ]; + char *serialized_vcard = NULL; + size_t total_len = 0; + struct vCard *v; + int i; + char *key, *value; + char whatuser[256]; + + char lastname[256]; + char firstname[256]; + char middlename[256]; + char prefix[256]; + char suffix[256]; + char pobox[256]; + char extadr[256]; + char street[256]; + char city[256]; + char state[256]; + char zipcode[256]; + char country[256]; + char hometel[256]; + char worktel[256]; + char primary_inetemail[256]; + char other_inetemail[SIZ]; + char extrafields[SIZ]; + char fullname[256]; + char title[256]; + char org[256]; + + lastname[0] = 0; + firstname[0] = 0; + middlename[0] = 0; + prefix[0] = 0; + suffix[0] = 0; + pobox[0] = 0; + extadr[0] = 0; + street[0] = 0; + city[0] = 0; + state[0] = 0; + zipcode[0] = 0; + country[0] = 0; + hometel[0] = 0; + worktel[0] = 0; + primary_inetemail[0] = 0; + other_inetemail[0] = 0; + title[0] = 0; + org[0] = 0; + extrafields[0] = 0; + + safestrncpy(whatuser, "", sizeof whatuser); + + if (msgnum >= 0) { + sprintf(buf, "MSG0 %ld|1", msgnum); + serv_puts(buf); + serv_getln(buf, sizeof buf); + if (buf[0] != '1') { + convenience_page("770000", _("Error"), &buf[4]); + return; + } + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + if (!strncasecmp(buf, "from=", 5)) { + safestrncpy(whatuser, &buf[5], sizeof whatuser); + } + else if (!strncasecmp(buf, "node=", 5)) { + strcat(whatuser, " @ "); + strcat(whatuser, &buf[5]); + } + } + + sprintf(buf, "OPNA %ld|%s", msgnum, partnum); + serv_puts(buf); + serv_getln(buf, sizeof buf); + if (buf[0] != '2') { + convenience_page("770000", "Error", &buf[4]); + return; + } + + total_len = atoi(&buf[4]); + serialized_vcard = malloc(total_len + 2); + + read_server_binary(serialized_vcard, total_len); + + serv_puts("CLOS"); + serv_getln(buf, sizeof buf); + serialized_vcard[total_len] = 0; + + v = vcard_load(serialized_vcard); + free(serialized_vcard); + + /* Populate the variables for our form */ + i = 0; + while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) { + value = vcard_get_prop(v, "", 0, i++, 0); + + if (!strcasecmp(key, "n")) { + extract_token(lastname, value, 0, ';', sizeof lastname); + extract_token(firstname, value, 1, ';', sizeof firstname); + extract_token(middlename, value, 2, ';', sizeof middlename); + extract_token(prefix, value, 3, ';', sizeof prefix); + extract_token(suffix, value, 4, ';', sizeof suffix); + } + + else if (!strcasecmp(key, "fn")) { + safestrncpy(fullname, value, sizeof fullname); + } + + else if (!strcasecmp(key, "title")) { + safestrncpy(title, value, sizeof title); + } + + else if (!strcasecmp(key, "org")) { + safestrncpy(org, value, sizeof org); + } + + else if (!strcasecmp(key, "adr")) { + extract_token(pobox, value, 0, ';', sizeof pobox); + extract_token(extadr, value, 1, ';', sizeof extadr); + extract_token(street, value, 2, ';', sizeof street); + extract_token(city, value, 3, ';', sizeof city); + extract_token(state, value, 4, ';', sizeof state); + extract_token(zipcode, value, 5, ';', sizeof zipcode); + extract_token(country, value, 6, ';', sizeof country); + } + + else if (!strcasecmp(key, "tel;home")) { + extract_token(hometel, value, 0, ';', sizeof hometel); + } + + else if (!strcasecmp(key, "tel;work")) { + extract_token(worktel, value, 0, ';', sizeof worktel); + } + + else if (!strcasecmp(key, "email;internet")) { + if (primary_inetemail[0] == 0) { + safestrncpy(primary_inetemail, value, sizeof primary_inetemail); + } + else { + if (other_inetemail[0] != 0) { + strcat(other_inetemail, "\n"); + } + strcat(other_inetemail, value); + } + } + + else { + strcat(extrafields, key); + strcat(extrafields, ":"); + strcat(extrafields, value); + strcat(extrafields, "\n"); + } + + } + + vcard_free(v); + } + + /** Display the form */ + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n" + "
" + "" + ""); + wprintf(_("Edit contact information")); + wprintf("" + "
\n" + "
\n
\n" + ); + + wprintf("
\n"); + wprintf("
" + "
\n"); + + wprintf("" + "" + "" + "" + "" + "\n", + _("Prefix"), _("First"), _("Middle"), _("Last"), _("Suffix") + ); + wprintf("", + prefix); + wprintf("", + firstname); + wprintf("", + middlename); + wprintf("", + lastname); + wprintf("
%s%s%s%s%s
\n", + suffix); + + wprintf(""); + wprintf("
"); + + wprintf(_("Display name:")); + wprintf("
" + "

\n", + fullname + ); + + wprintf(_("Title:")); + wprintf("
" + "

\n", + title + ); + + wprintf(_("Organization:")); + wprintf("
" + "

\n", + org + ); + + wprintf("
"); + + wprintf(""); + wprintf("\n", + pobox); + wprintf("\n", + extadr); + wprintf("\n", + street); + wprintf("\n", + city); + wprintf("\n", + state); + wprintf("\n", + zipcode); + wprintf("\n", + country); + wprintf("
"); + wprintf(_("PO box:")); + wprintf("" + "
"); + wprintf(_("Address:")); + wprintf("" + "
" + "
"); + wprintf(_("City:")); + wprintf("" + "
"); + wprintf(_("State:")); + wprintf("" + "
"); + wprintf(_("ZIP code:")); + wprintf("" + "
"); + wprintf(_("Country:")); + wprintf("" + "
\n"); + + wprintf("
\n"); + + wprintf("" + "\n", + hometel); + wprintf("" + "
"); + wprintf(_("Home telephone:")); + wprintf(""); + wprintf(_("Work telephone:")); + wprintf("
\n", + worktel); + + wprintf(""); + wprintf("
"); + + wprintf("" + "
"); + wprintf(_("Primary Internet e-mail address")); + wprintf("
" + "
" + "
"); + wprintf(_("Internet e-mail aliases")); + wprintf("
" + "
\n"); + + wprintf("
\n"); + + wprintf("\n"); + + wprintf("\n"); + + wprintf("
\n" + "" + " " + "" + "
\n", + _("Save changes"), + _("Cancel") + ); + + wprintf("
\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 0) { + serv_printf("email;internet:%s", buf); + } + } + + serv_printf("%s", bstr("extrafields") ); + serv_puts("end:vcard"); + serv_puts("000"); + + if (!strcmp(bstr("return_to"), "select_user_to_edit")) { + select_user_to_edit(NULL, NULL); + } + else if (!strcmp(bstr("return_to"), "do_welcome")) { + do_welcome(); + } + else { + readloop("readnew"); + } +} + + + +/*@}*/ diff --git a/webcit/src/webcit.c b/webcit/src/webcit.c new file mode 100644 index 000000000..85ee1ca4b --- /dev/null +++ b/webcit/src/webcit.c @@ -0,0 +1,1676 @@ +/* + * $Id$ + */ +/** + * \defgroup MainServer This is the main transaction loop of the web service. It maintains a + * persistent session to the Citadel server, handling HTTP WebCit requests as + * they arrive and presenting a user interface. + * \ingroup WebcitHttpServer + */ +/*@{*/ +#include "webcit.h" +#include "groupdav.h" +#include "webserver.h" +#include "mime_parser.h" + +/** + * Subdirectories from which the client may request static content + */ +char *static_content_dirs[] = { + "static", /** static templates */ + "tiny_mce" /** the JS editor */ +}; + +/** + * String to unset the cookie. + * Any date "in the past" will work, so I chose my birthday, right down to + * the exact minute. :) + */ +static char *unset = "; expires=28-May-1971 18:10:00 GMT"; + +/** + * \brief remove escaped strings from i.e. the url string (like %20 for blanks) + * \param buf the buffer to examine + */ +void unescape_input(char *buf) +{ + int a, b; + char hex[3]; + + while ((isspace(buf[strlen(buf) - 1])) && (strlen(buf) > 0)) + buf[strlen(buf) - 1] = 0; + + for (a = 0; a < strlen(buf); ++a) { + if (buf[a] == '+') + buf[a] = ' '; + if (buf[a] == '%') { + hex[0] = buf[a + 1]; + hex[1] = buf[a + 2]; + hex[2] = 0; + b = 0; + sscanf(hex, "%02x", &b); + buf[a] = (char) b; + strcpy(&buf[a + 1], &buf[a + 3]); + } + } + +} + +/** + * \brief Extract variables from the URL. + * \param url URL supplied by the HTTP parser + */ +void addurls(char *url) +{ + char *up, *ptr; + char buf[SIZ]; + int a, b; + struct urlcontent *u; + + up = url; + while (strlen(up) > 0) { + + /** locate the = sign */ + safestrncpy(buf, up, sizeof buf); + b = (-1); + for (a = 255; a >= 0; --a) + if (buf[a] == '=') + b = a; + if (b < 0) + return; + buf[b] = 0; + + u = (struct urlcontent *) malloc(sizeof(struct urlcontent)); + u->next = WC->urlstrings; + WC->urlstrings = u; + safestrncpy(u->url_key, buf, sizeof u->url_key); + + /** now chop that part off */ + for (a = 0; a <= b; ++a) + ++up; + + /** locate "&" and "?" delimiters */ + ptr = up; + b = strlen(up); + for (a = 0; a < strlen(up); ++a) { + if ( (ptr[0] == '&') || (ptr[0] == '?') ) { + b = a; + break; + } + ++ptr; + } + ptr = up; + for (a = 0; a < b; ++a) + ++ptr; + strcpy(ptr, ""); + + u->url_data = malloc(strlen(up) + 2); + safestrncpy(u->url_data, up, strlen(up) + 1); + u->url_data[b] = 0; + unescape_input(u->url_data); + up = ptr; + ++up; + } +} + +/** + * \brief free urlstring memory + */ +void free_urls(void) +{ + struct urlcontent *u; + + while (WC->urlstrings != NULL) { + free(WC->urlstrings->url_data); + u = WC->urlstrings->next; + free(WC->urlstrings); + WC->urlstrings = u; + } +} + +/** + * \brief Diagnostic function to display the contents of all variables + */ +void dump_vars(void) +{ + struct urlcontent *u; + + for (u = WC->urlstrings; u != NULL; u = u->next) { + wprintf("%38s = %s\n", u->url_key, u->url_data); + } +} + +/** + * \brief Return the value of a variable supplied to the current web page (from the url or a form) + * \param key The name of the variable we want + */ +char *bstr(char *key) +{ + struct urlcontent *u; + + for (u = WC->urlstrings; u != NULL; u = u->next) { + if (!strcasecmp(u->url_key, key)) + return (u->url_data); + } + return (""); +} + +/** + * \brief web-printing funcion. uses our vsnprintf wrapper + * \param format printf format string + * \param ... the varargs to put into formatstring + */ +void wprintf(const char *format,...) +{ + va_list arg_ptr; + char wbuf[4096]; + + va_start(arg_ptr, format); + vsnprintf(wbuf, sizeof wbuf, format, arg_ptr); + va_end(arg_ptr); + + client_write(wbuf, strlen(wbuf)); +} + + +/** + * \brief wrap up an HTTP session, closes tags, etc. + * \todo multiline params? + * \param print_standard_html_footer should be set to 0 to transmit only, 1 to + * append the main menu and closing tags, or 2 to + * append the closing tags only. + */ +void wDumpContent(int print_standard_html_footer) +{ + if (print_standard_html_footer) { + wprintf("
\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 section and opener */ + + int do_room_banner, /**< 0=no, 1=yes, + * 2 = I'm going to embed my own, so don't open the + *
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("
\n"); + wprintf("" + "%s
\n", WC->ImportantMessage); + wprintf("
\n"); + wprintf("\n"); + safestrncpy(WC->ImportantMessage, "", sizeof WC->ImportantMessage); + } + + if ( (WC->logged_in) && (!unset_cookies) ) { + wprintf("
"); + do_selected_iconbar(); + /** check for instant messages (these display in a new window) */ + page_popup(); + wprintf("
"); + } + + if (do_room_banner == 1) { + wprintf("
\n"); + embed_room_banner(NULL, navbar_default); + wprintf("
\n"); + } + } + + if (do_room_banner == 1) { + wprintf("
\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(""); + wprintf("Go here.", whichpage); + wprintf("\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("
\n"); + wprintf("
", titlebarcolor); + wprintf("%s\n", titlebarmsg); + wprintf("
\n"); + wprintf("
\n
\n"); + escputs(messagetext); + + wprintf("
\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("this_page); + wprintf("\">"); + wprintf(_("Make this my start page")); + wprintf(""); +/* + wprintf("
wc_roomname); + wprintf("\" title=\"RSS 2.0 feed for "); + escputs(WC->wc_roomname); + wprintf("\">\"RSS\"\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("

"); + wprintf(_("Authorization Required")); + wprintf("

\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
\n", WC->wc_session); + wprintf("Command:
\n");
+		escputs(cmd);
+		wprintf("

\n"); + wprintf("Variables:
\n");
+		dump_vars();
+		wprintf("

\n"); + wDumpContent(1); + } else if (!strcasecmp(action, "updatenote")) { + updatenote(); + } + + /** When all else fais, display the main menu. */ + else { + display_main_menu(); + } + +SKIP_ALL_THIS_CRAP: + fflush(stdout); + if (content != NULL) { + free(content); + content = NULL; + } + free_urls(); + if (WC->upload_length > 0) { + free(WC->upload); + WC->upload_length = 0; + } +} + + +/*@}*/ diff --git a/webcit/src/webcit.h b/webcit/src/webcit.h new file mode 100644 index 000000000..92eae15fd --- /dev/null +++ b/webcit/src/webcit.h @@ -0,0 +1,746 @@ +/* $Id$ */ + +/** we need _GNU_SOURCE for various functions arround the NLS-Stuff */ +#define _GNU_SOURCE + + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifdef HAVE_LIMITS_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +#ifdef HAVE_ICONV +#include +#endif + +#ifdef ENABLE_NLS +#include +#include +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 +#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 +#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 +#include +#include +#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 /**< ext & 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 \\\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; /** 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 +int ZEXPORT compress_gzip(Bytef * dest, uLongf * destLen, + const Bytef * source, uLong sourceLen, int level); +#endif + +#ifdef HAVE_ICONV +void utf8ify_rfc822_string(char *buf); +#endif + +void begin_burst(void); +void end_burst(void); + +extern char *hourname[]; /**< Names of hours (12am, 1am, etc.) */ + +void http_datestring(char *buf, size_t n, time_t xtime); + + +/** Views (from citadel.h) */ +#define VIEW_BBS 0 /**< Traditional Citadel BBS view */ +#define VIEW_MAILBOX 1 /**< Mailbox summary */ +#define VIEW_ADDRESSBOOK 2 /**< Address book view */ +#define VIEW_CALENDAR 3 /**< Calendar view */ +#define VIEW_TASKS 4 /**< Tasks view */ +#define VIEW_NOTES 5 /**< Notes view */ +#define VIEW_WIKI 6 /**< Wiki view */ +#define VIEW_CALBRIEF 7 /**< Brief Calendar view */ + + +/* These should be empty, but we have them for testing */ +#define DEFAULT_HTTPAUTH_USER "" +#define DEFAULT_HTTPAUTH_PASS "" + diff --git a/webcit/src/webserver.c b/webcit/src/webserver.c new file mode 100644 index 000000000..985add616 --- /dev/null +++ b/webcit/src/webserver.c @@ -0,0 +1,764 @@ +/* + * $Id$ + */ +/** + * \defgroup Webserver This contains a simple multithreaded TCP server manager. It sits around + * waiting on the specified port for incoming HTTP connections. When a + * connection is established, it calls context_loop() from context_loop.c. + * \ingroup WebcitHttpServer + */ + +/*@{*/ +#include "webcit.h" +#include "webserver.h" + +#ifndef HAVE_SNPRINTF +int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp); +#endif + +int verbosity = 9; /**< Logging level */ +int msock; /**< master listening socket */ +int is_https = 0; /**< Nonzero if I am an HTTPS service */ +int follow_xff = 0; /**< Follow X-Forwarded-For: header */ +extern void *context_loop(int); +extern void *housekeeping_loop(void); +extern pthread_mutex_t SessionListMutex; +extern pthread_key_t MyConKey; + + +char *server_cookie = NULL; /**< our Cookie connection to the client */ + +int http_port = PORT_NUM; /**< Port to listen on */ + +char *ctdlhost = DEFAULT_HOST; /**< our name */ +char *ctdlport = DEFAULT_PORT; /**< our Port */ +int setup_wizard = 0; /**< should we run the setup wizard? \todo */ +char wizard_filename[PATH_MAX];/**< where's the setup wizard? */ + +/** + * \brief This is a generic function to set up a master socket for listening on + * a TCP port. The server shuts down if the bind fails. + * \param ip_addr ip to bind to + * \param port_number the port to bind to + * \param queue_len the size of the input queue ???? + */ +int ig_tcp_server(char *ip_addr, int port_number, int queue_len) +{ + struct sockaddr_in sin; + int s, i; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + if (ip_addr == NULL) { + sin.sin_addr.s_addr = INADDR_ANY; + } else { + sin.sin_addr.s_addr = inet_addr(ip_addr); + } + + if (sin.sin_addr.s_addr == INADDR_NONE) { + sin.sin_addr.s_addr = INADDR_ANY; + } + + if (port_number == 0) { + lprintf(1, "Cannot start: no port number specified.\n"); + exit(1); + } + sin.sin_port = htons((u_short) port_number); + + s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto)); + if (s < 0) { + lprintf(1, "Can't create a socket: %s\n", strerror(errno)); + exit(errno); + } + /** Set some socket options that make sense. */ + i = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); + + if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + lprintf(1, "Can't bind: %s\n", strerror(errno)); + exit(errno); + } + if (listen(s, queue_len) < 0) { + lprintf(1, "Can't listen: %s\n", strerror(errno)); + exit(errno); + } + return (s); +} + + + +/** + * \brief Create a Unix domain socket and listen on it + * \param sockpath file name of the unix domain socket + * \param queue_len queue size of the kernel fifo???? + */ +int ig_uds_server(char *sockpath, int queue_len) +{ + struct sockaddr_un addr; + int s; + int i; + int actual_queue_len; + + actual_queue_len = queue_len; + if (actual_queue_len < 5) actual_queue_len = 5; + + i = unlink(sockpath); + if (i != 0) if (errno != ENOENT) { + lprintf(1, "citserver: can't unlink %s: %s\n", + sockpath, strerror(errno)); + exit(errno); + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + lprintf(1, "citserver: Can't create a socket: %s\n", + strerror(errno)); + exit(errno); + } + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + lprintf(1, "citserver: Can't bind: %s\n", + strerror(errno)); + exit(errno); + } + + if (listen(s, actual_queue_len) < 0) { + lprintf(1, "citserver: Can't listen: %s\n", + strerror(errno)); + exit(errno); + } + + chmod(sockpath, 0777); + return(s); +} + + + + +/** + * \brief Read data from the client socket. + * \param sock socket fd to read from ??? + * \param buf buffer to read into + * \param bytes how large is the read buffer? + * \param timeout how long should we wait for input? + * \return values are\ + * 1 Requested number of bytes has been read.\ + * 0 Request timed out.\ + * -1 Connection is broken, or other error. + */ +int client_read_to(int sock, char *buf, int bytes, int timeout) +{ + int len, rlen; + fd_set rfds; + struct timeval tv; + int retval; + + +#ifdef HAVE_OPENSSL + if (is_https) { + return (client_read_ssl(buf, bytes, timeout)); + } +#endif + + len = 0; + while (len < bytes) { + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + + retval = select((sock) + 1, &rfds, NULL, NULL, &tv); + if (FD_ISSET(sock, &rfds) == 0) { + return (0); + } + + rlen = read(sock, &buf[len], bytes - len); + + if (rlen < 1) { + lprintf(2, "client_read() failed: %s\n", + strerror(errno)); + return (-1); + } + len = len + rlen; + } + +#ifdef HTTP_TRACING + write(2, "\033[32m", 5); + write(2, buf, bytes); + write(2, "\033[30m", 5); +#endif + return (1); +} + +/** + * \brief write data to the client + * \param buf data to write to the client + * \param count size of buffer + */ +ssize_t client_write(const void *buf, size_t count) +{ + char *newptr; + size_t newalloc; + + if (WC->burst != NULL) { + if ((WC->burst_len + count) >= WC->burst_alloc) { + newalloc = (WC->burst_alloc * 2); + if ((WC->burst_len + count) >= newalloc) { + newalloc += count; + } + newptr = realloc(WC->burst, newalloc); + if (newptr != NULL) { + WC->burst = newptr; + WC->burst_alloc = newalloc; + } + } + if ((WC->burst_len + count) < WC->burst_alloc) { + memcpy(&WC->burst[WC->burst_len], buf, count); + WC->burst_len += count; + return (count); + } + else { + return(-1); + } + } +#ifdef HAVE_OPENSSL + if (is_https) { + client_write_ssl((char *) buf, count); + return (count); + } +#endif +#ifdef HTTP_TRACING + write(2, "\033[34m", 5); + write(2, buf, count); + write(2, "\033[30m", 5); +#endif + return (write(WC->http_sock, buf, count)); +} + +/** + * \brief what burst??? + */ +void begin_burst(void) +{ + if (WC->burst != NULL) { + free(WC->burst); + WC->burst = NULL; + } + WC->burst_len = 0; + WC->burst_alloc = 32768; + WC->burst = malloc(WC->burst_alloc); +} + + +/** + * \brief uses the same calling syntax as compress2(), but it + * creates a stream compatible with HTTP "Content-encoding: gzip" + */ +#ifdef HAVE_ZLIB +#define DEF_MEM_LEVEL 8 /**< memlevel??? */ +#define OS_CODE 0x03 /**< unix */ +int ZEXPORT compress_gzip(Bytef * dest, /**< compressed buffer*/ + uLongf * destLen, /**< length of the compresed data */ + const Bytef * source, /**< source to encode */ + uLong sourceLen, /**< length of the source to encode */ + int level) /**< what level??? */ +{ + const int gz_magic[2] = { 0x1f, 0x8b }; /** gzip magic header */ + + /** write gzip header */ + sprintf((char *) dest, "%c%c%c%c%c%c%c%c%c%c", + gz_magic[0], gz_magic[1], Z_DEFLATED, + 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /** xflags */ , + OS_CODE); + + /* normal deflate */ + z_stream stream; + int err; + stream.next_in = (Bytef *) source; + stream.avail_in = (uInt) sourceLen; + stream.next_out = dest + 10L; // after header + stream.avail_out = (uInt) * destLen; + if ((uLong) stream.avail_out != *destLen) + return Z_BUF_ERROR; + + stream.zalloc = (alloc_func) 0; + stream.zfree = (free_func) 0; + stream.opaque = (voidpf) 0; + + err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS, + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out + 10L; + + /* write CRC and Length */ + uLong crc = crc32(0L, source, sourceLen); + int n; + for (n = 0; n < 4; ++n, ++*destLen) { + dest[*destLen] = (int) (crc & 0xff); + crc >>= 8; + } + uLong len = stream.total_in; + for (n = 0; n < 4; ++n, ++*destLen) { + dest[*destLen] = (int) (len & 0xff); + len >>= 8; + } + err = deflateEnd(&stream); + return err; +} +#endif + +/** + * \brief what burst??? + */ +void end_burst(void) +{ + size_t the_len; + char *the_data; + + if (WC->burst == NULL) + return; + + the_len = WC->burst_len; + the_data = WC->burst; + + WC->burst_len = 0; + WC->burst_alloc = 0; + WC->burst = NULL; + +#ifdef HAVE_ZLIB + /* Handle gzip compression */ + if (WC->gzip_ok) { + char *compressed_data = NULL; + uLongf compressed_len; + + compressed_len = (uLongf) ((the_len * 101) / 100) + 100; + compressed_data = malloc(compressed_len); + + if (compress_gzip((Bytef *) compressed_data, + &compressed_len, + (Bytef *) the_data, + (uLongf) the_len, Z_BEST_SPEED) == Z_OK) { + wprintf("Content-encoding: gzip\r\n"); + free(the_data); + the_data = compressed_data; + the_len = compressed_len; + } else { + free(compressed_data); + } + } +#endif /* HAVE_ZLIB */ + + wprintf("Content-length: %d\r\n\r\n", the_len); + client_write(the_data, the_len); + free(the_data); + return; +} + + + +/** + * \brief Read data from the client socket with default timeout. + * (This is implemented in terms of client_read_to() and could be + * justifiably moved out of sysdep.c) + * \param sock the socket fd to read from??? + * \param buf the buffer to write to + * \param bytes how large is the buffer + */ +int client_read(int sock, char *buf, int bytes) +{ + return (client_read_to(sock, buf, bytes, SLEEPING)); +} + + +/** + * \brief Get a LF-terminated line of text from the client. + * (This is implemented in terms of client_read() and could be + * justifiably moved out of sysdep.c) + * \param sock socket fd to get client line from??? + * \param buf buffer to write read data to + * \param bufsiz how many bytes to read + * \return numer of bytes read??? + */ +int client_getln(int sock, char *buf, int bufsiz) +{ + int i, retval; + + /** Read one character at a time.*/ + for (i = 0;; i++) { + retval = client_read(sock, &buf[i], 1); + if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1)) + break; + if ( (!isspace(buf[i])) && (!isprint(buf[i])) ) { + /** Non printable character recieved from client */ + return(-1); + } + } + + /** If we got a long line, discard characters until the newline. */ + if (i == (bufsiz-1)) + while (buf[i] != '\n' && retval == 1) + retval = client_read(sock, &buf[i], 1); + + /** + * Strip any trailing non-printable characters. + */ + buf[i] = 0; + while ((strlen(buf) > 0) && (!isprint(buf[strlen(buf) - 1]))) { + buf[strlen(buf) - 1] = 0; + } + return (retval); +} + + +/** + * \brief Start running as a daemon. + * + * param do_close_stdio Only close stdio if set. + */ +void start_daemon(int do_close_stdio) +{ + if (do_close_stdio) { + /* close(0); */ + close(1); + close(2); + } + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + if (fork() != 0) { + exit(0); + } +} + +/** + * \brief Spawn an additional worker thread into the pool. + */ +void spawn_another_worker_thread() +{ + pthread_t SessThread; /**< Thread descriptor */ + pthread_attr_t attr; /**< Thread attributes */ + int ret; + + lprintf(3, "Creating a new thread\n"); + + /** set attributes for the new thread */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + /** + * Our per-thread stacks need to be bigger than the default size, otherwise + * the MIME parser crashes on FreeBSD, and the IMAP service crashes on + * 64-bit Linux. + */ + if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) { + lprintf(1, "pthread_attr_setstacksize: %s\n", + strerror(ret)); + pthread_attr_destroy(&attr); + } + + /** now create the thread */ + if (pthread_create(&SessThread, &attr, + (void *(*)(void *)) worker_entry, NULL) + != 0) { + lprintf(1, "Can't create thread: %s\n", strerror(errno)); + } + + /** free up the attributes */ + pthread_attr_destroy(&attr); +} + +/** + * \brief Here's where it all begins. + * \param argc number of commandline args + * \param argv the commandline arguments + */ +int main(int argc, char **argv) +{ + pthread_t SessThread; /**< Thread descriptor */ + pthread_attr_t attr; /**< Thread attributes */ + int a, i; /**< General-purpose variables */ + char tracefile[PATH_MAX]; + char ip_addr[256]; + char *webcitdir = WEBCITDIR; +#ifdef ENABLE_NLS + char *locale = NULL; + char *mo = NULL; +#endif /* ENABLE_NLS */ + char uds_listen_path[PATH_MAX]; /**< listen on a unix domain socket? */ + + strcpy(uds_listen_path, ""); + + /** Parse command line */ +#ifdef HAVE_OPENSSL + while ((a = getopt(argc, argv, "h:i:p:t:x:cfs")) != EOF) +#else + while ((a = getopt(argc, argv, "h:i:p:t:x:cf")) != EOF) +#endif + switch (a) { + case 'h': + webcitdir = strdup(optarg); + break; + case 'i': + safestrncpy(ip_addr, optarg, sizeof ip_addr); + break; + case 'p': + http_port = atoi(optarg); + if (http_port == 0) { + safestrncpy(uds_listen_path, optarg, sizeof uds_listen_path); + } + break; + case 't': + safestrncpy(tracefile, optarg, sizeof tracefile); + freopen(tracefile, "w", stdout); + freopen(tracefile, "w", stderr); + freopen(tracefile, "r", stdin); + break; + case 'x': + verbosity = atoi(optarg); + break; + case 'f': + follow_xff = 1; + break; + case 'c': + server_cookie = malloc(256); + if (server_cookie != NULL) { + safestrncpy(server_cookie, + "Set-cookie: wcserver=", + 256); + if (gethostname + (&server_cookie[strlen(server_cookie)], + 200) != 0) { + lprintf(2, "gethostname: %s\n", + strerror(errno)); + free(server_cookie); + } + } + break; + case 's': + is_https = 1; + break; + default: + fprintf(stderr, "usage: webserver " + "[-i ip_addr] [-p http_port] " + "[-t tracefile] [-c] [-f] " +#ifdef HAVE_OPENSSL + "[-s] " +#endif + "[remotehost [remoteport]]\n"); + return 1; + } + + if (optind < argc) { + ctdlhost = argv[optind]; + if (++optind < argc) + ctdlport = argv[optind]; + } + /** Tell 'em who's in da house */ + lprintf(1, SERVER "\n"); + lprintf(1, "Copyright (C) 1996-2006 by the Citadel development team.\n" + "This software is distributed under the terms of the " + "GNU General Public License.\n\n" + ); + + lprintf(9, "Changing directory to %s\n", webcitdir); + if (chdir(webcitdir) != 0) { + perror("chdir"); + } + + /** initialize the International Bright Young Thing */ +#ifdef ENABLE_NLS + + initialize_locales(); + + locale = setlocale(LC_ALL, ""); + + mo = malloc(strlen(webcitdir) + 20); + sprintf(mo, "%s/locale", webcitdir); + lprintf(9, "Message catalog directory: %s\n", + bindtextdomain("webcit", mo) + ); + free(mo); + lprintf(9, "Text domain: %s\n", + textdomain("webcit") + ); + lprintf(9, "Text domain Charset: %s\n", + bind_textdomain_codeset("webcit","UTF8") + ); +#endif + + initialize_viewdefs(); + initialize_axdefs(); + + /** + * Set up a place to put thread-specific data. + * We only need a single pointer per thread - it points to the + * wcsession struct to which the thread is currently bound. + */ + if (pthread_key_create(&MyConKey, NULL) != 0) { + lprintf(1, "Can't create TSD key: %s\n", strerror(errno)); + } + + /** + * Set up a place to put thread-specific SSL data. + * We don't stick this in the wcsession struct because SSL starts + * up before the session is bound, and it gets torn down between + * transactions. + */ +#ifdef HAVE_OPENSSL + if (pthread_key_create(&ThreadSSL, NULL) != 0) { + lprintf(1, "Can't create TSD key: %s\n", strerror(errno)); + } +#endif + + /** + * Bind the server to our favorite port. + * There is no need to check for errors, because ig_tcp_server() + * exits if it doesn't succeed. + */ + + if (strlen(uds_listen_path) > 0) { + lprintf(2, "Attempting to create listener socket at %s...\n", uds_listen_path); + msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH); + } + else { + lprintf(2, "Attempting to bind to port %d...\n", http_port); + msock = ig_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH); + } + + lprintf(2, "Listening on socket %d\n", msock); + signal(SIGPIPE, SIG_IGN); + + pthread_mutex_init(&SessionListMutex, NULL); + + /** + * Start up the housekeeping thread + */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&SessThread, &attr, + (void *(*)(void *)) housekeeping_loop, NULL); + + + /** + * If this is an HTTPS server, fire up SSL + */ +#ifdef HAVE_OPENSSL + if (is_https) { + init_ssl(); + } +#endif + + /** Start a few initial worker threads */ + for (i = 0; i < (MIN_WORKER_THREADS); ++i) { + spawn_another_worker_thread(); + } + + /* now the original thread becomes another worker */ + worker_entry(); + return 0; +} + + +/** + * Entry point for worker threads + */ +void worker_entry(void) +{ + int ssock; + int i = 0; + int time_to_die = 0; + int fail_this_transaction = 0; + + do { + /** Only one thread can accept at a time */ + fail_this_transaction = 0; + ssock = accept(msock, NULL, 0); + if (ssock < 0) { + lprintf(2, "accept() failed: %s\n", + strerror(errno)); + } else { + /** Set the SO_REUSEADDR socket option */ + i = 1; + setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, + &i, sizeof(i)); + + /** If we are an HTTPS server, go crypto now. */ +#ifdef HAVE_OPENSSL + if (is_https) { + if (starttls(ssock) != 0) { + fail_this_transaction = 1; + close(ssock); + } + } +#endif + + if (fail_this_transaction == 0) { + /** Perform an HTTP transaction... */ + context_loop(ssock); + /** ...and close the socket. */ + lingering_close(ssock); + } + + } + + } while (!time_to_die); + + pthread_exit(NULL); +} + +/** + * \brief logprintf. log messages + * logs to stderr if loglevel is lower than the verbosity set at startup + * \param loglevel level of the message + * \param format the printf like format string + * \param ... the strings to put into format + */ +int lprintf(int loglevel, const char *format, ...) +{ + va_list ap; + + if (loglevel <= verbosity) { + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fflush(stderr); + } + return 1; +} + + +/** + * \brief print the actual stack frame. + */ +void wc_backtrace(void) +{ +#ifdef HAVE_BACKTRACE + void *stack_frames[50]; + size_t size, i; + char **strings; + + + size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*)); + strings = backtrace_symbols(stack_frames, size); + for (i = 0; i < size; i++) { + if (strings != NULL) + lprintf(1, "%s\n", strings[i]); + else + lprintf(1, "%p\n", stack_frames[i]); + } + free(strings); +#endif +} + +/*@}*/ diff --git a/webcit/src/webserver.h b/webcit/src/webserver.h new file mode 100644 index 000000000..5809b1aa9 --- /dev/null +++ b/webcit/src/webserver.h @@ -0,0 +1,6 @@ +/* $Id$ */ +int client_getln(int sock, char *buf, int bufsiz); +int client_read(int sock, char *buf, int bytes); +int client_read_to(int sock, char *buf, int bytes, int timeout); +ssize_t client_write(const void *buf, size_t count); +int lprintf(int loglevel, const char *format, ...); diff --git a/webcit/src/who.c b/webcit/src/who.c new file mode 100644 index 000000000..e81b911e9 --- /dev/null +++ b/webcit/src/who.c @@ -0,0 +1,281 @@ +/* + * $Id$ + */ +/** + * \defgroup DislpayWho Display a list of all users currently logged on to the Citadel server. + * \ingroup WebcitDisplayItems + */ +/*@{*/ +#include "webcit.h" + + + +/** + * \brief Display inner div of Wholist + */ +void who_inner_div(void) { + char buf[SIZ], user[SIZ], room[SIZ], host[SIZ], + realroom[SIZ], realhost[SIZ]; + int sess; + time_t last_activity; + time_t now; + int bg = 0; + + wprintf("" + "\n"); + wprintf("\n"); + wprintf("\n", _("User name")); + wprintf("", _("Room")); + wprintf("\n\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("", + (bg ? "DDDDDD" : "FFFFFF") + ); + + + wprintf(""); + + /** (link to page this user) */ + wprintf(""); + + /** (idle flag) */ + wprintf("\n\n\t\n\t\n"); + } + } + wprintf("
%s%s%s
"); + if ((WC->is_aide) && + (sess != WC->ctdl_pid)) { + wprintf(" %s", _("(kill)")); + } + if (sess == WC->ctdl_pid) { + wprintf(" %s", _("(edit)")); + } + wprintf("" + " "); + wprintf(""); + if ((now - last_activity) > 900L) { + wprintf(" " + ""); + } + else { + wprintf(" " + ""); + } + wprintf(""); + + + + /** username (link to user bio/photo page) */ + wprintf(""); + escputs(user); + wprintf(""); + + /** room */ + wprintf(""); + escputs(room); + if (strlen(realroom) > 0) { + wprintf("
"); + escputs(realroom); + wprintf(""); + } + wprintf("
"); + + /** hostname */ + escputs(host); + if (strlen(realhost) > 0) { + wprintf("
"); + escputs(realhost); + wprintf(""); + } + wprintf("
"); +} + + +/** + * \brief who is on? + */ +void who(void) +{ + char title[256]; + + output_headers(1, 1, 2, 0, 0, 0); + + wprintf("\n", _("Do you really want to kill this session?") + ); + + wprintf("
\n"); + wprintf("
"); + wprintf("\""); + wprintf(" "); + + snprintf(title, sizeof title, _("Users currently on %s"), serv_info.serv_humannode); + escputs(title); + + wprintf(""); + offer_start_page(); + wprintf("
\n"); + wprintf("
\n"); + + wprintf("
\n"); + + wprintf("
"); + who_inner_div(); + wprintf("
\n"); + + wprintf("
"); + wprintf(_("Click on a name to read user info. Click on %s " + "to send an instant message to that user."), + "\"(p)\"" + ); + wprintf("
\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( + " \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("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Edit your session display")); + wprintf("
\n"); + wprintf("
\n
\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("
\n"); + + wprintf("
\n"); + + wprintf("\n"); + + wprintf("\n\n\n\n"); + + wprintf("\n\n\n"); + + if (WC->is_aide) { + wprintf("\n\n\n"); + } + wprintf("
"); + wprintf(_("Room name:")); + wprintf(""); + wprintf("\n"); + wprintf(""); + wprintf("", + _("Change room name")); + wprintf("
"); + wprintf(_("Host name:")); + wprintf(""); + wprintf("\n"); + wprintf(""); + wprintf("", + _("Change host name")); + wprintf("
"); + wprintf(_("User name:")); + wprintf(""); + wprintf("\n"); + wprintf(""); + wprintf("", + _("Change user name")); + wprintf("
"); + wprintf("", + _("Cancel")); + wprintf("
\n"); + wprintf("
\n"); + wDumpContent(1); + } +} + + +/*@}*/ diff --git a/webcit/src/wiki.c b/webcit/src/wiki.c new file mode 100644 index 000000000..0f35b6150 --- /dev/null +++ b/webcit/src/wiki.c @@ -0,0 +1,110 @@ +/* + * $Id: $ + */ +/** + * + * \defgroup Wiki Wiki; Functions pertaining to rooms with a wiki view + * \ingroup WebcitDisplayItems + */ + +/*@{*/ +#include "webcit.h" +#include "groupdav.h" + + + +/** + * \brief Convert a string to something suitable as a wiki index + * + * \param s The string to be converted. + */ +void str_wiki_index(char *s) +{ + int i; + + if (s == NULL) return; + + /* First remove all non-alphanumeric characters */ + for (i=0; i 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("

" + "
" + "" + "
" + ); + wprintf("
"); + wprintf(_("There is no page called '%s' here."), pagename); + wprintf("

"); + wprintf(_("Select the 'Edit this page' link in the room banner " + "if you would like to create this page.")); + wprintf("

"); + wprintf("
\n"); + wDumpContent(1); +} + + +/** @} */ diff --git a/webcit/subst.c b/webcit/subst.c deleted file mode 100644 index 435930698..000000000 --- a/webcit/subst.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup Subst Variable substitution type stuff - * \ingroup CitadelConfig - */ - -/*@{*/ - -#include "webcit.h" - - -/** - * \brief Clear out the list of substitution variables local to this session - */ -void clear_local_substs(void) { - struct wcsubst *ptr; - - while (WC->vars != NULL) { - ptr = WC->vars->next; - - if ((WC->vars->wcs_type == WCS_STRING) - || (WC->vars->wcs_type == WCS_SERVCMD)) { - free(WC->vars->wcs_value); - } - - free(WC->vars); - WC->vars = ptr; - } - - WC->vars = NULL; -} - - -/* - * \brief Add a substitution variable (local to this session) - * \param keyname the replacementstring to substitute - * \param keytype the kind of the key - * \param format the format string ala printf - * \param ... the arguments to substitute in the formatstring - */ -void svprintf(char *keyname, int keytype, const char *format,...) -{ - va_list arg_ptr; - char wbuf[SIZ]; - struct wcsubst *ptr = NULL; - struct wcsubst *scan; - - /** - * First scan through to see if we're doing a replacement of - * an existing key - */ - for (scan=WC->vars; scan!=NULL; scan=scan->next) { - if (!strcasecmp(scan->wcs_key, keyname)) { - ptr = scan; - free(ptr->wcs_value); - } - } - - /** Otherwise allocate a new one */ - if (ptr == NULL) { - ptr = (struct wcsubst *) malloc(sizeof(struct wcsubst)); - ptr->next = WC->vars; - safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key); - WC->vars = ptr; - } - - /** Format the string and save it */ - - va_start(arg_ptr, format); - vsnprintf(wbuf, sizeof wbuf, format, arg_ptr); - va_end(arg_ptr); - - ptr->wcs_type = keytype; - ptr->wcs_value = strdup(wbuf); -} - -/** - * \brief Add a substitution variable (local to this session) that does a callback - * \param keyname the keystring to substitute - * \param fcn_ptr the function callback to give the substitution string - */ -void svcallback(char *keyname, void (*fcn_ptr)() ) -{ - struct wcsubst *ptr; - - ptr = (struct wcsubst *) malloc(sizeof(struct wcsubst)); - ptr->next = WC->vars; - ptr->wcs_type = WCS_FUNCTION; - strcpy(ptr->wcs_key, keyname); - ptr->wcs_function = fcn_ptr; - WC->vars = ptr; -} - - - -/** - * \brief back end for print_value_of() ... does a server command - * \param servcmd server command to execute on the citadel server - */ -void pvo_do_cmd(char *servcmd) { - char buf[SIZ]; - - serv_puts(servcmd); - serv_getln(buf, sizeof buf); - - switch(buf[0]) { - case '2': - case '3': - case '5': - wprintf("%s\n", &buf[4]); - break; - case '1': - fmout("CENTER"); - break; - case '4': - wprintf("%s\n", &buf[4]); - serv_puts("000"); - break; - } -} - - - -/** - * \brief Print the value of a variable - * \param keyname get a key to print - */ -void print_value_of(char *keyname) { - struct wcsubst *ptr; - void *fcn(); - - if (keyname[0] == '=') { - do_template(&keyname[1]); - } - - if (!strcasecmp(keyname, "SERV_PID")) { - wprintf("%d", WC->ctdl_pid); - } - - else if (!strcasecmp(keyname, "SERV_NODENAME")) { - escputs(serv_info.serv_nodename); - } - - else if (!strcasecmp(keyname, "SERV_HUMANNODE")) { - escputs(serv_info.serv_humannode); - } - - else if (!strcasecmp(keyname, "SERV_FQDN")) { - escputs(serv_info.serv_fqdn); - } - - else if (!strcasecmp(keyname, "SERV_SOFTWARE")) { - escputs(serv_info.serv_software); - } - - else if (!strcasecmp(keyname, "SERV_REV_LEVEL")) { - wprintf("%d.%02d", - serv_info.serv_rev_level / 100, - serv_info.serv_rev_level % 100 - ); - } - - else if (!strcasecmp(keyname, "SERV_BBS_CITY")) { - escputs(serv_info.serv_bbs_city); - } - - else if (!strcasecmp(keyname, "CURRENT_USER")) { - escputs(WC->wc_fullname); - } - - else if (!strcasecmp(keyname, "CURRENT_ROOM")) { - escputs(WC->wc_roomname); - } - - /** Page-local variables */ - else for (ptr = WC->vars; ptr != NULL; ptr = ptr->next) { - if (!strcasecmp(ptr->wcs_key, keyname)) { - if (ptr->wcs_type == WCS_STRING) { - wprintf("%s", ptr->wcs_value); - } - else if (ptr->wcs_type == WCS_SERVCMD) { - pvo_do_cmd(ptr->wcs_value); - } - else if (ptr->wcs_type == WCS_FUNCTION) { - (*ptr->wcs_function) (); - } - } - } -} - - - -/** - * \brief Display a variable-substituted template - * \param templatename template file to load - */ -void do_template(void *templatename) { - char filename[PATH_MAX]; - FILE *fp; - char inbuf[1024]; - char outbuf[sizeof inbuf]; - char key[sizeof inbuf]; - int i, pos; - - strcpy(filename, "static/"); - strcat(filename, templatename); - if (WC->is_wap) - strcat(filename, ".wml"); - else - strcat(filename, ".html"); - - fp = fopen(filename, "r"); - if (fp == NULL) { - wprintf(_("ERROR: could not open template ")); - wprintf("'%s' - %s
\n", - templatename, strerror(errno)); - return; - } - - strcpy(inbuf, ""); - - while (fgets(inbuf, sizeof inbuf, fp) != NULL) { - strcpy(outbuf, ""); - - while (strlen(inbuf) > 0) { - pos = (-1); - for (i=strlen(inbuf); i>=0; --i) { - if ((inbuf[i]=='<')&&(inbuf[i+1]=='?')) pos = i; - } - if (pos < 0) { - wprintf("%s", inbuf); - strcpy(inbuf, ""); - } - else { - strncpy(outbuf, inbuf, pos); - outbuf[pos] = 0; - wprintf("%s", outbuf); - strcpy(inbuf, &inbuf[pos]); - pos = 1; - for (i=strlen(inbuf); i>=0; --i) { - if (inbuf[i]=='>') pos = i; - } - strncpy(key, &inbuf[2], pos-2); - key[pos-2] = 0; - print_value_of(key); - strcpy(inbuf, &inbuf[pos+1]); - } - } - } - - fclose(fp); -} - - - -/*@}*/ diff --git a/webcit/summary.c b/webcit/summary.c deleted file mode 100644 index 3d9940389..000000000 --- a/webcit/summary.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup SymaryFuncs Displays the "Summary Page" - * \ingroup WebcitDisplayItems - */ -/*@{*/ -#include "webcit.h" - -/** - * \brief Display today's date in a friendly format - */ -void output_date(void) { - struct tm tm; - time_t now; - char buf[128]; - - time(&now); - localtime_r(&now, &tm); - - wc_strftime(buf, 32, "%A, %x", &tm); - wprintf("%s", buf); -} - - - - -/** - * \brief Dummy section - */ -void dummy_section(void) { - svprintf("BOXTITLE", WCS_STRING, "(dummy 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("\n"); - for (i=0; i\n", - extract_int(&buf[4], 1), - extract_int(&buf[4], 2) - ); - } - } - wprintf("
"); - escputs(room); - wprintf("%d/%d
\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("
\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(""); - wprintf(_("(None)")); - wprintf("
\n"); - } - else { - for (i=0; imsgarr[i]); - } - } - - calendar_summary_view(); - -#else /* WEBCIT_WITH_CALENDAR_SERVICE */ - wprintf(""); - wprintf(_("(This server does not support task lists)")); - wprintf("\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(""); - wprintf(_("(Nothing)")); - wprintf("
\n"); - } - else { - for (i=0; imsgarr[i]); - } - calendar_summary_view(); - } - -#else /* WEBCIT_WITH_CALENDAR_SERVICE */ - wprintf(""); - wprintf(_("(This server does not support calendars)")); - wprintf("\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("
" - ""); - - /** - * Column One - */ - wprintf("
"); - wholist_section(); - - /** - * Column Two - */ - wprintf(""); - server_info_section(); - wprintf("
"); - tasks_section(); - - /** - * Column Three - */ - wprintf("
"); - new_messages_section(); - wprintf("
"); - calendar_section(); - - /** - * End of columns - */ - wprintf("
"); -} - - -/** - * \brief Display this user's summary page - */ -void summary(void) { - char title[256]; - - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n"); - wprintf("" - "
" - "" - ); - - snprintf(title, sizeof title, _("Summary page for %s"), WC->wc_fullname); - escputs(title); - wprintf("\n"); - wprintf(""); - output_date(); - wprintf("
"); - offer_start_page(); - wprintf("
\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("
\n
\n"); - summary_inner_div(); - wprintf("
\n"); - - wprintf( - " \n" - ); - - wDumpContent(1); -} - - -/*@}*/ diff --git a/webcit/sysmsgs.c b/webcit/sysmsgs.c deleted file mode 100644 index 7de399879..000000000 --- a/webcit/sysmsgs.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup ShowSysMsgs Editing of various text files on the Citadel server. - * \ingroup WebcitDisplayItems - */ -/*@{*/ -#include "webcit.h" - - -/** - * \brief display the form for editing something (room info, bio, etc) - * \param description the descriptive text for the box - * \param check_cmd command to check???? - * \param read_cmd read answer from citadel server??? - * \param save_cmd save comand to the citadel server?? - * \param with_room_banner should we bisplay a room banner? - */ -void display_edit(char *description, char *check_cmd, - char *read_cmd, char *save_cmd, int with_room_banner) -{ - char buf[SIZ]; - - serv_puts(check_cmd); - serv_getln(buf, sizeof buf); - - if (buf[0] != '2') { - safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage); - display_main_menu(); - return; - } - if (with_room_banner) { - output_headers(1, 1, 1, 0, 0, 0); - } - else { - output_headers(1, 1, 0, 0, 0, 0); - } - - svprintf("BOXTITLE", WCS_STRING, _("Edit %s"), description); - do_template("beginbox"); - - wprintf("
"); - 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("
"); - - wprintf("
\n", save_cmd); - wprintf("

\n"); - wprintf("", _("Save changes")); - wprintf(" "); - wprintf("
\n", _("Cancel")); - - wprintf("
\n"); - do_template("endbox"); - wDumpContent(1); -} - - -/** - * \brief save a screen which was displayed with display_edit() - * \param description the window title??? - * \param enter_cmd which command to enter at the citadel server??? - * \param regoto should we go to that room again after executing that command? - */ -void save_edit(char *description, char *enter_cmd, int regoto) -{ - char buf[SIZ]; - - if (strlen(bstr("save_button")) == 0) { - sprintf(WC->ImportantMessage, - _("Cancelled. %s was not saved."), - description); - display_main_menu(); - return; - } - serv_puts(enter_cmd); - serv_getln(buf, sizeof buf); - if (buf[0] != '4') { - safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage); - display_main_menu(); - return; - } - text_to_server(bstr("msgtext")); - serv_puts("000"); - - if (regoto) { - smart_goto(WC->wc_roomname); - } else { - sprintf(WC->ImportantMessage, - _("%s has been saved."), - description); - display_main_menu(); - return; - } -} - - -/*@}*/ diff --git a/webcit/tabs.c b/webcit/tabs.c deleted file mode 100644 index 5ecd53a5e..000000000 --- a/webcit/tabs.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * $Id: $ - */ -/** - * \defgroup TabUtils Utility functions for creating tabbed dialogs - * \ingroup WebcitDisplayItems - */ -/*@{*/ -#include "webcit.h" - -/** - * \brief print tabbed dialog - * \param num_tabs how many tabs do we have? - * \param tabnames the headers of the tables - */ -void tabbed_dialog(int num_tabs, char *tabnames[]) { - int i; - - wprintf(" \n" - ); - - wprintf("" - "" - ); - - for (i=0; i" - "", - i, - ( (i==0) ? "ffffff" : "cccccc" ), - i - ); - wprintf("%s", tabnames[i]); - wprintf(""); - - wprintf("\n"); - } - - wprintf("
  
\n"); - wprintf("
"); -} - -/** - * \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("
", - 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("
\n"); - - if (tabnum == num_tabs-1) { - wprintf("
\n"); - } -} - - -/*@}*/ diff --git a/webcit/tcp_sockets.c b/webcit/tcp_sockets.c deleted file mode 100644 index 2d5d985d8..000000000 --- a/webcit/tcp_sockets.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup TcpSockets TCP client socket module for WebCit - * \ingroup CitadelCommunitacion - */ -/*@{*/ - -/* - * Uncomment this to log all communications with the Citadel server -#define SERV_TRACE 1 - */ - -#include "webcit.h" -#include "webserver.h" - -/** - * \brief register the timeout - * \param signum signalhandler number - * \return signals - */ -RETSIGTYPE timeout(int signum) -{ - lprintf(1, "Connection timed out.\n"); - exit(3); -} - - -/** - * \brief Connect a unix domain socket - * \param sockpath where to open a unix domain socket - */ -int uds_connectsock(char *sockpath) -{ - struct sockaddr_un addr; - int s; - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, sockpath, sizeof addr.sun_path); - - s = socket(AF_UNIX, SOCK_STREAM, 0); - if (s < 0) { - lprintf(1, "Can't create socket: %s\n", - strerror(errno)); - return(-1); - } - - if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - lprintf(1, "Can't connect: %s\n", - strerror(errno)); - close(s); - return(-1); - } - - return s; -} - - -/** - * \brief Connect a TCP/IP socket - * \param host the host to connect to - * \param service the service on the host to call - */ -int tcp_connectsock(char *host, char *service) -{ - struct hostent *phe; - struct servent *pse; - struct protoent *ppe; - struct sockaddr_in sin; - int s; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - - pse = getservbyname(service, "tcp"); - if (pse) { - sin.sin_port = pse->s_port; - } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) { - lprintf(1, "Can't get %s service entry\n", service); - return (-1); - } - phe = gethostbyname(host); - if (phe) { - memcpy(&sin.sin_addr, phe->h_addr, phe->h_length); - } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { - lprintf(1, "Can't get %s host entry: %s\n", - host, strerror(errno)); - return (-1); - } - if ((ppe = getprotobyname("tcp")) == 0) { - lprintf(1, "Can't get TCP protocol entry: %s\n", - strerror(errno)); - return (-1); - } - - s = socket(PF_INET, SOCK_STREAM, ppe->p_proto); - if (s < 0) { - lprintf(1, "Can't create socket: %s\n", strerror(errno)); - return (-1); - } - signal(SIGALRM, timeout); - alarm(30); - - if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - lprintf(1, "Can't connect to %s.%s: %s\n", - host, service, strerror(errno)); - close(s); - return (-1); - } - alarm(0); - signal(SIGALRM, SIG_IGN); - - return (s); -} - - - - -/** - * \brief Input binary data from socket - * \param buf the buffer to get the input to - * \param bytes the maximal number of bytes to read - */ -void serv_read(char *buf, int bytes) -{ - int len, rlen; - - len = 0; - while (len < bytes) { - rlen = read(WC->serv_sock, &buf[len], bytes - len); - if (rlen < 1) { - lprintf(1, "Server connection broken: %s\n", - strerror(errno)); - close(WC->serv_sock); - WC->serv_sock = (-1); - WC->connected = 0; - WC->logged_in = 0; - memset(buf, 0, bytes); - return; - } - len = len + rlen; - } -} - - -/** - * \brief input string from pipe - */ -void serv_getln(char *strbuf, int bufsize) -{ - int ch, len; - char buf[2]; - - len = 0; - strbuf[0] = 0; - do { - serv_read(&buf[0], 1); - ch = buf[0]; - if ((ch != 13) && (ch != 10)) { - strbuf[len++] = ch; - } - } while ((ch != 10) && (ch != 0) && (len < (bufsize-1))); - strbuf[len] = 0; -#ifdef SERV_TRACE - lprintf(9, "%3d>%s\n", WC->serv_sock, strbuf); -#endif -} - - - -/** - * \brief send binary to server - * \param buf the buffer to write to citadel server - * \param nbytes how many bytes to send to citadel server - */ -void serv_write(char *buf, int nbytes) -{ - int bytes_written = 0; - int retval; - while (bytes_written < nbytes) { - retval = write(WC->serv_sock, &buf[bytes_written], - nbytes - bytes_written); - if (retval < 1) { - lprintf(1, "Server connection broken: %s\n", - strerror(errno)); - close(WC->serv_sock); - WC->serv_sock = (-1); - WC->connected = 0; - WC->logged_in = 0; - return; - } - bytes_written = bytes_written + retval; - } -} - - -/** - * \brief send line to server - * \param string the line to send to the citadel server - */ -void serv_puts(char *string) -{ - char buf[SIZ]; - -#ifdef SERV_TRACE - lprintf(9, "%3d<%s\n", WC->serv_sock, string); -#endif - sprintf(buf, "%s\n", string); - serv_write(buf, strlen(buf)); -} - - -/** - * \brief convenience function to send stuff to the server - * \param format the formatstring - * \param ... the entities to insert into format - */ -void serv_printf(const char *format,...) -{ - va_list arg_ptr; - char buf[SIZ]; - - va_start(arg_ptr, format); - vsnprintf(buf, sizeof buf, format, arg_ptr); - va_end(arg_ptr); - - strcat(buf, "\n"); - serv_write(buf, strlen(buf)); -#ifdef SERV_TRACE - lprintf(9, "<%s", buf); -#endif -} - - -/*@}*/ diff --git a/webcit/tools.c b/webcit/tools.c deleted file mode 100644 index 0a0718381..000000000 --- a/webcit/tools.c +++ /dev/null @@ -1,618 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup MiscRout Miscellaneous routines - * \ingroup tools - */ - -/*@{*/ -#include "webcit.h" -#include "webserver.h" - - -typedef unsigned char byte; /**< byte data type */ - -#define FALSE 0 /**< no. */ -#define TRUE 1 /**< yes. */ - -static byte dtable[256]; /**< base64 encode / decode table */ - -/** - * \brief sanitize strncopy. - * \param dest destination string - * \param src source string - * \param n length of source to copy - * \return result string - */ -char *safestrncpy(char *dest, const char *src, size_t n) -{ - if (dest == NULL || src == NULL) { - abort(); - } - strncpy(dest, src, n); - dest[n - 1] = 0; - return dest; -} - - - -/** - * \brief discover number of parameters/tokens in a string - * \param source string to inspect - * \param tok seperation token - * \return number of tokenized parts found - */ -int num_tokens(char *source, char tok) -{ - int a = 0; - int count = 1; - - if (source == NULL) - return (0); - for (a = 0; a < strlen(source); ++a) { - if (source[a] == tok) - ++count; - } - return (count); -} - -/** - * brief a string tokenizer - * \param dest destination string - * \param source the string to grab tokens from - * \param parmnum the n'th token to grab - * \param separator the tokenizer string - * \param maxlen the length of dest - */ -void extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen) -{ - char *d; /* dest */ - const char *s; /* source */ - int count = 0; - int len = 0; - - dest[0] = 0; - - /* Locate desired parameter */ - s = source; - while (count < parmnum) { - /* End of string, bail! */ - if (!*s) { - s = NULL; - break; - } - if (*s == separator) { - count++; - } - s++; - } - if (!s) return; /* Parameter not found */ - - for (d = dest; *s && *s != separator && ++len 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= 2) { - extract_token(histr, setstr, 1, ':', sizeof histr); - if (!strcmp(histr, "*")) { - snprintf(histr, sizeof histr, "%ld", LONG_MAX); - } - } - else { - strcpy(histr, lostr); - } - lo = atol(lostr); - hi = atol(histr); - - if ((msgnum >= lo) && (msgnum <= hi)) return(1); - } - - return(0); -} - - - -/** - * \brief Strip a boundarized substring out of a string - * (for example, remove - * parentheses and anything inside them). - * - * This improved version can strip out *multiple* boundarized substrings. - * \param str the string to process - * \param leftboundary the boundary character on the left side of the target string - * \param rightboundary the boundary character on the right side of the target string - */ -void stripout(char *str, char leftboundary, char rightboundary) -{ - int a; - int lb = (-1); - int rb = (-1); - - do { - lb = (-1); - rb = (-1); - - for (a = 0; a < strlen(str); ++a) { - if (str[a] == leftboundary) - lb = a; - if (str[a] == rightboundary) - rb = a; - } - - if ((lb > 0) && (rb > lb)) { - strcpy(&str[lb - 1], &str[rb + 1]); - } - - } while ((lb > 0) && (rb > lb)); - -} - - - -/** - * \brief Replacement for sleep() that uses select() in order to avoid SIGALRM - * \param seconds how many seconds should we sleep? - */ -void sleeeeeeeeeep(int seconds) -{ - struct timeval tv; - - tv.tv_sec = seconds; - tv.tv_usec = 0; - select(0, NULL, NULL, NULL, &tv); -} - - - -/** - * \brief encode a string into base64 to for example tunnel it through mail transport - * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by - * John Walker, copied over from the Citadel server. - * \param dest encrypted string - * \param source the string to encrypt - * \param sourcelen the length of the source data (may contain string terminators) - */ - -void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen) -{ - int i, hiteof = FALSE; - int spos = 0; - int dpos = 0; - int thisline = 0; - - /** Fill dtable with character encodings. */ - - for (i = 0; i < 26; i++) { - dtable[i] = 'A' + i; - dtable[26 + i] = 'a' + i; - } - for (i = 0; i < 10; i++) { - dtable[52 + i] = '0' + i; - } - dtable[62] = '+'; - dtable[63] = '/'; - - while (!hiteof) { - byte igroup[3], ogroup[4]; - int c, n; - - igroup[0] = igroup[1] = igroup[2] = 0; - for (n = 0; n < 3; n++) { - if (spos >= sourcelen) { - hiteof = TRUE; - break; - } - c = source[spos++]; - igroup[n] = (byte) c; - } - if (n > 0) { - ogroup[0] = dtable[igroup[0] >> 2]; - ogroup[1] = - dtable[((igroup[0] & 3) << 4) | - (igroup[1] >> 4)]; - ogroup[2] = - dtable[((igroup[1] & 0xF) << 2) | - (igroup[2] >> 6)]; - ogroup[3] = dtable[igroup[2] & 0x3F]; - - /** - * Replace characters in output stream with "=" pad - * characters if fewer than three characters were - * read from the end of the input stream. - */ - - if (n < 3) { - ogroup[3] = '='; - if (n < 2) { - ogroup[2] = '='; - } - } - for (i = 0; i < 4; i++) { - dest[dpos++] = ogroup[i]; - dest[dpos] = 0; - } - thisline += 4; - if (thisline > 70) { - dest[dpos++] = '\r'; - dest[dpos++] = '\n'; - dest[dpos] = 0; - thisline = 0; - } - } - } - if (thisline > 70) { - dest[dpos++] = '\r'; - dest[dpos++] = '\n'; - dest[dpos] = 0; - thisline = 0; - } -} - - -/** - * \brief Convert base64-encoded to binary. - * It will stop after reading 'length' bytes. - * - * \param dest The destination buffer - * \param source The base64 data to be decoded. - * \param length The number of bytes to decode. - * \return The actual length of the decoded data. - */ -int CtdlDecodeBase64(char *dest, const char *source, size_t length) -{ - int i, c; - int dpos = 0; - int spos = 0; - - for (i = 0; i < 255; i++) { - dtable[i] = 0x80; - } - for (i = 'A'; i <= 'Z'; i++) { - dtable[i] = 0 + (i - 'A'); - } - for (i = 'a'; i <= 'z'; i++) { - dtable[i] = 26 + (i - 'a'); - } - for (i = '0'; i <= '9'; i++) { - dtable[i] = 52 + (i - '0'); - } - dtable['+'] = 62; - dtable['/'] = 63; - dtable['='] = 0; - - /**CONSTANTCONDITION*/ while (TRUE) { - byte a[4], b[4], o[3]; - - for (i = 0; i < 4; i++) { - if (spos >= length) { - return (dpos); - } - c = source[spos++]; - - if (c == 0) { - if (i > 0) { - return (dpos); - } - return (dpos); - } - if (dtable[c] & 0x80) { - /** Ignoring errors: discard invalid character */ - i--; - continue; - } - a[i] = (byte) c; - b[i] = (byte) dtable[c]; - } - o[0] = (b[0] << 2) | (b[1] >> 4); - o[1] = (b[1] << 4) | (b[2] >> 2); - o[2] = (b[2] << 6) | b[3]; - i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3); - if (i >= 1) - dest[dpos++] = o[0]; - if (i >= 2) - dest[dpos++] = o[1]; - if (i >= 3) - dest[dpos++] = o[2]; - dest[dpos] = 0; - if (i < 3) { - return (dpos); - } - } -} - - - -/** - * \brief Generate a new, globally unique UID parameter for a calendar etc. object - * - * \param buf String buffer into which our newly created UUID should be placed - */ -void generate_uuid(char *buf) { - static int seq = 0; - - sprintf(buf, "%s-%lx-%x-%x", - serv_info.serv_nodename, - (long)time(NULL), - getpid(), - (seq++) - ); -} - - -/** - * \brief Local replacement for controversial C library function that generates - * names for temporary files. Included to shut up compiler warnings. - * \todo return a fd to the file instead of the name for security reasons - * \param name the created filename - * \param len the length of the filename - */ -void CtdlMakeTempFileName(char *name, int len) { - int i = 0; - - while (i++, i < 100) { - snprintf(name, len, "/tmp/ctdl.%04x.%04x", - getpid(), - rand() - ); - if (!access(name, F_OK)) { - return; - } - } -} - - - -/* - * \brief case-insensitive substring search - * - * This uses the Boyer-Moore search algorithm and is therefore quite fast. - * The code is roughly based on the strstr() replacement from 'tin' written - * by Urs Jannsen. - * - * \param text String to be searched - * \param pattern String to search for - */ -char *bmstrcasestr(char *text, char *pattern) { - - register unsigned char *p, *t; - register int i, j, *delta; - register size_t p1; - int deltaspace[256]; - size_t textlen; - size_t patlen; - - textlen = strlen (text); - patlen = strlen (pattern); - - /* algorithm fails if pattern is empty */ - if ((p1 = patlen) == 0) - return (text); - - /* code below fails (whenever i is unsigned) if pattern too long */ - if (p1 > textlen) - return (NULL); - - /* set up deltas */ - delta = deltaspace; - for (i = 0; i <= 255; i++) - delta[i] = p1; - for (p = (unsigned char *) pattern, i = p1; --i > 0;) - delta[tolower(*p++)] = i; - - /* - * From now on, we want patlen - 1. - * In the loop below, p points to the end of the pattern, - * t points to the end of the text to be tested against the - * pattern, and i counts the amount of text remaining, not - * including the part to be tested. - */ - p1--; - p = (unsigned char *) pattern + p1; - t = (unsigned char *) text + p1; - i = textlen - patlen; - while(1) { - if (tolower(p[0]) == tolower(t[0])) { - if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) { - return ((char *)t - p1); - } - } - j = delta[tolower(t[0])]; - if (i < j) - break; - i -= j; - t += j; - } - return (NULL); -} - - - - - -/*@}*/ diff --git a/webcit/useredit.c b/webcit/useredit.c deleted file mode 100644 index 265f91587..000000000 --- a/webcit/useredit.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup AdminTasks Administrative screen to add/change/delete user accounts - * \ingroup CitadelConfig - * - */ -/*@{*/ - -#include "webcit.h" -#include "webserver.h" - - -/** - * \brief show a list of available users to edit them - * \param message the header message??? - * \param preselect which user should be selected in the browser - */ -void select_user_to_edit(char *message, char *preselect) -{ - char buf[SIZ]; - char username[SIZ]; - - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n"); - wprintf("" - "
" - "" - ""); - wprintf(_("Edit or delete users")); - wprintf("
\n" - "
\n
\n" - ); - - if (message != NULL) wprintf(message); - - wprintf("
\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("

"); - - wprintf("
\n"); - wprintf(_("New user: ")); - wprintf("
\n" - "" - "
\n", _("Create")); - - do_template("endbox"); - - wprintf("
"); - - 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("

"); - - wprintf("
" - "
\n"); - wprintf("
\n"); - - wprintf("", _("Edit configuration")); - wprintf("", _("Edit address book entry")); - wprintf("", _("Delete user"), _("Delete this user?")); - wprintf("
\n"); - do_template("endbox"); - - wprintf("
\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, - "" - "%s

\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, - "%s

\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, - "" - "%s

\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("
\n"); - wprintf("
"); - wprintf(""); - wprintf(_("Edit user account: ")); - escputs(username); - wprintf("
\n"); - wprintf("
\n
\n"); - - wprintf("
" - "
\n"); - wprintf("
\n" - "\n"); - wprintf("\n" - "\n", - is_new, usernum); - - wprintf("\n", flags); - - wprintf("
"); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("\n"); - - now = time(NULL); - wprintf(""); - - wprintf("\n"); - - wprintf("
"); - wprintf(_("Password")); - wprintf("" - "
"); - wprintf(_("Permission to send Internet mail")); - wprintf(""); - wprintf("
"); - wprintf(_("Number of logins")); - wprintf("" - "
"); - wprintf(_("Messages submitted")); - wprintf("" - "
"); - wprintf(_("Access level")); - wprintf("" - "
"); - wprintf(_("User ID number")); - wprintf("" - "
"); - wprintf(_("Date and time of last login")); - wprintf("" - "
"); - wprintf(_("Auto-purge after this many days")); - wprintf("" - "
\n"); - - wprintf("\n" - " " - "\n" - "

\n", _("Save changes"), _("Cancel")); - - wprintf("
\n"); - wprintf("
\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, - "" - "%s

\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, - "" - "%s

\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, - "" - "%s

\n", &buf[4]); - select_user_to_edit(error_message, NULL); - } - -} - - - -/*@}*/ diff --git a/webcit/userlist.c b/webcit/userlist.c deleted file mode 100644 index cbd6c669a..000000000 --- a/webcit/userlist.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup AccDisplay Display a list of all accounts on a Citadel system. - * \ingroup CitadelConfig - */ - -/*@{*/ -#include "webcit.h" - -/** - * \brief structure to keep namelists in - */ -struct namelist { - struct namelist *next; /**< next item of the linked list */ - char name[32]; /**< name of the userentry */ -}; - -/** - * \brief display the userlist - */ -void userlist(void) -{ - char buf[256]; - char fl[256]; - char title[256]; - struct tm tmbuf; - time_t lc; - struct namelist *bio = NULL; - struct namelist *bptr; - int has_bio; - int bg = 0; - - serv_puts("LBIO"); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - bptr = (struct namelist *) malloc(sizeof(struct namelist)); - bptr->next = bio; - strcpy(bptr->name, buf); - bio = bptr; - } - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n" - "
" - ""); - snprintf(title, sizeof title, _("User list for %s"), serv_info.serv_humannode); - escputs(title); - wprintf("" - "
\n" - "
\n
\n" - ); - - serv_puts("LIST"); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - wprintf("%s
\n", &buf[4]); - goto DONE; - } - - wprintf("
" - "" - "", - _("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("\n", - extract_long(buf, 4), extract_long(buf, 5)); - - } - wprintf("
\n"); - wprintf("
%s%s%s%s%s%s
", - (bg ? "DDDDDD" : "FFFFFF") - ); - if (has_bio) { - wprintf(""); - escputs(fl); - wprintf(""); - } else { - escputs(fl); - } - wprintf("%ld%d", - 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("%ld%5ld
\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("
\n" - "" - "" - "
"); - wprintf(_("User profile")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("
" - "
\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("
"); - if (have_pic == 1) { - wprintf(""); - } - wprintf("

%s

\n", who); - serv_printf("RBIO %s", who); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') { - fmout("JUSTIFY"); - } - wprintf("
" - "  "); - snprintf(buf, sizeof buf, _("Click here to send an instant message to %s"), who); - escputs(buf); - wprintf("\n"); - - wprintf("
\n"); - wDumpContent(1); -} - - -/*@}*/ diff --git a/webcit/vcard.c b/webcit/vcard.c deleted file mode 100644 index d236b00c5..000000000 --- a/webcit/vcard.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - * $Id$ - * Copyright (C) 1999-2006 by Art Cancro - * This code is freely redistributable under the terms of the GNU General - * Public License. All other rights reserved. - */ -/** - * \defgroup VCardMain vCard data type implementation for the Citadel system. - * \ingroup VCards - */ -/*@{*/ -#include "webcit.h" -#include "webserver.h" -#include "vcard.h" - -/** - * \brief Constructor (empty vCard) - * \return an empty vcard - */ -struct vCard *vcard_new() { - struct vCard *v; - - v = (struct vCard *) malloc(sizeof(struct vCard)); - if (v == NULL) return v; - - v->magic = CTDL_VCARD_MAGIC; - v->numprops = 0; - v->prop = NULL; - - return v; -} - -/** - * \brief Remove the "charset=" attribute from a vCard property name - * - * \param strbuf The property name string to be stripped - */ -void remove_charset_attribute(char *strbuf) -{ - int i, t; - char compare[256]; - - t = num_tokens(strbuf, ';'); - for (i=0; i0) { - 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; iprop[i].value); ++i) { - if ( (v->prop[i].value[j] < 32) || (v->prop[i].value[j] > 126) ) { - is_utf8 = 1; - } - } - strcat(ser, v->prop[i].name); - if (is_utf8) { - strcat(ser, ";charset=UTF-8"); - } - strcat(ser, ":"); - strcat(ser, v->prop[i].value); - strcat(ser, "\r\n"); - } - } - strcat(ser, "end:vcard\r\n"); - - return ser; -} - - - -/* - * \brief Convert FN (Friendly Name) into N (Name) - * - * \param vname Supplied friendly-name - * \param n Target buffer to store Name - * \param vname_size Size of buffer - */ -void vcard_fn_to_n(char *vname, char *n, size_t vname_size) { - char lastname[256]; - char firstname[256]; - char middlename[256]; - char honorific_prefixes[256]; - char honorific_suffixes[256]; - char buf[256]; - - safestrncpy(buf, n, sizeof buf); - - /* Try to intelligently convert the screen name to a - * fully expanded vCard name based on the number of - * words in the name - */ - safestrncpy(lastname, "", sizeof lastname); - safestrncpy(firstname, "", sizeof firstname); - safestrncpy(middlename, "", sizeof middlename); - safestrncpy(honorific_prefixes, "", sizeof honorific_prefixes); - safestrncpy(honorific_suffixes, "", sizeof honorific_suffixes); - - /* Honorific suffixes */ - if (num_tokens(buf, ',') > 1) { - extract_token(honorific_suffixes, buf, (num_tokens(buf, ' ') - 1), ',', - sizeof honorific_suffixes); - remove_token(buf, (num_tokens(buf, ',') - 1), ','); - } - - /* Find a last name */ - extract_token(lastname, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof lastname); - remove_token(buf, (num_tokens(buf, ' ') - 1), ' '); - - /* Find honorific prefixes */ - if (num_tokens(buf, ' ') > 2) { - extract_token(honorific_prefixes, buf, 0, ' ', sizeof honorific_prefixes); - remove_token(buf, 0, ' '); - } - - /* Find a middle name */ - if (num_tokens(buf, ' ') > 1) { - extract_token(middlename, buf, (num_tokens(buf, ' ') - 1), ' ', sizeof middlename); - remove_token(buf, (num_tokens(buf, ' ') - 1), ' '); - } - - /* Anything left is probably the first name */ - safestrncpy(firstname, buf, sizeof firstname); - striplt(firstname); - - /* Compose the structured name */ - snprintf(vname, vname_size, "%s;%s;%s;%s;%s", lastname, firstname, middlename, - honorific_prefixes, honorific_suffixes); -} - - - - - - -/*@}*/ diff --git a/webcit/vcard.h b/webcit/vcard.h deleted file mode 100644 index ac30d4298..000000000 --- a/webcit/vcard.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * $Id$ - * Copyright (C) 1999 by Art Cancro - * This code is freely redistributable under the terms of the GNU General - * Public License. All other rights reserved. - */ -/** - * \defgroup VcardHeader vCard implementation for Citadel - * \ingroup VCards - * - */ - -/*@{ */ -#define CTDL_VCARD_MAGIC 0xa1f9 /**< magic byte vcards start with??? */ - -/** - * \brief This data structure represents a vCard object currently in memory. - */ -struct vCard { - int magic; /**< the Magic Byte */ - int numprops; /**< number of properties this vcard will have */ - struct vCardProp { - char *name; /**< Keyname of the property */ - char *value; /**< value of the property */ - } *prop; /**< Vcard Property. Linked list??? */ -}; - - -struct vCard *vcard_new(void); -struct vCard *vcard_load(char *); -void vcard_free(struct vCard *); -void vcard_set_prop(struct vCard *v, char *name, char *value, int append); -char *vcard_get_prop(struct vCard *v, char *propname, int is_partial, - int instance, int return_propname); -char *vcard_serialize(struct vCard *); - - -/*@}*/ diff --git a/webcit/vcard_edit.c b/webcit/vcard_edit.c deleted file mode 100644 index 2c78477e1..000000000 --- a/webcit/vcard_edit.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup vCardEdit Handles on-screen editing of vCard objects. - * \ingroup VCards - */ -/*@{*/ -#include "webcit.h" -#include "vcard.h" - -/** - * \brief Edit the vCard component of a MIME message. - * Supply the message number - * and MIME part number to fetch. Or, specify -1 for the message number - * to start with a blank card. - * \param msgnum number of the item on the citadel server - * \param partnum what??? - * \param return_to where to go back in the browser after edit ???? - */ -void do_edit_vcard(long msgnum, char *partnum, char *return_to) { - char buf[SIZ]; - char *serialized_vcard = NULL; - size_t total_len = 0; - struct vCard *v; - int i; - char *key, *value; - char whatuser[256]; - - char lastname[256]; - char firstname[256]; - char middlename[256]; - char prefix[256]; - char suffix[256]; - char pobox[256]; - char extadr[256]; - char street[256]; - char city[256]; - char state[256]; - char zipcode[256]; - char country[256]; - char hometel[256]; - char worktel[256]; - char primary_inetemail[256]; - char other_inetemail[SIZ]; - char extrafields[SIZ]; - char fullname[256]; - char title[256]; - char org[256]; - - lastname[0] = 0; - firstname[0] = 0; - middlename[0] = 0; - prefix[0] = 0; - suffix[0] = 0; - pobox[0] = 0; - extadr[0] = 0; - street[0] = 0; - city[0] = 0; - state[0] = 0; - zipcode[0] = 0; - country[0] = 0; - hometel[0] = 0; - worktel[0] = 0; - primary_inetemail[0] = 0; - other_inetemail[0] = 0; - title[0] = 0; - org[0] = 0; - extrafields[0] = 0; - - safestrncpy(whatuser, "", sizeof whatuser); - - if (msgnum >= 0) { - sprintf(buf, "MSG0 %ld|1", msgnum); - serv_puts(buf); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - convenience_page("770000", _("Error"), &buf[4]); - return; - } - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - if (!strncasecmp(buf, "from=", 5)) { - safestrncpy(whatuser, &buf[5], sizeof whatuser); - } - else if (!strncasecmp(buf, "node=", 5)) { - strcat(whatuser, " @ "); - strcat(whatuser, &buf[5]); - } - } - - sprintf(buf, "OPNA %ld|%s", msgnum, partnum); - serv_puts(buf); - serv_getln(buf, sizeof buf); - if (buf[0] != '2') { - convenience_page("770000", "Error", &buf[4]); - return; - } - - total_len = atoi(&buf[4]); - serialized_vcard = malloc(total_len + 2); - - read_server_binary(serialized_vcard, total_len); - - serv_puts("CLOS"); - serv_getln(buf, sizeof buf); - serialized_vcard[total_len] = 0; - - v = vcard_load(serialized_vcard); - free(serialized_vcard); - - /* Populate the variables for our form */ - i = 0; - while (key = vcard_get_prop(v, "", 0, i, 1), key != NULL) { - value = vcard_get_prop(v, "", 0, i++, 0); - - if (!strcasecmp(key, "n")) { - extract_token(lastname, value, 0, ';', sizeof lastname); - extract_token(firstname, value, 1, ';', sizeof firstname); - extract_token(middlename, value, 2, ';', sizeof middlename); - extract_token(prefix, value, 3, ';', sizeof prefix); - extract_token(suffix, value, 4, ';', sizeof suffix); - } - - else if (!strcasecmp(key, "fn")) { - safestrncpy(fullname, value, sizeof fullname); - } - - else if (!strcasecmp(key, "title")) { - safestrncpy(title, value, sizeof title); - } - - else if (!strcasecmp(key, "org")) { - safestrncpy(org, value, sizeof org); - } - - else if (!strcasecmp(key, "adr")) { - extract_token(pobox, value, 0, ';', sizeof pobox); - extract_token(extadr, value, 1, ';', sizeof extadr); - extract_token(street, value, 2, ';', sizeof street); - extract_token(city, value, 3, ';', sizeof city); - extract_token(state, value, 4, ';', sizeof state); - extract_token(zipcode, value, 5, ';', sizeof zipcode); - extract_token(country, value, 6, ';', sizeof country); - } - - else if (!strcasecmp(key, "tel;home")) { - extract_token(hometel, value, 0, ';', sizeof hometel); - } - - else if (!strcasecmp(key, "tel;work")) { - extract_token(worktel, value, 0, ';', sizeof worktel); - } - - else if (!strcasecmp(key, "email;internet")) { - if (primary_inetemail[0] == 0) { - safestrncpy(primary_inetemail, value, sizeof primary_inetemail); - } - else { - if (other_inetemail[0] != 0) { - strcat(other_inetemail, "\n"); - } - strcat(other_inetemail, value); - } - } - - else { - strcat(extrafields, key); - strcat(extrafields, ":"); - strcat(extrafields, value); - strcat(extrafields, "\n"); - } - - } - - vcard_free(v); - } - - /** Display the form */ - output_headers(1, 1, 2, 0, 0, 0); - wprintf("
\n" - "
" - "" - ""); - wprintf(_("Edit contact information")); - wprintf("" - "
\n" - "
\n
\n" - ); - - wprintf("
\n"); - wprintf("
" - "
\n"); - - wprintf("" - "" - "" - "" - "" - "\n", - _("Prefix"), _("First"), _("Middle"), _("Last"), _("Suffix") - ); - wprintf("", - prefix); - wprintf("", - firstname); - wprintf("", - middlename); - wprintf("", - lastname); - wprintf("
%s%s%s%s%s
\n", - suffix); - - wprintf(""); - wprintf("
"); - - wprintf(_("Display name:")); - wprintf("
" - "

\n", - fullname - ); - - wprintf(_("Title:")); - wprintf("
" - "

\n", - title - ); - - wprintf(_("Organization:")); - wprintf("
" - "

\n", - org - ); - - wprintf("
"); - - wprintf(""); - wprintf("\n", - pobox); - wprintf("\n", - extadr); - wprintf("\n", - street); - wprintf("\n", - city); - wprintf("\n", - state); - wprintf("\n", - zipcode); - wprintf("\n", - country); - wprintf("
"); - wprintf(_("PO box:")); - wprintf("" - "
"); - wprintf(_("Address:")); - wprintf("" - "
" - "
"); - wprintf(_("City:")); - wprintf("" - "
"); - wprintf(_("State:")); - wprintf("" - "
"); - wprintf(_("ZIP code:")); - wprintf("" - "
"); - wprintf(_("Country:")); - wprintf("" - "
\n"); - - wprintf("
\n"); - - wprintf("" - "\n", - hometel); - wprintf("" - "
"); - wprintf(_("Home telephone:")); - wprintf(""); - wprintf(_("Work telephone:")); - wprintf("
\n", - worktel); - - wprintf(""); - wprintf("
"); - - wprintf("" - "
"); - wprintf(_("Primary Internet e-mail address")); - wprintf("
" - "
" - "
"); - wprintf(_("Internet e-mail aliases")); - wprintf("
" - "
\n"); - - wprintf("
\n"); - - wprintf("\n"); - - wprintf("\n"); - - wprintf("
\n" - "" - " " - "" - "
\n", - _("Save changes"), - _("Cancel") - ); - - wprintf("
\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 0) { - serv_printf("email;internet:%s", buf); - } - } - - serv_printf("%s", bstr("extrafields") ); - serv_puts("end:vcard"); - serv_puts("000"); - - if (!strcmp(bstr("return_to"), "select_user_to_edit")) { - select_user_to_edit(NULL, NULL); - } - else if (!strcmp(bstr("return_to"), "do_welcome")) { - do_welcome(); - } - else { - readloop("readnew"); - } -} - - - -/*@}*/ diff --git a/webcit/webcit.c b/webcit/webcit.c deleted file mode 100644 index 85ee1ca4b..000000000 --- a/webcit/webcit.c +++ /dev/null @@ -1,1676 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup MainServer This is the main transaction loop of the web service. It maintains a - * persistent session to the Citadel server, handling HTTP WebCit requests as - * they arrive and presenting a user interface. - * \ingroup WebcitHttpServer - */ -/*@{*/ -#include "webcit.h" -#include "groupdav.h" -#include "webserver.h" -#include "mime_parser.h" - -/** - * Subdirectories from which the client may request static content - */ -char *static_content_dirs[] = { - "static", /** static templates */ - "tiny_mce" /** the JS editor */ -}; - -/** - * String to unset the cookie. - * Any date "in the past" will work, so I chose my birthday, right down to - * the exact minute. :) - */ -static char *unset = "; expires=28-May-1971 18:10:00 GMT"; - -/** - * \brief remove escaped strings from i.e. the url string (like %20 for blanks) - * \param buf the buffer to examine - */ -void unescape_input(char *buf) -{ - int a, b; - char hex[3]; - - while ((isspace(buf[strlen(buf) - 1])) && (strlen(buf) > 0)) - buf[strlen(buf) - 1] = 0; - - for (a = 0; a < strlen(buf); ++a) { - if (buf[a] == '+') - buf[a] = ' '; - if (buf[a] == '%') { - hex[0] = buf[a + 1]; - hex[1] = buf[a + 2]; - hex[2] = 0; - b = 0; - sscanf(hex, "%02x", &b); - buf[a] = (char) b; - strcpy(&buf[a + 1], &buf[a + 3]); - } - } - -} - -/** - * \brief Extract variables from the URL. - * \param url URL supplied by the HTTP parser - */ -void addurls(char *url) -{ - char *up, *ptr; - char buf[SIZ]; - int a, b; - struct urlcontent *u; - - up = url; - while (strlen(up) > 0) { - - /** locate the = sign */ - safestrncpy(buf, up, sizeof buf); - b = (-1); - for (a = 255; a >= 0; --a) - if (buf[a] == '=') - b = a; - if (b < 0) - return; - buf[b] = 0; - - u = (struct urlcontent *) malloc(sizeof(struct urlcontent)); - u->next = WC->urlstrings; - WC->urlstrings = u; - safestrncpy(u->url_key, buf, sizeof u->url_key); - - /** now chop that part off */ - for (a = 0; a <= b; ++a) - ++up; - - /** locate "&" and "?" delimiters */ - ptr = up; - b = strlen(up); - for (a = 0; a < strlen(up); ++a) { - if ( (ptr[0] == '&') || (ptr[0] == '?') ) { - b = a; - break; - } - ++ptr; - } - ptr = up; - for (a = 0; a < b; ++a) - ++ptr; - strcpy(ptr, ""); - - u->url_data = malloc(strlen(up) + 2); - safestrncpy(u->url_data, up, strlen(up) + 1); - u->url_data[b] = 0; - unescape_input(u->url_data); - up = ptr; - ++up; - } -} - -/** - * \brief free urlstring memory - */ -void free_urls(void) -{ - struct urlcontent *u; - - while (WC->urlstrings != NULL) { - free(WC->urlstrings->url_data); - u = WC->urlstrings->next; - free(WC->urlstrings); - WC->urlstrings = u; - } -} - -/** - * \brief Diagnostic function to display the contents of all variables - */ -void dump_vars(void) -{ - struct urlcontent *u; - - for (u = WC->urlstrings; u != NULL; u = u->next) { - wprintf("%38s = %s\n", u->url_key, u->url_data); - } -} - -/** - * \brief Return the value of a variable supplied to the current web page (from the url or a form) - * \param key The name of the variable we want - */ -char *bstr(char *key) -{ - struct urlcontent *u; - - for (u = WC->urlstrings; u != NULL; u = u->next) { - if (!strcasecmp(u->url_key, key)) - return (u->url_data); - } - return (""); -} - -/** - * \brief web-printing funcion. uses our vsnprintf wrapper - * \param format printf format string - * \param ... the varargs to put into formatstring - */ -void wprintf(const char *format,...) -{ - va_list arg_ptr; - char wbuf[4096]; - - va_start(arg_ptr, format); - vsnprintf(wbuf, sizeof wbuf, format, arg_ptr); - va_end(arg_ptr); - - client_write(wbuf, strlen(wbuf)); -} - - -/** - * \brief wrap up an HTTP session, closes tags, etc. - * \todo multiline params? - * \param print_standard_html_footer should be set to 0 to transmit only, 1 to - * append the main menu and closing tags, or 2 to - * append the closing tags only. - */ -void wDumpContent(int print_standard_html_footer) -{ - if (print_standard_html_footer) { - wprintf("
\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 section and opener */ - - int do_room_banner, /**< 0=no, 1=yes, - * 2 = I'm going to embed my own, so don't open the - *
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("
\n"); - wprintf("" - "%s
\n", WC->ImportantMessage); - wprintf("
\n"); - wprintf("\n"); - safestrncpy(WC->ImportantMessage, "", sizeof WC->ImportantMessage); - } - - if ( (WC->logged_in) && (!unset_cookies) ) { - wprintf("
"); - do_selected_iconbar(); - /** check for instant messages (these display in a new window) */ - page_popup(); - wprintf("
"); - } - - if (do_room_banner == 1) { - wprintf("
\n"); - embed_room_banner(NULL, navbar_default); - wprintf("
\n"); - } - } - - if (do_room_banner == 1) { - wprintf("
\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(""); - wprintf("Go here.", whichpage); - wprintf("\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("
\n"); - wprintf("
", titlebarcolor); - wprintf("%s\n", titlebarmsg); - wprintf("
\n"); - wprintf("
\n
\n"); - escputs(messagetext); - - wprintf("
\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("this_page); - wprintf("\">"); - wprintf(_("Make this my start page")); - wprintf(""); -/* - wprintf("
wc_roomname); - wprintf("\" title=\"RSS 2.0 feed for "); - escputs(WC->wc_roomname); - wprintf("\">\"RSS\"\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("

"); - wprintf(_("Authorization Required")); - wprintf("

\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
\n", WC->wc_session); - wprintf("Command:
\n");
-		escputs(cmd);
-		wprintf("

\n"); - wprintf("Variables:
\n");
-		dump_vars();
-		wprintf("

\n"); - wDumpContent(1); - } else if (!strcasecmp(action, "updatenote")) { - updatenote(); - } - - /** When all else fais, display the main menu. */ - else { - display_main_menu(); - } - -SKIP_ALL_THIS_CRAP: - fflush(stdout); - if (content != NULL) { - free(content); - content = NULL; - } - free_urls(); - if (WC->upload_length > 0) { - free(WC->upload); - WC->upload_length = 0; - } -} - - -/*@}*/ diff --git a/webcit/webcit.h b/webcit/webcit.h deleted file mode 100644 index 92eae15fd..000000000 --- a/webcit/webcit.h +++ /dev/null @@ -1,746 +0,0 @@ -/* $Id$ */ - -/** we need _GNU_SOURCE for various functions arround the NLS-Stuff */ -#define _GNU_SOURCE - - -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#ifdef HAVE_FCNTL_H -#include -#endif -#include -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include -#ifdef HAVE_LIMITS_H -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff -#endif - -#ifdef HAVE_ICONV -#include -#endif - -#ifdef ENABLE_NLS -#include -#include -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 -#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 -#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 -#include -#include -#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 /**< ext & 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 \\\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; /** 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 -int ZEXPORT compress_gzip(Bytef * dest, uLongf * destLen, - const Bytef * source, uLong sourceLen, int level); -#endif - -#ifdef HAVE_ICONV -void utf8ify_rfc822_string(char *buf); -#endif - -void begin_burst(void); -void end_burst(void); - -extern char *hourname[]; /**< Names of hours (12am, 1am, etc.) */ - -void http_datestring(char *buf, size_t n, time_t xtime); - - -/** Views (from citadel.h) */ -#define VIEW_BBS 0 /**< Traditional Citadel BBS view */ -#define VIEW_MAILBOX 1 /**< Mailbox summary */ -#define VIEW_ADDRESSBOOK 2 /**< Address book view */ -#define VIEW_CALENDAR 3 /**< Calendar view */ -#define VIEW_TASKS 4 /**< Tasks view */ -#define VIEW_NOTES 5 /**< Notes view */ -#define VIEW_WIKI 6 /**< Wiki view */ -#define VIEW_CALBRIEF 7 /**< Brief Calendar view */ - - -/* These should be empty, but we have them for testing */ -#define DEFAULT_HTTPAUTH_USER "" -#define DEFAULT_HTTPAUTH_PASS "" - diff --git a/webcit/webserver.c b/webcit/webserver.c deleted file mode 100644 index 985add616..000000000 --- a/webcit/webserver.c +++ /dev/null @@ -1,764 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup Webserver This contains a simple multithreaded TCP server manager. It sits around - * waiting on the specified port for incoming HTTP connections. When a - * connection is established, it calls context_loop() from context_loop.c. - * \ingroup WebcitHttpServer - */ - -/*@{*/ -#include "webcit.h" -#include "webserver.h" - -#ifndef HAVE_SNPRINTF -int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp); -#endif - -int verbosity = 9; /**< Logging level */ -int msock; /**< master listening socket */ -int is_https = 0; /**< Nonzero if I am an HTTPS service */ -int follow_xff = 0; /**< Follow X-Forwarded-For: header */ -extern void *context_loop(int); -extern void *housekeeping_loop(void); -extern pthread_mutex_t SessionListMutex; -extern pthread_key_t MyConKey; - - -char *server_cookie = NULL; /**< our Cookie connection to the client */ - -int http_port = PORT_NUM; /**< Port to listen on */ - -char *ctdlhost = DEFAULT_HOST; /**< our name */ -char *ctdlport = DEFAULT_PORT; /**< our Port */ -int setup_wizard = 0; /**< should we run the setup wizard? \todo */ -char wizard_filename[PATH_MAX];/**< where's the setup wizard? */ - -/** - * \brief This is a generic function to set up a master socket for listening on - * a TCP port. The server shuts down if the bind fails. - * \param ip_addr ip to bind to - * \param port_number the port to bind to - * \param queue_len the size of the input queue ???? - */ -int ig_tcp_server(char *ip_addr, int port_number, int queue_len) -{ - struct sockaddr_in sin; - int s, i; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - if (ip_addr == NULL) { - sin.sin_addr.s_addr = INADDR_ANY; - } else { - sin.sin_addr.s_addr = inet_addr(ip_addr); - } - - if (sin.sin_addr.s_addr == INADDR_NONE) { - sin.sin_addr.s_addr = INADDR_ANY; - } - - if (port_number == 0) { - lprintf(1, "Cannot start: no port number specified.\n"); - exit(1); - } - sin.sin_port = htons((u_short) port_number); - - s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto)); - if (s < 0) { - lprintf(1, "Can't create a socket: %s\n", strerror(errno)); - exit(errno); - } - /** Set some socket options that make sense. */ - i = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); - - if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - lprintf(1, "Can't bind: %s\n", strerror(errno)); - exit(errno); - } - if (listen(s, queue_len) < 0) { - lprintf(1, "Can't listen: %s\n", strerror(errno)); - exit(errno); - } - return (s); -} - - - -/** - * \brief Create a Unix domain socket and listen on it - * \param sockpath file name of the unix domain socket - * \param queue_len queue size of the kernel fifo???? - */ -int ig_uds_server(char *sockpath, int queue_len) -{ - struct sockaddr_un addr; - int s; - int i; - int actual_queue_len; - - actual_queue_len = queue_len; - if (actual_queue_len < 5) actual_queue_len = 5; - - i = unlink(sockpath); - if (i != 0) if (errno != ENOENT) { - lprintf(1, "citserver: can't unlink %s: %s\n", - sockpath, strerror(errno)); - exit(errno); - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path); - - s = socket(AF_UNIX, SOCK_STREAM, 0); - if (s < 0) { - lprintf(1, "citserver: Can't create a socket: %s\n", - strerror(errno)); - exit(errno); - } - - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - lprintf(1, "citserver: Can't bind: %s\n", - strerror(errno)); - exit(errno); - } - - if (listen(s, actual_queue_len) < 0) { - lprintf(1, "citserver: Can't listen: %s\n", - strerror(errno)); - exit(errno); - } - - chmod(sockpath, 0777); - return(s); -} - - - - -/** - * \brief Read data from the client socket. - * \param sock socket fd to read from ??? - * \param buf buffer to read into - * \param bytes how large is the read buffer? - * \param timeout how long should we wait for input? - * \return values are\ - * 1 Requested number of bytes has been read.\ - * 0 Request timed out.\ - * -1 Connection is broken, or other error. - */ -int client_read_to(int sock, char *buf, int bytes, int timeout) -{ - int len, rlen; - fd_set rfds; - struct timeval tv; - int retval; - - -#ifdef HAVE_OPENSSL - if (is_https) { - return (client_read_ssl(buf, bytes, timeout)); - } -#endif - - len = 0; - while (len < bytes) { - FD_ZERO(&rfds); - FD_SET(sock, &rfds); - tv.tv_sec = timeout; - tv.tv_usec = 0; - - retval = select((sock) + 1, &rfds, NULL, NULL, &tv); - if (FD_ISSET(sock, &rfds) == 0) { - return (0); - } - - rlen = read(sock, &buf[len], bytes - len); - - if (rlen < 1) { - lprintf(2, "client_read() failed: %s\n", - strerror(errno)); - return (-1); - } - len = len + rlen; - } - -#ifdef HTTP_TRACING - write(2, "\033[32m", 5); - write(2, buf, bytes); - write(2, "\033[30m", 5); -#endif - return (1); -} - -/** - * \brief write data to the client - * \param buf data to write to the client - * \param count size of buffer - */ -ssize_t client_write(const void *buf, size_t count) -{ - char *newptr; - size_t newalloc; - - if (WC->burst != NULL) { - if ((WC->burst_len + count) >= WC->burst_alloc) { - newalloc = (WC->burst_alloc * 2); - if ((WC->burst_len + count) >= newalloc) { - newalloc += count; - } - newptr = realloc(WC->burst, newalloc); - if (newptr != NULL) { - WC->burst = newptr; - WC->burst_alloc = newalloc; - } - } - if ((WC->burst_len + count) < WC->burst_alloc) { - memcpy(&WC->burst[WC->burst_len], buf, count); - WC->burst_len += count; - return (count); - } - else { - return(-1); - } - } -#ifdef HAVE_OPENSSL - if (is_https) { - client_write_ssl((char *) buf, count); - return (count); - } -#endif -#ifdef HTTP_TRACING - write(2, "\033[34m", 5); - write(2, buf, count); - write(2, "\033[30m", 5); -#endif - return (write(WC->http_sock, buf, count)); -} - -/** - * \brief what burst??? - */ -void begin_burst(void) -{ - if (WC->burst != NULL) { - free(WC->burst); - WC->burst = NULL; - } - WC->burst_len = 0; - WC->burst_alloc = 32768; - WC->burst = malloc(WC->burst_alloc); -} - - -/** - * \brief uses the same calling syntax as compress2(), but it - * creates a stream compatible with HTTP "Content-encoding: gzip" - */ -#ifdef HAVE_ZLIB -#define DEF_MEM_LEVEL 8 /**< memlevel??? */ -#define OS_CODE 0x03 /**< unix */ -int ZEXPORT compress_gzip(Bytef * dest, /**< compressed buffer*/ - uLongf * destLen, /**< length of the compresed data */ - const Bytef * source, /**< source to encode */ - uLong sourceLen, /**< length of the source to encode */ - int level) /**< what level??? */ -{ - const int gz_magic[2] = { 0x1f, 0x8b }; /** gzip magic header */ - - /** write gzip header */ - sprintf((char *) dest, "%c%c%c%c%c%c%c%c%c%c", - gz_magic[0], gz_magic[1], Z_DEFLATED, - 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /** xflags */ , - OS_CODE); - - /* normal deflate */ - z_stream stream; - int err; - stream.next_in = (Bytef *) source; - stream.avail_in = (uInt) sourceLen; - stream.next_out = dest + 10L; // after header - stream.avail_out = (uInt) * destLen; - if ((uLong) stream.avail_out != *destLen) - return Z_BUF_ERROR; - - stream.zalloc = (alloc_func) 0; - stream.zfree = (free_func) 0; - stream.opaque = (voidpf) 0; - - err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS, - DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (err != Z_OK) - return err; - - err = deflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - deflateEnd(&stream); - return err == Z_OK ? Z_BUF_ERROR : err; - } - *destLen = stream.total_out + 10L; - - /* write CRC and Length */ - uLong crc = crc32(0L, source, sourceLen); - int n; - for (n = 0; n < 4; ++n, ++*destLen) { - dest[*destLen] = (int) (crc & 0xff); - crc >>= 8; - } - uLong len = stream.total_in; - for (n = 0; n < 4; ++n, ++*destLen) { - dest[*destLen] = (int) (len & 0xff); - len >>= 8; - } - err = deflateEnd(&stream); - return err; -} -#endif - -/** - * \brief what burst??? - */ -void end_burst(void) -{ - size_t the_len; - char *the_data; - - if (WC->burst == NULL) - return; - - the_len = WC->burst_len; - the_data = WC->burst; - - WC->burst_len = 0; - WC->burst_alloc = 0; - WC->burst = NULL; - -#ifdef HAVE_ZLIB - /* Handle gzip compression */ - if (WC->gzip_ok) { - char *compressed_data = NULL; - uLongf compressed_len; - - compressed_len = (uLongf) ((the_len * 101) / 100) + 100; - compressed_data = malloc(compressed_len); - - if (compress_gzip((Bytef *) compressed_data, - &compressed_len, - (Bytef *) the_data, - (uLongf) the_len, Z_BEST_SPEED) == Z_OK) { - wprintf("Content-encoding: gzip\r\n"); - free(the_data); - the_data = compressed_data; - the_len = compressed_len; - } else { - free(compressed_data); - } - } -#endif /* HAVE_ZLIB */ - - wprintf("Content-length: %d\r\n\r\n", the_len); - client_write(the_data, the_len); - free(the_data); - return; -} - - - -/** - * \brief Read data from the client socket with default timeout. - * (This is implemented in terms of client_read_to() and could be - * justifiably moved out of sysdep.c) - * \param sock the socket fd to read from??? - * \param buf the buffer to write to - * \param bytes how large is the buffer - */ -int client_read(int sock, char *buf, int bytes) -{ - return (client_read_to(sock, buf, bytes, SLEEPING)); -} - - -/** - * \brief Get a LF-terminated line of text from the client. - * (This is implemented in terms of client_read() and could be - * justifiably moved out of sysdep.c) - * \param sock socket fd to get client line from??? - * \param buf buffer to write read data to - * \param bufsiz how many bytes to read - * \return numer of bytes read??? - */ -int client_getln(int sock, char *buf, int bufsiz) -{ - int i, retval; - - /** Read one character at a time.*/ - for (i = 0;; i++) { - retval = client_read(sock, &buf[i], 1); - if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1)) - break; - if ( (!isspace(buf[i])) && (!isprint(buf[i])) ) { - /** Non printable character recieved from client */ - return(-1); - } - } - - /** If we got a long line, discard characters until the newline. */ - if (i == (bufsiz-1)) - while (buf[i] != '\n' && retval == 1) - retval = client_read(sock, &buf[i], 1); - - /** - * Strip any trailing non-printable characters. - */ - buf[i] = 0; - while ((strlen(buf) > 0) && (!isprint(buf[strlen(buf) - 1]))) { - buf[strlen(buf) - 1] = 0; - } - return (retval); -} - - -/** - * \brief Start running as a daemon. - * - * param do_close_stdio Only close stdio if set. - */ -void start_daemon(int do_close_stdio) -{ - if (do_close_stdio) { - /* close(0); */ - close(1); - close(2); - } - signal(SIGHUP, SIG_IGN); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - if (fork() != 0) { - exit(0); - } -} - -/** - * \brief Spawn an additional worker thread into the pool. - */ -void spawn_another_worker_thread() -{ - pthread_t SessThread; /**< Thread descriptor */ - pthread_attr_t attr; /**< Thread attributes */ - int ret; - - lprintf(3, "Creating a new thread\n"); - - /** set attributes for the new thread */ - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - /** - * Our per-thread stacks need to be bigger than the default size, otherwise - * the MIME parser crashes on FreeBSD, and the IMAP service crashes on - * 64-bit Linux. - */ - if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) { - lprintf(1, "pthread_attr_setstacksize: %s\n", - strerror(ret)); - pthread_attr_destroy(&attr); - } - - /** now create the thread */ - if (pthread_create(&SessThread, &attr, - (void *(*)(void *)) worker_entry, NULL) - != 0) { - lprintf(1, "Can't create thread: %s\n", strerror(errno)); - } - - /** free up the attributes */ - pthread_attr_destroy(&attr); -} - -/** - * \brief Here's where it all begins. - * \param argc number of commandline args - * \param argv the commandline arguments - */ -int main(int argc, char **argv) -{ - pthread_t SessThread; /**< Thread descriptor */ - pthread_attr_t attr; /**< Thread attributes */ - int a, i; /**< General-purpose variables */ - char tracefile[PATH_MAX]; - char ip_addr[256]; - char *webcitdir = WEBCITDIR; -#ifdef ENABLE_NLS - char *locale = NULL; - char *mo = NULL; -#endif /* ENABLE_NLS */ - char uds_listen_path[PATH_MAX]; /**< listen on a unix domain socket? */ - - strcpy(uds_listen_path, ""); - - /** Parse command line */ -#ifdef HAVE_OPENSSL - while ((a = getopt(argc, argv, "h:i:p:t:x:cfs")) != EOF) -#else - while ((a = getopt(argc, argv, "h:i:p:t:x:cf")) != EOF) -#endif - switch (a) { - case 'h': - webcitdir = strdup(optarg); - break; - case 'i': - safestrncpy(ip_addr, optarg, sizeof ip_addr); - break; - case 'p': - http_port = atoi(optarg); - if (http_port == 0) { - safestrncpy(uds_listen_path, optarg, sizeof uds_listen_path); - } - break; - case 't': - safestrncpy(tracefile, optarg, sizeof tracefile); - freopen(tracefile, "w", stdout); - freopen(tracefile, "w", stderr); - freopen(tracefile, "r", stdin); - break; - case 'x': - verbosity = atoi(optarg); - break; - case 'f': - follow_xff = 1; - break; - case 'c': - server_cookie = malloc(256); - if (server_cookie != NULL) { - safestrncpy(server_cookie, - "Set-cookie: wcserver=", - 256); - if (gethostname - (&server_cookie[strlen(server_cookie)], - 200) != 0) { - lprintf(2, "gethostname: %s\n", - strerror(errno)); - free(server_cookie); - } - } - break; - case 's': - is_https = 1; - break; - default: - fprintf(stderr, "usage: webserver " - "[-i ip_addr] [-p http_port] " - "[-t tracefile] [-c] [-f] " -#ifdef HAVE_OPENSSL - "[-s] " -#endif - "[remotehost [remoteport]]\n"); - return 1; - } - - if (optind < argc) { - ctdlhost = argv[optind]; - if (++optind < argc) - ctdlport = argv[optind]; - } - /** Tell 'em who's in da house */ - lprintf(1, SERVER "\n"); - lprintf(1, "Copyright (C) 1996-2006 by the Citadel development team.\n" - "This software is distributed under the terms of the " - "GNU General Public License.\n\n" - ); - - lprintf(9, "Changing directory to %s\n", webcitdir); - if (chdir(webcitdir) != 0) { - perror("chdir"); - } - - /** initialize the International Bright Young Thing */ -#ifdef ENABLE_NLS - - initialize_locales(); - - locale = setlocale(LC_ALL, ""); - - mo = malloc(strlen(webcitdir) + 20); - sprintf(mo, "%s/locale", webcitdir); - lprintf(9, "Message catalog directory: %s\n", - bindtextdomain("webcit", mo) - ); - free(mo); - lprintf(9, "Text domain: %s\n", - textdomain("webcit") - ); - lprintf(9, "Text domain Charset: %s\n", - bind_textdomain_codeset("webcit","UTF8") - ); -#endif - - initialize_viewdefs(); - initialize_axdefs(); - - /** - * Set up a place to put thread-specific data. - * We only need a single pointer per thread - it points to the - * wcsession struct to which the thread is currently bound. - */ - if (pthread_key_create(&MyConKey, NULL) != 0) { - lprintf(1, "Can't create TSD key: %s\n", strerror(errno)); - } - - /** - * Set up a place to put thread-specific SSL data. - * We don't stick this in the wcsession struct because SSL starts - * up before the session is bound, and it gets torn down between - * transactions. - */ -#ifdef HAVE_OPENSSL - if (pthread_key_create(&ThreadSSL, NULL) != 0) { - lprintf(1, "Can't create TSD key: %s\n", strerror(errno)); - } -#endif - - /** - * Bind the server to our favorite port. - * There is no need to check for errors, because ig_tcp_server() - * exits if it doesn't succeed. - */ - - if (strlen(uds_listen_path) > 0) { - lprintf(2, "Attempting to create listener socket at %s...\n", uds_listen_path); - msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH); - } - else { - lprintf(2, "Attempting to bind to port %d...\n", http_port); - msock = ig_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH); - } - - lprintf(2, "Listening on socket %d\n", msock); - signal(SIGPIPE, SIG_IGN); - - pthread_mutex_init(&SessionListMutex, NULL); - - /** - * Start up the housekeeping thread - */ - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&SessThread, &attr, - (void *(*)(void *)) housekeeping_loop, NULL); - - - /** - * If this is an HTTPS server, fire up SSL - */ -#ifdef HAVE_OPENSSL - if (is_https) { - init_ssl(); - } -#endif - - /** Start a few initial worker threads */ - for (i = 0; i < (MIN_WORKER_THREADS); ++i) { - spawn_another_worker_thread(); - } - - /* now the original thread becomes another worker */ - worker_entry(); - return 0; -} - - -/** - * Entry point for worker threads - */ -void worker_entry(void) -{ - int ssock; - int i = 0; - int time_to_die = 0; - int fail_this_transaction = 0; - - do { - /** Only one thread can accept at a time */ - fail_this_transaction = 0; - ssock = accept(msock, NULL, 0); - if (ssock < 0) { - lprintf(2, "accept() failed: %s\n", - strerror(errno)); - } else { - /** Set the SO_REUSEADDR socket option */ - i = 1; - setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, - &i, sizeof(i)); - - /** If we are an HTTPS server, go crypto now. */ -#ifdef HAVE_OPENSSL - if (is_https) { - if (starttls(ssock) != 0) { - fail_this_transaction = 1; - close(ssock); - } - } -#endif - - if (fail_this_transaction == 0) { - /** Perform an HTTP transaction... */ - context_loop(ssock); - /** ...and close the socket. */ - lingering_close(ssock); - } - - } - - } while (!time_to_die); - - pthread_exit(NULL); -} - -/** - * \brief logprintf. log messages - * logs to stderr if loglevel is lower than the verbosity set at startup - * \param loglevel level of the message - * \param format the printf like format string - * \param ... the strings to put into format - */ -int lprintf(int loglevel, const char *format, ...) -{ - va_list ap; - - if (loglevel <= verbosity) { - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - fflush(stderr); - } - return 1; -} - - -/** - * \brief print the actual stack frame. - */ -void wc_backtrace(void) -{ -#ifdef HAVE_BACKTRACE - void *stack_frames[50]; - size_t size, i; - char **strings; - - - size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*)); - strings = backtrace_symbols(stack_frames, size); - for (i = 0; i < size; i++) { - if (strings != NULL) - lprintf(1, "%s\n", strings[i]); - else - lprintf(1, "%p\n", stack_frames[i]); - } - free(strings); -#endif -} - -/*@}*/ diff --git a/webcit/webserver.h b/webcit/webserver.h deleted file mode 100644 index 5809b1aa9..000000000 --- a/webcit/webserver.h +++ /dev/null @@ -1,6 +0,0 @@ -/* $Id$ */ -int client_getln(int sock, char *buf, int bufsiz); -int client_read(int sock, char *buf, int bytes); -int client_read_to(int sock, char *buf, int bytes, int timeout); -ssize_t client_write(const void *buf, size_t count); -int lprintf(int loglevel, const char *format, ...); diff --git a/webcit/who.c b/webcit/who.c deleted file mode 100644 index e81b911e9..000000000 --- a/webcit/who.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * $Id$ - */ -/** - * \defgroup DislpayWho Display a list of all users currently logged on to the Citadel server. - * \ingroup WebcitDisplayItems - */ -/*@{*/ -#include "webcit.h" - - - -/** - * \brief Display inner div of Wholist - */ -void who_inner_div(void) { - char buf[SIZ], user[SIZ], room[SIZ], host[SIZ], - realroom[SIZ], realhost[SIZ]; - int sess; - time_t last_activity; - time_t now; - int bg = 0; - - wprintf("" - "\n"); - wprintf("\n"); - wprintf("\n", _("User name")); - wprintf("", _("Room")); - wprintf("\n\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("", - (bg ? "DDDDDD" : "FFFFFF") - ); - - - wprintf(""); - - /** (link to page this user) */ - wprintf(""); - - /** (idle flag) */ - wprintf("\n\n\t\n\t\n"); - } - } - wprintf("
%s%s%s
"); - if ((WC->is_aide) && - (sess != WC->ctdl_pid)) { - wprintf(" %s", _("(kill)")); - } - if (sess == WC->ctdl_pid) { - wprintf(" %s", _("(edit)")); - } - wprintf("" - " "); - wprintf(""); - if ((now - last_activity) > 900L) { - wprintf(" " - ""); - } - else { - wprintf(" " - ""); - } - wprintf(""); - - - - /** username (link to user bio/photo page) */ - wprintf(""); - escputs(user); - wprintf(""); - - /** room */ - wprintf(""); - escputs(room); - if (strlen(realroom) > 0) { - wprintf("
"); - escputs(realroom); - wprintf(""); - } - wprintf("
"); - - /** hostname */ - escputs(host); - if (strlen(realhost) > 0) { - wprintf("
"); - escputs(realhost); - wprintf(""); - } - wprintf("
"); -} - - -/** - * \brief who is on? - */ -void who(void) -{ - char title[256]; - - output_headers(1, 1, 2, 0, 0, 0); - - wprintf("\n", _("Do you really want to kill this session?") - ); - - wprintf("
\n"); - wprintf("
"); - wprintf("\""); - wprintf(" "); - - snprintf(title, sizeof title, _("Users currently on %s"), serv_info.serv_humannode); - escputs(title); - - wprintf(""); - offer_start_page(); - wprintf("
\n"); - wprintf("
\n"); - - wprintf("
\n"); - - wprintf("
"); - who_inner_div(); - wprintf("
\n"); - - wprintf("
"); - wprintf(_("Click on a name to read user info. Click on %s " - "to send an instant message to that user."), - "\"(p)\"" - ); - wprintf("
\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( - " \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("
\n"); - wprintf("
"); - wprintf(""); - wprintf(_("Edit your session display")); - wprintf("
\n"); - wprintf("
\n
\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("
\n"); - - wprintf("
\n"); - - wprintf("\n"); - - wprintf("\n\n\n\n"); - - wprintf("\n\n\n"); - - if (WC->is_aide) { - wprintf("\n\n\n"); - } - wprintf("
"); - wprintf(_("Room name:")); - wprintf(""); - wprintf("\n"); - wprintf(""); - wprintf("", - _("Change room name")); - wprintf("
"); - wprintf(_("Host name:")); - wprintf(""); - wprintf("\n"); - wprintf(""); - wprintf("", - _("Change host name")); - wprintf("
"); - wprintf(_("User name:")); - wprintf(""); - wprintf("\n"); - wprintf(""); - wprintf("", - _("Change user name")); - wprintf("
"); - wprintf("", - _("Cancel")); - wprintf("
\n"); - wprintf("
\n"); - wDumpContent(1); - } -} - - -/*@}*/ diff --git a/webcit/wiki.c b/webcit/wiki.c deleted file mode 100644 index 0f35b6150..000000000 --- a/webcit/wiki.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * $Id: $ - */ -/** - * - * \defgroup Wiki Wiki; Functions pertaining to rooms with a wiki view - * \ingroup WebcitDisplayItems - */ - -/*@{*/ -#include "webcit.h" -#include "groupdav.h" - - - -/** - * \brief Convert a string to something suitable as a wiki index - * - * \param s The string to be converted. - */ -void str_wiki_index(char *s) -{ - int i; - - if (s == NULL) return; - - /* First remove all non-alphanumeric characters */ - for (i=0; i 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("

" - "
" - "" - "
" - ); - wprintf("
"); - wprintf(_("There is no page called '%s' here."), pagename); - wprintf("

"); - wprintf(_("Select the 'Edit this page' link in the room banner " - "if you would like to create this page.")); - wprintf("

"); - wprintf("
\n"); - wDumpContent(1); -} - - -/** @} */