First-draft of RSS support.
authorMichael Hampton <io_error@uncensored.citadel.org>
Thu, 18 Aug 2005 22:59:38 +0000 (22:59 +0000)
committerMichael Hampton <io_error@uncensored.citadel.org>
Thu, 18 Aug 2005 22:59:38 +0000 (22:59 +0000)
webcit/Makefile.in
webcit/context_loop.c
webcit/rss.c [new file with mode: 0644]
webcit/webcit.c

index 315824fee4fc0ef1ea4b4161538cb1bfedb9c199..b53acba4ed29fba095aa53d942395523c384afaa 100644 (file)
@@ -34,7 +34,7 @@ webserver: webserver.o context_loop.o tools.o ical_dezonify.o \
        webcit.o auth.o tcp_sockets.o mainmenu.o serv_func.o who.o \
        roomops.o messages.o userlist.o paging.o sysmsgs.o useredit.o \
        vcard.o vcard_edit.o preferences.o html2html.o listsub.o \
-       mime_parser.o graphics.o netconf.o siteconfig.o subst.o \
+       mime_parser.o graphics.o netconf.o siteconfig.o subst.o rss.o \
        calendar.o calendar_tools.o calendar_view.o event.o \
        availability.o iconbar.o crypto.o inetconf.o notes.o \
        groupdav_main.o groupdav_get.o groupdav_propfind.o fmt_date.o \
@@ -45,7 +45,7 @@ webserver: webserver.o context_loop.o tools.o ical_dezonify.o \
        webcit.o auth.o tcp_sockets.o mainmenu.o serv_func.o who.o listsub.o \
        roomops.o messages.o userlist.o paging.o sysmsgs.o useredit.o \
        locate_host.o siteconfig.o subst.o vcard.o vcard_edit.o floors.o \
-       mime_parser.o graphics.o netconf.o preferences.o html2html.o \
+       mime_parser.o graphics.o netconf.o preferences.o html2html.o rss.o \
        summary.o calendar.o calendar_tools.o calendar_view.o event.o \
        availability.o ical_dezonify.o iconbar.o crypto.o inetconf.o notes.o \
        groupdav_main.o groupdav_get.o groupdav_propfind.o groupdav_delete.o \
index 53129a78061a1812c3f355c7671baea2498ae44f..f64433afd728072f0265b6429a634970d117013d 100644 (file)
@@ -337,6 +337,7 @@ void context_loop(int sock)
                && (strncasecmp(buf, "/freebusy", 9))
                && (strncasecmp(buf, "/do_logout", 10))
                && (strncasecmp(buf, "/groupdav", 9))
+               && (strncasecmp(buf, "/rss", 4))
                && (got_cookie == 0)) {
                strcpy(req->line, "GET /static/nocookies.html"
                                "?force_close_session=yes HTTP/1.0");
diff --git a/webcit/rss.c b/webcit/rss.c
new file mode 100644 (file)
index 0000000..1c250a9
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * $Id$
+ *
+ * Generate some RSS for our rooms.
+ */
+
+#include "webcit.h"
+#include "webserver.h"
+
+void display_rss(const char *roomname)
+{
+       int nummsgs;
+       int a, b;
+       int bq = 0;
+       time_t now = 0L;
+       struct tm now_tm;
+       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       */
+       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];
+
+       lprintf(3, "Running RSS reader\n");
+       if (!WC->logged_in) {
+               authorization_required();
+               return;
+       }
+
+       lprintf(3, "Going to a room %s\n", roomname);
+       if (gotoroom(roomname)) {
+               wprintf("HTTP/1.0 404 Not Found\r\n");
+               wprintf("Content-Type: text/html\r\n");
+               wprintf("\r\n");
+               wprintf("Error retrieving RSS feed: couldn't find room or messages\n");
+               return;
+       }
+
+       lprintf(3, "Loading up all the messages\n");
+       nummsgs = load_msg_ptrs("MSGS LAST|50", 0);
+       if (nummsgs == 0) {
+               wprintf("HTTP/1.0 404 Not Found\r\n");
+               wprintf("Content-Type: text/html\r\n");
+               wprintf("\r\n");
+               wprintf("Error retrieving RSS feed: couldn't find room or messages\n");
+               return;
+       }
+
+       lprintf(3, "Getting date of the last one\n");
+       /* 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, "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);
+                       }
+               }
+       }
+
+       /* Do RSS header */
+       output_headers(0, 0, 0, 0, 0, 0, 0);
+       wprintf("Content-Type: application/rss+xml\r\n");
+       wprintf("\r\n");
+       wprintf("<?xml version=\"1.0\"?>\n");
+       wprintf("<rss version=\"2.0\">\n");
+       wprintf("   <channel>\n");
+       wprintf("   <title>%s - %s</title>\n", WC->wc_roomname, serv_info.serv_humannode);
+       wprintf("   <description>");
+       /* Get room info for description */
+       serv_puts("RINF");
+       serv_getln(buf, sizeof buf);
+       if (buf[0] == '1') {
+               while (1) {
+                       serv_getln(buf, sizeof buf);
+                       if (!strcmp(buf, "000"))
+                               break;
+                       wprintf("%s", buf);
+               }
+       }
+       wprintf("</description>\n");
+       if (now) {
+               wprintf("   <pubDate>%s</pubDate>\n", buf);
+       }
+       wprintf("   <generator>%s</generator>\n", SERVER);
+       wprintf("   <docs>http://blogs.law.harvard.edu/tech/rss</docs>\n");
+       wprintf("   <ttl>30</ttl>\n");
+
+       /* Read all messages and output as RSS items */
+       for (a = 0; a < nummsgs; ++a) {
+               lprintf(3, "Sending message %d of %d\n", a+1, nummsgs);
+               /* 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")) {
+                               lprintf(3, "ENDITEM 1\n");
+                               goto ENDITEM;   /* screw it */
+                       } else if (!strncasecmp(buf, "from=", 5)) {
+                               strcpy(from, &buf[5]);
+#ifdef HAVE_ICONV
+                               utf8ify_rfc822_string(from);
+#endif
+                       } else if (!strncasecmp(buf, "subj=", 5)) {
+                               strcpy(subj, &buf[5]);
+#ifdef HAVE_ICONV
+                               utf8ify_rfc822_string(subj);
+#endif
+                       } else if (!strncasecmp(buf, "hnod=", 5)) {
+                               strcpy(node, &buf[5]);
+                       } else if (!strncasecmp(buf, "room=", 5)) {
+                               strcpy(room, &buf[5]);
+                       } else if (!strncasecmp(buf, "rfca=", 5)) {
+                               strcpy(rfca, &buf[5]);
+                       } else if (!strncasecmp(buf, "rcpt=", 5)) {
+                               strcpy(rcpt, &buf[5]);
+                       } else if (!strncasecmp(buf, "msgn=", 5)) {
+                               strcpy(msgn, &buf[5]);
+                       } else if (!strncasecmp(buf, "time=", 5)) {
+                               now = atol(&buf[5]);
+                               gmtime_r(&now, &now_tm);
+                               strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
+                       }
+               }
+               wprintf("   <item>\n");
+               if (subj[0]) {
+                       wprintf("      <title>%s from", subj);
+               } else {
+                       wprintf("      <title>From");
+               }
+               wprintf(" %s", from);
+               wprintf(" in %s", room);
+               if (strcmp(hnod, serv_info.serv_humannode) && strlen(hnod) > 0) {
+                       wprintf(" on %s", hnod);
+               }
+               wprintf("</title>\n");
+               if (now) {
+                       wprintf("      <pubDate>%s</pubDate>\n", date);
+               }
+               wprintf("      <guid isPermaLink=\"false\">%s</guid>\n", msgn);
+               /* Now the hard part, the message itself */
+               strcpy(content_type, "text/plain");
+               while (serv_getln(buf, sizeof buf), strlen(buf) > 0) {
+                       if (!strcmp(buf, "000")) {
+                               lprintf(3, "ENDBODY 1\n");
+                               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")) {
+                       ic = iconv_open("UTF-8", charset);
+                       if (ic == (iconv_t)(-1)) {
+                               lprintf(5, "iconv_open() failed: %s\n", strerror(errno));
+                               goto ENDBODY;
+                       }
+               }
+#endif
+
+               /* Messages in legacy Citadel variformat get handled thusly... */
+               if (!strcasecmp(content_type, "text/x-citadel-variformat")) {
+                       int intext = 0;
+
+                       wprintf("      <description><![CDATA[");
+                       while (1) {
+                               serv_getln(buf, sizeof buf);
+                               if (!strcmp(buf, "000")) {
+                                       if (bq == 1)
+                                               wprintf("</blockquote>");
+                                       wprintf("\n");
+                                       break;
+                               }
+                               if (intext == 1 && isspace(buf[0])) {
+                                       wprintf("<br/>");
+                               }
+                               intext = 1;
+                               if (bq == 0 && !strncmp(buf, " >", 2)) {
+                                       wprintf("<blockquote>");
+                                       bq = 1;
+                               } else if (bq == 1 && strncmp(buf, " >", 2)) {
+                                       wprintf("</blockquote>");
+                                       bq = 0;
+                               }
+                               url(buf);
+                               escputs(buf);
+                               wprintf("\n");
+                       }
+                       wprintf("]]></description>\n");
+               }
+               /* Boring old 80-column fixed format text gets handled this way... */
+               else if (!strcasecmp(content_type, "text/plain")) {
+                       wprintf("      <description><![CDATA[");
+                       while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+                               if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
+                               if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
+       
+#ifdef HAVE_ICONV
+                               if (ic != (iconv_t)(-1) ) {
+                                       ibuf = buf;
+                                       ibuflen = strlen(ibuf);
+                                       obuflen = SIZ;
+                                       obuf = (char *) malloc(obuflen);
+                                       osav = obuf;
+                                       iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
+                                       osav[SIZ-obuflen] = 0;
+                                       safestrncpy(buf, osav, sizeof buf);
+                                       free(osav);
+                               }
+#endif
+
+                               while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
+                                       buf[strlen(buf) - 1] = 0;
+                               if ((bq == 0) &&
+                               ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) {
+                                       wprintf("<blockquote>");
+                                       bq = 1;
+                               } else if ((bq == 1) &&
+                                       (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) {
+                                       wprintf("</blockquote>");
+                                       bq = 0;
+                               }
+                               wprintf("<tt>");
+                               url(buf);
+                               escputs(buf);
+                               wprintf("</tt><br />\n");
+                       }
+                       wprintf("]]></description>\n");
+               }
+               /* HTML is fun, but we've got to strip it first */
+               else if (!strcasecmp(content_type, "text/html")) {
+                       wprintf("      <description><![CDATA[");
+                       output_html(charset);
+                       wprintf("]]></description>\n");
+               }
+
+ENDBODY:
+               wprintf("   </item>\n");
+ENDITEM:
+               lprintf(3, "Finished message %d of %d\n", a+1, nummsgs);
+               now = 0L;
+       }
+
+       /* Do RSS footer */
+       wprintf("   </channel>\n");
+       wprintf("</rss>\n");
+       wDumpContent(0);
+}
index fd74ae10e1cf6851dfbbd520a9714fc9ce0ad57a..aa9af913a7004ee28bd9ebb78303b7fbb0d618a4 100644 (file)
@@ -757,6 +757,19 @@ void display_success(char *successmessage)
 }
 
 
+/* Authorization required page */
+/* This is probably temporary and should be revisited */
+void authorization_required(const char *message)
+{
+       wprintf("HTTP/1.0 401 Authorization Required\r\n");
+       wprintf("WWW-Authenticate: Basic realm=\"\"\r\n", serv_info.serv_humannode);
+       wprintf("Content-Type: text/html\r\n\r\n");
+       wprintf("<h1>Authorization Required</h1>\r\n");
+       wprintf("The resource you requested requires a valid username and password.");
+       wprintf("I could not log you in: %s\n", message);
+       wDumpContent(0);
+}
+
 
 void upload_handler(char *name, char *filename, char *partnum, char *disp,
                        void *content, char *cbtype, char *cbcharset,
@@ -1031,10 +1044,20 @@ void session_loop(struct httprequest *req)
                                                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"));
+               goto SKIP_ALL_THIS_CRAP;
+       }
+
        /* 
         * The GroupDAV stuff relies on HTTP authentication instead of
         * our session's authentication.