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 */
+ 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[SIZ];
- char httpnow[SIZ];
+ char cookie[1024];
+ char httpnow[128];
wprintf("HTTP/1.1 200 OK\n");
- httpdate(httpnow, time(NULL));
+ http_datestring(httpnow, sizeof httpnow, time(NULL));
if (do_httpheaders) {
wprintf("Content-type: text/html; charset=utf-8\r\n"
@@ -385,34 +434,34 @@ void output_headers( int do_httpheaders, /* 1 = output HTTP headers
}
if (do_htmlhead) {
- /* wprintf("\n"); */
begin_burst();
do_template("head");
}
- /* ICONBAR */
+ /** ICONBAR */
if (do_htmlhead) {
- if (WC->HaveInstantMessages) {
- wprintf("
");
}
+
if (do_room_banner == 1) {
wprintf("\n");
- page_popup();
- wprintf("
\n");
- }
+
+ /** 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("" + "%s
\n", WC->ImportantMessage); wprintf("
\n");
wprintf("\n");
safestrncpy(WC->ImportantMessage, "", sizeof WC->ImportantMessage);
}
+
if ( (WC->logged_in) && (!unset_cookies) ) {
wprintf("\n", WC->ImportantMessage); + wprintf("" + "%s
\n", WC->ImportantMessage); wprintf("
");
- do_iconbar();
- wprintf("
\n");
+ do_selected_iconbar();
+ /** check for instant messages (these display in a new window) */
+ page_popup();
+ wprintf("\n");
embed_room_banner(NULL, navbar_default);
@@ -426,8 +475,9 @@ void output_headers( int do_httpheaders, /* 1 = output HTTP headers
}
-/*
- * Generic function to do an HTTP redirect. Easy and fun.
+/**
+ * \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");
@@ -435,26 +485,14 @@ void http_redirect(char *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("Go here.", whichpage);
wprintf("\n");
}
-void check_for_instant_messages()
-{
- char buf[SIZ];
-
- serv_puts("NOOP");
- serv_getln(buf, sizeof buf);
- if (buf[3] == '*') WC->HaveInstantMessages = 1;
-}
-
-
-
-
-/*
- * Output a piece of content to the web browser
+/**
+ * \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) {
@@ -468,7 +506,7 @@ void http_transmit_thing(char *thing, size_t length, char *content_type,
SERVER);
#ifdef HAVE_ZLIB
- /* If we can send the data out compressed, please do so. */
+ /** If we can send the data out compressed, please do so. */
if (WC->gzip_ok) {
char *compressed_data = NULL;
uLongf compressed_len;
@@ -492,7 +530,7 @@ void http_transmit_thing(char *thing, size_t length, char *content_type,
}
#endif
- /* No compression ... just send it out as-is */
+ /** No compression ... just send it out as-is */
wprintf("Content-length: %ld\r\n"
"\r\n",
(long) length
@@ -502,7 +540,10 @@ void http_transmit_thing(char *thing, size_t length, char *content_type,
-
+/**
+ * \brief dump out static pages from disk
+ * \param what the file urs to print
+ */
void output_static(char *what)
{
FILE *fp;
@@ -566,8 +607,8 @@ void output_static(char *what)
}
-/*
- * When the browser requests an image file from the Citadel server,
+/**
+ * \brief When the browser requests an image file from the Citadel server,
* this function is called to transmit it.
*/
void output_image()
@@ -582,17 +623,18 @@ void output_image()
bytes = extract_long(&buf[4], 0);
xferbuf = malloc(bytes + 2);
- /* Read it from the server */
+ /** Read it from the server */
read_server_binary(xferbuf, bytes);
serv_puts("CLOS");
serv_getln(buf, sizeof buf);
- /* Write it to the browser */
+ /** 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
+ /**
+ * Instead of an ugly 404, send a 1x1 transparent GIF
* when there's no such image on the server.
*/
output_static("static/blank.gif");
@@ -602,15 +644,20 @@ void output_image()
}
-/*
- * Generic function to output an arbitrary MIME part from an arbitrary
- * message number on the server.
+/**
+ * \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)
+void mimepart(char *msgnum, char *partnum, int force_download)
{
- char buf[SIZ];
+ char buf[256];
off_t bytes;
- char content_type[SIZ];
+ char content_type[256];
char *content = NULL;
serv_printf("OPNA %s|%s", msgnum, partnum);
@@ -618,7 +665,12 @@ void mimepart(char *msgnum, char *partnum)
if (buf[0] == '2') {
bytes = extract_long(&buf[4], 0);
content = malloc(bytes + 2);
- extract_token(content_type, &buf[4], 3, '|', sizeof content_type);
+ 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");
@@ -636,8 +688,10 @@ void mimepart(char *msgnum, char *partnum)
}
-/*
- * Read any MIME part of a message, from the server, into memory.
+/**
+ * \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)
{
@@ -667,17 +721,20 @@ char *load_mimepart(long msgnum, char *partnum)
}
-/*
- * Convenience functions to display a page containing only a string
+/**
+ * \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("
\n");
+ wprintf("
\n");
wprintf("
\n", titlebarcolor); - wprintf("%s\n", titlebarmsg); - wprintf(" |
", titlebarcolor); + wprintf("%s\n", titlebarmsg); + wprintf(" |
\n");
escputs(messagetext);
@@ -686,8 +743,8 @@ void convenience_page(char *titlebarcolor, char *titlebarmsg, char *messagetext)
}
-/*
- * Display a blank page.
+/**
+ * \brief Display a blank page.
*/
void blank_page(void) {
output_headers(1, 1, 0, 0, 0, 0);
@@ -695,8 +752,8 @@ void blank_page(void) {
}
-/*
- * A template has been requested
+/**
+ * \brief A template has been requested
*/
void url_do_template(void) {
do_template(bstr("template"));
@@ -704,27 +761,27 @@ void url_do_template(void) {
-/*
- * Offer to make any page the user's "start page."
+/**
+ * \brief Offer to make any page the user's "start page."
*/
void offer_start_page(void) {
- wprintf("this_page);
- wprintf("\">");
+ wprintf("\">");
wprintf(_("Make this my start page"));
- wprintf("");
+ wprintf("");
/*
- wprintf("
wc_roomname); wprintf("\" title=\"RSS 2.0 feed for "); escputs(WC->wc_roomname); - wprintf("\">\n"); + wprintf("\">\n"); */ } -/* - * Change the user's start page +/** + * \brief Change the user's start page */ void change_start_page(void) { @@ -745,15 +802,21 @@ void change_start_page(void) { - +/** + * \brief convenience function to indicate success + * \param successmessage the mesage itself + */ void display_success(char *successmessage) { convenience_page("007700", "OK", successmessage); } -/* Authorization required page */ -/* This is probably temporary and should be revisited */ +/** + * \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"); @@ -767,15 +830,30 @@ void authorization_required(const char *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); + /* lprintf(9, "upload_handler() name=%s, type=%s, len=%d\n", + name, cbtype, length); */ /* Form fields */ if ( (length > 0) && (strlen(cbtype) == 0) ) { @@ -788,7 +866,7 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp, u->url_data[length] = 0; } - /* Uploaded files */ + /** Uploaded files */ if ( (length > 0) && (strlen(cbtype) > 0) ) { WC->upload = malloc(length); if (WC->upload != NULL) { @@ -806,8 +884,8 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp, } -/* - * Convenience functions to wrap around asynchronous ajax responses +/** + * \brief Convenience functions to wrap around asynchronous ajax responses */ void begin_ajax_response(void) { output_headers(0, 0, 0, 0, 0, 0); @@ -821,20 +899,106 @@ void begin_ajax_response(void) { 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(); -/* - * Entry point for WebCit transaction + 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[128]; + char action[1024]; char arg1[128]; char arg2[128]; char arg3[128]; @@ -844,7 +1008,7 @@ void session_loop(struct httprequest *req) char arg7[128]; char buf[SIZ]; char request_method[128]; - char pathname[512]; + char pathname[1024]; int a, b; int ContentLength = 0; int BytesRead = 0; @@ -852,12 +1016,13 @@ void session_loop(struct httprequest *req) char *content = NULL; char *content_end = NULL; struct httprequest *hptr; - char browser_host[SIZ]; - char user_agent[SIZ]; + 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, + /** + * 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]; @@ -874,11 +1039,11 @@ void session_loop(struct httprequest *req) 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; @@ -887,9 +1052,9 @@ void session_loop(struct httprequest *req) safestrncpy(cmd, hptr->line, sizeof cmd); hptr = hptr->next; extract_token(request_method, cmd, 0, ' ', sizeof request_method); - extract_token(pathname, cmd, 1, ' ', sizeof request_method); + extract_token(pathname, cmd, 1, ' ', sizeof pathname); - /* Figure out the action */ + /** Figure out the action */ extract_token(action, pathname, 1, '/', sizeof action); if (strstr(action, "?")) *strstr(action, "?") = 0; if (strstr(action, "&")) *strstr(action, "&") = 0; @@ -932,6 +1097,7 @@ void session_loop(struct httprequest *req) 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)) { @@ -955,10 +1121,24 @@ void session_loop(struct httprequest *req) 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)) { - safestrncpy(WC->http_host, &buf[6], sizeof WC->http_host); + if (strlen(WC->http_host) == 0) { + safestrncpy(WC->http_host, &buf[6], sizeof WC->http_host); + } } - /* Only WAP gateways explicitly name this content-type */ + 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; } @@ -972,7 +1152,7 @@ void session_loop(struct httprequest *req) ContentType, ContentLength); body_start = strlen(content); - /* Read the entire input data at once. */ + /** Read the entire input data at once. */ client_read(WC->http_sock, &content[BytesRead+body_start], ContentLength); @@ -988,12 +1168,12 @@ void session_loop(struct httprequest *req) content = NULL; } - /* make a note of where we are in case the user wants to save it */ + /** 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 */ + /** 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) @@ -1004,8 +1184,16 @@ void session_loop(struct httprequest *req) } } + /** 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. */ + /** 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])) { @@ -1029,7 +1217,7 @@ void session_loop(struct httprequest *req) 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. */ @@ -1050,8 +1238,18 @@ void session_loop(struct httprequest *req) } else { WC->connected = 1; - serv_getln(buf, sizeof buf); /* get the server welcome message */ - locate_host(browser_host, WC->http_sock); + 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 " @@ -1070,7 +1268,7 @@ void session_loop(struct httprequest *req) } } - /* + /** * Functions which can be performed without logging in */ if (!strcasecmp(action, "listsub")) { @@ -1084,7 +1282,7 @@ void session_loop(struct httprequest *req) } #endif - /* + /** * If we're not logged in, but we have HTTP Authentication data, * try logging in to Citadel using that. */ @@ -1102,20 +1300,20 @@ void session_loop(struct httprequest *req) 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 */ + /** Should only display when password is wrong */ authorization_required(&buf[4]); goto SKIP_ALL_THIS_CRAP; } } } - /* This needs to run early */ + /** 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. */ @@ -1129,20 +1327,20 @@ void session_loop(struct httprequest *req) } - /* + /** * 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 */ + 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 */ + 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. */ @@ -1159,7 +1357,7 @@ void session_loop(struct httprequest *req) } } } - /* + /** * If we don't have a current room, but a cookie specifying the * current room is supplied, make an effort to go there. */ @@ -1171,25 +1369,20 @@ void session_loop(struct httprequest *req) } } - /* - * If there are instant messages waiting, retrieve them for display. - */ - check_for_instant_messages(); - if (!strcasecmp(action, "image")) { output_image(); - /* - * All functions handled below this point ... make sure we log in - * before doing anything else! - */ + /** + * 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... */ @@ -1205,10 +1398,20 @@ void session_loop(struct httprequest *req) 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")) { @@ -1236,9 +1439,13 @@ void session_loop(struct httprequest *req) } else if (!strcasecmp(action, "headers")) { readloop("headers"); } else if (!strcasecmp(action, "msg")) { - embed_message(); + embed_message(arg1); } else if (!strcasecmp(action, "printmsg")) { - print_message(); + 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")) { @@ -1286,7 +1493,7 @@ void session_loop(struct httprequest *req) } else if (!strcasecmp(action, "editinfo")) { save_edit(_("Room info"), "EINF 1", 1); } else if (!strcasecmp(action, "display_editbio")) { - sprintf(buf, "RBIO %s", WC->wc_username); + 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); @@ -1299,13 +1506,13 @@ void session_loop(struct httprequest *req) } else if (!strcasecmp(action, "display_editpic")) { display_graphics_upload(_("your photo"), "UIMG 0|_userpic_", - "/editpic"); + "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"); + "editroompic"); } else if (!strcasecmp(action, "editroompic")) { do_graphics_upload("UIMG 1|_roompic_"); } else if (!strcasecmp(action, "delete_floor")) { @@ -1319,7 +1526,7 @@ void session_loop(struct httprequest *req) bstr("which_floor")); display_graphics_upload(_("the icon for this floor"), buf, - "/editfloorpic"); + "editfloorpic"); } else if (!strcasecmp(action, "editfloorpic")) { sprintf(buf, "UIMG 1|_floorpic_|%s", bstr("which_floor")); @@ -1361,10 +1568,14 @@ void session_loop(struct httprequest *req) 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); + 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")) { @@ -1379,8 +1590,6 @@ void session_loop(struct httprequest *req) create_user(); } else if (!strcasecmp(action, "changeview")) { change_view(); - } else if (!strcasecmp(action, "do_stuff_to_msgs")) { - do_stuff_to_msgs(); } else if (!strcasecmp(action, "change_start_page")) { change_start_page(); } else if (!strcasecmp(action, "display_floorconfig")) { @@ -1407,8 +1616,6 @@ void session_loop(struct httprequest *req) begin_ajax_response(); summary_inner_div(); end_ajax_response(); - } else if (!strcasecmp(action, "iconbar")) { - do_iconbar(); } else if (!strcasecmp(action, "display_customize_iconbar")) { display_customize_iconbar(); } else if (!strcasecmp(action, "commit_iconbar")) { @@ -1431,6 +1638,8 @@ void session_loop(struct httprequest *req) 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); @@ -1441,9 +1650,11 @@ void session_loop(struct httprequest *req) dump_vars(); wprintf("
\n"); wDumpContent(1); + } else if (!strcasecmp(action, "updatenote")) { + updatenote(); } - /* When all else fais, display the main menu. */ + /** When all else fais, display the main menu. */ else { display_main_menu(); } @@ -1459,5 +1670,7 @@ SKIP_ALL_THIS_CRAP: free(WC->upload); WC->upload_length = 0; } - } + + +/*@}*/
wc_roomname); wprintf("\" title=\"RSS 2.0 feed for "); escputs(WC->wc_roomname); - wprintf("\">\n"); + wprintf("\">\n"); */ } -/* - * Change the user's start page +/** + * \brief Change the user's start page */ void change_start_page(void) { @@ -745,15 +802,21 @@ void change_start_page(void) { - +/** + * \brief convenience function to indicate success + * \param successmessage the mesage itself + */ void display_success(char *successmessage) { convenience_page("007700", "OK", successmessage); } -/* Authorization required page */ -/* This is probably temporary and should be revisited */ +/** + * \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"); @@ -767,15 +830,30 @@ void authorization_required(const char *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); + /* lprintf(9, "upload_handler() name=%s, type=%s, len=%d\n", + name, cbtype, length); */ /* Form fields */ if ( (length > 0) && (strlen(cbtype) == 0) ) { @@ -788,7 +866,7 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp, u->url_data[length] = 0; } - /* Uploaded files */ + /** Uploaded files */ if ( (length > 0) && (strlen(cbtype) > 0) ) { WC->upload = malloc(length); if (WC->upload != NULL) { @@ -806,8 +884,8 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp, } -/* - * Convenience functions to wrap around asynchronous ajax responses +/** + * \brief Convenience functions to wrap around asynchronous ajax responses */ void begin_ajax_response(void) { output_headers(0, 0, 0, 0, 0, 0); @@ -821,20 +899,106 @@ void begin_ajax_response(void) { 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(); -/* - * Entry point for WebCit transaction + 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[128]; + char action[1024]; char arg1[128]; char arg2[128]; char arg3[128]; @@ -844,7 +1008,7 @@ void session_loop(struct httprequest *req) char arg7[128]; char buf[SIZ]; char request_method[128]; - char pathname[512]; + char pathname[1024]; int a, b; int ContentLength = 0; int BytesRead = 0; @@ -852,12 +1016,13 @@ void session_loop(struct httprequest *req) char *content = NULL; char *content_end = NULL; struct httprequest *hptr; - char browser_host[SIZ]; - char user_agent[SIZ]; + 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, + /** + * 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]; @@ -874,11 +1039,11 @@ void session_loop(struct httprequest *req) 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; @@ -887,9 +1052,9 @@ void session_loop(struct httprequest *req) safestrncpy(cmd, hptr->line, sizeof cmd); hptr = hptr->next; extract_token(request_method, cmd, 0, ' ', sizeof request_method); - extract_token(pathname, cmd, 1, ' ', sizeof request_method); + extract_token(pathname, cmd, 1, ' ', sizeof pathname); - /* Figure out the action */ + /** Figure out the action */ extract_token(action, pathname, 1, '/', sizeof action); if (strstr(action, "?")) *strstr(action, "?") = 0; if (strstr(action, "&")) *strstr(action, "&") = 0; @@ -932,6 +1097,7 @@ void session_loop(struct httprequest *req) 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)) { @@ -955,10 +1121,24 @@ void session_loop(struct httprequest *req) 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)) { - safestrncpy(WC->http_host, &buf[6], sizeof WC->http_host); + if (strlen(WC->http_host) == 0) { + safestrncpy(WC->http_host, &buf[6], sizeof WC->http_host); + } } - /* Only WAP gateways explicitly name this content-type */ + 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; } @@ -972,7 +1152,7 @@ void session_loop(struct httprequest *req) ContentType, ContentLength); body_start = strlen(content); - /* Read the entire input data at once. */ + /** Read the entire input data at once. */ client_read(WC->http_sock, &content[BytesRead+body_start], ContentLength); @@ -988,12 +1168,12 @@ void session_loop(struct httprequest *req) content = NULL; } - /* make a note of where we are in case the user wants to save it */ + /** 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 */ + /** 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) @@ -1004,8 +1184,16 @@ void session_loop(struct httprequest *req) } } + /** 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. */ + /** 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])) { @@ -1029,7 +1217,7 @@ void session_loop(struct httprequest *req) 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. */ @@ -1050,8 +1238,18 @@ void session_loop(struct httprequest *req) } else { WC->connected = 1; - serv_getln(buf, sizeof buf); /* get the server welcome message */ - locate_host(browser_host, WC->http_sock); + 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 " @@ -1070,7 +1268,7 @@ void session_loop(struct httprequest *req) } } - /* + /** * Functions which can be performed without logging in */ if (!strcasecmp(action, "listsub")) { @@ -1084,7 +1282,7 @@ void session_loop(struct httprequest *req) } #endif - /* + /** * If we're not logged in, but we have HTTP Authentication data, * try logging in to Citadel using that. */ @@ -1102,20 +1300,20 @@ void session_loop(struct httprequest *req) 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 */ + /** Should only display when password is wrong */ authorization_required(&buf[4]); goto SKIP_ALL_THIS_CRAP; } } } - /* This needs to run early */ + /** 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. */ @@ -1129,20 +1327,20 @@ void session_loop(struct httprequest *req) } - /* + /** * 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 */ + 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 */ + 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. */ @@ -1159,7 +1357,7 @@ void session_loop(struct httprequest *req) } } } - /* + /** * If we don't have a current room, but a cookie specifying the * current room is supplied, make an effort to go there. */ @@ -1171,25 +1369,20 @@ void session_loop(struct httprequest *req) } } - /* - * If there are instant messages waiting, retrieve them for display. - */ - check_for_instant_messages(); - if (!strcasecmp(action, "image")) { output_image(); - /* - * All functions handled below this point ... make sure we log in - * before doing anything else! - */ + /** + * 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... */ @@ -1205,10 +1398,20 @@ void session_loop(struct httprequest *req) 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")) { @@ -1236,9 +1439,13 @@ void session_loop(struct httprequest *req) } else if (!strcasecmp(action, "headers")) { readloop("headers"); } else if (!strcasecmp(action, "msg")) { - embed_message(); + embed_message(arg1); } else if (!strcasecmp(action, "printmsg")) { - print_message(); + 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")) { @@ -1286,7 +1493,7 @@ void session_loop(struct httprequest *req) } else if (!strcasecmp(action, "editinfo")) { save_edit(_("Room info"), "EINF 1", 1); } else if (!strcasecmp(action, "display_editbio")) { - sprintf(buf, "RBIO %s", WC->wc_username); + 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); @@ -1299,13 +1506,13 @@ void session_loop(struct httprequest *req) } else if (!strcasecmp(action, "display_editpic")) { display_graphics_upload(_("your photo"), "UIMG 0|_userpic_", - "/editpic"); + "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"); + "editroompic"); } else if (!strcasecmp(action, "editroompic")) { do_graphics_upload("UIMG 1|_roompic_"); } else if (!strcasecmp(action, "delete_floor")) { @@ -1319,7 +1526,7 @@ void session_loop(struct httprequest *req) bstr("which_floor")); display_graphics_upload(_("the icon for this floor"), buf, - "/editfloorpic"); + "editfloorpic"); } else if (!strcasecmp(action, "editfloorpic")) { sprintf(buf, "UIMG 1|_floorpic_|%s", bstr("which_floor")); @@ -1361,10 +1568,14 @@ void session_loop(struct httprequest *req) 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); + 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")) { @@ -1379,8 +1590,6 @@ void session_loop(struct httprequest *req) create_user(); } else if (!strcasecmp(action, "changeview")) { change_view(); - } else if (!strcasecmp(action, "do_stuff_to_msgs")) { - do_stuff_to_msgs(); } else if (!strcasecmp(action, "change_start_page")) { change_start_page(); } else if (!strcasecmp(action, "display_floorconfig")) { @@ -1407,8 +1616,6 @@ void session_loop(struct httprequest *req) begin_ajax_response(); summary_inner_div(); end_ajax_response(); - } else if (!strcasecmp(action, "iconbar")) { - do_iconbar(); } else if (!strcasecmp(action, "display_customize_iconbar")) { display_customize_iconbar(); } else if (!strcasecmp(action, "commit_iconbar")) { @@ -1431,6 +1638,8 @@ void session_loop(struct httprequest *req) 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); @@ -1441,9 +1650,11 @@ void session_loop(struct httprequest *req) dump_vars(); wprintf("
\n"); wDumpContent(1); + } else if (!strcasecmp(action, "updatenote")) { + updatenote(); } - /* When all else fais, display the main menu. */ + /** When all else fais, display the main menu. */ else { display_main_menu(); } @@ -1459,5 +1670,7 @@ SKIP_ALL_THIS_CRAP: free(WC->upload); WC->upload_length = 0; } - } + + +/*@}*/