* typedef wcsession, so we don't always need to say gcc again its a struct.
[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(char *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 buf[SIZ];
64         char date[30];
65         char from[256];
66         char subj[256];
67         char node[256];
68         char hnod[256];
69         char room[256];
70         char rfca[256];
71         char rcpt[256];
72         char msgn[256];
73         char content_type[256];
74         char charset[256];
75         
76         if (!WCC->logged_in) {
77                 #ifdef ALLOW_ANON_RSS
78                 serv_printf("USER %s", ANON_RSS_USER);
79                 serv_getln(buf, sizeof buf);
80                 serv_printf("PASS %s", ANON_RSS_PASS);
81                 serv_getln(buf, sizeof buf);
82                 become_logged_in(ANON_RSS_USER, ANON_RSS_PASS, buf);
83                 WCC->killthis = 1;
84                 #else
85                 authorization_required(_("Not logged in"));
86                 return;
87                 #endif
88         }
89
90         if (gotoroom((char *)roomname)) {
91                 lprintf(3, "RSS: Can't goto requested room\n");
92                 hprintf("HTTP/1.1 404 Not Found\r\n");
93                 hprintf("Content-Type: text/html\r\n");
94                 wprintf("Error retrieving RSS feed: couldn't find room\n");
95                 end_burst();
96                 return;
97         }
98
99          nummsgs = load_msg_ptrs("MSGS LAST|15", 0);
100         if (nummsgs == 0) {
101                 lprintf(3, "RSS: No messages found\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 messages\n"));
105                 end_burst();
106                 return;
107         } 
108         
109
110         /** Read time of last message immediately */
111         Msg = GetMessagePtrAt(nummsgs - 1, WCC->summ);
112
113         serv_printf("MSG4 %ld", (Msg==NULL)? 0 : Msg->msgnum);
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                 hprintf("HTTP/1.1 304 Not Modified\r\n");
131                 hprintf("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                 hprintf("Date: %s\r\n", date);
136                 if (*msgn) hprintf("ETag: %s\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         hprintf("HTTP/1.1 200 OK\r\n");
144         hprintf("Last-Modified: %s\r\n", date);
145 /*      if (*msgn) wprintf("ETag: %s\r\n\r\n", msgn); */
146         hprintf("Content-Type: application/rss+xml\r\n");
147         hprintf("Server: %s\r\n", PACKAGE_STRING);
148         hprintf("Connection: close\r\n");
149         if (!strcasecmp(ChrPtr(request_method), "HEAD"))
150                 return;
151
152         /* <?xml.. etc confuses our subst parser, so do it here */
153         svput("XML_HEAD", WCS_STRING, "<?xml version=\"1.0\" ?>");
154         svput("XML_STYLE", WCS_STRING, "<?xml-stylesheet type=\"text/css\" href=\"/static/rss_browser.css\" ?>");
155         svput("ROOM", WCS_STRING, WCC->wc_roomname);
156         svput("NODE", WCS_STRING, serv_info.serv_humannode);
157         // Fix me
158         svprintf(HKEY("ROOM_LINK"), WCS_STRING, "%s://%s/", (is_https ? "https" : "http"), WCC->http_host);
159         
160         /** Get room info for description */
161         serv_puts("RINF");
162         serv_getln(buf, sizeof buf);
163         char description[SIZ] = "";
164         if (buf[0] == '1') {
165                 while (1) {
166                         serv_getln(buf, sizeof buf);
167                         if (!strcmp(buf, "000"))
168                                 break;
169                         strncat(description, buf, strlen(buf));
170                 }
171         }
172         svput("ROOM_DESCRIPTION", WCS_STRING, description);
173         if (now) {
174                 svput("822_PUB_DATE", WCS_STRING, date);
175         }
176         svput("GENERATOR", WCS_STRING, PACKAGE_STRING);
177         do_template("rss_head", NULL);
178
179         /** Read all messages and output as RSS items */
180         for (a = 0; a < nummsgs; ++a) {
181                 /** Read message and output each as RSS item */
182                 Msg = GetMessagePtrAt(a, WCC->summ);
183                 serv_printf("MSG4 %ld", (Msg==NULL)? 0 : Msg->msgnum);
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", NULL);
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                         ctdl_iconv_open("UTF-8", charset, &ic);
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                         Msg = GetMessagePtrAt(a, WCC->summ);
343
344                         output_html(charset, 0, (Msg==NULL)? 0 : Msg->msgnum, NULL,  NULL); 
345                 } 
346
347 ENDBODY:
348                 //wprintf("   </item>\n");
349                 do_template("rss_item_end", NULL);
350 ENDITEM:
351                 now = 0L;
352         }
353
354         /** Do RSS footer */
355         wprintf("   </channel>\n");
356         wprintf("</rss>\n");
357         wDumpContent(0);
358         #ifdef ALLOW_ANON_RSS
359         end_webcit_session();
360         #endif
361 }
362
363