X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit-ng%2Fforum_view.c;h=6cd4f01f547c00b95c9c5ea69bb3c4129d5da18a;hb=03d5df9a4e11ce16fbfeac4ad03f2825591fa4ae;hp=995a6e187ff28d176ed2a913da66bddf1c2c1a0e;hpb=a2e2b0eab5e29c1bd61ec21737d28d3cb70ee364;p=citadel.git diff --git a/webcit-ng/forum_view.c b/webcit-ng/forum_view.c index 995a6e187..6cd4f01f5 100644 --- a/webcit-ng/forum_view.c +++ b/webcit-ng/forum_view.c @@ -24,10 +24,24 @@ struct mthread { }; +// Commands we need to send to Citadel Server before we begin rendering forum view. +// These are common to flat and threaded views. +// +void setup_for_forum_view(struct ctdlsession *c) +{ + char buf[1024]; + ctdl_printf(c, "MSGP text/html|text/plain"); // Declare the MIME types we know how to render + ctdl_readline(c, buf, sizeof(buf)); // Ignore the response + ctdl_printf(c, "MSGP dont_decode"); // Tell the server we will decode base64/etc client-side + ctdl_readline(c, buf, sizeof(buf)); // Ignore the response +} + + +#if 0 // Renderer for one message in the threaded view // (This will probably work for the flat view too.) // -void forum_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum) +void forum_render_one_message(struct ctdlsession *c, StrBuf * sj, long msgnum) { StrBuf *raw_msg = NULL; StrBuf *sanitized_msg = NULL; @@ -35,7 +49,7 @@ void forum_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum) char content_transfer_encoding[1024] = { 0 }; char content_type[1024] = { 0 }; char author[128] = { 0 }; - char datetime[128] = { 0 } ; + char datetime[128] = { 0 }; ctdl_printf(c, "MSG4 %ld", msgnum); ctdl_readline(c, buf, sizeof(buf)); @@ -44,7 +58,7 @@ void forum_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum) return; } - while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "text")) && (strcmp(buf, "000")) ) { + while ((ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "text")) && (strcmp(buf, "000"))) { // citadel header parsing here if (!strncasecmp(buf, "from=", 5)) { safestrncpy(author, &buf[5], sizeof author); @@ -59,7 +73,7 @@ void forum_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum) } if (!strcmp(buf, "text")) { - while ( (ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "")) && (strcmp(buf, "000")) ) { + while ((ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "")) && (strcmp(buf, "000"))) { // rfc822 header parsing here if (!strncasecmp(buf, "Content-transfer-encoding:", 26)) { strcpy(content_transfer_encoding, &buf[26]); @@ -71,23 +85,22 @@ void forum_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum) } } raw_msg = ctdl_readtextmsg(c); - } - else { + } else { raw_msg = NULL; } // begin output - StrBufAppendPrintf(sj, "
"); // begin message wrapper - StrBufAppendPrintf(sj, "
"); // begin avatar FIXME move the style to a stylesheet - StrBufAppendPrintf(sj, " "); // FIXME temporary avatar - StrBufAppendPrintf(sj, "
"); // end avatar - StrBufAppendPrintf(sj, "
"); // begin content - StrBufAppendPrintf(sj, "
"); // begin header + StrBufAppendPrintf(sj, "
"); // begin message wrapper + StrBufAppendPrintf(sj, "
"); // begin avatar FIXME move the style to a stylesheet + StrBufAppendPrintf(sj, " "); // FIXME temporary avatar + StrBufAppendPrintf(sj, "
"); // end avatar + StrBufAppendPrintf(sj, "
"); // begin content + StrBufAppendPrintf(sj, "
"); // begin header StrBufAppendPrintf(sj, "%s ", author); // FIXME link to user profile or whatever StrBufAppendPrintf(sj, "%s ", datetime); - StrBufAppendPrintf(sj, "
"); // end header - StrBufAppendPrintf(sj, "
"); // begin body + StrBufAppendPrintf(sj, "
"); // end header + StrBufAppendPrintf(sj, "
"); // begin body if (raw_msg) { @@ -99,20 +112,16 @@ void forum_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum) if (!strcasecmp(content_transfer_encoding, "quoted-printable")) { StrBufDecodeQP(raw_msg); } - // At this point, raw_msg contains the decoded message. // Now run through the renderers we have available. if (!strncasecmp(content_type, "text/html", 9)) { sanitized_msg = html2html("UTF-8", 0, c->room, msgnum, raw_msg); - } - else if (!strncasecmp(content_type, "text/plain", 10)) { + } else if (!strncasecmp(content_type, "text/plain", 10)) { sanitized_msg = text2html("UTF-8", 0, c->room, msgnum, raw_msg); - } - else if (!strncasecmp(content_type, "text/x-citadel-variformat", 25)) { + } else if (!strncasecmp(content_type, "text/x-citadel-variformat", 25)) { sanitized_msg = variformat2html(raw_msg); - } - else { + } else { sanitized_msg = NewStrBufPlain(HKEY("No renderer for this content type
")); } FreeStrBuf(&raw_msg); @@ -125,45 +134,35 @@ void forum_render_one_message(struct ctdlsession *c, StrBuf *sj, long msgnum) } } - StrBufAppendPrintf(sj, "
"); // end body - StrBufAppendPrintf(sj, "
"); // end content - StrBufAppendPrintf(sj, "
"); // end wrapper + StrBufAppendPrintf(sj, "
"); // end body + StrBufAppendPrintf(sj, "
"); // end content + StrBufAppendPrintf(sj, "
"); // end wrapper } -// Commands we need to send to Citadel Server before we begin rendering forum view. -// These are common to flat and threaded views. -// -void setup_for_forum_view(struct ctdlsession *c) -{ - char buf[1024]; - ctdl_printf(c, "MSGP text/html|text/plain"); // Declare the MIME types we know how to render - ctdl_readline(c, buf, sizeof(buf)); // Ignore the response - ctdl_printf(c, "MSGP dont_decode"); // Tell the server we will decode base64/etc client-side - ctdl_readline(c, buf, sizeof(buf)); // Ignore the response -} - +// This code implements the thread display code. The thread sorting algorithm is working nicely but we're trying +// not to do rendering in the C server of webcit. Maybe move it into the server as "MSGS threaded" or something like that? // Threaded view (recursive section) // -void thread_o_print(struct ctdlsession *c, StrBuf *sj, struct mthread *m, int num_msgs, int where_parent_is, int nesting_level) +void thread_o_print(struct ctdlsession *c, StrBuf * sj, struct mthread *m, int num_msgs, int where_parent_is, int nesting_level) { int i = 0; int j = 0; int num_printed = 0; - for (i=0; i"); } - StrBufAppendPrintf(sj, "
  • ", m[i].msgnum); + StrBufAppendPrintf(sj, "
  • ", m[i].msgnum); forum_render_one_message(c, sj, m[i].msgnum); StrBufAppendPrintf(sj, "
  • \r\n"); if (i != 0) { - thread_o_print(c, sj, m, num_msgs, i, nesting_level+1); + thread_o_print(c, sj, m, num_msgs, i, nesting_level + 1); } } } @@ -185,7 +184,7 @@ void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *whic char refs[1024]; int i, j, k; - ctdl_printf(c, "MSGS ALL|||9"); // 9 == headers + thread references + ctdl_printf(c, "MSGS ALL|||9"); // 9 == headers + thread references ctdl_readline(c, buf, sizeof(buf)); if (buf[0] != '1') { do_404(h); @@ -195,25 +194,24 @@ void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *whic StrBuf *sj = NewStrBuf(); StrBufAppendPrintf(sj, "\r\n"); - while (ctdl_readline(c, buf, sizeof buf), strcmp(buf,"000")) { + while (ctdl_readline(c, buf, sizeof buf), strcmp(buf, "000")) { ++num_msgs; if (num_msgs > num_alloc) { if (num_alloc == 0) { num_alloc = 100; m = malloc(num_alloc * sizeof(struct mthread)); - } - else { + } else { num_alloc *= 2; m = realloc(m, (num_alloc * sizeof(struct mthread))); } } - memset(&m[num_msgs-1], 0, sizeof(struct mthread)); - m[num_msgs-1].msgnum = extract_long(buf, 0); - m[num_msgs-1].datetime = extract_long(buf, 1); - extract_token(m[num_msgs-1].from, buf, 2, '|', sizeof m[num_msgs-1].from); - m[num_msgs-1].threadhash = extract_int(buf, 6); + memset(&m[num_msgs - 1], 0, sizeof(struct mthread)); + m[num_msgs - 1].msgnum = extract_long(buf, 0); + m[num_msgs - 1].datetime = extract_long(buf, 1); + extract_token(m[num_msgs - 1].from, buf, 2, '|', sizeof m[num_msgs - 1].from); + m[num_msgs - 1].threadhash = extract_int(buf, 6); extract_token(refs, buf, 7, '|', sizeof refs); char *t; @@ -221,11 +219,10 @@ void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *whic i = 0; while ((t = strtok_r(r, ",", &r))) { if (i == 0) { - m[num_msgs-1].refhashes[0] = atoi(t); // always keep the first one - } - else { - memcpy(&m[num_msgs-1].refhashes[1], &m[num_msgs-1].refhashes[2], sizeof(int)*8 ); // shift the rest - m[num_msgs-1].refhashes[9] = atoi(t); + m[num_msgs - 1].refhashes[0] = atoi(t); // always keep the first one + } else { + memcpy(&m[num_msgs - 1].refhashes[1], &m[num_msgs - 1].refhashes[2], sizeof(int) * 8); // shift the rest + m[num_msgs - 1].refhashes[9] = atoi(t); } ++i; } @@ -233,9 +230,9 @@ void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *whic } // 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. - for (i=0; i=0)&&(m[i].parent==0); --j) { - for (k=0; (k= 0) && (m[i].parent == 0); --j) { + for (k = 0; (k < num_msgs) && (m[i].parent == 0); ++k) { if (m[i].refhashes[j] == m[k].threadhash) { m[i].parent = k; } @@ -245,7 +242,7 @@ void threaded_view(struct http_transaction *h, struct ctdlsession *c, char *whic // Now render it setup_for_forum_view(c); - thread_o_print(c, sj, m, num_msgs, 0, 0); // Render threads recursively and recursively + thread_o_print(c, sj, m, num_msgs, 0, 0); // Render threads recursively and recursively // Garbage collection is for people who aren't smart enough to manage their own memory. if (num_msgs > 0) { @@ -274,7 +271,7 @@ void flat_view(struct http_transaction *h, struct ctdlsession *c, char *which) long *msglist = get_msglist(c, "ALL"); if (msglist) { int i; - for (i=0; (msglist[i] > 0); ++i) { + for (i = 0; (msglist[i] > 0); ++i) { forum_render_one_message(c, sj, msglist[i]); } free(msglist); @@ -289,3 +286,122 @@ void flat_view(struct http_transaction *h, struct ctdlsession *c, char *which) h->response_body = SmashStrBuf(&sj); return; } + + +// render one message (entire transaction) FIXME EXTERMINATE +// +void html_render_one_message(struct http_transaction *h, struct ctdlsession *c, long msgnum) +{ + StrBuf *sj = NewStrBuf(); + StrBufAppendPrintf(sj, "\r\n"); + setup_for_forum_view(c); // FIXME way too inefficient to do this for every message !!!!!!!!!!!!! + forum_render_one_message(c, sj, msgnum); + StrBufAppendPrintf(sj, "\r\n"); + add_response_header(h, strdup("Content-type"), strdup("text/html; charset=utf-8")); + h->response_code = 200; + h->response_string = strdup("OK"); + h->response_body_length = StrLength(sj); + h->response_body = SmashStrBuf(&sj); + return; +} + +#endif + +// Fetch a single message and return it in JSON format for client-side rendering +// +void json_render_one_message(struct http_transaction *h, struct ctdlsession *c, long msgnum) +{ + StrBuf *raw_msg = NULL; + StrBuf *sanitized_msg = NULL; + char buf[1024]; + char content_transfer_encoding[1024] = { 0 }; + char content_type[1024] = { 0 }; + char author[128] = { 0 }; + char datetime[128] = { 0 }; + + setup_for_forum_view(c); + + ctdl_printf(c, "MSG4 %ld", msgnum); + ctdl_readline(c, buf, sizeof(buf)); + if (buf[0] != '1') { + do_404(h); + return; + } + + JsonValue *j = NewJsonObject(HKEY("message")); + + while ((ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "text")) && (strcmp(buf, "000"))) { + // citadel header parsing here + if (!strncasecmp(buf, "from=", 5)) { + JsonObjectAppend(j, NewJsonPlainString(HKEY("from"), &buf[5], -1)); + } + if (!strncasecmp(buf, "rfca=", 5)) { + JsonObjectAppend(j, NewJsonPlainString(HKEY("from"), &buf[5], -1)); + } + if (!strncasecmp(buf, "time=", 5)) { + time_t tt; + struct tm tm; + tt = atol(&buf[5]); + localtime_r(&tt, &tm); + strftime(datetime, sizeof datetime, "%c", &tm); + JsonObjectAppend(j, NewJsonPlainString(HKEY("time"), datetime, -1)); + } + } + + if (!strcmp(buf, "text")) { + while ((ctdl_readline(c, buf, sizeof(buf)) >= 0) && (strcmp(buf, "")) && (strcmp(buf, "000"))) { + // rfc822 header parsing here + if (!strncasecmp(buf, "Content-transfer-encoding:", 26)) { + strcpy(content_transfer_encoding, &buf[26]); + striplt(content_transfer_encoding); + } + if (!strncasecmp(buf, "Content-type:", 13)) { + strcpy(content_type, &buf[13]); + striplt(content_type); + } + } + raw_msg = ctdl_readtextmsg(c); + } else { + raw_msg = NULL; + } + + if (raw_msg) { + + // These are the encodings we know how to handle. Decode in-place. + + if (!strcasecmp(content_transfer_encoding, "base64")) { + StrBufDecodeBase64(raw_msg); + } + if (!strcasecmp(content_transfer_encoding, "quoted-printable")) { + StrBufDecodeQP(raw_msg); + } + // At this point, raw_msg contains the decoded message. + // Now run through the renderers we have available. + + if (!strncasecmp(content_type, "text/html", 9)) { + sanitized_msg = html2html("UTF-8", 0, c->room, msgnum, raw_msg); + } else if (!strncasecmp(content_type, "text/plain", 10)) { + sanitized_msg = text2html("UTF-8", 0, c->room, msgnum, raw_msg); + } else if (!strncasecmp(content_type, "text/x-citadel-variformat", 25)) { + sanitized_msg = variformat2html(raw_msg); + } else { + sanitized_msg = NewStrBufPlain(HKEY("No renderer for this content type
    ")); + } + FreeStrBuf(&raw_msg); + + // If sanitized_msg is not NULL, we have rendered the message and can output it. + + if (sanitized_msg) { + JsonObjectAppend(j, NewJsonString(HKEY("text"), sanitized_msg, NEWJSONSTRING_SMASHBUF)); + } + } + + StrBuf *sj = NewStrBuf(); + SerializeJson(sj, j, 1); // '1' == free the source object + + add_response_header(h, strdup("Content-type"), strdup("application/json")); + h->response_code = 200; + h->response_string = strdup("OK"); + h->response_body_length = StrLength(sj); + h->response_body = SmashStrBuf(&sj); +}