X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmessages.c;h=bc5a6e5cfd4bd0c9ef44baced9fa9e74a8b21c62;hb=1bf28924fb95c17730a07c1a1547bca3de9afa20;hp=21af67c642123cd2c21b17805b3fa84778daa964;hpb=40e76149ab4b067ad21df60598355896c3fcc239;p=citadel.git diff --git a/citadel/messages.c b/citadel/messages.c index 21af67c64..bc5a6e5cf 100644 --- a/citadel/messages.c +++ b/citadel/messages.c @@ -1,7 +1,7 @@ /* * $Id$ * - * Citadel/UX message support routines + * Citadel message support routines * see copyright.txt for copyright information */ @@ -30,15 +30,18 @@ # endif #endif +#ifdef HAVE_PTHREAD_H +#include +#endif + #include +#include #include "citadel.h" #include "citadel_ipc.h" #include "citadel_decls.h" #include "messages.h" #include "commands.h" #include "rooms.h" -#include "tools.h" -#include "html.h" #ifndef HAVE_SNPRINTF #include "snprintf.h" #endif @@ -49,26 +52,28 @@ char reply_to[SIZ]; char reply_subject[SIZ]; +char reply_references[SIZ]; struct cittext { struct cittext *next; char text[MAXWORDBUF]; }; -void sttybbs(int cmd); +void stty_ctdl(int cmd); int haschar(const char *st, int ch); -void getline(char *string, int lim); +void ctdl_getline(char *string, int lim); int file_checksum(char *filename); -void progress(unsigned long curr, unsigned long cmax); +void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax); unsigned long *msg_arr = NULL; int msg_arr_size = 0; int num_msgs; char rc_alt_semantics; extern char room_name[]; +extern char tempdir[]; extern unsigned room_flags; +extern unsigned room_flags2; extern long highest_msg_read; -extern struct CtdlServInfo serv_info; extern char temp[]; extern char temp2[]; extern int screenwidth; @@ -81,13 +86,19 @@ extern char fullname[]; extern char axlevel; extern unsigned userflags; extern char sigcaught; -extern char editor_path[]; extern char printcmd[]; extern int rc_allow_attachments; extern int rc_display_message_numbers; extern int rc_force_mail_prompts; extern int editor_pid; extern CtdlIPC *ipc_for_signal_handlers; /* KLUDGE cover your eyes */ +int num_urls = 0; +char urls[MAXURLS][SIZ]; +char imagecmd[SIZ]; +int has_images = 0; /* Current msg has images */ +struct parts *last_message_parts = NULL; /* Parts from last msg */ + + void ka_sigcatch(int signum) { @@ -251,12 +262,10 @@ void citedit(CtdlIPC *ipc, FILE * fp) if (a == 127) a = 8; - /******* new ***********/ - if ((a > 32) && (a < 127) && (prev == 13)) { + if ((a != 32) && (prev == 13)) { add_word(textlist, "\n"); scr_printf(" "); } - /***********************/ if ((a == 32) && (prev == 13)) { add_word(textlist, "\n"); @@ -264,7 +273,7 @@ void citedit(CtdlIPC *ipc, FILE * fp) } if (a == 8) { - if (strlen(wordbuf) > 0) { + if (!IsEmptyStr(wordbuf)) { wordbuf[strlen(wordbuf) - 1] = 0; scr_putc(8); scr_putc(32); @@ -276,10 +285,10 @@ void citedit(CtdlIPC *ipc, FILE * fp) scr_putc(8); scr_putc(32); scr_putc(8); - } while (strlen(wordbuf) && wordbuf[strlen(wordbuf) - 1] != ' '); + } while (!IsEmptyStr(wordbuf) && wordbuf[strlen(wordbuf) - 1] != ' '); } else if (a == 13) { scr_printf("\n"); - if (strlen(wordbuf) == 0) + if (IsEmptyStr(wordbuf)) finished = 1; else { for (b = 0; b < strlen(wordbuf); ++b) @@ -347,7 +356,26 @@ void citedit(CtdlIPC *ipc, FILE * fp) } } -/* Read a message from the server + +/* + * Free the struct parts + */ +void free_parts(struct parts *p) +{ + struct parts *a_part = p; + + while (a_part) { + struct parts *q; + + q = a_part; + a_part = a_part->next; + free(q); + } +} + + +/* + * Read a message from the server */ int read_message(CtdlIPC *ipc, long num, /* message number */ @@ -362,22 +390,32 @@ int read_message(CtdlIPC *ipc, struct ctdlipcmessage *message = NULL; int r; /* IPC response code */ char *converted_text = NULL; + char *lineptr; + char *nextline; + char *searchptr; + int i; + char ch; + int linelen; + int final_line_is_blank = 0; + + has_images = 0; sigcaught = 0; - sttybbs(1); + stty_ctdl(1); strcpy(reply_to, NO_REPLY_TO); strcpy(reply_subject, ""); + strcpy(reply_references, ""); - r = CtdlIPCGetSingleMessage(ipc, num, (pagin == READ_HEADER ? 1 : 0), - (can_do_msg4 ? 4 : 0), - &message, buf); + r = CtdlIPCGetSingleMessage(ipc, num, (pagin == READ_HEADER ? 1 : 0), 4, &message, buf); if (r / 100 != 1) { err_printf("*** msg #%ld: %d %s\n", num, r, buf); ++lines_printed; - lines_printed = - checkpagin(lines_printed, pagin, screenheight); - sttybbs(0); + lines_printed = checkpagin(lines_printed, pagin, screenheight); + stty_ctdl(0); + free(message->text); + free_parts(message->attachments); + free(message); return (0); } @@ -400,17 +438,17 @@ int read_message(CtdlIPC *ipc, message->nhdr ? "yes" : "no", message->author, message->type, message->msgid); - if (strlen(message->subject)) { + if (!IsEmptyStr(message->subject)) { pprintf("subj=%s\n", message->subject); } - if (strlen(message->email)) { + if (!IsEmptyStr(message->email)) { pprintf("rfca=%s\n", message->email); } pprintf("hnod=%s\nroom=%s\nnode=%s\ntime=%s", message->hnod, message->room, message->node, asctime(localtime(&message->time))); - if (strlen(message->recipient)) { + if (!IsEmptyStr(message->recipient)) { pprintf("rcpt=%s\n", message->recipient); } if (message->attachments) { @@ -424,7 +462,10 @@ int read_message(CtdlIPC *ipc, } } pprintf("\n"); - sttybbs(0); + stty_ctdl(0); + free(message->text); + free_parts(message->attachments); + free(message); return (0); } @@ -450,7 +491,7 @@ int read_message(CtdlIPC *ipc, fmt_date(now, sizeof now, message->time, 0); if (dest) { fprintf(dest, "%s from %s ", now, message->author); - if (strlen(message->email)) { + if (!IsEmptyStr(message->email)) { fprintf(dest, "<%s> ", message->email); } } else { @@ -460,7 +501,7 @@ int read_message(CtdlIPC *ipc, scr_printf("from "); color(BRIGHT_CYAN); scr_printf("%s ", message->author); - if (strlen(message->email)) { + if (!IsEmptyStr(message->email)) { color(DIM_WHITE); scr_printf("<"); color(BRIGHT_BLUE); @@ -469,11 +510,11 @@ int read_message(CtdlIPC *ipc, scr_printf("> "); } } - if (strlen(message->node)) { + if (!IsEmptyStr(message->node)) { if ((room_flags & QR_NETWORK) - || ((strcasecmp(message->node, serv_info.serv_nodename) - && (strcasecmp(message->node, serv_info.serv_fqdn))))) { - if (strlen(message->email) == 0) { + || ((strcasecmp(message->node, ipc->ServInfo.nodename) + && (strcasecmp(message->node, ipc->ServInfo.fqdn))))) { + if (IsEmptyStr(message->email)) { if (dest) { fprintf(dest, "@%s ", message->node); } else { @@ -485,8 +526,8 @@ int read_message(CtdlIPC *ipc, } } } - if (strcasecmp(message->hnod, serv_info.serv_humannode) - && (strlen(message->hnod)) && (!strlen(message->email))) { + if (strcasecmp(message->hnod, ipc->ServInfo.humannode) + && (!IsEmptyStr(message->hnod)) && (IsEmptyStr(message->email))) { if (dest) { fprintf(dest, "(%s) ", message->hnod); } else { @@ -498,7 +539,7 @@ int read_message(CtdlIPC *ipc, scr_printf(") "); } } - if (strcasecmp(message->room, room_name) && (strlen(message->email) == 0)) { + if (strcasecmp(message->room, room_name) && (IsEmptyStr(message->email))) { if (dest) { fprintf(dest, "in %s> ", message->room); } else { @@ -508,7 +549,7 @@ int read_message(CtdlIPC *ipc, scr_printf("%s> ", message->room); } } - if (strlen(message->recipient)) { + if (!IsEmptyStr(message->recipient)) { if (dest) { fprintf(dest, "to %s ", message->recipient); } else { @@ -528,8 +569,13 @@ int read_message(CtdlIPC *ipc, /* Set the reply-to address to an Internet e-mail address if possible */ - if (message->email != NULL) if (strlen(message->email) > 0) { - safestrncpy(reply_to, message->email, sizeof reply_to); + if (message->email != NULL) if (!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); + } } /* But if we can't do that, set it to a Citadel address. @@ -544,16 +590,29 @@ int read_message(CtdlIPC *ipc, lines_printed = checkpagin(lines_printed, pagin, screenheight); } + + /* Always do msgid before references ... the latter is a concatenation! */ + if (message->msgid != NULL) { + safestrncpy(reply_references, message->msgid, sizeof reply_references); + } + + if (message->references != NULL) if (!IsEmptyStr(message->references)) { + int l = strlen(reply_references); + strcpy(&reply_references[l++], "|"); + safestrncpy(&reply_references[l], message->references, (sizeof(reply_references) - l)); + } + if (message->subject != NULL) { - safestrncpy(reply_subject, message->subject, - sizeof reply_subject); - if (strlen(message->subject) > 0) { + safestrncpy(reply_subject, message->subject, sizeof reply_subject); + if (!IsEmptyStr(message->subject)) { if (dest) { fprintf(dest, "Subject: %s\n", message->subject); } else { - color(BRIGHT_MAGENTA); - scr_printf("Subject: %s\n", message->subject); + color(DIM_WHITE); + scr_printf("Subject: "); + color(BRIGHT_CYAN); + scr_printf("%s\n", message->subject); ++lines_printed; lines_printed = checkpagin(lines_printed, pagin, screenheight); @@ -572,7 +631,7 @@ int read_message(CtdlIPC *ipc, * of the client screen. */ if (!strcasecmp(message->content_type, "text/html")) { - converted_text = html_to_ascii(message->text, screenwidth, 0); + converted_text = html_to_ascii(message->text, 0, screenwidth, 0); if (converted_text != NULL) { free(message->text); message->text = converted_text; @@ -585,6 +644,26 @@ int read_message(CtdlIPC *ipc, format_type = 1; } + /* Extract URL's */ + num_urls = 0; /* Start with a clean slate */ + searchptr = message->text; + while ( (searchptr != NULL) && (num_urls < MAXURLS) ) { + searchptr = strstr(searchptr, "http://"); + if (searchptr != NULL) { + safestrncpy(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') { + urls[num_urls][i] = 0; + break; + } + } + num_urls++; + ++searchptr; + } + } + /* * Here we go */ @@ -592,67 +671,92 @@ int read_message(CtdlIPC *ipc, fr = fmout(screenwidth, NULL, message->text, dest, ((pagin == 1) ? 1 : 0), screenheight, (-1), 1); } else { - /* FIXME: renderer for text/plain */ - char *msgtext; - char *lineptr; + /* renderer for text/plain */ - msgtext = message->text; + lineptr = message->text; - while (lineptr = strtok(msgtext, "\n"), lineptr != NULL) { - msgtext = NULL; + do { + nextline = strchr(lineptr, '\n'); + if (nextline != NULL) { + *nextline = 0; + ++nextline; + if (*nextline == 0) nextline = NULL; + } if (sigcaught == 0) { + linelen = strlen(lineptr); + if (linelen && (lineptr[linelen-1] == '\r')) { + lineptr[--linelen] = 0; + } if (dest) { fprintf(dest, "%s\n", lineptr); } else { scr_printf("%s\n", lineptr); lines_printed = lines_printed + 1 + - (strlen(lineptr) / screenwidth); + (linelen / screenwidth); lines_printed = checkpagin(lines_printed, pagin, screenheight); } } - } + if (lineptr[0] == 0) final_line_is_blank = 1; + else final_line_is_blank = 0; + lineptr = nextline; + } while (nextline); fr = sigcaught; } - if (dest) { - fprintf(dest, "\n"); - } else { - scr_printf("\n"); - /* scr_flush(); */ - ++lines_printed; - lines_printed = checkpagin(lines_printed, pagin, screenheight); + if (!final_line_is_blank) { + if (dest) { + fprintf(dest, "\n"); + } + else { + scr_printf("\n"); + ++lines_printed; + lines_printed = checkpagin(lines_printed, pagin, screenheight); + fr = sigcaught; + } } /* Enumerate any attachments */ - if ( (pagin == 1) && (can_do_msg4) && (message->attachments) ) { + if ( (pagin == 1) && (message->attachments) ) { struct parts *ptr; for (ptr = message->attachments; ptr; ptr = ptr->next) { if ( (!strcasecmp(ptr->disposition, "attachment")) - || (!strcasecmp(ptr->disposition, "inline"))) { - color(DIM_WHITE); - scr_printf("Part "); - color(BRIGHT_MAGENTA); - scr_printf("%s", ptr->number); - color(DIM_WHITE); - scr_printf(": "); - color(BRIGHT_CYAN); - scr_printf("%s", ptr->filename); - color(DIM_WHITE); - scr_printf(" (%s, %ld bytes)\n", ptr->mimetype, ptr->length); + || (!strcasecmp(ptr->disposition, "inline")) + || (!strcasecmp(ptr->disposition, "")) + ) { + if ( (strcasecmp(ptr->number, message->mime_chosen)) + && (!IsEmptyStr(ptr->mimetype)) + ) { + color(DIM_WHITE); + pprintf("Part "); + color(BRIGHT_MAGENTA); + pprintf("%s", ptr->number); + color(DIM_WHITE); + pprintf(": "); + color(BRIGHT_CYAN); + pprintf("%s", ptr->filename); + color(DIM_WHITE); + pprintf(" (%s, %ld bytes)\n", ptr->mimetype, ptr->length); + if (!strncmp(ptr->mimetype, "image/", 6)) { + has_images++; + } + } } } } + /* Save the attachments info for later */ + last_message_parts = message->attachments; + /* Now we're done */ free(message->text); free(message); if (pagin == 1 && !dest) color(DIM_WHITE); - sttybbs(0); + stty_ctdl(0); return (fr); } @@ -672,12 +776,12 @@ void replace_string(char *filename, long int startpos) long msglen = 0L; scr_printf("Enter text to be replaced:\n: "); - getline(srch_str, (sizeof(srch_str)-1) ); - if (strlen(srch_str) == 0) + ctdl_getline(srch_str, (sizeof(srch_str)-1) ); + if (IsEmptyStr(srch_str)) return; scr_printf("Enter text to replace it with:\n: "); - getline(rplc_str, (sizeof(rplc_str)-1) ); + ctdl_getline(rplc_str, (sizeof(rplc_str)-1) ); fp = fopen(filename, "r+"); if (fp == NULL) @@ -707,7 +811,7 @@ void replace_string(char *filename, long int startpos) } } fseek(fp, wpos, 0); - if (strlen(buf) > 0) + if (!IsEmptyStr(buf)) fwrite((char *) buf, strlen(buf), 1, fp); wpos = ftell(fp); fclose(fp); @@ -719,26 +823,34 @@ void replace_string(char *filename, long int startpos) * 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 anon_type, /* see MES_ types in header file */ - int format_type, - int mode, - char *subject) /* buffer to store subject line */ + 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; char datestr[SIZ]; char header[SIZ]; + char *editor_path = NULL; int cksum = 0; - if (mode == 2) - if (strlen(editor_path) == 0) { - err_printf - ("*** No editor available, using built-in editor\n"); + if (mode >= 2) + { + if((mode-2) < MAX_EDITORS && !IsEmptyStr(editor_paths[mode-2])) { + editor_path = editor_paths[mode-2]; + } else if (!IsEmptyStr(editor_paths[0])) { + editor_path = editor_paths[0]; + } else { + err_printf("*** No editor available, " + "using built-in editor\n"); mode = 0; } + } fmt_date(datestr, sizeof datestr, time(NULL), 0); header[0] = 0; @@ -748,17 +860,24 @@ int client_make_message(CtdlIPC *ipc, } else { snprintf(header, sizeof header, - " %s from %s", datestr, fullname); - if (strlen(recipient) > 0) { + " %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); } } scr_printf("%s\n", header); - if (subject != NULL) if (strlen(subject) > 0) { + if (subject != NULL) if (!IsEmptyStr(subject)) { scr_printf("Subject: %s\n", subject); } + + if ( (subject_required) && (IsEmptyStr(subject)) ) { + newprompt("Subject: ", subject, 70); + } beg = 0L; @@ -769,7 +888,8 @@ int client_make_message(CtdlIPC *ipc, if (mode == 0) { fp = fopen(filename, "r"); if (fp != NULL) { - fmout(screenwidth, fp, NULL, NULL, 0, screenheight, 0, 0); + fmout(screenwidth, fp, NULL, NULL, 0, + screenheight, 0, 0); beg = ftell(fp); fclose(fp); } else { @@ -823,11 +943,12 @@ ME1: switch (mode) { break; case 2: + default: /* allow 2+ modes */ e_ex_code = 1; /* start with a failed exit code */ + screen_reset(); + stty_ctdl(SB_RESTORE); editor_pid = fork(); cksum = file_checksum(filename); - screen_reset(); - sttybbs(SB_RESTORE); if (editor_pid == 0) { char tmp[SIZ]; @@ -843,18 +964,19 @@ ME1: switch (mode) { b = ka_wait(&e_ex_code); } while ((b != editor_pid) && (b >= 0)); editor_pid = (-1); - sttybbs(0); + stty_ctdl(0); screen_set(); break; } -MECR: if (mode == 2) { +MECR: if (mode >= 2) { if (file_checksum(filename) == cksum) { err_printf("*** Aborted message.\n"); e_ex_code = 1; } - if (e_ex_code == 0) + if (e_ex_code == 0) { goto MEFIN; + } goto MEABT2; } @@ -863,18 +985,16 @@ MECR: if (mode == 2) { "add sbject|" "eplace string|old message"); - if (b == 'a') - goto MEABT; - if (b == 'c') - goto ME1; - if (b == 's') - goto MEFIN; + if (b == 'a') goto MEABT; + if (b == 'c') goto ME1; + if (b == 's') goto MEFIN; if (b == 'p') { scr_printf(" %s from %s", datestr, fullname); - if (strlen(recipient) > 0) + if (!IsEmptyStr(recipient)) { scr_printf(" to %s", recipient); + } scr_printf("\n"); - if (subject != NULL) if (strlen(subject) > 0) { + if (subject != NULL) if (!IsEmptyStr(subject)) { scr_printf("Subject: %s\n", subject); } fp = fopen(filename, "r"); @@ -911,61 +1031,6 @@ MEABT2: unlink(filename); return (2); } -/* - * Transmit message text to the server. - * - * This loop also implements a "tick" counter that displays the progress, if - * we're sending something that will take a long time to transmit. - */ -void transmit_message(CtdlIPC *ipc, FILE *fp) -{ - char buf[SIZ]; - int ch, a; - long msglen; - time_t lasttick; - - fseek(fp, 0L, SEEK_END); - msglen = ftell(fp); - rewind(fp); - lasttick = time(NULL); - strcpy(buf, ""); - while (ch = getc(fp), (ch >= 0)) { - if (ch == 10) { - if (!strcmp(buf, "000")) - strcpy(buf, ">000"); - CtdlIPC_putline(ipc, buf); - strcpy(buf, ""); - } else { - a = strlen(buf); - buf[a + 1] = 0; - buf[a] = ch; - if ((ch == 32) && (strlen(buf) > 200)) { - buf[a] = 0; - if (!strcmp(buf, "000")) - strcpy(buf, ">000"); - CtdlIPC_putline(ipc, buf); - strcpy(buf, ""); - } - if (strlen(buf) > 250) { - if (!strcmp(buf, "000")) - strcpy(buf, ">000"); - CtdlIPC_putline(ipc, buf); - strcpy(buf, ""); - } - } - - if ((time(NULL) - lasttick) > 2L) { - scr_printf(" %3ld%% completed\r", - ((ftell(fp) * 100L) / msglen)); - scr_flush(); - lasttick = time(NULL); - } - - } - CtdlIPC_putline(ipc, buf); - scr_printf(" \r"); - scr_flush(); -} /* * Make sure there's room in msg_arr[] for at least one more. @@ -979,6 +1044,29 @@ void check_msg_arr_size(void) { } +/* + * break_big_lines() - break up lines that are >1024 characters + * otherwise the server will truncate + */ +void break_big_lines(char *msg) { + char *ptr; + char *break_here; + + if (msg == NULL) { + return; + } + + ptr = msg; + while (strlen(ptr) > 1000) { + break_here = strchr(&ptr[900], ' '); + if ((break_here == NULL) || (break_here > &ptr[999])) { + break_here = &ptr[999]; + } + *break_here = '\n'; + ptr = break_here++; + } +} + /* * entmsg() - edit and create a message @@ -986,16 +1074,20 @@ void check_msg_arr_size(void) { */ int entmsg(CtdlIPC *ipc, int is_reply, /* nonzero if this was a eply command */ - int c) /* */ -{ - char buf[300]; - char cmd[SIZ]; + int c, /* mode */ + int masquerade /* prompt for a non-default display name? */ +) { + char buf[SIZ]; int a, b; int need_recp = 0; int mode; long highmsg = 0L; FILE *fp; char subject[SIZ]; + struct ctdlipcmessage message; + unsigned long *msgarr = NULL; + int r; /* IPC response code */ + int subject_required = 0; if (c > 0) mode = 1; @@ -1008,12 +1100,22 @@ int entmsg(CtdlIPC *ipc, * 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. */ - snprintf(cmd, sizeof cmd, "ENT0 0||0|%d", mode); - CtdlIPC_putline(ipc, cmd); - CtdlIPC_getline(ipc, cmd); + strcpy(message.recipient, ""); + strcpy(message.author, ""); + strcpy(message.subject, ""); + strcpy(message.references, ""); + message.text = ""; /* point to "", changes later */ + message.anonymous = 0; + message.type = mode; + + if (masquerade) { + newprompt("Display name for this message: ", message.author, 40); + } + + r = CtdlIPCPostMessage(ipc, 0, &subject_required, &message, buf); - if ((strncmp(cmd, "570", 3)) && (strncmp(cmd, "200", 3))) { - scr_printf("%s\n", &cmd[4]); + if (r / 100 != 2 && r / 10 != 57) { + scr_printf("%s\n", buf); return (1); } @@ -1021,8 +1123,9 @@ int entmsg(CtdlIPC *ipc, * in this room, but a recipient needs to be specified. */ need_recp = 0; - if (!strncmp(cmd, "570", 3)) + if (r / 10 == 57) { need_recp = 1; + } /* If the user is a dumbass, tell them how to type. */ if ((userflags & US_EXPERT) == 0) { @@ -1037,43 +1140,44 @@ int entmsg(CtdlIPC *ipc, strcpy(buf, reply_to); } else { scr_printf("Enter recipient: "); - getline(buf, (SIZ-100) ); - if (strlen(buf) == 0) + ctdl_getline(buf, (SIZ-100) ); + if (IsEmptyStr(buf)) return (1); } } else strcpy(buf, "sysop"); } + strcpy(message.recipient, buf); if (is_reply) { - if (strlen(reply_subject) > 0) { + + if (!IsEmptyStr(reply_subject)) { if (!strncasecmp(reply_subject, "Re: ", 3)) { - strcpy(subject, reply_subject); + strcpy(message.subject, reply_subject); } else { - snprintf(subject, - sizeof subject, + snprintf(message.subject, + sizeof message.subject, "Re: %s", reply_subject); } } + + safestrncpy(message.references, reply_references, sizeof message.references); } - b = 0; if (room_flags & QR_ANONOPT) { scr_printf("Anonymous (Y/N)? "); if (yesno() == 1) - b = 1; + message.anonymous = 1; } /* If it's mail, we've got to check the validity of the recipient... */ - if (strlen(buf) > 0) { - snprintf(cmd, sizeof cmd, "ENT0 0|%s|%d|%d|%s", buf, b, mode, subject); - CtdlIPC_putline(ipc, cmd); - CtdlIPC_getline(ipc, cmd); - if (cmd[0] != '2') { - scr_printf("%s\n", &cmd[4]); + if (!IsEmptyStr(message.recipient)) { + r = CtdlIPCPostMessage(ipc, 0, &subject_required, &message, buf); + if (r / 100 != 2) { + scr_printf("%s\n", buf); return (1); } } @@ -1082,72 +1186,73 @@ int entmsg(CtdlIPC *ipc, * tell upon saving whether someone else has posted too. */ num_msgs = 0; - CtdlIPC_putline(ipc, "MSGS LAST|1"); - CtdlIPC_getline(ipc, cmd); - if (cmd[0] != '1') { - scr_printf("%s\n", &cmd[5]); + r = CtdlIPCGetMessages(ipc, LastMessages, 1, NULL, &msgarr, buf); + if (r / 100 != 1) { + scr_printf("%s\n", buf); } else { - while (CtdlIPC_getline(ipc, cmd), strcmp(cmd, "000")) { - check_msg_arr_size(); - msg_arr[num_msgs++] = atol(cmd); - } + for (num_msgs = 0; msgarr[num_msgs]; num_msgs++) + ; } /* Now compose the message... */ - if (client_make_message(ipc, temp, buf, b, 0, c, subject) != 0) { + if (client_make_message(ipc, temp, message.recipient, + message.anonymous, 0, c, message.subject, subject_required) != 0) { + if (msgarr) free(msgarr); return (2); } /* Reopen the temp file that was created, so we can send it */ fp = fopen(temp, "r"); - /* Yes, unlink it now, so it doesn't stick around if we crash */ - unlink(temp); - - if (fp == NULL) { + if (!fp || !(message.text = load_message_from_file(fp))) { err_printf("*** Internal error while trying to save message!\n" - " %s: %s\n", + "%s: %s\n", temp, strerror(errno)); + unlink(temp); return(errno); } + if (fp) fclose(fp); + + /* Break lines that are >1024 characters, otherwise the server + * will truncate them. + */ + break_big_lines(message.text); + /* Transmit message to the server */ - snprintf(cmd, sizeof cmd, "ENT0 1|%s|%d|%d|%s|", buf, b, mode, subject); - CtdlIPC_putline(ipc, cmd); - CtdlIPC_getline(ipc, cmd); - if (cmd[0] != '4') { - scr_printf("%s\n", &cmd[4]); + r = CtdlIPCPostMessage(ipc, 1, NULL, &message, buf); + if (r / 100 != 4) { + scr_printf("%s\n", buf); return (1); } - transmit_message(ipc, fp); - CtdlIPC_putline(ipc, "000"); + /* Yes, unlink it now, so it doesn't stick around if we crash */ + unlink(temp); - fclose(fp); + if (num_msgs >= 1) highmsg = msgarr[num_msgs - 1]; - if (num_msgs >= 1) highmsg = msg_arr[num_msgs - 1]; - num_msgs = 0; - CtdlIPC_putline(ipc, "MSGS NEW"); - CtdlIPC_getline(ipc, cmd); - if (cmd[0] != '1') { - scr_printf("%s\n", &cmd[5]); + if (msgarr) free(msgarr); + msgarr = NULL; + r = CtdlIPCGetMessages(ipc, NewMessages, 0, NULL, &msgarr, buf); + if (r / 100 != 1) { + scr_printf("%s\n", buf); } else { - while (CtdlIPC_getline(ipc, cmd), strcmp(cmd, "000")) { - check_msg_arr_size(); - msg_arr[num_msgs++] = atol(cmd); - } + for (num_msgs = 0; msgarr[num_msgs]; num_msgs++) + ; } /* get new highest message number in room to set lrp for goto... */ - maxmsgnum = msg_arr[num_msgs - 1]; + maxmsgnum = msgarr[num_msgs - 1]; /* now see if anyone else has posted in here */ b = (-1); for (a = 0; a < num_msgs; ++a) { - if (msg_arr[a] > highmsg) { + if (msgarr[a] > highmsg) { ++b; } } + if (msgarr) free(msgarr); + msgarr = NULL; /* In the Mail> room, this algorithm always counts one message * higher than in public rooms, so we decrement it by one. @@ -1164,6 +1269,7 @@ int entmsg(CtdlIPC *ipc, scr_printf("*** %d additional messages have been entered " "in this room by other users.\n", b); } + free(message.text); return(0); } @@ -1187,13 +1293,13 @@ void process_quote(void) line = 0; fgets(buf, 128, qfile); while (fgets(buf, 128, qfile) != NULL) { - scr_printf("%2d %s", ++line, buf); + scr_printf("%3d %s", ++line, buf); } - scr_printf("Begin quoting at [ 1] : "); - getline(buf, 3); + scr_printf("Begin quoting at [1] : "); + ctdl_getline(buf, 4); qstart = (buf[0] == 0) ? (1) : atoi(buf); scr_printf(" End quoting at [%d] : ", line); - getline(buf, 3); + ctdl_getline(buf, 4); qend = (buf[0] == 0) ? (line) : atoi(buf); rewind(qfile); line = 0; @@ -1236,12 +1342,136 @@ void list_urls(CtdlIPC *ipc) scr_printf("\n"); } + +/* + * Run image viewer in background + */ +int do_image_view(const char *filename) +{ + char cmd[SIZ]; + pid_t childpid; + + snprintf(cmd, sizeof cmd, imagecmd, filename); + childpid = fork(); + if (childpid < 0) { + unlink(filename); + return childpid; + } + + if (childpid == 0) { + int retcode; + pid_t grandchildpid; + + grandchildpid = fork(); + if (grandchildpid < 0) { + return grandchildpid; + } + + if (grandchildpid == 0) { + int nullfd; + int outfd = -1; + int errfd = -1; + + nullfd = open("/dev/null", O_WRONLY); + if (nullfd > -1) { + dup2(1, outfd); + dup2(2, errfd); + dup2(nullfd, 1); + dup2(nullfd, 2); + } + retcode = system(cmd); + if (nullfd > -1) { + dup2(outfd, 1); + dup2(errfd, 2); + close(nullfd); + } + unlink(filename); + exit(retcode); + } + + if (grandchildpid > 0) { + exit(0); + } + } + + if (childpid > 0) { + int retcode; + + waitpid(childpid, &retcode, 0); + return retcode; + } + + return -1; +} + + +/* + * View an image attached to a message + */ +void image_view(CtdlIPC *ipc, unsigned long msg) +{ + struct parts *ptr = last_message_parts; + char part[SIZ]; + int found = 0; + + /* Run through available parts */ + for (ptr = last_message_parts; ptr; ptr = ptr->next) { + if ((!strcasecmp(ptr->disposition, "attachment") + || !strcasecmp(ptr->disposition, "inline")) + && !strncmp(ptr->mimetype, "image/", 6)) { + found++; + if (found == 1) { + strcpy(part, ptr->number); + } + } + } + + while (found > 0) { + if (found > 1) + strprompt("View which part (0 when done)", part, SIZ-1); + found = -found; + for (ptr = last_message_parts; ptr; ptr = ptr->next) { + if ((!strcasecmp(ptr->disposition, "attachment") + || !strcasecmp(ptr->disposition, "inline")) + && !strncmp(ptr->mimetype, "image/", 6) + && !strcasecmp(ptr->number, part)) { + char tmp[PATH_MAX]; + char buf[SIZ]; + void *file = NULL; /* The downloaded file */ + int r; + + /* view image */ + found = -found; + r = CtdlIPCAttachmentDownload(ipc, msg, ptr->number, &file, progress, buf); + if (r / 100 != 2) { + scr_printf("%s\n", buf); + } else { + size_t len; + + len = (size_t)extract_long(buf, 0); + progress(ipc, len, len); + scr_flush(); + CtdlMakeTempFileName(tmp, sizeof tmp); + strcat(tmp, ptr->filename); + save_buffer(file, len, tmp); + free(file); + do_image_view(tmp); + } + break; + } + } + if (found == 1) + break; + } +} + + /* * Read the messages in the current room */ void readmsgs(CtdlIPC *ipc, - int c, /* 0=Read all 1=Read new 2=Read old 3=Read last q */ - int rdir, /* 1=Forward (-1)=Reverse */ + 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, b, e, f, g, start; @@ -1257,48 +1487,34 @@ void readmsgs(CtdlIPC *ipc, char filename[PATH_MAX]; char save_to[PATH_MAX]; void *attachment = NULL; /* Downloaded attachment */ - FILE *dest = NULL; /* Alternate destination other than screen */ + FILE *dest = NULL; /* Alternate destination other than screen */ int r; /* IPC response code */ + static int att_seq = 0; /* Attachment download sequence number */ if (c < 0) b = (num_msgs - 1); else b = 0; - strcpy(prtfile, tmpnam(NULL)); + CtdlMakeTempFileName(prtfile, sizeof prtfile); - num_msgs = 0; - strcpy(cmd, "MSGS "); - switch (c) { - case 0: - strcat(cmd, "ALL"); - break; - case 1: - strcat(cmd, "NEW"); - break; - case 2: - strcat(cmd, "OLD"); - break; - case 3: - snprintf(&cmd[5], sizeof cmd - 5, "LAST|%d", q); - break; + if (msg_arr) { + free(msg_arr); + msg_arr = NULL; } - CtdlIPC_putline(ipc, cmd); - CtdlIPC_getline(ipc, cmd); - if (cmd[0] != '1') { - scr_printf("%s\n", &cmd[5]); + r = CtdlIPCGetMessages(ipc, c, q, NULL, &msg_arr, cmd); + if (r / 100 != 1) { + scr_printf("%s\n", cmd); } else { - while (CtdlIPC_getline(ipc, cmd), strcmp(cmd, "000")) { - check_msg_arr_size(); - msg_arr[num_msgs++] = atol(cmd); - } + for (num_msgs = 0; msg_arr[num_msgs]; num_msgs++) + ; } - if (num_msgs == 0) { - if (c == 3) return; + if (num_msgs == 0) { /* TODO look at this later */ + if (c == LastMessages) return; scr_printf("*** There are no "); - if (c == 1) scr_printf("new "); - if (c == 2) scr_printf("old "); + if (c == NewMessages) scr_printf("new "); + if (c == OldMessages) scr_printf("old "); scr_printf("messages in this room.\n"); return; } @@ -1330,6 +1546,10 @@ RAGAIN: pagin = ((arcflag == 0) screenwidth = 80; } + /* clear parts list */ + free_parts(last_message_parts); + last_message_parts = NULL; + /* now read the message... */ e = read_message(ipc, msg_arr[a], pagin, dest); @@ -1355,9 +1575,9 @@ RMSGREAD: scr_flush(); if (f == 0) { freopen(prtfile, "r", stdin); screen_reset(); - sttybbs(SB_RESTORE); + stty_ctdl(SB_RESTORE); ka_system(printcmd); - sttybbs(SB_NO_INTR); + stty_ctdl(SB_NO_INTR); screen_set(); unlink(prtfile); exit(0); @@ -1373,10 +1593,10 @@ RMSGREAD: scr_flush(); r = CtdlIPCSetMessageSeen(ipc, msg_arr[a], 1, buf); } - if (e == 3) + if (e == SIGQUIT) return; - if (((userflags & US_NOPROMPT) || (e == 2)) - && (((room_flags & QR_MAILBOX) == 0) + if (((userflags & US_NOPROMPT) || (e == SIGINT)) + && (((room_flags & QR_MAILBOX) == 0) || (rc_force_mail_prompts == 0))) { e = 'n'; } else { @@ -1387,9 +1607,11 @@ RMSGREAD: scr_flush(); color(DIM_WHITE); scr_printf(") "); - keyopt("ack gain uote eply ext top m next "); + keyopt("ack gain uote eply ext top "); if (rc_url_cmd[0] && num_urls) keyopt("RLview "); + if (has_images > 0 && !IsEmptyStr(imagecmd)) + keyopt("mages "); keyopt("help -> "); do { @@ -1401,14 +1623,15 @@ RMSGREAD: scr_flush(); /* space key same as */ if (e == 32) e = 'n'; /* del/move for aides only */ - if ((!is_room_aide) - && ((room_flags & QR_MAILBOX) == - 0)) { + if ( (!is_room_aide) + && ((room_flags & QR_MAILBOX) == 0) + && ((room_flags2 & QR2_COLLABDEL) == 0) + ) { if ((e == 'd') || (e == 'm')) e = 0; } /* print only if available */ - if ((e == 'p') && (strlen(printcmd) == 0)) + if ((e == 'p') && (IsEmptyStr(printcmd))) e = 0; /* can't file if not allowed */ if ((e == 'f') @@ -1416,13 +1639,17 @@ RMSGREAD: scr_flush(); e = 0; /* link only if browser avail*/ if ((e == 'u') - && (strlen(rc_url_cmd) == 0)) + && (IsEmptyStr(rc_url_cmd))) + e = 0; + if ((e == 'i') + && (IsEmptyStr(imagecmd) || !has_images)) e = 0; } while ((e != 'a') && (e != 'n') && (e != 's') && (e != 'd') && (e != 'm') && (e != 'p') && (e != 'q') && (e != 'b') && (e != 'h') && (e != 'r') && (e != 'f') && (e != '?') - && (e != 'u') && (e != 'c') && (e != 'y')); + && (e != 'u') && (e != 'c') && (e != 'y') + && (e != 'i') && (e != 'o') ); switch (e) { case 's': scr_printf("Stop"); @@ -1457,6 +1684,9 @@ RMSGREAD: scr_flush(); case 'r': scr_printf("Reply"); break; + case 'o': + scr_printf("Open attachments"); + break; case 'f': scr_printf("File"); break; @@ -1466,11 +1696,13 @@ RMSGREAD: scr_flush(); case 'y': scr_printf("mY next"); break; + case 'i': + break; case '?': scr_printf("? "); break; } - if (userflags & US_DISAPPEAR) + if (userflags & US_DISAPPEAR || e == 'i') scr_printf("\r%79s\r", ""); else scr_printf("\n"); @@ -1485,24 +1717,29 @@ RMSGREAD: scr_flush(); " 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)) { + 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"); - if (strlen(printcmd) > 0) + if (!IsEmptyStr(printcmd)) scr_printf(" P Print this message\n"); scr_printf( " Q Quote portions of this message for your next post\n" " H Headers (display message headers only)\n"); if (is_mail) scr_printf(" R Reply to this message\n"); - if (rc_allow_attachments) - scr_printf - (" F (save attachments to a file)\n"); - if (strlen(rc_url_cmd) > 0) + if (rc_allow_attachments) { + scr_printf(" O (Open attachments)\n"); + scr_printf(" F (save attachments to a File)\n"); + } + if (!IsEmptyStr(rc_url_cmd)) scr_printf(" U (list URL's for display)\n"); + if (!IsEmptyStr(imagecmd) && has_images > 0) + scr_printf(" I Image viewer\n"); scr_printf("\n"); goto RMSGREAD; case 'p': @@ -1530,7 +1767,7 @@ RMSGREAD: scr_flush(); case 'c': newprompt("Enter target room: ", targ, ROOMNAMELEN - 1); - if (strlen(targ) > 0) { + if (!IsEmptyStr(targ)) { r = CtdlIPCMoveMessage(ipc, (e == 'c' ? 1 : 0), msg_arr[a], targ, cmd); scr_printf("%s\n", cmd); @@ -1542,23 +1779,41 @@ RMSGREAD: scr_flush(); if (r / 100 != 2) /* r will be init'ed, FIXME */ goto RMSGREAD; /* the logic here sucks */ break; + case 'o': case 'f': - newprompt("Which section? ", filename, - ((sizeof filename) - 1)); + newprompt("Which section? ", filename, ((sizeof filename) - 1)); r = CtdlIPCAttachmentDownload(ipc, msg_arr[a], filename, &attachment, progress, cmd); - extract(filename, cmd, 2); - destination_directory(save_to, filename); - r = CtdlIPCAttachmentDownload(ipc, msg_arr[a], - filename, &attachment, progress, cmd); if (r / 100 != 2) { scr_printf("%s\n", cmd); } else { - save_buffer(attachment, - extract_unsigned_long(cmd, 0), - save_to); + extract_token(filename, cmd, 2, '|', sizeof filename); + /* + * Part 1 won't have a filename; use the + * subject of the message instead. IO + */ + if (IsEmptyStr(filename)) { + strcpy(filename, reply_subject); + } + if (e == 'o') { /* open attachment */ + mkdir(tempdir, 0700); + snprintf(save_to, sizeof save_to, "%s/%04x.%s", + tempdir, + ++att_seq, + filename); + save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to); + snprintf(cmd, sizeof cmd, rc_open_cmd, save_to); + system(cmd); + } + else { /* save attachment to disk */ + destination_directory(save_to, filename); + save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to); + } + } + if (attachment) { + free(attachment); + attachment = NULL; } - if (attachment) free(attachment); goto RMSGREAD; case 'd': scr_printf("*** Delete this message? "); @@ -1576,42 +1831,40 @@ RMSGREAD: scr_flush(); goto RMSGREAD; case 'r': savedpos = num_msgs; - entmsg(ipc, 1, (DEFAULT_ENTRY == 46 ? 2 : 0)); + entmsg(ipc, 1, ((userflags & US_EXTEDIT) ? 2 : 0), 0); num_msgs = savedpos; goto RMSGREAD; case 'u': list_urls(ipc); goto RMSGREAD; + case 'i': + image_view(ipc, msg_arr[a]); + goto RMSGREAD; case 'y': { /* hack hack hack */ /* find the next message by me, stay here if we find nothing */ int finda; int lasta = a; - for (finda = a; ((finda < num_msgs) && (finda >= 0)); finda += rdir) + for (finda = (a + rdir); ((finda < num_msgs) && (finda >= 0)); finda += rdir) { - /* This is repetitively dumb, but that's what computers are for. - We have to load up messages until we find one by us */ - char buf[SIZ]; - int founda = 0; + /* This is repetitively dumb, but that's what computers are for. + We have to load up messages until we find one by us */ + char buf[SIZ]; + int founda = 0; + struct ctdlipcmessage *msg = NULL; - snprintf(buf, sizeof buf, "MSG0 %ld|1", msg_arr[finda]); /* read the header so we can get 'from=' */ - CtdlIPC_putline(ipc, buf); - CtdlIPC_getline(ipc, buf); - while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) - { - if ((!strncasecmp(buf, "from=", 5)) && (finda != a)) /* Skip current message. */ - { - if (strcasecmp(buf+5, fullname) == 0) - { - a = lasta; /* meesa current */ - founda = 1; - } - } - } - // we are now in synch with the server - if (founda) - break; /* for */ - lasta = finda; /* keep one behind or we skip on the reentrance to the for */ + /* read the header so we can get 'from=' */ + r = CtdlIPCGetSingleMessage(ipc, msg_arr[finda], 1, 0, &msg, buf); + if (!strncasecmp(msg->author, fullname, sizeof(fullname))) { + a = lasta; /* meesa current */ + founda = 1; + } + + free(msg); + + if (founda) + break; /* for */ + lasta = finda; /* keep one behind or we skip on the reentrance to the for */ } /* for */ } /* case 'y' */ } /* switch */ @@ -1645,6 +1898,8 @@ void edit_system_message(CtdlIPC *ipc, char *which_message) void check_message_base(CtdlIPC *ipc) { char buf[SIZ]; + char *transcript = NULL; + int r; /* IPC response code */ scr_printf ("Please read the documentation before running this command.\n" @@ -1652,14 +1907,56 @@ void check_message_base(CtdlIPC *ipc) if (yesno() == 0) return; - CtdlIPC_putline(ipc, "FSCK"); - CtdlIPC_getline(ipc, buf); - if (buf[0] != '1') { - scr_printf("%s\n", &buf[4]); + r = CtdlIPCMessageBaseCheck(ipc, &transcript, buf); + if (r / 100 != 1) { + scr_printf("%s\n", buf); return; } - while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) { - scr_printf("%s\n", buf); + while (transcript && !IsEmptyStr(transcript)) { + lines_printed = 1; + extract_token(buf, transcript, 0, '\n', sizeof buf); + remove_token(transcript, 0, '\n'); + pprintf("%s\n", buf); + } + if (transcript) free(transcript); + return; +} + + +/* + * Loads the contents of a file into memory. Caller must free the allocated + * memory. + */ +char *load_message_from_file(FILE *src) +{ + size_t i; + size_t got = 0; + char *dest = NULL; + + fseek(src, 0, SEEK_END); + i = ftell(src); + rewind(src); + + dest = (char *)calloc(1, i + 1); + if (!dest) + return NULL; + + while (got < i) { + size_t g; + + g = fread(dest + got, 1, i - got, src); + got += g; + if (g < i - got) { + /* Interrupted system call, keep going */ + if (errno == EINTR) + continue; + /* At this point we have either EOF or error */ + i = got; + break; + } + dest[i] = 0; } + + return dest; }