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