silenced a silly little compiler warning
[citadel.git] / webcit-ng / threaded_view.c
1 /*
2  * Threaded message view
3  *
4  * Copyright (c) 1996-2017 by the citadel.org team
5  *
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.
8  *
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.
13  */
14
15 #include "webcit.h"
16
17 struct mthread {
18         long msgnum;
19         time_t datetime;
20         int threadhash;
21         int refhashes[10];
22         char from[64];
23         int parent;
24 };
25
26
27 // Renderer for one message in the threaded view
28 void thread_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum)
29 {
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
36         ctdl_printf(c, "MSG4 %ld", msgnum);
37         ctdl_readline(c, buf, sizeof(buf));
38         if (buf[0] == '1') {
39                 while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "text")) && (strcmp(buf, "000")) ) {
40                         // citadel header parsing here
41                         if (!strncasecmp(buf, "from=", 5)) {
42                                 StrBufAppendPrintf(sj, "<b>From %s</b><br>", &buf[5]);  // FIXME that was temporary
43                         }
44                         if (!strncasecmp(buf, "part=", 5)) {
45                                 //StrBufAppendPrintf(sj, "MIME part: %s<br>", &buf[5]); // FIXME that was temporary
46                         }
47                 }
48                 if (!strcmp(buf, "text")) {
49                         while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "")) && (strcmp(buf, "000")) ) {
50                                 // rfc822 header parsing here
51                                 if (!strncasecmp(buf, "Content-transfer-encoding:", 26)) {
52                                         strcpy(content_transfer_encoding, &buf[26]);
53                                         striplt(content_transfer_encoding);
54                                 }
55                                 if (!strncasecmp(buf, "Content-type:", 13)) {
56                                         strcpy(content_type, &buf[13]);
57                                         striplt(content_type);
58                                         //StrBufAppendPrintf(sj, "Content-type: %s<br>", content_type); // FIXME that was temporary
59                                 }
60                         }
61                         raw_msg = ctdl_readtextmsg(c);
62                 }
63                 else {
64                         raw_msg = NULL;
65                 }
66                 if (raw_msg) {
67                         if (!strcasecmp(content_transfer_encoding, "base64")) {
68                                 StrBufDecodeBase64(raw_msg);
69                         }
70                         if (!strcasecmp(content_transfer_encoding, "quoted-printable")) {
71                                 StrBufDecodeQP(raw_msg);
72                         }
73                         if (!strncasecmp(content_type, "text/html", 9)) {
74                                 sanitized_msg = html2html("UTF-8", 0, c->room, msgnum, raw_msg);
75                         }
76                         else if (!strncasecmp(content_type, "text/plain", 10)) {
77                                 sanitized_msg = text2html("UTF-8", 0, c->room, msgnum, raw_msg);
78                         }
79                         else if (!strncasecmp(content_type, "text/x-citadel-variformat", 25)) {
80                                 sanitized_msg = variformat2html(raw_msg);
81                         }
82                         else {
83                                 sanitized_msg = NewStrBufPlain(HKEY("<i>No renderer for this content type</i><br>"));
84                         }
85                         //sanitized_msg = NewStrBufDup(raw_msg);
86                         FreeStrBuf(&raw_msg);
87                         if (sanitized_msg) {
88                                 StrBufAppendBuf(sj, sanitized_msg, 0);
89                                 FreeStrBuf(&sanitized_msg);
90                         }
91                 }
92         }
93 }
94
95
96 void thread_o_print(struct ctdlsession *c, StrBuf *sj, struct mthread *m, int num_msgs, int where_parent_is, int nesting_level)
97 {
98         int i = 0;
99         int j = 0;
100         int num_printed = 0;
101
102         for (i=0; i<num_msgs; ++i) {
103                 if (m[i].parent == where_parent_is) {
104
105                         if (++num_printed == 1) {
106                                 StrBufAppendPrintf(sj, "<ul>");
107                                 StrBufAppendPrintf(sj, "<table style=\"border: 1px solid black;\"><tr><td>");   // temporary, for visualization
108                         }
109
110                         //for (j=nesting_level; j>0; --j) {
111                                 //StrBufAppendPrintf(sj, " ");
112                         //}
113
114                         StrBufAppendPrintf(sj, "<li class=\"post\" id=\"post-%ld\">",  m[i].msgnum);
115                         thread_render_one_message(c, sj, m[i].msgnum);
116                         StrBufAppendPrintf(sj, "</li>\r\n");
117                         if (i != 0) {
118                                 thread_o_print(c, sj, m, num_msgs, i, nesting_level+1);
119                         }
120                 }
121         }
122
123         if (num_printed > 0) {
124                 StrBufAppendPrintf(sj, "</td></tr></table>");           // temporary, for visualization
125                 StrBufAppendPrintf(sj, "</ul>");
126         }
127 }
128
129
130
131 void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *which)
132 {
133         int num_msgs = 0;
134         int num_alloc = 0;
135         struct mthread *m;
136         char buf[1024];
137         char refs[1024];
138         int i, j, k;
139
140         ctdl_printf(c, "MSGS ALL|||9");                 // 9 == headers + thread references
141         ctdl_readline(c, buf, sizeof(buf));
142         if (buf[0] != '1') {
143                 do_404(h);
144                 return;
145         }
146
147         StrBuf *sj = NewStrBuf();
148         StrBufAppendPrintf(sj, "<html><body>\r\n");
149
150         while (ctdl_readline(c, buf, sizeof buf), strcmp(buf,"000")) {
151
152                 ++num_msgs;
153                 if (num_msgs > num_alloc) {
154                         if (num_alloc == 0) {
155                                 num_alloc = 100;
156                                 m = malloc(num_alloc * sizeof(struct mthread));
157                         }
158                         else {
159                                 num_alloc *= 2;
160                                 m = realloc(m, (num_alloc * sizeof(struct mthread)));
161                         }
162                 }
163
164                 memset(&m[num_msgs-1], 0, sizeof(struct mthread));
165                 m[num_msgs-1].msgnum = extract_long(buf, 0);
166                 m[num_msgs-1].datetime = extract_long(buf, 1);
167                 extract_token(m[num_msgs-1].from, buf, 2, '|', sizeof m[num_msgs-1].from);
168                 m[num_msgs-1].threadhash = extract_int(buf, 6);
169                 extract_token(refs, buf, 7, '|', sizeof refs);
170
171                 char *t;
172                 char *r = refs;
173                 i = 0;
174                 while ((t = strtok_r(r, ",", &r))) {
175                         if (i == 0) {
176                                 m[num_msgs-1].refhashes[0] = atoi(t);           // always keep the first one
177                         }
178                         else {
179                                 memcpy(&m[num_msgs-1].refhashes[1], &m[num_msgs-1].refhashes[2], sizeof(int)*8 );       // shift the rest
180                                 m[num_msgs-1].refhashes[9] = atoi(t);
181                         }
182                         ++i;
183                 }
184
185         }
186
187         // 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.
188         for (i=0; i<num_msgs; ++i) {
189                 for (j=9; (j>=0)&&(m[i].parent==0); --j) {
190                         for (k=0; (k<num_msgs)&&(m[i].parent==0); ++k) {
191                                 if (m[i].refhashes[j] == m[k].threadhash) {
192                                         m[i].parent = k;
193                                 }
194                         }
195                 }
196         }
197
198         // Now render it
199         ctdl_printf(c, "MSGP text/html|text/plain");            // Declare the MIME types we know how to render
200         ctdl_readline(c, buf, sizeof(buf));                     // Ignore the response
201         ctdl_printf(c, "MSGP dont_decode");                     // Tell the server we will decode base64/etc client-side
202         ctdl_readline(c, buf, sizeof(buf));                     // Ignore the response
203         thread_o_print(c, sj, m, num_msgs, 0, 0);               // Render threads recursively and recursively
204
205         // haha h0h0
206         if (num_msgs > 0) {
207                 free(m);
208         }
209
210         StrBufAppendPrintf(sj, "</body></html>\r\n");
211
212         add_response_header(h, strdup("Content-type"), strdup("text/html; charset=utf-8"));
213         h->response_code = 200;
214         h->response_string = strdup("OK");
215         h->response_body_length = StrLength(sj);
216         h->response_body = SmashStrBuf(&sj);
217         return;
218 }
219
220