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