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