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