-/*
- * Text client functions for reading and writing of messages
- *
- * Copyright (c) 1987-2019 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// Text client functions for reading and writing of messages
+//
+// Beware: this is really old and crappy code, written in the
+// late 1980s when my coding style was absolute garbage. It
+// works, but we probably should replace most of it.
+//
+// Copyright (c) 1987-2022 by the citadel.org team
+//
+// This program is open source software. Use, duplication, or disclosure is subject to the GNU General Public License version 3.
#include "textclient.h"
struct parts *last_message_parts = NULL; /* Parts from last msg */
-void ka_sigcatch(int signum)
-{
+void ka_sigcatch(int signum) {
alarm(S_KEEPALIVE);
signal(SIGALRM, ka_sigcatch);
CtdlIPCNoop(ipc_for_signal_handlers);
/*
* server keep-alive version of wait() (needed for external editor)
*/
-pid_t ka_wait(int *kstatus)
-{
+pid_t ka_wait(int *kstatus) {
pid_t p;
alarm(S_KEEPALIVE);
/*
* version of system() that uses ka_wait()
*/
-int ka_system(char *shc)
-{
+int ka_system(char *shc) {
pid_t childpid;
pid_t waitpid;
int retcode;
/*
* add a newline to the buffer...
*/
-void add_newline(struct cittext *textlist)
-{
+void add_newline(struct cittext *textlist) {
struct cittext *ptr;
ptr = textlist;
while (ptr->text[strlen(ptr->text) - 1] == 32)
ptr->text[strlen(ptr->text) - 1] = 0;
- /* strcat(ptr->text,"\n"); */
ptr->next = (struct cittext *)
malloc(sizeof(struct cittext));
/*
* add a word to the buffer...
*/
-void add_word(struct cittext *textlist, char *wordbuf)
-{
+void add_word(struct cittext *textlist, char *wordbuf) {
struct cittext *ptr;
ptr = textlist;
/*
* begin editing of an opened file pointed to by fp
*/
-void citedit(FILE * fp)
-{
+void citedit(FILE * fp) {
int a, prev, finished, b, last_space;
int appending = 0;
struct cittext *textlist = NULL;
add_newline(textlist);
add_word(textlist, "");
}
- } else {
+ }
+ else {
wordbuf[strlen(wordbuf) + 1] = 0;
wordbuf[strlen(wordbuf)] = a;
}
scr_putc(32);
scr_putc(8);
}
- } else if (a == 23) {
+ }
+ else if (a == 23) {
do {
wordbuf[strlen(wordbuf) - 1] = 0;
scr_putc(8);
scr_putc(32);
scr_putc(8);
} while (!IsEmptyStr(wordbuf) && wordbuf[strlen(wordbuf) - 1] != ' ');
- } else if (a == 13) {
+ }
+ else if (a == 13) {
scr_printf("\n");
if (IsEmptyStr(wordbuf))
finished = 1;
add_word(textlist, wordbuf);
strcpy(wordbuf, "");
}
- } else {
+ }
+ else {
scr_putc(a);
wordbuf[strlen(wordbuf) + 1] = 0;
wordbuf[strlen(wordbuf)] = a;
scr_putc(8);
}
scr_printf("\n%s", wordbuf);
- } else {
+ }
+ else {
add_word(textlist, wordbuf);
strcpy(wordbuf, "");
scr_printf("\n");
/*
* Free the struct parts
*/
-void free_parts(struct parts *p)
-{
+void free_parts(struct parts *p) {
struct parts *a_part = p;
while (a_part) {
/*
* This is a mini RFC2047 decoder.
* It only handles strings encoded from UTF-8 as Quoted-printable.
+ * We can do this "in place" because the converted string will always be smaller than the source string.
*/
-void mini_2047_decode(char *s)
-{
- if (!s)
+void mini_2047_decode(char *s) {
+ if (!s) { // no null strings allowed!
return;
+ }
- char *qstart = strstr(s, "=?UTF-8?Q?");
- if (!qstart)
+ char *qstart = strstr(s, "=?UTF-8?Q?"); // Must start with this string
+ if (!qstart) {
return;
+ }
- char *qend = strstr(s, "?=");
- if (!qend)
+ char *qend = strstr(qstart + 10, "?="); // Must end with this string
+ if (!qend) {
return;
+ }
- if (qend <= qstart)
+ if (qend <= qstart) { // And there must be something in between them.
return;
+ }
- strcpy(qstart, &qstart[10]);
- qend -= 10;
+ // The string has qualified for conversion.
- char *p = qstart;
- while (p < qend) {
+ strcpy(qend, ""); // Strip the trailer
+ strcpy(qstart, &qstart[10]); // Strip the header
- if (p[0] == '=') {
+ char *r = qstart; // Pointer to where in the string we're reading
+ char *w = s; // Pointer to where in the string we're writing
+ while (*r) { // Loop through the source string
+ if (r[0] == '=') { // "=" means read a hex character
char ch[3];
- ch[0] = p[1];
- ch[1] = p[2];
- ch[2] = p[3];
+ ch[0] = r[1];
+ ch[1] = r[2];
+ ch[2] = r[3];
int c;
sscanf(ch, "%02x", &c);
- p[0] = c;
- strcpy(&p[1], &p[3]);
- qend -= 2;
+ w[0] = c;
+ r += 3;
+ ++w;
}
-
- if (p[0] == '_') {
- p[0] = ' ';
+ else if (r[0] == '_') { // "_" is a space
+ w[0] = ' ';
+ ++r;
+ ++w;
+ }
+ else { // anything else pass through literally
+ w[0] = r[0];
+ ++r;
+ ++w;
}
-
- ++p;
}
-
- strcpy(qend, &qend[2]);
+ w[0] = 0; // null terminate
}
*/
int read_message(CtdlIPC * ipc, long num, /* message number */
int pagin, /* 0 = normal read, 1 = read with pagination, 2 = header */
- FILE * dest)
-{ /* Destination file, NULL for screen */
+ FILE * dest /* Destination file, NULL for screen */
+ ) {
char buf[SIZ];
char now[256];
int format_type = 0;
char ch;
int linelen;
int final_line_is_blank = 0;
-
has_images = 0;
sigcaught = 0;
if (dest) {
fprintf(dest, "\n ");
- } else {
+ }
+ else {
scr_printf("\n");
if (pagin != 2) {
scr_printf(" ");
if (!IsEmptyStr(message->email)) {
scr_printf("rfca=%s\n", message->email);
}
- scr_printf("room=%s\ntime=%s", message->room, asctime(localtime(&message->time))
- );
+ scr_printf("room=%s\ntime=%s", message->room, asctime(localtime(&message->time)));
if (!IsEmptyStr(message->recipient)) {
scr_printf("rcpt=%s\n", message->recipient);
}
if (rc_display_message_numbers) {
if (dest) {
fprintf(dest, "[#%s] ", message->msgid);
- } else {
+ }
+ else {
color(DIM_WHITE);
scr_printf("[");
color(BRIGHT_WHITE);
if (nhdr == 1 && !is_room_aide) {
if (dest) {
fprintf(dest, " ****");
- } else {
+ }
+ else {
scr_printf(" ****");
}
- } else {
+ }
+ else {
struct tm thetime;
localtime_r(&message->time, &thetime);
strftime(now, sizeof now, "%F %R", &thetime);
if (dest) {
fprintf(dest, "%s from %s ", now, message->author);
- if (!IsEmptyStr(message->email)) {
+ if (!message->is_local) {
fprintf(dest, "<%s> ", message->email);
}
- } else {
+ }
+ else {
color(BRIGHT_CYAN);
scr_printf("%s ", now);
color(DIM_WHITE);
scr_printf("from ");
color(BRIGHT_CYAN);
scr_printf("%s ", message->author);
- if (!IsEmptyStr(message->email)) {
+ if (!message->is_local) {
color(DIM_WHITE);
scr_printf("<");
color(BRIGHT_BLUE);
if (strcasecmp(message->room, room_name) && (IsEmptyStr(message->email))) {
if (dest) {
fprintf(dest, "in %s> ", message->room);
- } else {
+ }
+ else {
color(DIM_WHITE);
scr_printf("in ");
color(BRIGHT_MAGENTA);
if (!IsEmptyStr(message->recipient)) {
if (dest) {
fprintf(dest, "to %s ", message->recipient);
- } else {
+ }
+ else {
color(DIM_WHITE);
scr_printf("to ");
color(BRIGHT_CYAN);
if (dest) {
fprintf(dest, "\n");
- } else {
+ }
+ else {
scr_printf("\n");
}
- /* Set the reply-to address to an Internet e-mail address if possible
- */
+ // Set the reply-to address to an Internet e-mail address if possible
if ((message->email != NULL) && (!IsEmptyStr(message->email))) {
if (!IsEmptyStr(message->author)) {
snprintf(reply_to, sizeof reply_to, "%s <%s>", message->author, message->email);
- } else {
- safestrncpy(reply_to, message->email, sizeof reply_to);
+ }
+ else {
+ strncpy(reply_to, message->email, sizeof reply_to);
}
}
- /* But if we can't do that, set it to a Citadel address.
- */
+ // But if we can't do that, set it to a Citadel address.
if (!strcmp(reply_to, NO_REPLY_TO)) {
- safestrncpy(reply_to, message->author, sizeof(reply_to));
+ strncpy(reply_to, message->author, sizeof(reply_to));
}
if (message->msgid != NULL) {
- safestrncpy(reply_inreplyto, message->msgid, sizeof reply_inreplyto);
+ strncpy(reply_inreplyto, message->msgid, sizeof reply_inreplyto);
}
- if (message->references != NULL)
+ if (message->references != NULL) {
if (!IsEmptyStr(message->references)) {
- safestrncpy(reply_references, message->references, sizeof reply_references);
+ strncpy(reply_references, message->references, sizeof reply_references);
}
+ }
if (message->subject != NULL) {
- safestrncpy(reply_subject, message->subject, sizeof reply_subject);
+ strncpy(reply_subject, message->subject, sizeof reply_subject);
if (!IsEmptyStr(message->subject)) {
if (dest) {
fprintf(dest, "Subject: %s\n", message->subject);
- } else {
+ }
+ else {
color(DIM_WHITE);
scr_printf("Subject: ");
color(BRIGHT_CYAN);
mini_2047_decode(message->subject);
scr_printf("%s\n", message->subject);
+
}
}
}
* of the client screen.
*/
if (!strcasecmp(message->content_type, "text/html")) {
- converted_text = html_to_ascii(message->text, 0, screenwidth);
+ converted_text = html_to_ascii(message->text, 0, screenwidth, (enable_color ? 1 : 0));
if (converted_text != NULL) {
free(message->text);
message->text = converted_text;
while ((searchptr != NULL) && (num_urls < MAXURLS)) {
searchptr = strstr(searchptr, urlprefixes[p]);
if (searchptr != NULL) {
- safestrncpy(urls[num_urls], searchptr, sizeof(urls[num_urls]));
+ strncpy(urls[num_urls], searchptr, sizeof(urls[num_urls]));
for (i = 0; i < strlen(urls[num_urls]); i++) {
ch = urls[num_urls][i];
if (ch == '>' || ch == '\"' || ch == ')' || ch == ' ' || ch == '\n') {
*/
if (format_type == 0) {
fr = fmout(screenwidth, NULL, message->text, dest, 1);
- } else {
+ }
+ else {
/* renderer for text/plain */
lineptr = message->text;
}
if (dest) {
fprintf(dest, "%s\n", lineptr);
- } else {
+ }
+ else {
scr_printf("%s\n", lineptr);
}
}
if (!final_line_is_blank) {
if (dest) {
fprintf(dest, "\n");
- } else {
+ }
+ else {
scr_printf("\n");
fr = sigcaught;
}
/*
* replace string function for the built-in editor
*/
-void replace_string(char *filename, long int startpos)
-{
+void replace_string(char *filename, long int startpos) {
char buf[512];
char srch_str[128];
char rplc_str[128];
}
-/*
- * Function to begin composing a new message
- */
-int client_make_message(CtdlIPC * ipc, char *filename, /* temporary file name */
- char *recipient, /* NULL if it's not mail */
- int is_anonymous, int format_type, int mode, char *subject, /* buffer to store subject line */
- int subject_required)
-{
+// Function to begin composing a new message
+int client_make_message(CtdlIPC * ipc, char *filename, // temporary file name
+ char *recipient, // NULL if it's not mail
+ int is_anonymous, int format_type, int mode, char *subject, // buffer to store subject line
+ int subject_required) {
FILE *fp;
int a, b, e_ex_code;
long beg;
if (room_flags & QR_ANONONLY && !recipient) {
snprintf(header, sizeof header, " ****");
- } else {
- snprintf(header, sizeof header, " %s from %s", datestr, (is_anonymous ? "[anonymous]" : fullname)
- );
+ }
+ else {
+ snprintf(header, sizeof header, " %s from %s", datestr, (is_anonymous ? "[anonymous]" : fullname));
if (!IsEmptyStr(recipient)) {
size_t tmp = strlen(header);
snprintf(&header[tmp], sizeof header - tmp, " to %s", recipient);
if (beg < 0)
scr_printf("failed to get stream position %s\n", strerror(errno));
fclose(fp);
- } else {
+ }
+ else {
fp = fopen(filename, "w");
if (fp == NULL) {
- scr_printf("*** Error opening temp file!\n %s: %s\n", filename, strerror(errno)
- );
+ scr_printf("*** Error opening temp file!\n %s: %s\n", filename, strerror(errno));
return (1);
}
fclose(fp);
/*
* Make sure there's room in msg_arr[] for at least one more.
*/
-void check_msg_arr_size(void)
-{
+void check_msg_arr_size(void) {
if ((num_msgs + 1) > msg_arr_size) {
msg_arr_size += 512;
msg_arr = realloc(msg_arr, ((sizeof(long)) * msg_arr_size));
* break_big_lines() - break up lines that are >1024 characters
* otherwise the server will truncate
*/
-void break_big_lines(char *msg)
-{
+void break_big_lines(char *msg) {
char *ptr;
char *break_here;
int entmsg(CtdlIPC * ipc, int is_reply, /* nonzero if this was a <R>eply command */
int c, /* mode */
int masquerade /* prompt for a non-default display name? */
- )
-{
+ ) {
char buf[SIZ];
int a, b;
int need_recp = 0;
int r; /* IPC response code */
int subject_required = 0;
- if (!entmsg_ok) {
+ /*
+ * First, check to see if we have permission to enter a message in
+ * this room. The server will return an error code if we can't.
+ */
+ if (entmsg_ok == ENTMSG_OK_YES) {
+ /* no problem, go right ahead */
+ }
+ else if (entmsg_ok == ENTMSG_OK_BLOG) {
+ if (!is_reply) {
+ scr_printf("WARNING: this is a BLOG room.\n");
+ scr_printf("The '<E>nter Message' command will create a BLOG POST.\n");
+ scr_printf("If you want to leave a comment or reply to a comment, use the '<R>eply' command.\n");
+ scr_printf("Do you really want to create a new blog post? ");
+ if (!yesno()) {
+ return (1);
+ }
+ }
+ }
+ else {
scr_printf("You may not enter messages in this type of room.\n");
return (1);
}
if (c > 0) {
mode = 1;
- } else {
+ }
+ else {
mode = 0;
}
strcpy(subject, "");
- /*
- * First, check to see if we have permission to enter a message in
- * this room. The server will return an error code if we can't.
- */
strcpy(message.recipient, "");
strcpy(message.author, "");
strcpy(message.subject, "");
if (!IsEmptyStr(reply_subject)) {
if (!strncasecmp(reply_subject, "Re: ", 3)) {
strcpy(message.subject, reply_subject);
- } else {
+ }
+ else {
snprintf(message.subject, sizeof message.subject, "Re: %s", reply_subject);
}
}
if (axlevel >= AxProbU) {
if (is_reply) {
strcpy(buf, reply_to);
- } else {
+ }
+ else {
newprompt("Enter recipient: ", buf, SIZ - 100);
if (IsEmptyStr(buf)) {
return (1);
}
}
- } else
+ }
+ else {
strcpy(buf, "sysop");
+ }
}
strcpy(message.recipient, buf);
r = CtdlIPCGetMessages(ipc, LastMessages, 1, NULL, &msgarr, buf);
if (r / 100 != 1) {
scr_printf("%s\n", buf);
- } else {
+ }
+ else {
for (num_msgs = 0; msgarr[num_msgs]; num_msgs++);
}
r = CtdlIPCGetMessages(ipc, NewMessages, 0, NULL, &msgarr, buf);
if (r / 100 != 1) {
scr_printf("%s\n", buf);
- } else {
+ }
+ else {
for (num_msgs = 0; msgarr[num_msgs]; num_msgs++);
}
++b;
}
}
- if (msgarr)
+ if (msgarr) {
free(msgarr);
+ }
msgarr = NULL;
/* In the Mail> room, this algorithm always counts one message
}
if (b == 1) {
- scr_printf("*** 1 additional message has been entered " "in this room by another user.\n");
- } else if (b > 1) {
- scr_printf("*** %d additional messages have been entered " "in this room by other users.\n", b);
+ scr_printf("*** 1 additional message has been entered in this room by another user.\n");
+ }
+ else if (b > 1) {
+ scr_printf("*** %d additional messages have been entered in this room by other users.\n", b);
}
free(message.text);
/*
* Do editing on a quoted file
*/
-void process_quote(void)
-{
+void process_quote(void) {
FILE *qfile, *tfile;
char buf[128];
int line, qstart, qend;
- /* Unlink the second temp file as soon as it's opened, so it'll get
- * deleted even if the program dies
- */
+ // Unlink the second temp file as soon as it's opened, so it'll get
+ // deleted even if the program dies
qfile = fopen(temp2, "r");
unlink(temp2);
/*
* List the URLs which were embedded in the previous message
*/
-void list_urls(CtdlIPC * ipc)
-{
+void list_urls(CtdlIPC * ipc) {
int i;
char cmd[SIZ];
int rv;
scr_printf("%3d %s\n", i + 1, urls[i]);
}
- if ((i = num_urls) != 1)
+ if ((i = num_urls) != 1) {
i = intprompt("Display which one", 1, 1, num_urls);
+ }
snprintf(cmd, sizeof cmd, rc_url_cmd, urls[i - 1]);
rv = system(cmd);
/*
* Run image viewer in background
*/
-int do_image_view(const char *filename)
-{
+int do_image_view(const char *filename) {
char cmd[SIZ];
pid_t childpid;
/*
* View an image attached to a message
*/
-void image_view(CtdlIPC * ipc, unsigned long msg)
-{
+void image_view(CtdlIPC * ipc, unsigned long msg) {
struct parts *ptr = last_message_parts;
char part[SIZ];
int found = 0;
r = CtdlIPCAttachmentDownload(ipc, msg, ptr->number, &file, progress, buf);
if (r / 100 != 2) {
scr_printf("%s\n", buf);
- } else {
+ }
+ else {
size_t len;
len = (size_t) extract_long(buf, 0);
/*
* Read the messages in the current room
*/
-void readmsgs(CtdlIPC * ipc, enum MessageList c, /* see listing in citadel_ipc.h */
- enum MessageDirection rdir, /* 1=Forward (-1)=Reverse */
- int q /* Number of msgs to read (if c==3) */
- )
-{
+void readmsgs(CtdlIPC * ipc, enum MessageList c, // see listing in citadel_ipc.h
+ enum MessageDirection rdir, // 1=Forward (-1)=Reverse
+ int q // Number of msgs to read (if c==3)
+ ) {
int a, e, f, g, start;
int savedpos;
int hold_sw = 0;
r = CtdlIPCGetMessages(ipc, c, q, NULL, &msg_arr, cmd);
if (r / 100 != 1) {
scr_printf("%s\n", cmd);
- } else {
+ }
+ else {
for (num_msgs = 0; msg_arr[num_msgs]; num_msgs++);
}
&& (((room_flags & QR_MAILBOX) == 0)
|| (rc_force_mail_prompts == 0))) {
e = 'n';
- } else {
+ }
+ else {
color(DIM_WHITE);
scr_printf("(");
color(BRIGHT_WHITE);
do {
e = (inkey() & 127);
e = tolower(e);
+
/* return key same as <N> */ if (e == 10)
e = 'n';
+
/* space key same as <N> */ if (e == 32)
e = 'n';
+
/* del/move for aides only */
if ((!is_room_aide)
&& ((room_flags & QR_MAILBOX) == 0)
if ((e == 'd') || (e == 'm'))
e = 0;
}
+
/* print only if available */
if ((e == 'p') && (IsEmptyStr(printcmd)))
e = 0;
+
/* can't file if not allowed */
if ((e == 'f')
&& (rc_allow_attachments == 0))
e = 0;
+
/* link only if browser avail*/
if ((e == 'u')
&& (IsEmptyStr(rc_url_cmd)))
" N Next (continue with next message)\n"
" Y My Next (continue with next message you authored)\n"
" B Back (go back to previous message)\n");
- if ((is_room_aide)
- || (room_flags & QR_MAILBOX)
- || (room_flags2 & QR2_COLLABDEL)
- ) {
+ if ((is_room_aide) || (room_flags & QR_MAILBOX) || (room_flags2 & QR2_COLLABDEL)) {
scr_printf(" D Delete this message\n" " M Move message to another room\n");
}
scr_printf(" C Copy message to another room\n");
scr_printf("%s\n", cmd);
if (r / 100 == 2)
msg_arr[a] = 0L;
- } else {
+ }
+ else {
goto RMSGREAD;
}
if (r / 100 != 2) /* r will be init'ed, FIXME */
r = CtdlIPCAttachmentDownload(ipc, msg_arr[a], filename, &attachment, progress, cmd);
if (r / 100 != 2) {
scr_printf("%s\n", cmd);
- } else {
+ }
+ else {
extract_token(filename, cmd, 2, '|', sizeof filename);
/*
* Part 1 won't have a filename; use the
rv = system(cmd);
if (rv != 0)
scr_printf("failed to save %s Reason %d\n", cmd, rv);
- } else { /* save attachment to disk */
+ }
+ else { /* save attachment to disk */
destination_directory(save_to, filename);
save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to);
}
scr_printf("%s\n", cmd);
if (r / 100 == 2)
msg_arr[a] = 0L;
- } else {
+ }
+ else {
goto RMSGREAD;
}
break;
/*
* View and edit a system message
*/
-void edit_system_message(CtdlIPC * ipc, char *which_message)
-{
+void edit_system_message(CtdlIPC * ipc, char *which_message) {
char desc[SIZ];
char read_cmd[SIZ];
char write_cmd[SIZ];
/*
* Loads the contents of a file into memory. Caller must free the allocated memory.
*/
-char *load_message_from_file(FILE * src)
-{
+char *load_message_from_file(FILE * src) {
size_t i;
size_t got = 0;
char *dest = NULL;