/* * Authorization required page (sends a 401, causing the browser to request login credentials) */ void authorization_required(void) { wcsession *WCC = WC; const char *message = ""; hprintf("HTTP/1.1 401 Authorization Required\r\n"); hprintf( "Server: %s / %s\r\n" "Connection: close\r\n", PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software) ); hprintf("WWW-Authenticate: Basic realm=\"%s\"\r\n", ChrPtr(WC->serv_info->serv_humannode)); /* if this is a false cookie authentication, remove it to avoid endless loops. */ if (StrLength(WCC->Hdr->HR.RawCookie) > 0) stuff_to_cookie(1); hprintf("Content-Type: text/html\r\n"); begin_burst(); wc_printf("<h1>"); wc_printf(_("Authorization Required")); wc_printf("</h1>\r\n"); if (WCC->ImportantMsg != NULL) { message = ChrPtr(WCC->ImportantMsg); } wc_printf( _("The resource you requested requires a valid username and password. " "You could not be logged in: %s\n"), message ); wDumpContent(0); }
/* * Embed the room banner * * got The information returned from a GOTO server command * navbar_style Determines which navigation buttons to display */ void tmplput_roombanner(StrBuf *Target, WCTemplputParams *TP) { wcsession *WCC = WC; /* Refresh current room states. Doesn't work? gotoroom(NULL); */ wc_printf("<div id=\"banner\">\n"); /* The browser needs some information for its own use */ wc_printf("<script type=\"text/javascript\"> \n" " room_is_trash = %d; \n" "</script>\n", ((WC->CurRoom.RAFlags & UA_ISTRASH) != 0) ); /* * 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. */ if (WCC->Hdr->this_page == NULL) { WCC->Hdr->this_page = NewStrBuf(); } StrBufPrintf(WCC->Hdr->this_page, "dotskip?room=%s", ChrPtr(WC->CurRoom.name)); do_template("roombanner"); do_template("navbar"); wc_printf("</div>\n"); }
/* * Generic function to do an HTTP redirect. Easy and fun. */ void http_redirect(const char *whichpage) { hprintf("HTTP/1.1 302 Moved Temporarily\n"); hprintf("Location: %s\r\n", whichpage); hprintf("URI: %s\r\n", whichpage); hprintf("Content-type: text/html; charset=utf-8\r\n"); stuff_to_cookie(0); begin_burst(); wc_printf("<html><body>"); wc_printf("Go <a href=\"%s\">here</A>.", whichpage); wc_printf("</body></html>\n"); end_burst(); }
/* * Save a URL destination so we can go to it later */ void push_destination(void) { wcsession *WCC = WC; if (!WCC) { wc_printf("no session"); return; } FreeStrBuf(&WCC->PushedDestination); WCC->PushedDestination = NewStrBufDup(SBSTR("url")); if (verbose) syslog(LOG_DEBUG, "Push: %s", ChrPtr(WCC->PushedDestination)); wc_printf("OK"); }
/* If it's a "force 404" situation then display the error and bail. */ void do_404(void) { hprintf("HTTP/1.1 404 Not found\r\n"); hprintf("Content-Type: text/plain\r\n"); wc_printf("Not found\r\n"); end_burst(); }
/* * This function is for uploading an ENTIRE calendar, not just one * component. This would be for webcal:// 'publish' operations, not * for GroupDAV. */ void dav_put_bigics(void) { wcsession *WCC = WC; char buf[1024]; /* * Tell the server that when we save a calendar event, we * do *not* want the server to generate invitations. */ serv_puts("ICAL sgi|0"); serv_getln(buf, sizeof buf); serv_puts("ICAL putics"); serv_getln(buf, sizeof buf); if (buf[0] != '4') { hprintf("HTTP/1.1 502 Bad Gateway\r\n"); dav_common_headers(); hprintf("Content-type: text/plain\r\n"); begin_burst(); wc_printf("%s\r\n", &buf[4]); end_burst(); return; } serv_putbuf(WCC->upload); serv_printf("\n000"); /* Report success and not much else. */ hprintf("HTTP/1.1 204 No Content\r\n"); syslog(LOG_DEBUG, "HTTP/1.1 204 No Content\r\n"); dav_common_headers(); begin_burst(); end_burst(); }
/* * Output a string to the client as a CDATA block */ void cdataout(char *rawdata) { char *ptr = rawdata; wc_printf("<![CDATA["); while ((ptr != NULL) && (ptr[0] != 0)) { if (!strncmp(ptr, "]]>", 3)) { wc_printf("]]]]><![CDATA[>"); ++ptr; ++ptr; ++ptr; } else { wc_printf("%c", ptr[0]); ++ptr; } } wc_printf("]]>"); }
/* * 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]; if ( (time(NULL) - WC->last_pager_check) < 30) { wc_printf("NO\n"); } else { memset(buf, 0, 5); serv_puts("NOOP"); serv_getln(buf, sizeof buf); if (buf[3] == '*') { wc_printf("YES"); } else { wc_printf("NO"); } } }
/* * wholist for chat */ void chat_rwho(void) { char buf[1024]; serv_puts("RCHT rwho"); serv_getln(buf, sizeof buf); if (buf[0] == '1') { while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { if (!strcasecmp(buf, ChrPtr(WC->wc_fullname))) { wc_printf("<span class=\"chat_myname_class\">"); } else { wc_printf("<span class=\"chat_notmyname_class\">"); } wc_printf("<img src=\"static/webcit_icons/essen/16x16/chat.png\">"); escputs(buf); wc_printf("</span><br>\n"); } } }
/* * wrap up an HTTP session, closes tags, etc. * * print_standard_html_footer should be set to: * 0 - to transmit only, * nonzero - to append the closing tags */ void wDumpContent(int print_standard_html_footer) { if (print_standard_html_footer) { wc_printf("</div> <!-- end of 'content' div -->\n"); do_template("trailing"); } /* If we've been saving it all up for one big output burst, * go ahead and do that now. */ end_burst(); }
/* * Display a specific page from a wiki room * * "rev" may be set to an empty string to display the current version. * "do_revert" may be set to nonzero to perform a reversion to the specified version. */ void display_wiki_page_backend(char *pagename, char *rev, int do_revert) { wcsession *WCC = WC; const StrBuf *Mime; long msgnum = (-1L); char buf[256]; if ((WCC->CurRoom.view != VIEW_WIKI) && (WCC->CurRoom.view != VIEW_WIKIMD)) { wc_printf(_("'%s' is not a Wiki room."), ChrPtr(WCC->CurRoom.name) ); return; } if (IsEmptyStr(pagename)) { strcpy(pagename, "home"); } str_wiki_index(pagename); /* convert index name to lowercase and numeric only */ if ((rev != NULL) && (strlen(rev) > 0)) { /* read an older revision */ serv_printf("WIKI rev|%s|%s|%s", pagename, rev, (do_revert ? "revert" : "fetch") ); serv_getln(buf, sizeof buf); if (buf[0] == '2') { msgnum = extract_long(&buf[4], 0); } } else { /* read the current revision */ msgnum = locate_message_by_uid(pagename); } if (msgnum >= 0L) { read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime); return; } wc_printf("<br><br>" "<div align=\"center\">" "<table border=\"0\" bgcolor=\"#ffffff\" cellpadding=\"10\">" "<tr><td align=\"center\">" ); wc_printf("<br><b>"); wc_printf(_("There is no page called '%s' here."), pagename); wc_printf("</b><br><br>"); wc_printf(_("Select the 'Edit this page' link in the room banner " "if you would like to create this page.")); wc_printf("<br><br>"); wc_printf("</td></tr></table></div>\n"); }
int notes_GetParamsGetServerCall(SharedMessageStatus *Stat, void **ViewSpecific, long oper, char *cmd, long len, char *filter, long flen) { strcpy(cmd, "MSGS ALL"); Stat->maxmsgs = 32767; wc_printf("<div id=\"new_notes_here\"></div>\n"); return 200; }
/* * The pathname is always going to be /groupdav/room_name/msg_num */ void dav_report(void) { char datestring[256]; time_t now = time(NULL); http_datestring(datestring, sizeof datestring, now); const char *req = ChrPtr(WC->upload); syslog(LOG_DEBUG, "REPORT: \033[31m%s\033[0m", req); hprintf("HTTP/1.1 500 Internal Server Error\r\n"); dav_common_headers(); hprintf("Date: %s\r\n", datestring); hprintf("Content-Type: text/plain\r\n"); wc_printf("An internal error has occurred at %s:%d.\r\n", __FILE__ , __LINE__ ); end_burst(); return; }
/* * Convenience functions to display a page containing only a string * * titlebarcolor color of the titlebar of the frame * titlebarmsg text to display in the title bar * messagetext body of the box */ void convenience_page(const char *titlebarcolor, const char *titlebarmsg, const char *messagetext) { hprintf("HTTP/1.1 200 OK\n"); output_headers(1, 1, 1, 0, 0, 0); wc_printf("<div id=\"room_banner_override\">\n"); wc_printf("<table width=100%% border=0 bgcolor=\"#%s\"><tr><td>", titlebarcolor); wc_printf("<span class=\"titlebar\">%s</span>\n", titlebarmsg); wc_printf("</td></tr></table>\n"); wc_printf("</div>\n<div id=\"content\">\n"); escputs(messagetext); wc_printf("<hr />\n"); wDumpContent(1); }
/* * Display a list of all pages in a Wiki room (template callback) */ void tmplput_display_wiki_pagelist(StrBuf *Target, WCTemplputParams *TP) { StrBuf *Buf; int row = 0; if (!IsEmptyStr(bstr("query"))) { serv_printf("MSGS SEARCH|%s||4", bstr("query")); /* search-reduced list */ } else { serv_printf("MSGS ALL|||4"); /* full list */ } Buf = NewStrBuf(); StrBuf_ServGetln(Buf); if (GetServerStatus(Buf, NULL) == 1) { StrBuf *pagetitle = NewStrBuf(); wc_printf("<table class=\"wiki_pagelist_background\">"); wc_printf("<th>%s</th>", _("Page title")); while((StrBuf_ServGetln(Buf) >= 0) && strcmp(ChrPtr(Buf), "000")) { StrBufExtract_token(pagetitle, Buf, 1, '|'); if (!bmstrcasestr((char *)ChrPtr(pagetitle), "_HISTORY_")) { /* no history pages */ wc_printf("<tr bgcolor=\"%s\">", ((row%2) ? "#FFFFFF" : "#DDDDDD")); wc_printf("<td><a href=\"wiki?page="); urlescputs(ChrPtr(pagetitle)); wc_printf("\">"); escputs(ChrPtr(pagetitle)); wc_printf("</a></td>"); wc_printf("</tr>\n"); ++row; } } wc_printf("</table>\n"); FreeStrBuf(&pagetitle); } FreeStrBuf(&Buf); }
/* * Receiving side of the chat window. * This does JavaScript writes to * other divs whenever it refreshes and finds new data. */ void chat_recv(void) { char buf[SIZ]; char cl_user[SIZ]; serv_printf("RCHT poll|%d", WC->last_chat_seq); serv_getln(buf, sizeof buf); if (buf[0] == '1') { WC->last_chat_seq = extract_int(&buf[4], 0); extract_token(cl_user, &buf[4], 2, '|', sizeof cl_user); /* who is speaking ... */ if (strcasecmp(cl_user, WC->last_chat_user)) { wc_printf("<br>\n"); if (!strcasecmp(cl_user, ChrPtr(WC->wc_fullname))) { wc_printf("<span class=\"chat_myname_class\">"); } else { wc_printf("<span class=\"chat_notmyname_class\">"); } escputs(cl_user); strcpy(WC->last_chat_user, cl_user); wc_printf(": </span>"); } else { wc_printf(" "); } /* what did they say ... */ wc_printf("<span class=\"chat_text_class\">"); while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { escputs(buf); } wc_printf("<br></span>\n"); } }
/* * RSS feed generator -- do one message */ void feed_rss_one_message(long msgnum) { int in_body = 0; int in_messagetext = 0; int found_title = 0; int found_guid = 0; char pubdate[128]; StrBuf *messagetext = NULL; int is_top_level_post = 1; const char *BufPtr = NULL; StrBuf *Line = NewStrBufPlain(NULL, 1024); char buf[1024]; int permalink_hash = 0; /* Phase 1: read the message into memory */ serv_printf("MSG4 %ld", msgnum); serv_getln(buf, sizeof buf); if (buf[0] != '1') return; StrBuf *ServerResponse = NewStrBuf(); while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { StrBufAppendPrintf(ServerResponse, "%s\n", buf); } /* Phase 2: help SkyNet become self-aware */ BufPtr = NULL; while (StrBufSipLine(Line, ServerResponse, &BufPtr), ((BufPtr!=StrBufNOTNULL)&&(BufPtr!=NULL)) ) { if (in_body) { /* do nothing */ } else if (StrLength(Line) == 0) { ++in_body; } else if ((StrLength(Line) > 5) && (!strncasecmp(ChrPtr(Line), "wefw=", 5))) { is_top_level_post = 0; /* presence of references means it's a reply/comment */ } else if ((StrLength(Line) > 5) && (!strncasecmp(ChrPtr(Line), "msgn=", 5))) { StrBufCutLeft(Line, 5); permalink_hash = ThreadIdHash(Line); } } /* * Phase 3: output the message in RSS <item> form * (suppress replies [comments] if this is a blog room) */ if ( (WC->CurRoom.view != VIEW_BLOG) || (is_top_level_post == 1) ) { wc_printf("<item>"); wc_printf("<link>%s/readfwd?go=", ChrPtr(site_prefix)); urlescputs(ChrPtr(WC->CurRoom.name)); if ((WC->CurRoom.view == VIEW_BLOG) && (permalink_hash != 0)) { wc_printf("?p=%d", permalink_hash); } else { wc_printf("?start_reading_at=%ld", msgnum); } wc_printf("</link>"); BufPtr = NULL; in_body = 0; in_messagetext = 0; while (StrBufSipLine(Line, ServerResponse, &BufPtr), ((BufPtr!=StrBufNOTNULL)&&(BufPtr!=NULL)) ) { safestrncpy(buf, ChrPtr(Line), sizeof buf); /* XML parsers can be picky; strip out nonprintable header characters */ if ((strlen(buf)>=6) && (buf[4]=='=')) { char *p = &buf[5]; while (*p) { if (!isprint(*p)) { *p = 0; } ++p; } } /* Now output fields */ if (in_body) { if (in_messagetext) { StrBufAppendBufPlain(messagetext, buf, -1, 0); StrBufAppendBufPlain(messagetext, HKEY("\r\n"), 0); } else if (IsEmptyStr(buf)) { in_messagetext = 1; } } else if (!strncasecmp(buf, "subj=", 5)) { wc_printf("<title>"); escputs(&buf[5]); wc_printf("</title>"); ++found_title; } else if (!strncasecmp(buf, "exti=", 5)) { wc_printf("<guid isPermaLink=\"false\">"); escputs(&buf[5]); wc_printf("</guid>"); ++found_guid; } else if (!strncasecmp(buf, "time=", 5)) { http_datestring(pubdate, sizeof pubdate, atol(&buf[5])); wc_printf("<pubDate>%s</pubDate>", pubdate); } else if (!strncasecmp(buf, "text", 4)) { if (!found_title) { wc_printf("<title>Message #%ld</title>", msgnum); } if (!found_guid) { wc_printf("<guid isPermaLink=\"false\">%ld@%s</guid>", msgnum, ChrPtr(WC->serv_info->serv_humannode) ); } wc_printf("<description>"); in_body = 1; messagetext = NewStrBuf(); } } if (in_body) { cdataout((char*)ChrPtr(messagetext)); FreeStrBuf(&messagetext); wc_printf("</description>"); } wc_printf("</item>"); } FreeStrBuf(&Line); FreeStrBuf(&ServerResponse); return; }
/* * Entry point for RSS feed generator */ void feed_rss(void) { char buf[1024]; output_headers(0, 0, 0, 0, 1, 0); hprintf("Content-type: text/xml; charset=utf-8\r\n"); hprintf( "Server: %s / %s\r\n" "Connection: close\r\n" , PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software) ); begin_burst(); wc_printf("<?xml version=\"1.0\"?>" "<rss version=\"2.0\">" "<channel>" ); wc_printf("<title>"); escputs(ChrPtr(WC->CurRoom.name)); wc_printf("</title>"); wc_printf("<link>"); escputs(ChrPtr(site_prefix)); wc_printf("/</link>"); serv_puts("RINF"); serv_getln(buf, sizeof buf); if (buf[0] == '1') { wc_printf("<description>\r\n"); while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { escputs(buf); wc_printf("\r\n"); } wc_printf("</description>"); } wc_printf("<image><title>"); escputs(ChrPtr(WC->CurRoom.name)); wc_printf("</title><url>"); escputs(ChrPtr(site_prefix)); wc_printf("/roompic?room="); urlescputs(ChrPtr(WC->CurRoom.name)); wc_printf("</url><link>"); escputs(ChrPtr(site_prefix)); wc_printf("/</link></image>\r\n"); feed_rss_do_room_info_as_description(); feed_rss_do_messages(); wc_printf("</channel>" "</rss>" "\r\n\r\n" ); wDumpContent(0); }
/* * Output the room info file of the current room as a <description> for the channel */ void feed_rss_do_room_info_as_description(void) { wc_printf("<description>"); escputs(ChrPtr(WC->CurRoom.name)); /* FIXME use the output of RINF instead */ wc_printf("</description>\r\n"); }
/* * Entry point for WebCit transaction */ void session_loop(void) { int xhttp; StrBuf *Buf; /* * 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. */ wcsession *WCC; WCC= WC; WCC->upload_length = 0; WCC->upload = NULL; WCC->Hdr->nWildfireHeaders = 0; if (WCC->Hdr->HR.ContentLength > 0) { if (ReadPostData() < 0) { return; } } Buf = NewStrBuf(); WCC->trailing_javascript = NewStrBuf(); /* Convert base64-encoded URL's back to plain text */ if (!strncmp(ChrPtr(WCC->Hdr->this_page), "/B64", 4)) { StrBufCutLeft(WCC->Hdr->this_page, 4); StrBufDecodeBase64(WCC->Hdr->this_page); http_redirect(ChrPtr(WCC->Hdr->this_page)); goto SKIP_ALL_THIS_CRAP; } /* If there are variables in the URL, we must grab them now */ if (WCC->Hdr->PlainArgs != NULL) ParseURLParams(WCC->Hdr->PlainArgs); /* If the client sent a nonce that is incorrect, kill the request. */ if (havebstr("nonce")) { if (verbose) syslog(LOG_DEBUG, "Comparing supplied nonce %s to session nonce %d", bstr("nonce"), WCC->nonce ); if (ibstr("nonce") != WCC->nonce) { syslog(LOG_INFO, "Ignoring request with mismatched nonce."); hprintf("HTTP/1.1 404 Security check failed\r\n"); hprintf("Content-Type: text/plain\r\n"); begin_burst(); wc_printf("Security check failed.\r\n"); end_burst(); goto SKIP_ALL_THIS_CRAP; } } /* * If we're not connected to a Citadel server, try to hook up the connection now. */ if (!WCC->connected) { if (GetConnected()) { hprintf("HTTP/1.1 503 Service Unavailable\r\n"); hprintf("Content-Type: text/html\r\n"); begin_burst(); wc_printf("<html><head><title>503 Service Unavailable</title></head><body>\n"); wc_printf(_("This program was unable to connect or stay " "connected to the Citadel server. Please report " "this problem to your system administrator.") ); wc_printf("<br>"); wc_printf("<a href=\"http://www.citadel.org/doku.php/" "faq:generalquestions:webcit_unable_to_connect\">%s</a>", _("Read More...") ); wc_printf("</body></html>\n"); end_burst(); goto SKIP_ALL_THIS_CRAP; } } /* * If we're not logged in, but we have authentication data (either from * a cookie or from http-auth), try logging in to Citadel using that. */ if ( (!WCC->logged_in) && (StrLength(WCC->Hdr->c_username) > 0) && (StrLength(WCC->Hdr->c_password) > 0) ) { long Status; FlushStrBuf(Buf); serv_printf("USER %s", ChrPtr(WCC->Hdr->c_username)); StrBuf_ServGetln(Buf); if (GetServerStatus(Buf, &Status) == 3) { serv_printf("PASS %s", ChrPtr(WCC->Hdr->c_password)); StrBuf_ServGetln(Buf); if (GetServerStatus(Buf, NULL) == 2) { become_logged_in(WCC->Hdr->c_username, WCC->Hdr->c_password, Buf); } else { /* Should only display when password is wrong */ WCC->ImportantMsg = NewStrBufPlain(ChrPtr(Buf) + 4, StrLength(Buf) - 4); authorization_required(); FreeStrBuf(&Buf); goto SKIP_ALL_THIS_CRAP; } } else if (Status == 541) { WCC->logged_in = 1; } } xhttp = (WCC->Hdr->HR.eReqType != eGET) && (WCC->Hdr->HR.eReqType != ePOST) && (WCC->Hdr->HR.eReqType != eHEAD); /* * If a 'go' (or 'gotofirst') parameter has been specified, attempt to goto that room * prior to doing anything else. */ if (havebstr("go")) { int ret; if (verbose) syslog(LOG_DEBUG, "Explicit room selection: %s", bstr("go")); ret = gotoroom(sbstr("go")); /* do quietly to avoid session output! */ if ((ret/100) != 2) { if (verbose) syslog(LOG_DEBUG, "Unable to change to [%s]; Reason: %d", bstr("go"), ret); } } else if (havebstr("gotofirst")) { int ret; if (verbose) syslog(LOG_DEBUG, "Explicit room selection: %s", bstr("gotofirst")); ret = gotoroom(sbstr("gotofirst")); /* do quietly to avoid session output! */ if ((ret/100) != 2) { syslog(LOG_INFO, "Unable to change to [%s]; Reason: %d", bstr("gotofirst"), ret); } } /* * If we aren't in any room yet, but we have cookie data telling us where we're * supposed to be, and 'go' was not specified, then go there. */ else if ( (StrLength(WCC->CurRoom.name) == 0) && ( (StrLength(WCC->Hdr->c_roomname) > 0) )) { int ret; if (verbose) syslog(LOG_DEBUG, "We are in '%s' but cookie indicates '%s', going there...", ChrPtr(WCC->CurRoom.name), ChrPtr(WCC->Hdr->c_roomname) ); ret = gotoroom(WCC->Hdr->c_roomname); /* do quietly to avoid session output! */ if ((ret/100) != 2) { if (verbose) syslog(LOG_DEBUG, "COOKIEGOTO: Unable to change to [%s]; Reason: %d", ChrPtr(WCC->Hdr->c_roomname), ret); } } if (WCC->Hdr->HR.Handler != NULL) { if ( (!WCC->logged_in) && ((WCC->Hdr->HR.Handler->Flags & ANONYMOUS) == 0) && (WCC->serv_info != NULL) && (WCC->serv_info->serv_supports_guest == 0) ) { display_login(); } else { if ((WCC->Hdr->HR.Handler->Flags & AJAX) != 0) { begin_ajax_response(); } WCC->Hdr->HR.Handler->F(); if ((WCC->Hdr->HR.Handler->Flags & AJAX) != 0) { end_ajax_response(); } } } /* When all else fails, display the default landing page or a main menu. */ else { /* * ordinary browser users get a nice login screen, DAV etc. requsets * are given a 401 so they can handle it appropriate. */ if (!WCC->logged_in) { if (xhttp) { authorization_required(); } else { display_default_landing_page(); } } /* * Toplevel dav requests? or just a flat browser request? */ else { if (xhttp) { dav_main(); } else { display_main_menu(); } } } SKIP_ALL_THIS_CRAP: FreeStrBuf(&Buf); fflush(stdout); }
/* * The pathname is always going to be /groupdav/room_name/msg_num */ void dav_options(void) { wcsession *WCC = WC; StrBuf *dav_roomname; StrBuf *dav_uid; long dav_msgnum = (-1); char datestring[256]; time_t now; now = time(NULL); http_datestring(datestring, sizeof datestring, now); dav_roomname = NewStrBuf(); dav_uid = NewStrBuf(); StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/'); StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/'); syslog(LOG_DEBUG, "\033[35m%s (logged_in=%d)\033[0m", ChrPtr(WCC->Hdr->HR.ReqLine), WC->logged_in); /* * If the room name is blank, the client is doing an OPTIONS on the root. */ if (StrLength(dav_roomname) == 0) { syslog(LOG_DEBUG, "\033[36mOPTIONS requested for root\033[0m"); hprintf("HTTP/1.1 200 OK\r\n"); dav_common_headers(); hprintf("Date: %s\r\n", datestring); hprintf("DAV: 1\r\n"); hprintf("Allow: OPTIONS, PROPFIND\r\n"); hprintf("\r\n"); begin_burst(); end_burst(); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); return; } /* Go to the correct room. */ if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) { gotoroom(dav_roomname); } if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) { syslog(LOG_DEBUG, "\033[36mOPTIONS requested for invalid item\033[0m"); hprintf("HTTP/1.1 404 not found\r\n"); dav_common_headers(); hprintf("Date: %s\r\n", datestring); hprintf( "Content-Type: text/plain\r\n"); begin_burst(); wc_printf( "There is no folder called \"%s\" on this server.\r\n", ChrPtr(dav_roomname) ); end_burst(); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); return; } /* If dav_uid is non-empty, client is requesting an OPTIONS on * a specific item in the room. */ if (StrLength(dav_uid) != 0) { syslog(LOG_DEBUG, "\033[36mOPTIONS requested for specific item\033[0m"); dav_msgnum = locate_message_by_uid(ChrPtr(dav_uid)); if (dav_msgnum < 0) { hprintf("HTTP/1.1 404 not found\r\n"); dav_common_headers(); hprintf("Content-Type: text/plain\r\n"); begin_burst(); wc_printf( "Object \"%s\" was not found in the \"%s\" folder.\r\n", ChrPtr(dav_uid), ChrPtr(dav_roomname) ); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); end_burst();return; } hprintf("HTTP/1.1 200 OK\r\n"); dav_common_headers(); hprintf("Date: %s\r\n", datestring); hprintf("DAV: 1\r\n"); hprintf("Allow: OPTIONS, PROPFIND, GET, PUT, DELETE\r\n"); begin_burst(); end_burst(); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); return; } FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); /* * We got to this point, which means that the client is requesting * an OPTIONS on the room itself. */ syslog(LOG_DEBUG, "\033[36mOPTIONS requested for room '%s' (%slogged in)\033[0m", ChrPtr(WC->CurRoom.name), ((WC->logged_in) ? "" : "not ") ); hprintf("HTTP/1.1 200 OK\r\n"); dav_common_headers(); hprintf("Date: %s\r\n", datestring); /* * Offer CalDAV (RFC 4791) if this is a calendar room */ if ( (WC->CurRoom.view == VIEW_CALENDAR) || (WC->CurRoom.view == VIEW_CALBRIEF) ) { hprintf("DAV: 1, calendar-access\r\n"); syslog(LOG_DEBUG, "\033[36mDAV: 1, calendar-access\033[0m"); } else { hprintf("DAV: 1\r\n"); syslog(LOG_DEBUG, "\033[36mDAV: 1\033[0m"); } hprintf("Allow: OPTIONS, PROPFIND, GET, PUT, REPORT\r\n"); begin_burst(); end_burst(); }
/* * Display the OpenIDs associated with an account */ void display_openids(void) { wcsession *WCC = WC; char buf[1024]; int bg = 0; output_headers(1, 1, 1, 0, 0, 0); do_template("box_begin_1"); StrBufAppendBufPlain(WCC->WBuf, _("Manage Account/OpenID Associations"), -1, 0); do_template("box_begin_2"); if (WCC->serv_info->serv_supports_openid) { wc_printf("<table class=\"altern\">"); serv_puts("OIDL"); serv_getln(buf, sizeof buf); if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { bg = 1 - bg; wc_printf("<tr class=\"%s\">", (bg ? "even" : "odd")); wc_printf("<td><img src=\"static/webcit_icons/openid-small.gif\"></td><td>"); escputs(buf); wc_printf("</td><td>"); wc_printf("<a href=\"openid_detach?id_to_detach="); urlescputs(buf); wc_printf("\" onClick=\"return confirm('%s');\">", _("Do you really want to delete this OpenID?")); wc_printf("%s</a>", _("(delete)")); wc_printf("</td></tr>\n"); } wc_printf("</table><br>\n"); wc_printf("<form method=\"POST\" action=\"openid_attach\">\n"); wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WCC->nonce); wc_printf(_("Add an OpenID: ")); wc_printf("<input type=\"text\" name=\"openid_url\" class=\"openid_urlarea\" size=\"40\">\n"); wc_printf("<input type=\"submit\" name=\"attach_button\" value=\"%s\">" "</form></center>\n", _("Attach")); } else { wc_printf(_("%s does not permit authentication via OpenID."), ChrPtr(WCC->serv_info->serv_humannode)); } do_template("box_end"); wDumpContent(2); }
/* * The pathname is always going to take one of two formats: * [/groupdav/]room_name/euid (GroupDAV) * [/groupdav/]room_name (webcal) */ void dav_put(void) { wcsession *WCC = WC; StrBuf *dav_roomname; StrBuf *dav_uid; long new_msgnum = (-2L); long old_msgnum = (-1L); char buf[SIZ]; int n = 0; if (StrBufNum_tokens(WCC->Hdr->HR.ReqLine, '/') < 2) { hprintf("HTTP/1.1 404 not found\r\n"); dav_common_headers(); hprintf("Content-Type: text/plain\r\n"); begin_burst(); wc_printf("The object you requested was not found.\r\n"); end_burst(); return; } dav_roomname = NewStrBuf();; dav_uid = NewStrBuf();; StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/'); StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/'); if ((!strcasecmp(ChrPtr(dav_uid), "ics")) || (!strcasecmp(ChrPtr(dav_uid), "calendar.ics"))) { FlushStrBuf(dav_uid); } /* Go to the correct room. */ if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) { gotoroom(dav_roomname); } if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) { hprintf("HTTP/1.1 404 not found\r\n"); dav_common_headers(); hprintf("Content-Type: text/plain\r\n"); begin_burst(); wc_printf("There is no folder called \"%s\" on this server.\r\n", ChrPtr(dav_roomname)); end_burst(); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); 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 (StrLength(WCC->Hdr->HR.dav_ifmatch) > 0) { syslog(LOG_DEBUG, "dav_ifmatch: %s\n", ChrPtr(WCC->Hdr->HR.dav_ifmatch)); old_msgnum = locate_message_by_uid(ChrPtr(dav_uid)); syslog(LOG_DEBUG, "old_msgnum: %ld\n", old_msgnum); if (StrTol(WCC->Hdr->HR.dav_ifmatch) != old_msgnum) { hprintf("HTTP/1.1 412 Precondition Failed\r\n"); syslog(LOG_INFO, "HTTP/1.1 412 Precondition Failed (ifmatch=%ld, old_msgnum=%ld)\r\n", StrTol(WCC->Hdr->HR.dav_ifmatch), old_msgnum); dav_common_headers(); end_burst(); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); return; } } /** PUT on the collection itself uploads an ICS of the entire collection. */ if (StrLength(dav_uid) == 0) { dav_put_bigics(); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); 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') { hprintf("HTTP/1.1 502 Bad Gateway\r\n"); dav_common_headers(); hprintf("Content-type: text/plain\r\n"); begin_burst(); wc_printf("%s\r\n", &buf[4]); end_burst(); return; } /* Send the content to the Citadel server */ //serv_printf("Content-type: %s\n\n", WCC->upload_content_type); serv_putbuf(WCC->upload); serv_puts("\n000"); /* Fetch the reply from the Citadel server */ n = 0; FlushStrBuf(dav_uid); while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { switch(n++) { case 0: new_msgnum = atol(buf); break; case 1: syslog(LOG_DEBUG, "new_msgnum=%ld (%s)\n", new_msgnum, buf); break; case 2: StrBufAppendBufPlain(dav_uid, buf, -1, 0); break; default: break; } } /* Tell the client what happened. */ /* Citadel failed in some way? */ if (new_msgnum < 0L) { hprintf("HTTP/1.1 502 Bad Gateway\r\n"); dav_common_headers(); hprintf("Content-type: text/plain\r\n"); begin_burst(); wc_printf("new_msgnum is %ld\r\n" "\r\n", new_msgnum); end_burst(); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); return; } /* We created this item for the first time. */ if (old_msgnum < 0L) { char escaped_uid[1024]; hprintf("HTTP/1.1 201 Created\r\n"); syslog(LOG_DEBUG, "HTTP/1.1 201 Created\r\n"); dav_common_headers(); hprintf("etag: \"%ld\"\r\n", new_msgnum); hprintf("Location: "); dav_identify_hosthdr(); hprintf("/groupdav/");/* TODO */ hurlescputs(ChrPtr(dav_roomname)); euid_escapize(escaped_uid, ChrPtr(dav_uid)); hprintf("/%s\r\n", escaped_uid); end_burst(); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); return; } /* We modified an existing item. */ hprintf("HTTP/1.1 204 No Content\r\n"); syslog(LOG_DEBUG, "HTTP/1.1 204 No Content\r\n"); dav_common_headers(); hprintf("Etag: \"%ld\"\r\n", new_msgnum); /* 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); begin_burst(); end_burst(); FreeStrBuf(&dav_roomname); FreeStrBuf(&dav_uid); return; }
/* * Output HTTP headers and leading HTML for a page */ void output_headers( int do_httpheaders, /* 1 = output HTTP headers */ int do_htmlhead, /* 1 = output HTML <head> section and <body> opener */ int do_room_banner, /* 1 = include the room banner and <div id="content"></div> */ 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 */ ) { wcsession *WCC = WC; char httpnow[128]; if (WCC->Hdr->HaveRange > 1) hprintf("HTTP/1.1 206 Partial Content\r\n"); else hprintf("HTTP/1.1 200 OK\r\n"); http_datestring(httpnow, sizeof httpnow, time(NULL)); if (do_httpheaders) { if (WCC->serv_info != NULL) hprintf("Content-type: text/html; charset=utf-8\r\n" "Server: %s / %s\n" "Connection: close\r\n", PACKAGE_STRING, ChrPtr(WCC->serv_info->serv_software)); else hprintf("Content-type: text/html; charset=utf-8\r\n" "Server: %s / [n/a]\n" "Connection: close\r\n", PACKAGE_STRING); } if (cache > 0) { char httpTomorow[128]; http_datestring(httpTomorow, sizeof httpTomorow, time(NULL) + 60 * 60 * 24 * 2); hprintf("Pragma: public\r\n" "Cache-Control: max-age=3600, must-revalidate\r\n" "Last-modified: %s\r\n" "Expires: %s\r\n", httpnow, httpTomorow ); } else { hprintf("Pragma: no-cache\r\n" "Cache-Control: no-store\r\n" "Expires: -1\r\n" ); } if (cache < 2) stuff_to_cookie(unset_cookies); if (do_htmlhead) { begin_burst(); do_template("head"); if ( (WCC->logged_in) && (!unset_cookies) ) { DoTemplate(HKEY("paging"), NULL, &NoCtx); } if (do_room_banner) { tmplput_roombanner(NULL, NULL); } } if (do_room_banner) { wc_printf("<div id=\"content\">\n"); } }
/* * Display the revision history for a wiki page (template callback) */ void tmplput_display_wiki_history(StrBuf *Target, WCTemplputParams *TP) { char pagename[128]; StrBuf *Buf; int row = 0; safestrncpy(pagename, bstr("page"), sizeof pagename); str_wiki_index(pagename); serv_printf("WIKI history|%s", pagename); Buf = NewStrBuf(); StrBuf_ServGetln(Buf); if (GetServerStatus(Buf, NULL) == 1) { time_t rev_date; char rev_date_displayed[64]; StrBuf *rev_uuid = NewStrBuf(); StrBuf *author = NewStrBuf(); StrBuf *node = NewStrBuf(); wc_printf("<table class=\"wiki_history_background\">"); wc_printf("<th>%s</th>", _("Date")); wc_printf("<th>%s</th>", _("Author")); while((StrBuf_ServGetln(Buf) >= 0) && strcmp(ChrPtr(Buf), "000")) { rev_date = extract_long(ChrPtr(Buf), 1); webcit_fmt_date(rev_date_displayed, sizeof rev_date_displayed, rev_date, DATEFMT_FULL); StrBufExtract_token(author, Buf, 2, '|'); wc_printf("<tr bgcolor=\"%s\">", ((row%2) ? "#FFFFFF" : "#DDDDDD")); wc_printf("<td>%s</td><td>", rev_date_displayed); if (!strcasecmp(ChrPtr(node), (char *)WC->serv_info->serv_nodename)) { escputs(ChrPtr(author)); wc_printf(" @ "); escputs(ChrPtr(node)); } else { wc_printf("<a href=\"showuser?who="); urlescputs(ChrPtr(author)); wc_printf("\">"); escputs(ChrPtr(author)); wc_printf("</a>"); } wc_printf("</td>"); if (row == 0) { wc_printf("<td><a href=\"wiki?page=%s", bstr("page")); wc_printf("?go="); urlescputs(ChrPtr(WC->CurRoom.name)); wc_printf("\">%s</a></td>", _("(show)")); wc_printf("<td>(%s)</td>", _("Current version")); } else { wc_printf("<td><a href=\"wiki?page=%s?rev=%s", bstr("page"), ChrPtr(rev_uuid) ); wc_printf("?go="); urlescputs(ChrPtr(WC->CurRoom.name)); wc_printf("\">%s</a></td>", _("(show)")); wc_printf("<td><a href=\"javascript:GetLoggedInFirst(encodeURIComponent('wiki?page=%s?rev=%s?revert=1'))\">%s</a></td>", bstr("page"), ChrPtr(rev_uuid), _("(revert)") ); } wc_printf("</tr>\n"); /* Extract all fields except the author and date after displaying the row. This * is deliberate, because the timestamp reflects when the diff was written, not * when the version which it reflects was written. Similarly, the name associated * with each diff is the author who created the newer version of the page that * made the diff happen. */ StrBufExtract_token(rev_uuid, Buf, 0, '|'); StrBufExtract_token(node, Buf, 3, '|'); ++row; } wc_printf("</table>\n"); FreeStrBuf(&author); FreeStrBuf(&node); FreeStrBuf(&rev_uuid); } else { wc_printf("%s", ChrPtr(Buf)); } FreeStrBuf(&Buf); }
/* * Background ajax call to receive updates from the browser when a note is moved, resized, or updated. */ void ajax_update_note(void) { char buf[1024]; int msgnum; struct vnote *v = NULL; if (!havebstr("note_uid")) { begin_ajax_response(); wc_printf("Received ajax_update_note() request without a note UID."); end_ajax_response(); return; } serv_printf("EUID %s", bstr("note_uid")); serv_getln(buf, sizeof buf); if (buf[0] != '2') { begin_ajax_response(); wc_printf("Cannot find message containing vNote with the requested uid!"); end_ajax_response(); return; } msgnum = atol(&buf[4]); /* Was this request a delete operation? If so, nuke it... */ if (havebstr("deletenote")) { if (!strcasecmp(bstr("deletenote"), "yes")) { serv_printf("DELE %d", msgnum); serv_getln(buf, sizeof buf); begin_ajax_response(); wc_printf("%s", buf); end_ajax_response(); return; } } /* If we get to this point it's an update, not a delete */ v = vnote_new_from_msg(msgnum, 0); if (!v) { begin_ajax_response(); wc_printf("Cannot locate a vNote within message %d\n", msgnum); end_ajax_response(); return; } /* Make any requested changes */ if (havebstr("top")) { v->pos_top = atoi(bstr("top")); } if (havebstr("left")) { v->pos_left = atoi(bstr("left")); } if (havebstr("height")) { v->pos_height = atoi(bstr("height")); } if (havebstr("width")) { v->pos_width = atoi(bstr("width")); } if (havebstr("red")) { v->color_red = atoi(bstr("red")); } if (havebstr("green")) { v->color_green = atoi(bstr("green")); } if (havebstr("blue")) { v->color_blue = atoi(bstr("blue")); } if (havebstr("value")) { /* I would have preferred 'body' but InPlaceEditor hardcodes 'value' */ if (v->body) free(v->body); v->body = strdup(bstr("value")); } /* Serialize it and save it to the message base. Server will delete the old one. */ write_vnote_to_server(v); begin_ajax_response(); if (v->body) { escputs(v->body); } end_ajax_response(); vnote_free(v); }