2 * Forum view (threaded/flat)
4 * Copyright (c) 1996-2018 by the citadel.org team
6 * This program is open source software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
27 // Renderer for one message in the threaded view
28 // (This will probably work for the flat view too.)
30 void forum_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum)
32 StrBuf *raw_msg = NULL;
33 StrBuf *sanitized_msg = NULL;
35 char content_transfer_encoding[1024] = { 0 };
36 char content_type[1024] = { 0 };
37 char author[128] = { 0 };
38 char datetime[128] = { 0 } ;
40 ctdl_printf(c, "MSG4 %ld", msgnum);
41 ctdl_readline(c, buf, sizeof(buf));
43 StrBufAppendPrintf(sj, "<div>ERROR CONDITION FIXME WRITE A BOX</div>");
47 while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "text")) && (strcmp(buf, "000")) ) {
48 // citadel header parsing here
49 if (!strncasecmp(buf, "from=", 5)) {
50 safestrncpy(author, &buf[5], sizeof author);
52 if (!strncasecmp(buf, "time=", 5)) {
56 localtime_r(&tt, &tm);
57 strftime(datetime, sizeof datetime, "%c", &tm);
61 if (!strcmp(buf, "text")) {
62 while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "")) && (strcmp(buf, "000")) ) {
63 // rfc822 header parsing here
64 if (!strncasecmp(buf, "Content-transfer-encoding:", 26)) {
65 strcpy(content_transfer_encoding, &buf[26]);
66 striplt(content_transfer_encoding);
68 if (!strncasecmp(buf, "Content-type:", 13)) {
69 strcpy(content_type, &buf[13]);
70 striplt(content_type);
73 raw_msg = ctdl_readtextmsg(c);
81 StrBufAppendPrintf(sj, "<div>"); // begin message wrapper
82 StrBufAppendPrintf(sj, "<div style=\"float:left;padding-right:2px\">"); // begin avatar FIXME move the style to a stylesheet
83 StrBufAppendPrintf(sj, "<i class=\"fa fa-user-circle fa-2x\"></i> "); // FIXME temporary avatar
84 StrBufAppendPrintf(sj, "</div>"); // end avatar
85 StrBufAppendPrintf(sj, "<div>"); // begin content
86 StrBufAppendPrintf(sj, "<div>"); // begin header
87 StrBufAppendPrintf(sj, "<span class=\"ctdl-username\"><a href=\"#\">%s</a></span> ", author); // FIXME link to user profile or whatever
88 StrBufAppendPrintf(sj, "<span class=\"ctdl-msgdate\">%s</span> ", datetime);
89 StrBufAppendPrintf(sj, "</div>"); // end header
90 StrBufAppendPrintf(sj, "<div>"); // begin body
94 // These are the encodings we know how to handle. Decode in-place.
96 if (!strcasecmp(content_transfer_encoding, "base64")) {
97 StrBufDecodeBase64(raw_msg);
99 if (!strcasecmp(content_transfer_encoding, "quoted-printable")) {
100 StrBufDecodeQP(raw_msg);
103 // At this point, raw_msg contains the decoded message.
104 // Now run through the renderers we have available.
106 if (!strncasecmp(content_type, "text/html", 9)) {
107 sanitized_msg = html2html("UTF-8", 0, c->room, msgnum, raw_msg);
109 else if (!strncasecmp(content_type, "text/plain", 10)) {
110 sanitized_msg = text2html("UTF-8", 0, c->room, msgnum, raw_msg);
112 else if (!strncasecmp(content_type, "text/x-citadel-variformat", 25)) {
113 sanitized_msg = variformat2html(raw_msg);
116 sanitized_msg = NewStrBufPlain(HKEY("<i>No renderer for this content type</i><br>"));
118 FreeStrBuf(&raw_msg);
120 // If sanitized_msg is not NULL, we have rendered the message and can output it.
123 StrBufAppendBuf(sj, sanitized_msg, 0);
124 FreeStrBuf(&sanitized_msg);
128 StrBufAppendPrintf(sj, "</div>"); // end body
129 StrBufAppendPrintf(sj, "</div>"); // end content
130 StrBufAppendPrintf(sj, "</div>"); // end wrapper
134 // Commands we need to send to Citadel Server before we begin rendering forum view.
135 // These are common to flat and threaded views.
137 void setup_for_forum_view(struct ctdlsession *c)
140 ctdl_printf(c, "MSGP text/html|text/plain"); // Declare the MIME types we know how to render
141 ctdl_readline(c, buf, sizeof(buf)); // Ignore the response
142 ctdl_printf(c, "MSGP dont_decode"); // Tell the server we will decode base64/etc client-side
143 ctdl_readline(c, buf, sizeof(buf)); // Ignore the response
149 // This code implements the thread display code. The thread sorting algorithm is working nicely but we're trying
150 // not to do rendering in the C server of webcit. Maybe move it into the server as "MSGS threaded" or something like that?
152 // Threaded view (recursive section)
154 void thread_o_print(struct ctdlsession *c, StrBuf *sj, struct mthread *m, int num_msgs, int where_parent_is, int nesting_level)
160 for (i=0; i<num_msgs; ++i) {
161 if (m[i].parent == where_parent_is) {
163 if (++num_printed == 1) {
164 StrBufAppendPrintf(sj, "<ul style=\"list-style-type: none;\">");
167 StrBufAppendPrintf(sj, "<li class=\"post\" id=\"post-%ld\">", m[i].msgnum);
168 forum_render_one_message(c, sj, m[i].msgnum);
169 StrBufAppendPrintf(sj, "</li>\r\n");
171 thread_o_print(c, sj, m, num_msgs, i, nesting_level+1);
176 if (num_printed > 0) {
177 StrBufAppendPrintf(sj, "</ul>");
182 // Threaded view (entry point)
184 void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *which)
193 ctdl_printf(c, "MSGS ALL|||9"); // 9 == headers + thread references
194 ctdl_readline(c, buf, sizeof(buf));
200 StrBuf *sj = NewStrBuf();
201 StrBufAppendPrintf(sj, "<html><body>\r\n");
203 while (ctdl_readline(c, buf, sizeof buf), strcmp(buf,"000")) {
206 if (num_msgs > num_alloc) {
207 if (num_alloc == 0) {
209 m = malloc(num_alloc * sizeof(struct mthread));
213 m = realloc(m, (num_alloc * sizeof(struct mthread)));
217 memset(&m[num_msgs-1], 0, sizeof(struct mthread));
218 m[num_msgs-1].msgnum = extract_long(buf, 0);
219 m[num_msgs-1].datetime = extract_long(buf, 1);
220 extract_token(m[num_msgs-1].from, buf, 2, '|', sizeof m[num_msgs-1].from);
221 m[num_msgs-1].threadhash = extract_int(buf, 6);
222 extract_token(refs, buf, 7, '|', sizeof refs);
227 while ((t = strtok_r(r, ",", &r))) {
229 m[num_msgs-1].refhashes[0] = atoi(t); // always keep the first one
232 memcpy(&m[num_msgs-1].refhashes[1], &m[num_msgs-1].refhashes[2], sizeof(int)*8 ); // shift the rest
233 m[num_msgs-1].refhashes[9] = atoi(t);
240 // Sort by thread. I did read jwz's sorting algorithm and it looks pretty good, but jwz is a self-righteous asshole so we do it our way.
241 for (i=0; i<num_msgs; ++i) {
242 for (j=9; (j>=0)&&(m[i].parent==0); --j) {
243 for (k=0; (k<num_msgs)&&(m[i].parent==0); ++k) {
244 if (m[i].refhashes[j] == m[k].threadhash) {
252 setup_for_forum_view(c);
253 thread_o_print(c, sj, m, num_msgs, 0, 0); // Render threads recursively and recursively
255 // Garbage collection is for people who aren't smart enough to manage their own memory.
260 StrBufAppendPrintf(sj, "</body></html>\r\n");
262 add_response_header(h, strdup("Content-type"), strdup("text/html; charset=utf-8"));
263 h->response_code = 200;
264 h->response_string = strdup("OK");
265 h->response_body_length = StrLength(sj);
266 h->response_body = SmashStrBuf(&sj);
271 // flat view (entry point)
273 void flat_view(struct http_transaction *h, struct ctdlsession *c, char *which)
275 StrBuf *sj = NewStrBuf();
276 StrBufAppendPrintf(sj, "<html><body>\r\n");
278 setup_for_forum_view(c);
279 long *msglist = get_msglist(c, "ALL");
282 for (i=0; (msglist[i] > 0); ++i) {
283 forum_render_one_message(c, sj, msglist[i]);
288 StrBufAppendPrintf(sj, "</body></html>\r\n");
290 add_response_header(h, strdup("Content-type"), strdup("text/html; charset=utf-8"));
291 h->response_code = 200;
292 h->response_string = strdup("OK");
293 h->response_body_length = StrLength(sj);
294 h->response_body = SmashStrBuf(&sj);
300 // render one message (entire transaction) FIXME EXTERMINATE
302 void html_render_one_message(struct http_transaction *h, struct ctdlsession *c, long msgnum)
304 StrBuf *sj = NewStrBuf();
305 StrBufAppendPrintf(sj, "<html><body>\r\n");
306 setup_for_forum_view(c); // FIXME way too inefficient to do this for every message !!!!!!!!!!!!!
307 forum_render_one_message(c, sj, msgnum);
308 StrBufAppendPrintf(sj, "</body></html>\r\n");
309 add_response_header(h, strdup("Content-type"), strdup("text/html; charset=utf-8"));
310 h->response_code = 200;
311 h->response_string = strdup("OK");
312 h->response_body_length = StrLength(sj);
313 h->response_body = SmashStrBuf(&sj);
318 // Fetch a single message and return it in JSON format for client-side rendering
320 void json_render_one_message(struct http_transaction *h, struct ctdlsession *c, long msgnum)
322 JsonValue *j = NULL; // FIXME do something useful
324 StrBuf *sj = NewStrBuf();
325 SerializeJson(sj, j, 1); // '1' == free the source object
327 add_response_header(h, strdup("Content-type"), strdup("application/json"));
328 h->response_code = 200;
329 h->response_string = strdup("OK");
330 h->response_body_length = StrLength(sj);
331 h->response_body = SmashStrBuf(&sj);