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