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