It's 2022 ... updating all of the copyright notices in webcit-ng
[citadel.git] / webcit-ng / forum_view.c
1 // Forum view (threaded/flat)
2 //
3 // Copyright (c) 1996-2022 by the citadel.org team
4 //
5 // This program is open source software.  It runs great on the
6 // Linux operating system (and probably elsewhere).  You can use,
7 // copy, and run it under the terms of the GNU General Public
8 // License version 3.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14
15 #include "webcit.h"
16
17 // Commands we need to send to Citadel Server before we begin rendering forum view.
18 // These are common to flat and threaded views.
19 void setup_for_forum_view(struct ctdlsession *c) {
20         char buf[1024];
21         ctdl_printf(c, "MSGP text/html|text/plain");    // Declare the MIME types we know how to render
22         ctdl_readline(c, buf, sizeof(buf));             // Ignore the response
23         ctdl_printf(c, "MSGP dont_decode");             // Tell the server we will decode base64/etc client-side
24         ctdl_readline(c, buf, sizeof(buf));             // Ignore the response
25 }
26
27
28 // Fetch a single message and return it in JSON format for client-side rendering
29 void json_render_one_message(struct http_transaction *h, struct ctdlsession *c, long msgnum) {
30         StrBuf *raw_msg = NULL;
31         StrBuf *sanitized_msg = NULL;
32         char buf[1024];
33         char content_transfer_encoding[1024] = { 0 };
34         char content_type[1024] = { 0 };
35         char datetime[128] = { 0 };
36         char author[1024] = { 0 };
37         char emailaddr[1024] = { 0 };
38         int message_originated_locally = 0;
39
40         setup_for_forum_view(c);
41
42         ctdl_printf(c, "MSG4 %ld", msgnum);
43         ctdl_readline(c, buf, sizeof(buf));
44         if (buf[0] != '1') {
45                 do_404(h);
46                 return;
47         }
48
49         JsonValue *j = NewJsonObject(HKEY("message"));
50
51         while ((ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "text")) && (strcmp(buf, "000"))) {
52
53                 // citadel header parsing here
54                 if (!strncasecmp(buf, "from=", 5)) {
55                         safestrncpy(author, &buf[5], sizeof author);
56                 }
57                 else if (!strncasecmp(buf, "rfca=", 5)) {
58                         safestrncpy(emailaddr, &buf[5], sizeof emailaddr);
59                 }
60                 else if (!strncasecmp(buf, "time=", 5)) {
61                         time_t tt;
62                         struct tm tm;
63                         tt = atol(&buf[5]);
64                         localtime_r(&tt, &tm);
65                         strftime(datetime, sizeof datetime, "%c", &tm);
66                         JsonObjectAppend(j, NewJsonPlainString(HKEY("time"), datetime, -1));
67                 }
68                 else if (!strncasecmp(buf, "locl=", 5)) {
69                         message_originated_locally = 1;
70                 }
71                 else if (!strncasecmp(buf, "subj=", 5)) {
72                         JsonObjectAppend(j, NewJsonPlainString(HKEY("subj"), &buf[5], -1));
73                 }
74                 else if (!strncasecmp(buf, "msgn=", 5)) {
75                         JsonObjectAppend(j, NewJsonPlainString(HKEY("msgn"), &buf[5], -1));
76                 }
77                 else if (!strncasecmp(buf, "wefw=", 5)) {
78                         JsonObjectAppend(j, NewJsonPlainString(HKEY("wefw"), &buf[5], -1));
79                 }
80         }
81
82         if (message_originated_locally) {
83                 JsonObjectAppend(j, NewJsonPlainString(HKEY("from"), author, -1));
84         }
85         else {
86                 JsonObjectAppend(j, NewJsonPlainString(HKEY("from"), emailaddr, -1));           // FIXME do the compound address string
87         }
88
89         if (!strcmp(buf, "text")) {
90                 while ((ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "")) && (strcmp(buf, "000"))) {
91                         // rfc822 header parsing here
92                         if (!strncasecmp(buf, "Content-transfer-encoding:", 26)) {
93                                 strcpy(content_transfer_encoding, &buf[26]);
94                                 striplt(content_transfer_encoding);
95                         }
96                         if (!strncasecmp(buf, "Content-type:", 13)) {
97                                 strcpy(content_type, &buf[13]);
98                                 striplt(content_type);
99                         }
100                 }
101                 raw_msg = ctdl_readtextmsg(c);
102         }
103         else {
104                 raw_msg = NULL;
105         }
106
107         if (raw_msg) {
108                 // These are the encodings we know how to handle.  Decode in-place.
109
110                 if (!strcasecmp(content_transfer_encoding, "base64")) {
111                         StrBufDecodeBase64(raw_msg);
112                 }
113                 if (!strcasecmp(content_transfer_encoding, "quoted-printable")) {
114                         StrBufDecodeQP(raw_msg);
115                 }
116
117                 // At this point, raw_msg contains the decoded message.
118                 // Now run through the renderers we have available.
119
120                 if (!strncasecmp(content_type, "text/html", 9)) {
121                         sanitized_msg = html2html("UTF-8", 0, c->room, msgnum, raw_msg);
122                 }
123                 else if (!strncasecmp(content_type, "text/plain", 10)) {
124                         sanitized_msg = text2html("UTF-8", 0, c->room, msgnum, raw_msg);
125                 }
126                 else if (!strncasecmp(content_type, "text/x-citadel-variformat", 25)) {
127                         sanitized_msg = variformat2html(raw_msg);
128                 }
129                 else {
130                         sanitized_msg = NewStrBufPlain(HKEY("<i>No renderer for this content type</i><br>"));
131                         syslog(LOG_WARNING, "forum_view: no renderer for content type %s", content_type);
132                 }
133                 FreeStrBuf(&raw_msg);
134
135                 // If sanitized_msg is not NULL, we have rendered the message and can output it.
136                 if (sanitized_msg) {
137                         JsonObjectAppend(j, NewJsonString(HKEY("text"), sanitized_msg, NEWJSONSTRING_SMASHBUF));
138                 }
139                 else {
140                         syslog(LOG_WARNING, "forum_view: message %ld of content type %s converted to NULL", msgnum, content_type);
141                 }
142         }
143
144         StrBuf *sj = NewStrBuf();
145         SerializeJson(sj, j, 1);        // '1' == free the source object
146
147         add_response_header(h, strdup("Content-type"), strdup("application/json"));
148         h->response_code = 200;
149         h->response_string = strdup("OK");
150         h->response_body_length = StrLength(sj);
151         h->response_body = SmashStrBuf(&sj);
152 }