* Created IsEmptyStr define to be used rather then using some weird strlen constructs
[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", SERVER);
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", SERVER);
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 #ifdef HAVE_ICONV
186                                 utf8ify_rfc822_string(from);
187 #endif
188                         } else if (!strncasecmp(buf, "subj=", 5)) {
189                                 strcpy(subj, &buf[5]);
190 #ifdef HAVE_ICONV
191                                 utf8ify_rfc822_string(subj);
192 #endif
193                         } else if (!strncasecmp(buf, "hnod=", 5)) {
194                                 strcpy(node, &buf[5]);
195                         } else if (!strncasecmp(buf, "room=", 5)) {
196                                 strcpy(room, &buf[5]);
197                         } else if (!strncasecmp(buf, "rfca=", 5)) {
198                                 strcpy(rfca, &buf[5]);
199                         } else if (!strncasecmp(buf, "rcpt=", 5)) {
200                                 strcpy(rcpt, &buf[5]);
201                         } else if (!strncasecmp(buf, "msgn=", 5)) {
202                                 strcpy(msgn, &buf[5]);
203                         } else if (!strncasecmp(buf, "time=", 5)) {
204                                 now = atol(&buf[5]);
205                                 gmtime_r(&now, &now_tm);
206                                 strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", &now_tm);
207                         }
208                 }
209                 wprintf("   <item>\n");
210                 if (subj[0]) {
211                         wprintf("      <title>%s from", subj);
212                 } else {
213                         wprintf("      <title>From");
214                 }
215                 wprintf(" %s", from);
216                 wprintf(" in %s", room);
217                 if (strcmp(hnod, serv_info.serv_humannode) && !IsEmptyStr(hnod)) {
218                         wprintf(" on %s", hnod);
219                 }
220                 wprintf("</title>\n");
221                 if (now) {
222                         wprintf("      <pubDate>%s</pubDate>\n", date);
223                 }
224                 wprintf("      <guid isPermaLink=\"false\">%s</guid>\n", msgn);
225                 /** Now the hard part, the message itself */
226                 strcpy(content_type, "text/plain");
227                 while (serv_getln(buf, sizeof buf), !IsEmptyStr(buf)) {
228                         if (!strcmp(buf, "000")) {
229                                 goto ENDBODY;
230                         }
231                         if (!strncasecmp(buf, "Content-type: ", 14)) {
232                                 int len;
233                                 safestrncpy(content_type, &buf[14], sizeof content_type);
234                                 len = strlen (content_type);
235                                 for (b = 0; b < len; ++b) {
236                                         if (!strncasecmp(&content_type[b], "charset=", 8)) {
237                                                 safestrncpy(charset, &content_type[b + 8], sizeof charset);
238                                         }
239                                 }
240                                 for (b = 0; b < len; ++b) {
241                                         if (content_type[b] == ';') {
242                                                 content_type[b] = 0;
243                                                 len = b - 1;
244                                         }
245                                 }
246                         }
247                 }
248
249                 /** Set up a character set conversion if we need to */
250 #ifdef HAVE_ICONV
251                 if (strcasecmp(charset, "us-ascii") && strcasecmp(charset, "utf-8") && strcasecmp(charset, "") ) {
252                         ic = ctdl_iconv_open("UTF-8", charset);
253                         if (ic == (iconv_t)(-1)) {
254                                 lprintf(5, "%s:%d iconv_open() failed: %s\n",
255                                         __FILE__, __LINE__, strerror(errno));
256                                 goto ENDBODY;
257                         }
258                 }
259 #endif
260
261                 /** Messages in legacy Citadel variformat get handled thusly... */
262                 if (!strcasecmp(content_type, "text/x-citadel-variformat")) {
263                         int intext = 0;
264
265                         wprintf("      <description><![CDATA[");
266                         while (1) {
267                                 serv_getln(buf, sizeof buf);
268                                 if (!strcmp(buf, "000")) {
269                                         if (bq == 1)
270                                                 wprintf("</blockquote>");
271                                         wprintf("\n");
272                                         break;
273                                 }
274                                 if (intext == 1 && isspace(buf[0])) {
275                                         wprintf("<br/>");
276                                 }
277                                 intext = 1;
278                                 if (bq == 0 && !strncmp(buf, " >", 2)) {
279                                         wprintf("<blockquote>");
280                                         bq = 1;
281                                 } else if (bq == 1 && strncmp(buf, " >", 2)) {
282                                         wprintf("</blockquote>");
283                                         bq = 0;
284                                 }
285                                 url(buf);
286                                 escputs(buf);
287                                 wprintf("\n");
288                         }
289                         display_rss_control(from, subj);
290                         wprintf("]]></description>\n");
291                 }
292                 /** Boring old 80-column fixed format text gets handled this way... */
293                 else if (!strcasecmp(content_type, "text/plain")) {
294                         wprintf("      <description><![CDATA[");
295                         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
296                                 int len;
297                                 len = strlen(buf);
298                                 if (buf[len-1] == '\n') buf[--len] = 0;
299                                 if (buf[len-1] == '\r') buf[--len] = 0;
300         
301 #ifdef HAVE_ICONV
302                                 if (ic != (iconv_t)(-1) ) {
303                                         ibuf = buf;
304                                         ibuflen = len;
305                                         obuflen = SIZ;
306                                         obuf = (char *) malloc(obuflen);
307                                         osav = obuf;
308                                         iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
309                                         osav[SIZ-obuflen] = 0;
310                                         safestrncpy(buf, osav, sizeof buf);
311                                         free(osav);
312                                 }
313 #endif
314                                 len = strlen (buf);
315                                 while ((!IsEmptyStr(buf)) && (isspace(buf[len - 1])))
316                                         buf[--len] = 0;
317                                 if ((bq == 0) &&
318                                 ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) {
319                                         wprintf("<blockquote>");
320                                         bq = 1;
321                                 } else if ((bq == 1) &&
322                                         (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) {
323                                         wprintf("</blockquote>");
324                                         bq = 0;
325                                 }
326                                 wprintf("<tt>");
327                                 url(buf);
328                                 escputs(buf);
329                                 wprintf("</tt><br />\n");
330                         }
331                         display_rss_control(from, subj);
332                         wprintf("]]></description>\n");
333                 }
334                 /** HTML is fun, but we've got to strip it first */
335                 else if (!strcasecmp(content_type, "text/html")) {
336                         wprintf("      <description><![CDATA[");
337                         output_html(charset, 0);
338                         wprintf("]]></description>\n");
339                 }
340
341 ENDBODY:
342                 wprintf("   </item>\n");
343 ENDITEM:
344                 now = 0L;
345         }
346
347         /** Do RSS footer */
348         wprintf("   </channel>\n");
349         wprintf("</rss>\n");
350         wDumpContent(0);
351 }
352
353
354 /*@}*/