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.
4 // Copyright (c) 1996-2023 by the citadel.org team
6 // This program is open source software. Use, duplication, or
7 // disclosure are subject to the GNU General Public License v3.
11 // Commands we need to send to Citadel Server before we begin rendering forum view.
12 // These are common to flat and threaded views.
13 void setup_for_forum_view(struct ctdlsession *c) {
15 ctdl_printf(c, "MSGP text/html|text/plain"); // Declare the MIME types we know how to render
16 ctdl_readline(c, buf, sizeof(buf)); // Ignore the response
17 ctdl_printf(c, "MSGP dont_decode"); // Tell the server we will decode base64/etc client-side
18 ctdl_readline(c, buf, sizeof(buf)); // Ignore the response
22 // Convenience function for json_render_one_message()
23 // Converts a string of comma-separated recipients into a JSON Array
24 JsonValue *json_tokenize_recipients(const char *Key, long keylen, char *recp) {
27 JsonValue *j = NewJsonArray(Key, keylen);
28 int num_recp = num_tokens(recp, ',');
29 for (int i=0; i<num_recp; ++i) {
30 extract_token(tokbuf, recp, i, ',', sizeof(tokbuf));
32 JsonArrayAppend(j, NewJsonPlainString(HKEY("r"), tokbuf, strlen(tokbuf)));
38 // Fetch a single message and return it in JSON format for client-side rendering
39 void json_render_one_message(struct http_transaction *h, struct ctdlsession *c, long msgnum) {
40 StrBuf *raw_msg = NULL;
41 StrBuf *sanitized_msg = NULL;
43 char content_transfer_encoding[1024] = { 0 };
44 char content_type[1024] = { 0 };
45 char datetime[128] = { 0 };
46 int message_originated_locally = 0;
48 setup_for_forum_view(c);
50 ctdl_printf(c, "MSG4 %ld", msgnum);
51 ctdl_readline(c, buf, sizeof(buf));
57 JsonValue *attachments = NULL;
59 JsonValue *j = NewJsonObject(HKEY(""));
60 JsonObjectAppend(j, NewJsonNumber(HKEY("msgnum"), msgnum));
62 while ((ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "text")) && (strcmp(buf, "000"))) {
63 utf8ify_rfc822_string(&buf[5]);
65 // citadel header parsing here
66 if (!strncasecmp(buf, "from=", 5)) {
67 JsonObjectAppend(j, NewJsonPlainString(HKEY("from"), &buf[5], -1));
69 else if (!strncasecmp(buf, "rfca=", 5)) {
70 JsonObjectAppend(j, NewJsonPlainString(HKEY("rfca"), &buf[5], -1));
72 else if (!strncasecmp(buf, "time=", 5)) {
73 JsonObjectAppend(j, NewJsonNumber(HKEY("time"), atol(&buf[5])));
75 else if (!strncasecmp(buf, "locl=", 5)) {
76 message_originated_locally = 1;
78 else if (!strncasecmp(buf, "subj=", 5)) {
79 JsonObjectAppend(j, NewJsonPlainString(HKEY("subj"), &buf[5], -1));
81 else if (!strncasecmp(buf, "msgn=", 5)) {
82 JsonObjectAppend(j, NewJsonPlainString(HKEY("msgn"), &buf[5], -1));
84 else if (!strncasecmp(buf, "wefw=", 5)) {
85 JsonObjectAppend(j, NewJsonPlainString(HKEY("wefw"), &buf[5], -1));
87 else if (!strncasecmp(buf, "rcpt=", 5)) {
88 JsonObjectAppend(j, json_tokenize_recipients(HKEY("rcpt"), &buf[5]));
90 else if (!strncasecmp(buf, "cccc=", 5)) {
91 JsonObjectAppend(j, json_tokenize_recipients(HKEY("cccc"), &buf[5]));
93 else if (!strncasecmp(buf, "part=", 5)) {
94 if (attachments == NULL) {
95 attachments = NewJsonArray(HKEY("part"));
97 JsonValue *part = NewJsonObject(HKEY(""));
99 extract_token(tokbuf, &buf[5], 0, '|', sizeof tokbuf);
100 JsonObjectAppend(part, NewJsonPlainString(HKEY("name"), tokbuf, -1));
101 extract_token(tokbuf, &buf[5], 1, '|', sizeof tokbuf);
102 JsonObjectAppend(part, NewJsonPlainString(HKEY("filename"), tokbuf, -1));
103 extract_token(tokbuf, &buf[5], 2, '|', sizeof tokbuf);
104 JsonObjectAppend(part, NewJsonPlainString(HKEY("partnum"), tokbuf, -1));
105 extract_token(tokbuf, &buf[5], 3, '|', sizeof tokbuf);
106 JsonObjectAppend(part, NewJsonPlainString(HKEY("disp"), tokbuf, -1));
107 extract_token(tokbuf, &buf[5], 4, '|', sizeof tokbuf);
108 JsonObjectAppend(part, NewJsonPlainString(HKEY("type"), tokbuf, -1));
109 JsonObjectAppend(part, NewJsonNumber(HKEY("len"), extract_long(&buf[5], 5)));
110 extract_token(tokbuf, &buf[5], 6, '|', sizeof tokbuf);
111 JsonObjectAppend(part, NewJsonPlainString(HKEY("id"), tokbuf, -1));
112 extract_token(tokbuf, &buf[5], 7, '|', sizeof tokbuf);
113 JsonObjectAppend(part, NewJsonPlainString(HKEY("charset"), tokbuf, -1));
114 JsonArrayAppend(attachments, part);
120 JsonObjectAppend(j, NewJsonNumber(HKEY("locl"), message_originated_locally));
121 if (attachments != NULL) {
122 JsonObjectAppend(j, attachments);
125 if (!strcmp(buf, "text")) {
126 while ((ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "")) && (strcmp(buf, "000"))) {
127 // rfc822 header parsing here
128 if (!strncasecmp(buf, "Content-transfer-encoding:", 26)) {
129 strcpy(content_transfer_encoding, &buf[26]);
130 string_trim(content_transfer_encoding);
132 if (!strncasecmp(buf, "Content-type:", 13)) {
133 strcpy(content_type, &buf[13]);
134 string_trim(content_type);
137 if (!strcmp(buf, "000")) { // if we have an empty message, don't try to read further
141 raw_msg = ctdl_readtextmsg(c);
149 // These are the encodings we know how to handle. Decode in-place.
151 if (!strcasecmp(content_transfer_encoding, "base64")) {
152 StrBufDecodeBase64(raw_msg);
154 if (!strcasecmp(content_transfer_encoding, "quoted-printable")) {
155 StrBufDecodeQP(raw_msg);
158 // At this point, raw_msg contains the decoded message.
159 // Now run through the renderers we have available.
161 if (!strncasecmp(content_type, "text/html", 9)) {
162 sanitized_msg = html2html("UTF-8", 0, c->room, msgnum, raw_msg);
164 else if (!strncasecmp(content_type, "text/plain", 10)) {
165 sanitized_msg = text2html("UTF-8", 0, c->room, msgnum, raw_msg);
167 else if (!strncasecmp(content_type, "text/x-citadel-variformat", 25)) {
168 sanitized_msg = variformat2html(raw_msg);
171 sanitized_msg = NewStrBufPlain(HKEY("<i>No renderer for this content type</i><br>"));
172 syslog(LOG_WARNING, "forum_view: no renderer for content type %s", content_type);
174 FreeStrBuf(&raw_msg);
176 // If sanitized_msg is not NULL, we have rendered the message and can output it.
178 JsonObjectAppend(j, NewJsonString(HKEY("text"), sanitized_msg, NEWJSONSTRING_SMASHBUF));
181 syslog(LOG_WARNING, "forum_view: message %ld of content type %s converted to NULL", msgnum, content_type);
185 StrBuf *sj = NewStrBuf();
186 SerializeJson(sj, j, 1); // '1' == free the source object
188 add_response_header(h, strdup("Content-type"), strdup("application/json"));
189 h->response_code = 200;
190 h->response_string = strdup("OK");
191 h->response_body_length = StrLength(sj);
192 h->response_body = SmashStrBuf(&sj);