0e71987f2401c8a6e20fdb8d06ec1512ac1bf189
[citadel.git] / webcit / rss.c
1 /*
2  * $Id$
3  *
4  * Generate some RSS for our rooms.
5  */
6
7 #include "webcit.h"
8 #include "webserver.h"
9
10
11 time_t if_modified_since;
12
13
14 void display_rss_control(char *reply_to, char *subject)
15 {
16         wprintf("<div style=\"align: right;\"><p>\n");
17         wprintf("<a href=\"/display_enter?recp=");
18         urlescputs(reply_to);
19         wprintf("&subject=");
20         if (strncasecmp(subject, "Re: ", 3)) wprintf("Re:%20");
21         urlescputs(subject);
22         wprintf("\">[%s]</a> \n", _("Reply"));
23         wprintf("<a href=\"/display_enter?recp=");
24         urlescputs(reply_to);
25         wprintf("&force_room=_MAIL_&subject=");
26         if (strncasecmp(subject, "Re: ", 3)) wprintf("Re:%20");
27         urlescputs(subject);
28         wprintf("\">[%s]</a>\n", _("Email"));
29         wprintf("</p></div>\n");
30 }
31
32
33 void display_rss(char *roomname, char *request_method)
34 {
35         int nummsgs;
36         int a, b;
37         int bq = 0;
38         time_t now = 0L;
39         struct tm now_tm;
40         iconv_t ic = (iconv_t)(-1) ;
41         char *ibuf;                   /* Buffer of characters to be converted */
42         char *obuf;                   /* Buffer for converted characters      */
43         size_t ibuflen;               /* Length of input buffer               */
44         size_t obuflen;               /* Length of output buffer              */
45         char *osav;                   /* Saved pointer to output buffer       */
46         char buf[SIZ];
47         char date[30];
48         char from[256];
49         char subj[256];
50         char node[256];
51         char hnod[256];
52         char room[256];
53         char rfca[256];
54         char rcpt[256];
55         char msgn[256];
56         char content_type[256];
57         char charset[256];
58
59         if (!WC->logged_in) {
60                 authorization_required(_("Not logged in"));
61                 return;
62         }
63
64         if (gotoroom((char *)roomname)) {
65                 lprintf(3, "RSS: Can't goto requested room\n");
66                 wprintf("HTTP/1.1 404 Not Found\r\n");
67                 wprintf("Content-Type: text/html\r\n");
68                 wprintf("\r\n");
69                 wprintf("Error retrieving RSS feed: couldn't find room\n");
70                 return;
71         }
72
73         nummsgs = load_msg_ptrs("MSGS LAST|15", 0);
74         if (nummsgs == 0) {
75                 lprintf(3, "RSS: No messages found\n");
76                 wprintf("HTTP/1.1 404 Not Found\r\n");
77                 wprintf("Content-Type: text/html\r\n");
78                 wprintf("\r\n");
79                 wprintf(_("Error retrieving RSS feed: couldn't find messages\n"));
80                 return;
81         }
82
83         /* Read time of last message immediately */
84         serv_printf("MSG4 %ld", WC->msgarr[nummsgs - 1]);
85         serv_getln(buf, sizeof buf);
86         if (buf[0] == '1') {
87                 while (serv_getln(buf, sizeof buf), strcasecmp(buf, "000")) {
88                         if (!strncasecmp(buf, "msgn=", 5)) {
89                                 strcpy(msgn, &buf[5]);
90                         }
91                         if (!strncasecmp(buf, "time=", 5)) {
92                                 now = atol(&buf[5]);
93                                 gmtime_r(&now, &now_tm);
94                                 strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
95                         }
96                 }
97         }
98
99         if (if_modified_since > 0 && if_modified_since > now) {
100                 lprintf(3, "RSS: Feed not updated since the last time you looked\n");
101                 wprintf("HTTP/1.1 304 Not Modified\r\n");
102                 wprintf("Last-Modified: %s\r\n", date);
103                 now = time(NULL);
104                 gmtime_r(&now, &now_tm);
105                 strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
106                 wprintf("Date: %s\r\n", date);
107 /*              if (*msgn) wprintf("ETag: %s\r\n\r\n", msgn); */
108                 wDumpContent(0);
109                 return;
110         }
111
112         /* Do RSS header */
113         lprintf(3, "RSS: Yum yum! This feed is tasty!\n");
114         wprintf("HTTP/1.1 200 OK\r\n");
115         wprintf("Last-Modified: %s\r\n", date);
116 /*      if (*msgn) wprintf("ETag: %s\r\n\r\n", msgn); */
117         wprintf("Content-Type: application/rss+xml\r\n");
118         wprintf("$erver: %s\r\n", SERVER);
119         wprintf("Connection: close\r\n");
120         wprintf("\r\n");
121         if (!strcasecmp(request_method, "HEAD"))
122                 return;
123
124         wprintf("<?xml version=\"1.0\"?>\n");
125         wprintf("<rss version=\"2.0\">\n");
126         wprintf("   <channel>\n");
127         wprintf("   <title>%s - %s</title>\n", WC->wc_roomname, serv_info.serv_humannode);
128         wprintf("   <link>%s://%s:%d/dotgoto?room=", (is_https ? "https" : "http"), WC->http_host, PORT_NUM);
129         escputs(roomname);
130         wprintf("</link>\n");
131         wprintf("   <description>");
132         /* Get room info for description */
133         serv_puts("RINF");
134         serv_getln(buf, sizeof buf);
135         if (buf[0] == '1') {
136                 while (1) {
137                         serv_getln(buf, sizeof buf);
138                         if (!strcmp(buf, "000"))
139                                 break;
140                         wprintf("%s\n", buf);
141                 }
142         }
143         wprintf("</description>\n");
144         if (now) {
145                 wprintf("   <pubDate>%s</pubDate>\n", date);
146         }
147         wprintf("   <generator>%s</generator>\n", SERVER);
148         wprintf("   <docs>http://blogs.law.harvard.edu/tech/rss</docs>\n");
149         wprintf("   <ttl>30</ttl>\n");
150
151         /* Read all messages and output as RSS items */
152         for (a = 0; a < nummsgs; ++a) {
153                 /* Read message and output each as RSS item */
154                 serv_printf("MSG4 %ld", WC->msgarr[a]);
155                 serv_getln(buf, sizeof buf);
156                 if (buf[0] != '1') continue;
157
158                 now = 0L;
159                 strcpy(subj, "");
160                 strcpy(hnod, "");
161                 strcpy(node, "");
162                 strcpy(room, "");
163                 strcpy(rfca, "");
164                 strcpy(rcpt, "");
165                 strcpy(msgn, "");
166
167                 while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
168                         if (!strcmp(buf, "000")) {
169                                 goto ENDITEM;   /* screw it */
170                         } else if (!strncasecmp(buf, "from=", 5)) {
171                                 strcpy(from, &buf[5]);
172 #ifdef HAVE_ICONV
173                                 utf8ify_rfc822_string(from);
174 #endif
175                         } else if (!strncasecmp(buf, "subj=", 5)) {
176                                 strcpy(subj, &buf[5]);
177 #ifdef HAVE_ICONV
178                                 utf8ify_rfc822_string(subj);
179 #endif
180                         } else if (!strncasecmp(buf, "hnod=", 5)) {
181                                 strcpy(node, &buf[5]);
182                         } else if (!strncasecmp(buf, "room=", 5)) {
183                                 strcpy(room, &buf[5]);
184                         } else if (!strncasecmp(buf, "rfca=", 5)) {
185                                 strcpy(rfca, &buf[5]);
186                         } else if (!strncasecmp(buf, "rcpt=", 5)) {
187                                 strcpy(rcpt, &buf[5]);
188                         } else if (!strncasecmp(buf, "msgn=", 5)) {
189                                 strcpy(msgn, &buf[5]);
190                         } else if (!strncasecmp(buf, "time=", 5)) {
191                                 now = atol(&buf[5]);
192                                 gmtime_r(&now, &now_tm);
193                                 strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
194                         }
195                 }
196                 wprintf("   <item>\n");
197                 if (subj[0]) {
198                         wprintf("      <title>%s from", subj);
199                 } else {
200                         wprintf("      <title>From");
201                 }
202                 wprintf(" %s", from);
203                 wprintf(" in %s", room);
204                 if (strcmp(hnod, serv_info.serv_humannode) && strlen(hnod) > 0) {
205                         wprintf(" on %s", hnod);
206                 }
207                 wprintf("</title>\n");
208                 if (now) {
209                         wprintf("      <pubDate>%s</pubDate>\n", date);
210                 }
211                 wprintf("      <guid isPermaLink=\"false\">%s</guid>\n", msgn);
212                 /* Now the hard part, the message itself */
213                 strcpy(content_type, "text/plain");
214                 while (serv_getln(buf, sizeof buf), strlen(buf) > 0) {
215                         if (!strcmp(buf, "000")) {
216                                 goto ENDBODY;
217                         }
218                         if (!strncasecmp(buf, "Content-type: ", 14)) {
219                                 safestrncpy(content_type, &buf[14], sizeof content_type);
220                                 for (b = 0; b < strlen(content_type); ++b) {
221                                         if (!strncasecmp(&content_type[b], "charset=", 8)) {
222                                                 safestrncpy(charset, &content_type[b + 8], sizeof charset);
223                                         }
224                                 }
225                                 for (b = 0; b < strlen(content_type); ++b) {
226                                         if (content_type[b] == ';') {
227                                                 content_type[b] = 0;
228                                         }
229                                 }
230                         }
231                 }
232
233                 /* Set up a character set conversion if we need to */
234 #ifdef HAVE_ICONV
235                 if (strcasecmp(charset, "us-ascii") && strcasecmp(charset, "utf-8")) {
236                         ic = iconv_open("UTF-8", charset);
237                         if (ic == (iconv_t)(-1)) {
238                                 lprintf(5, "iconv_open() failed: %s\n", strerror(errno));
239                                 goto ENDBODY;
240                         }
241                 }
242 #endif
243
244                 /* Messages in legacy Citadel variformat get handled thusly... */
245                 if (!strcasecmp(content_type, "text/x-citadel-variformat")) {
246                         int intext = 0;
247
248                         wprintf("      <description><![CDATA[");
249                         while (1) {
250                                 serv_getln(buf, sizeof buf);
251                                 if (!strcmp(buf, "000")) {
252                                         if (bq == 1)
253                                                 wprintf("</blockquote>");
254                                         wprintf("\n");
255                                         break;
256                                 }
257                                 if (intext == 1 && isspace(buf[0])) {
258                                         wprintf("<br/>");
259                                 }
260                                 intext = 1;
261                                 if (bq == 0 && !strncmp(buf, " >", 2)) {
262                                         wprintf("<blockquote>");
263                                         bq = 1;
264                                 } else if (bq == 1 && strncmp(buf, " >", 2)) {
265                                         wprintf("</blockquote>");
266                                         bq = 0;
267                                 }
268                                 url(buf);
269                                 escputs(buf);
270                                 wprintf("\n");
271                         }
272                         display_rss_control(from, subj);
273                         wprintf("]]></description>\n");
274                 }
275                 /* Boring old 80-column fixed format text gets handled this way... */
276                 else if (!strcasecmp(content_type, "text/plain")) {
277                         wprintf("      <description><![CDATA[");
278                         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
279                                 if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
280                                 if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
281         
282 #ifdef HAVE_ICONV
283                                 if (ic != (iconv_t)(-1) ) {
284                                         ibuf = buf;
285                                         ibuflen = strlen(ibuf);
286                                         obuflen = SIZ;
287                                         obuf = (char *) malloc(obuflen);
288                                         osav = obuf;
289                                         iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
290                                         osav[SIZ-obuflen] = 0;
291                                         safestrncpy(buf, osav, sizeof buf);
292                                         free(osav);
293                                 }
294 #endif
295
296                                 while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
297                                         buf[strlen(buf) - 1] = 0;
298                                 if ((bq == 0) &&
299                                 ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) {
300                                         wprintf("<blockquote>");
301                                         bq = 1;
302                                 } else if ((bq == 1) &&
303                                         (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) {
304                                         wprintf("</blockquote>");
305                                         bq = 0;
306                                 }
307                                 wprintf("<tt>");
308                                 url(buf);
309                                 escputs(buf);
310                                 wprintf("</tt><br />\n");
311                         }
312                         display_rss_control(from, subj);
313                         wprintf("]]></description>\n");
314                 }
315                 /* HTML is fun, but we've got to strip it first */
316                 else if (!strcasecmp(content_type, "text/html")) {
317                         wprintf("      <description><![CDATA[");
318                         output_html(charset);
319                         wprintf("]]></description>\n");
320                 }
321
322 ENDBODY:
323                 wprintf("   </item>\n");
324 ENDITEM:
325                 now = 0L;
326         }
327
328         /* Do RSS footer */
329         wprintf("   </channel>\n");
330         wprintf("</rss>\n");
331         wDumpContent(0);
332 }