22 #include "sysdep_decls.h"
23 #include "citserver.h"
28 #include "dynloader.h"
30 #include "mime_parser.h"
39 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
41 extern struct config config;
45 * This function is self explanatory.
46 * (What can I say, I'm in a weird mood today...)
48 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
52 for (i = 0; i < strlen(name); ++i)
55 if (isspace(name[i - 1])) {
56 strcpy(&name[i - 1], &name[i]);
59 while (isspace(name[i + 1])) {
60 strcpy(&name[i + 1], &name[i + 2]);
67 * Aliasing for network mail.
68 * (Error messages have been commented out, because this is a server.)
71 { /* process alias and routing info for mail */
74 char aaa[300], bbb[300];
76 lprintf(9, "alias() called for <%s>\n", name);
78 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
80 fp = fopen("network/mail.aliases", "r");
82 fp = fopen("/dev/null", "r");
87 while (fgets(aaa, sizeof aaa, fp) != NULL) {
88 while (isspace(name[0]))
89 strcpy(name, &name[1]);
90 aaa[strlen(aaa) - 1] = 0;
92 for (a = 0; a < strlen(aaa); ++a) {
94 strcpy(bbb, &aaa[a + 1]);
98 if (!strcasecmp(name, aaa))
102 lprintf(7, "Mail is being forwarded to %s\n", name);
104 /* determine local or remote type, see citadel.h */
105 for (a = 0; a < strlen(name); ++a)
107 return (MES_INTERNET);
108 for (a = 0; a < strlen(name); ++a)
110 for (b = a; b < strlen(name); ++b)
112 return (MES_INTERNET);
114 for (a = 0; a < strlen(name); ++a)
118 lprintf(7, "Too many @'s in address\n");
122 for (a = 0; a < strlen(name); ++a)
124 strcpy(bbb, &name[a + 1]);
126 strcpy(bbb, &bbb[1]);
127 fp = fopen("network/mail.sysinfo", "r");
131 a = getstring(fp, aaa);
132 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
133 a = getstring(fp, aaa);
134 if (!strncmp(aaa, "use ", 4)) {
135 strcpy(bbb, &aaa[4]);
140 if (!strncmp(aaa, "uum", 3)) {
142 for (a = 0; a < strlen(bbb); ++a) {
148 while (bbb[strlen(bbb) - 1] == '_')
149 bbb[strlen(bbb) - 1] = 0;
150 sprintf(name, &aaa[4], bbb);
151 return (MES_INTERNET);
153 if (!strncmp(aaa, "bin", 3)) {
156 while (aaa[strlen(aaa) - 1] != '@')
157 aaa[strlen(aaa) - 1] = 0;
158 aaa[strlen(aaa) - 1] = 0;
159 while (aaa[strlen(aaa) - 1] == ' ')
160 aaa[strlen(aaa) - 1] = 0;
161 while (bbb[0] != '@')
162 strcpy(bbb, &bbb[1]);
163 strcpy(bbb, &bbb[1]);
164 while (bbb[0] == ' ')
165 strcpy(bbb, &bbb[1]);
166 sprintf(name, "%s @%s", aaa, bbb);
179 fp = fopen("citadel.control", "r");
180 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
186 void simple_listing(long msgnum) {
187 cprintf("%ld\n", msgnum);
192 * API function to perform an operation for each qualifying message in the
195 void CtdlForEachMessage(int mode, long ref,
196 void (*CallBack) (long msgnum) ) {
200 struct cdbdata *cdbfr;
201 long *msglist = NULL;
206 getuser(&CC->usersupp, CC->curr_user);
207 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
209 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
211 msglist = mallok(cdbfr->len);
212 memcpy(msglist, cdbfr->ptr, cdbfr->len);
213 num_msgs = cdbfr->len / sizeof(long);
219 if (num_msgs > 0) for (a = 0; a < (num_msgs); ++a) {
220 thismsg = msglist[a];
221 lprintf(9, "CtdlForEachMessage() iterating msg %ld\n", thismsg);
226 || ((mode == MSGS_OLD) && (thismsg <= vbuf.v_lastseen))
227 || ((mode == MSGS_NEW) && (thismsg > vbuf.v_lastseen))
228 || ((mode == MSGS_NEW) && (thismsg >= vbuf.v_lastseen)
229 && (CC->usersupp.flags & US_LASTOLD))
230 || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
231 || ((mode == MSGS_FIRST) && (a < ref))
232 || ((mode == MSGS_GT) && (thismsg > ref))
244 * cmd_msgs() - get list of message #'s in this room
245 * implements the MSGS server command using CtdlForEachMessage()
247 void cmd_msgs(char *cmdbuf)
253 extract(which, cmdbuf, 0);
254 cm_ref = extract_int(cmdbuf, 1);
258 if (!strncasecmp(which, "OLD", 3))
260 else if (!strncasecmp(which, "NEW", 3))
262 else if (!strncasecmp(which, "FIRST", 5))
264 else if (!strncasecmp(which, "LAST", 4))
266 else if (!strncasecmp(which, "GT", 2))
269 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
270 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
274 cprintf("%d Message list...\n", LISTING_FOLLOWS);
275 CtdlForEachMessage(mode, cm_ref, simple_listing);
283 * help_subst() - support routine for help file viewer
285 void help_subst(char *strbuf, char *source, char *dest)
290 while (p = pattern2(strbuf, source), (p >= 0)) {
291 strcpy(workbuf, &strbuf[p + strlen(source)]);
292 strcpy(&strbuf[p], dest);
293 strcat(strbuf, workbuf);
298 void do_help_subst(char *buffer)
302 help_subst(buffer, "^nodename", config.c_nodename);
303 help_subst(buffer, "^humannode", config.c_humannode);
304 help_subst(buffer, "^fqdn", config.c_fqdn);
305 help_subst(buffer, "^username", CC->usersupp.fullname);
306 sprintf(buf2, "%ld", CC->usersupp.usernum);
307 help_subst(buffer, "^usernum", buf2);
308 help_subst(buffer, "^sysadm", config.c_sysadm);
309 help_subst(buffer, "^variantname", CITADEL);
310 sprintf(buf2, "%d", config.c_maxsessions);
311 help_subst(buffer, "^maxsessions", buf2);
317 * memfmout() - Citadel text formatter and paginator.
318 * Although the original purpose of this routine was to format
319 * text to the reader's screen width, all we're really using it
320 * for here is to format text out to 80 columns before sending it
321 * to the client. The client software may reformat it again.
323 void memfmout(int width, char *mptr, char subst)
324 /* screen width to use */
325 /* where are we going to get our text from? */
326 /* nonzero if we should use hypertext mode */
338 c = 1; /* c is the current pos */
341 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
343 buffer[strlen(buffer) + 1] = 0;
344 buffer[strlen(buffer)] = ch;
347 if (buffer[0] == '^')
348 do_help_subst(buffer);
350 buffer[strlen(buffer) + 1] = 0;
352 strcpy(buffer, &buffer[1]);
361 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
363 if (((old == 13) || (old == 10)) && (isspace(real))) {
371 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
372 cprintf("\n%s", aaa);
381 if ((strlen(aaa) + c) > (width - 5)) {
391 if ((ch == 13) || (ch == 10)) {
392 cprintf("%s\n", aaa);
399 FMTEND: cprintf("%s\n", aaa);
405 * Callback function for mime parser that simply lists the part
407 void list_this_part(char *name, char *filename, char *partnum, char *disp,
408 void *content, char *cbtype, size_t length)
411 cprintf("part=%s|%s|%s|%s|%s|%d\n",
412 name, filename, partnum, disp, cbtype, length);
417 * Callback function for mime parser that wants to display text
419 void fixed_output(char *name, char *filename, char *partnum, char *disp,
420 void *content, char *cbtype, size_t length)
423 if (!strcasecmp(cbtype, "text/plain")) {
424 client_write(content, length);
426 cprintf("Part %s: %s (%s) (%d bytes)\n",
427 partnum, filename, cbtype, length);
433 * Callback function for mime parser that opens a section for downloading
435 void mime_download(char *name, char *filename, char *partnum, char *disp,
436 void *content, char *cbtype, size_t length)
439 /* Silently go away if there's already a download open... */
440 if (CC->download_fp != NULL)
443 /* ...or if this is not the desired section */
444 if (strcasecmp(desired_section, partnum))
447 CC->download_fp = tmpfile();
448 if (CC->download_fp == NULL)
451 fwrite(content, length, 1, CC->download_fp);
452 fflush(CC->download_fp);
453 rewind(CC->download_fp);
455 OpenCmdResult(filename, cbtype);
461 * Get a message off disk. (return value is the message's timestamp)
464 time_t output_message(char *msgid, int mode, int headers_only)
469 CIT_UBYTE format_type, anon_flag;
474 struct cdbdata *dmsgtext;
477 /* buffers needed for RFC822 translation */
486 msg_num = atol(msgid);
489 if ((!(CC->logged_in)) && (!(CC->internal_pgm)) && (mode != MT_DATE)) {
490 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
494 /* FIX ... small security issue
495 * We need to check to make sure the requested message is actually
496 * in the current room, and set msg_ok to 1 only if it is. This
497 * functionality is currently missing because I'm in a hurry to replace
498 * broken production code with nonbroken pre-beta code. :( -- ajc
504 cprintf("%d Message %ld is not in this room.\n",
508 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msg_num, sizeof(long));
510 if (dmsgtext == NULL) {
512 cprintf("%d Can't find message %ld\n",
513 (ERROR + INTERNAL_ERROR), msg_num);
516 msg_len = (long) dmsgtext->len;
517 mptr = dmsgtext->ptr;
519 /* this loop spews out the whole message if we're doing raw format */
520 if (mode == MT_RAW) {
521 cprintf("%d %ld\n", BINARY_FOLLOWS, msg_len);
522 client_write(dmsgtext->ptr, (int) msg_len);
526 /* Otherwise, we'll start parsing it field by field... */
529 cprintf("%d Illegal message format on disk\n",
530 ERROR + INTERNAL_ERROR);
535 format_type = *mptr++;
537 /* Are we downloading a MIME component? */
538 if (mode == MT_DOWNLOAD) {
539 if (format_type != 4) {
540 cprintf("%d This is not a MIME message.\n",
542 } else if (CC->download_fp != NULL) {
543 cprintf("%d You already have a download open.\n",
546 /* Skip to the message body */
547 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
550 buf[strlen(buf) + 1] = 0;
552 buf[strlen(buf)] = rch;
556 mime_parser(mptr, NULL, *mime_download);
557 /* If there's no file open by this time, the requested
558 * section wasn't found, so print an error
560 if (CC->download_fp == NULL) {
561 cprintf("%d Section %s not found.\n",
562 ERROR + FILE_NOT_FOUND,
569 /* Are we just looking for the message date? */
571 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
574 buf[strlen(buf) + 1] = 0;
576 buf[strlen(buf)] = rch;
585 /* now for the user-mode message reading loops */
586 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
588 if (mode == MT_CITADEL)
589 cprintf("type=%d\n", format_type);
591 if ((anon_flag == MES_ANON) && (mode == MT_CITADEL)) {
592 cprintf("nhdr=yes\n");
594 /* begin header processing loop for Citadel message format */
596 if ((mode == MT_CITADEL) || (mode == MT_MIME))
597 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
600 buf[strlen(buf) + 1] = 0;
602 buf[strlen(buf)] = rch;
606 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
607 if (anon_flag == MES_ANON)
608 cprintf("from=****");
609 else if (anon_flag == MES_AN2)
610 cprintf("from=anonymous");
612 cprintf("from=%s", buf);
613 if ((is_room_aide()) && ((anon_flag == MES_ANON)
614 || (anon_flag == MES_AN2)))
615 cprintf(" [%s]", buf);
617 } else if (ch == 'P')
618 cprintf("path=%s\n", buf);
620 cprintf("subj=%s\n", buf);
622 cprintf("msgn=%s\n", buf);
624 cprintf("hnod=%s\n", buf);
626 cprintf("room=%s\n", buf);
628 cprintf("node=%s\n", buf);
630 cprintf("rcpt=%s\n", buf);
632 cprintf("time=%s\n", buf);
633 /* else cprintf("fld%c=%s\n",ch,buf); */
635 /* begin header processing loop for RFC822 transfer format */
639 strcpy(snode, NODENAME);
640 strcpy(lnode, HUMANNODE);
641 if (mode == MT_RFC822)
642 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
645 buf[strlen(buf) + 1] = 0;
647 buf[strlen(buf)] = rch;
652 else if (ch == 'P') {
653 cprintf("Path: %s\n", buf);
654 for (a = 0; a < strlen(buf); ++a) {
656 strcpy(buf, &buf[a + 1]);
661 } else if (ch == 'U')
662 cprintf("Subject: %s\n", buf);
668 cprintf("X-Citadel-Room: %s\n", buf);
672 cprintf("To: %s\n", buf);
673 else if (ch == 'T') {
675 cprintf("Date: %s", asctime(localtime(&xtime)));
678 if (mode == MT_RFC822) {
679 if (!strcasecmp(snode, NODENAME)) {
682 cprintf("Message-ID: <%s@%s>\n", mid, snode);
683 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
684 cprintf("From: %s@%s (%s)\n",
685 suser, snode, luser);
686 cprintf("Organization: %s\n", lnode);
688 /* end header processing loop ... at this point, we're in the text */
691 cprintf("text\n*** ?Message truncated\n000\n");
695 /* do some sort of MIME output */
696 if (format_type == 4) {
697 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
698 mime_parser(mptr, NULL, *list_this_part);
700 if (mode == MT_MIME) { /* If MT_MIME then it's parts only */
707 /* give 'em a length */
709 while (ch = *mptr++, ch > 0) {
712 cprintf("mlen=%ld\n", msg_len);
717 /* signify start of msg text */
718 if (mode == MT_CITADEL)
720 if ((mode == MT_RFC822) && (format_type != 4))
723 /* If the format type on disk is 1 (fixed-format), then we want
724 * everything to be output completely literally ... regardless of
725 * what message transfer format is in use.
727 if (format_type == 1) {
729 while (ch = *mptr++, ch > 0) {
732 if ((ch == 10) || (strlen(buf) > 250)) {
733 cprintf("%s\n", buf);
736 buf[strlen(buf) + 1] = 0;
737 buf[strlen(buf)] = ch;
741 cprintf("%s\n", buf);
743 /* If the message on disk is format 0 (Citadel vari-format), we
744 * output using the formatter at 80 columns. This is the final output
745 * form if the transfer format is RFC822, but if the transfer format
746 * is Citadel proprietary, it'll still work, because the indentation
747 * for new paragraphs is correct and the client will reformat the
748 * message to the reader's screen width.
750 if (format_type == 0) {
751 memfmout(80, mptr, 0);
753 /* If the message on disk is format 4 (MIME), we've gotta hand it
754 * off to the MIME parser. The client has already been told that
755 * this message is format 1 (fixed format), so the callback function
756 * we use will display those parts as-is.
758 if (format_type == 4) {
759 mime_parser(mptr, NULL, *fixed_output);
769 * display a message (mode 0 - Citadel proprietary)
771 void cmd_msg0(char *cmdbuf)
774 int headers_only = 0;
776 extract(msgid, cmdbuf, 0);
777 headers_only = extract_int(cmdbuf, 1);
779 output_message(msgid, MT_CITADEL, headers_only);
785 * display a message (mode 2 - RFC822)
787 void cmd_msg2(char *cmdbuf)
790 int headers_only = 0;
792 extract(msgid, cmdbuf, 0);
793 headers_only = extract_int(cmdbuf, 1);
795 output_message(msgid, MT_RFC822, headers_only);
799 * display a message (mode 3 - IGnet raw format - internal programs only)
801 void cmd_msg3(char *cmdbuf)
804 int headers_only = 0;
806 if (CC->internal_pgm == 0) {
807 cprintf("%d This command is for internal programs only.\n",
811 extract(msgid, cmdbuf, 0);
812 headers_only = extract_int(cmdbuf, 1);
814 output_message(msgid, MT_RAW, headers_only);
818 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
820 void cmd_msg4(char *cmdbuf)
824 extract(msgid, cmdbuf, 0);
826 output_message(msgid, MT_MIME, 0);
832 * Open a component of a MIME message as a download file
834 void cmd_opna(char *cmdbuf)
838 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
840 extract(msgid, cmdbuf, 0);
841 extract(desired_section, cmdbuf, 1);
843 output_message(msgid, MT_DOWNLOAD, 0);
849 * Message base operation to send a message to the master file
850 * (returns new message number)
852 long send_message(char *message_in_memory, /* pointer to buffer */
853 size_t message_length, /* length of buffer */
855 { /* 1 to generate an I field */
858 char *actual_message;
859 size_t actual_length;
863 /* Get a new message number */
864 newmsgid = get_new_message_number();
867 sprintf(msgidbuf, "I%ld", newmsgid);
868 actual_length = message_length + strlen(msgidbuf) + 1;
869 actual_message = mallok(actual_length);
870 memcpy(actual_message, message_in_memory, 3);
871 memcpy(&actual_message[3], msgidbuf, (strlen(msgidbuf) + 1));
872 memcpy(&actual_message[strlen(msgidbuf) + 4],
873 &message_in_memory[3], message_length - 3);
875 actual_message = message_in_memory;
876 actual_length = message_length;
879 /* Write our little bundle of joy into the message base */
880 begin_critical_section(S_MSGMAIN);
881 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
882 actual_message, actual_length) < 0) {
883 lprintf(2, "Can't store message\n");
888 end_critical_section(S_MSGMAIN);
891 phree(actual_message);
893 /* Finally, return the pointers */
900 * this is a simple file copy routine.
902 void copy_file(char *from, char *to)
907 ffp = fopen(from, "r");
910 tfp = fopen(to, "w");
915 while (a = getc(ffp), a >= 0) {
926 * message base operation to save a message and install its pointers
928 void save_message(char *mtmp, /* file containing proper message */
929 char *rec, /* Recipient (if mail) */
930 char *force, /* if non-zero length, force a room */
931 int mailtype, /* local or remote type, see citadel.h */
933 { /* set to 1 to generate an 'I' field */
935 char hold_rm[ROOMNAMELEN];
936 char actual_rm[ROOMNAMELEN];
937 char force_room[ROOMNAMELEN];
938 char content_type[256]; /* We have to learn this */
942 char *message_in_memory;
947 struct usersupp userbuf;
949 static int seqnum = 0;
950 int successful_local_recipients = 0;
951 struct quickroom qtemp;
952 struct SuppMsgInfo smi;
954 lprintf(9, "save_message(%s,%s,%s,%d,%d)\n",
955 mtmp, rec, force, mailtype, generate_id);
957 strcpy(force_room, force);
959 /* Strip non-printable characters out of the recipient name */
960 strcpy(recipient, rec);
961 for (a = 0; a < strlen(recipient); ++a)
962 if (!isprint(recipient[a]))
963 strcpy(&recipient[a], &recipient[a + 1]);
965 /* Measure the message */
966 stat(mtmp, &statbuf);
967 templen = statbuf.st_size;
969 /* Now read it into memory */
970 message_in_memory = (char *) mallok(templen);
971 if (message_in_memory == NULL) {
972 lprintf(2, "Can't allocate memory to save message!\n");
975 fp = fopen(mtmp, "rb");
976 fread(message_in_memory, templen, 1, fp);
979 /* Learn about what's inside, because it's what's inside that counts */
980 mptr = message_in_memory;
981 ++mptr; /* advance past 0xFF header */
982 ++mptr; /* advance past anon flag */
986 strcpy(content_type, "text/x-citadel-variformat");
989 strcpy(content_type, "text/plain");
992 strcpy(content_type, "text/plain");
993 /* advance past header fields */
994 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
1001 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1002 safestrncpy(content_type, mptr,
1003 sizeof(content_type));
1004 lprintf(9, "%s\n", content_type);
1005 strcpy(content_type, &content_type[14]);
1006 for (a=0; a<strlen(content_type); ++a)
1007 if ( (content_type[a]==';')
1008 || (content_type[a]==' ')
1009 || (content_type[a]==13)
1010 || (content_type[a]==10) )
1011 content_type[a] = 0;
1017 lprintf(9, "Content type is <%s>\n", content_type);
1019 /* Save it to disk */
1020 newmsgid = send_message(message_in_memory, templen, generate_id);
1021 phree(message_in_memory);
1025 strcpy(actual_rm, CC->quickroom.QRname);
1026 strcpy(hold_rm, "");
1028 /* If this is being done by the networker delivering a private
1029 * message, we want to BYPASS saving the sender's copy (because there
1030 * is no local sender; it would otherwise go to the Trashcan).
1032 if ( (!CC->internal_pgm) || (strlen(recipient)==0) ) {
1033 /* If the user is a twit, move to the twit room for posting */
1035 if (CC->usersupp.axlevel == 2) {
1036 strcpy(hold_rm, actual_rm);
1037 strcpy(actual_rm, config.c_twitroom);
1039 /* ...or if this message is destined for Aide> then go there. */
1040 lprintf(9, "actual room forcing loop\n");
1041 if (strlen(force_room) > 0) {
1042 strcpy(hold_rm, actual_rm);
1043 strcpy(actual_rm, force_room);
1045 /* This call to usergoto() changes rooms if necessary. It also
1046 * causes the latest message list to be read into memory.
1048 usergoto(actual_rm, 0);
1050 /* read in the quickroom record, obtaining a lock... */
1051 lgetroom(&CC->quickroom, actual_rm);
1053 /* Fix an obscure bug */
1054 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1055 CC->quickroom.QRflags =
1056 CC->quickroom.QRflags & ~QR_MAILBOX;
1059 /* Add the message pointer to the room */
1060 CC->quickroom.QRhighest =
1061 AddMessageToRoom(&CC->quickroom, newmsgid);
1063 /* update quickroom */
1064 lputroom(&CC->quickroom);
1065 ++successful_local_recipients;
1068 /* Network mail - send a copy to the network program. */
1069 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1070 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1071 (long) getpid(), CC->cs_pid, ++seqnum);
1072 copy_file(mtmp, aaa);
1073 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1075 /* Bump this user's messages posted counter. */
1076 lgetuser(&CC->usersupp, CC->curr_user);
1077 CC->usersupp.posted = CC->usersupp.posted + 1;
1078 lputuser(&CC->usersupp);
1080 /* If this is private, local mail, make a copy in the
1081 * recipient's mailbox and bump the reference count.
1083 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1084 if (getuser(&userbuf, recipient) == 0) {
1085 MailboxName(actual_rm, &userbuf, MAILROOM);
1086 lprintf(9, "Targeting mailbox: <%s>\n", actual_rm);
1087 if (lgetroom(&qtemp, actual_rm) == 0) {
1089 AddMessageToRoom(&qtemp, newmsgid);
1091 ++successful_local_recipients;
1095 /* If we've posted in a room other than the current room, then we
1096 * have to now go back to the current room...
1098 if (strlen(hold_rm) > 0) {
1099 usergoto(hold_rm, 0);
1101 unlink(mtmp); /* delete the temporary file */
1103 /* Write a supplemental message info record. This doesn't have to
1104 * be a critical section because nobody else knows about this message
1107 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1108 smi.smi_msgnum = newmsgid;
1109 smi.smi_refcount = successful_local_recipients;
1110 safestrncpy(smi.smi_content_type, content_type, 64);
1111 PutSuppMsgInfo(&smi);
1116 * Generate an administrative message and post it in the Aide> room.
1118 void aide_message(char *text)
1122 fp = fopen(CC->temp, "wb");
1123 fprintf(fp, "%c%c%c", 255, MES_NORMAL, 0);
1124 fprintf(fp, "Psysop%c", 0);
1125 fprintf(fp, "T%ld%c", (long) time(NULL), 0);
1126 fprintf(fp, "ACitadel%c", 0);
1127 fprintf(fp, "OAide%c", 0);
1128 fprintf(fp, "N%s%c", NODENAME, 0);
1129 fprintf(fp, "M%s\n%c", text, 0);
1131 save_message(CC->temp, "", AIDEROOM, MES_LOCAL, 1);
1132 syslog(LOG_NOTICE, text);
1138 * Build a binary message to be saved on disk.
1141 char *filename, /* temporary file name */
1142 struct usersupp *author, /* author's usersupp structure */
1143 char *recipient, /* NULL if it's not mail */
1144 char *room, /* room where it's going */
1145 int type, /* see MES_ types in header file */
1146 int net_type, /* see MES_ types in header file */
1147 int format_type, /* local or remote (see citadel.h) */
1149 { /* who we're masquerading as */
1157 /* Don't confuse the poor folks if it's not routed mail. */
1158 strcpy(dest_node, "");
1161 /* If net_type is MES_BINARY, split out the destination node. */
1162 if (net_type == MES_BINARY) {
1163 strcpy(dest_node, NODENAME);
1164 for (a = 0; a < strlen(recipient); ++a) {
1165 if (recipient[a] == '@') {
1167 strcpy(dest_node, &recipient[a + 1]);
1171 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1172 if (net_type == MES_INTERNET) {
1173 strcpy(dest_node, "internet");
1175 while (isspace(recipient[strlen(recipient) - 1]))
1176 recipient[strlen(recipient) - 1] = 0;
1179 fp = fopen(filename, "w");
1181 putc(type, fp); /* Normal or anonymous, see MES_ flags */
1182 putc(format_type, fp); /* Formatted or unformatted */
1183 fprintf(fp, "Pcit%ld%c", author->usernum, 0); /* path */
1184 fprintf(fp, "T%ld%c", (long) now, 0); /* date/time */
1186 fprintf(fp, "A%s%c", fake_name, 0);
1188 fprintf(fp, "A%s%c", author->fullname, 0); /* author */
1190 if (CC->quickroom.QRflags & QR_MAILBOX) { /* room */
1191 fprintf(fp, "O%s%c", &CC->quickroom.QRname[11], 0);
1193 fprintf(fp, "O%s%c", CC->quickroom.QRname, 0);
1196 fprintf(fp, "N%s%c", NODENAME, 0); /* nodename */
1197 fprintf(fp, "H%s%c", HUMANNODE, 0); /* human nodename */
1199 if (recipient[0] != 0)
1200 fprintf(fp, "R%s%c", recipient, 0);
1201 if (dest_node[0] != 0)
1202 fprintf(fp, "D%s%c", dest_node, 0);
1206 while (client_gets(buf), strcmp(buf, "000")) {
1207 fprintf(fp, "%s\n", buf);
1218 * message entry - mode 0 (normal) <bc>
1220 void cmd_ent0(char *entargs)
1223 char recipient[256];
1225 int format_type = 0;
1226 char newusername[256]; /* <bc> */
1231 struct usersupp tempUS;
1234 post = extract_int(entargs, 0);
1235 extract(recipient, entargs, 1);
1236 anon_flag = extract_int(entargs, 2);
1237 format_type = extract_int(entargs, 3);
1239 /* first check to make sure the request is valid. */
1241 if (!(CC->logged_in)) {
1242 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1245 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1246 cprintf("%d Need to be validated to enter ",
1247 ERROR + HIGHER_ACCESS_REQUIRED);
1248 cprintf("(except in %s> to sysop)\n", MAILROOM);
1251 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1252 cprintf("%d Need net privileges to enter here.\n",
1253 ERROR + HIGHER_ACCESS_REQUIRED);
1256 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1257 cprintf("%d Sorry, this is a read-only room.\n",
1258 ERROR + HIGHER_ACCESS_REQUIRED);
1264 if (post == 2) { /* <bc> */
1265 if (CC->usersupp.axlevel < 6) {
1266 cprintf("%d You don't have permission to do an aide post.\n",
1267 ERROR + HIGHER_ACCESS_REQUIRED);
1270 extract(newusername, entargs, 4);
1271 memset(CC->fake_postname, 0, 32);
1272 strcpy(CC->fake_postname, newusername);
1273 cprintf("%d Ok\n", OK);
1276 CC->cs_flags |= CS_POSTING;
1279 if (CC->quickroom.QRflags & QR_MAILBOX) {
1280 if (CC->usersupp.axlevel >= 2) {
1281 strcpy(buf, recipient);
1283 strcpy(buf, "sysop");
1284 lprintf(9, "calling alias()\n");
1285 e = alias(buf); /* alias and mail type */
1286 lprintf(9, "alias() returned %d\n", e);
1287 if ((buf[0] == 0) || (e == MES_ERROR)) {
1288 cprintf("%d Unknown address - cannot send message.\n",
1289 ERROR + NO_SUCH_USER);
1292 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1293 cprintf("%d Net privileges required for network mail.\n",
1294 ERROR + HIGHER_ACCESS_REQUIRED);
1297 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1298 && ((CC->usersupp.flags & US_INTERNET) == 0)
1299 && (!CC->internal_pgm)) {
1300 cprintf("%d You don't have access to Internet mail.\n",
1301 ERROR + HIGHER_ACCESS_REQUIRED);
1304 if (!strcasecmp(buf, "sysop")) {
1309 goto SKFALL; /* don't search local file */
1310 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1311 cprintf("%d Can't send mail to yourself!\n",
1312 ERROR + NO_SUCH_USER);
1315 /* Check to make sure the user exists; also get the correct
1316 * upper/lower casing of the name.
1318 a = getuser(&tempUS, buf);
1320 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1323 strcpy(buf, tempUS.fullname);
1325 SKFALL:b = MES_NORMAL;
1326 if (CC->quickroom.QRflags & QR_ANONONLY)
1328 if (CC->quickroom.QRflags & QR_ANONOPT) {
1332 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1335 /* If we're only checking the validity of the request, return
1336 * success without creating the message.
1339 cprintf("%d %s\n", OK, buf);
1342 cprintf("%d send message\n", SEND_LISTING);
1343 if (CC->fake_postname[0])
1344 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, CC->fake_postname);
1345 else if (CC->fake_username[0])
1346 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, CC->fake_username);
1348 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, "");
1349 save_message(CC->temp, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1350 CC->fake_postname[0] = '\0';
1357 * message entry - mode 3 (raw)
1359 void cmd_ent3(char *entargs)
1365 struct usersupp tempUS;
1370 if (CC->internal_pgm == 0) {
1371 cprintf("%d This command is for internal programs only.\n",
1375 /* See if there's a recipient, but make sure it's a real one */
1376 extract(recp, entargs, 1);
1377 for (a = 0; a < strlen(recp); ++a)
1378 if (!isprint(recp[a]))
1379 strcpy(&recp[a], &recp[a + 1]);
1380 while (isspace(recp[0]))
1381 strcpy(recp, &recp[1]);
1382 while (isspace(recp[strlen(recp) - 1]))
1383 recp[strlen(recp) - 1] = 0;
1385 /* If we're in Mail, check the recipient */
1386 if (strlen(recp) > 0) {
1387 e = alias(recp); /* alias and mail type */
1388 if ((recp[0] == 0) || (e == MES_ERROR)) {
1389 cprintf("%d Unknown address - cannot send message.\n",
1390 ERROR + NO_SUCH_USER);
1393 if (e == MES_LOCAL) {
1394 a = getuser(&tempUS, recp);
1396 cprintf("%d No such user.\n",
1397 ERROR + NO_SUCH_USER);
1402 /* At this point, message has been approved. */
1403 if (extract_int(entargs, 0) == 0) {
1404 cprintf("%d OK to send\n", OK);
1407 /* open a temp file to hold the message */
1408 fp = fopen(CC->temp, "wb");
1410 cprintf("%d Cannot open %s: %s\n",
1411 ERROR + INTERNAL_ERROR,
1412 CC->temp, strerror(errno));
1415 msglen = extract_long(entargs, 2);
1416 cprintf("%d %ld\n", SEND_BINARY, msglen);
1417 while (msglen > 0L) {
1418 bloklen = ((msglen >= 255L) ? 255 : msglen);
1419 client_read(buf, (int) bloklen);
1420 fwrite(buf, (int) bloklen, 1, fp);
1421 msglen = msglen - bloklen;
1425 save_message(CC->temp, recp, "", e, 0);
1430 * API function to delete messages which match a set of criteria
1431 * (returns the actual number of messages deleted)
1432 * FIX ... still need to implement delete by content type
1434 int CtdlDeleteMessages( char *room_name, /* which room */
1435 long dmsgnum, /* or "0" for any */
1436 char *content_type /* or NULL for any */
1439 struct quickroom qrbuf;
1440 struct cdbdata *cdbfr;
1441 long *msglist = NULL;
1444 int num_deleted = 0;
1446 struct SuppMsgInfo smi;
1448 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1449 room_name, dmsgnum, content_type);
1451 /* get room record, obtaining a lock... */
1452 if (lgetroom(&qrbuf, room_name) != 0) {
1453 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1455 return(0); /* room not found */
1458 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1460 lprintf(9, "doing mallok/memcpy loop\n");
1461 if (cdbfr != NULL) {
1462 msglist = mallok(cdbfr->len);
1463 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1464 num_msgs = cdbfr->len / sizeof(long);
1469 for (i=0; i<num_msgs; ++i) {
1472 /* Set/clear a bit for each criterion */
1474 if ( (dmsgnum == 0L) || (msglist[i]==dmsgnum) ) {
1475 delete_this |= 0x01;
1478 if (content_type == NULL) {
1479 delete_this |= 0x02;
1481 GetSuppMsgInfo(&smi, msglist[i]);
1482 if (!strcasecmp(smi.smi_content_type,
1484 delete_this |= 0x02;
1488 /* Delete message only if all bits are set */
1489 if (delete_this == 0x03) {
1490 AdjRefCount(msglist[i], -1);
1496 num_msgs = sort_msglist(msglist, num_msgs);
1497 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1498 msglist, (num_msgs * sizeof(long)) );
1500 qrbuf.QRhighest = msglist[num_msgs - 1];
1504 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1505 return(num_deleted);
1511 * Delete message from current room
1513 void cmd_dele(char *delstr)
1518 getuser(&CC->usersupp, CC->curr_user);
1519 if ((CC->usersupp.axlevel < 6)
1520 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1521 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1522 cprintf("%d Higher access required.\n",
1523 ERROR + HIGHER_ACCESS_REQUIRED);
1526 delnum = extract_long(delstr, 0);
1528 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1531 cprintf("%d %d message%s deleted.\n", OK,
1532 num_deleted, ((num_deleted!=1) ? "s" : "") );
1534 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1540 * move a message to another room
1542 void cmd_move(char *args)
1546 struct quickroom qtemp;
1549 num = extract_long(args, 0);
1550 extract(targ, args, 1);
1552 getuser(&CC->usersupp, CC->curr_user);
1553 if ((CC->usersupp.axlevel < 6)
1554 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1555 cprintf("%d Higher access required.\n",
1556 ERROR + HIGHER_ACCESS_REQUIRED);
1559 if (getroom(&qtemp, targ) != 0) {
1560 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1564 /* Bump the reference count, otherwise the message will be deleted
1565 * from disk when we remove it from the source room.
1567 AdjRefCount(num, 1);
1569 /* yank the message out of the current room... */
1570 foundit = CtdlDeleteMessages(CC->quickroom.QRname, num, NULL);
1573 /* put the message into the target room */
1574 lgetroom(&qtemp, targ);
1575 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1577 cprintf("%d Message moved.\n", OK);
1579 AdjRefCount(num, (-1)); /* oops */
1580 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1587 * GetSuppMsgInfo() - Get the supplementary record for a message
1589 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1592 struct cdbdata *cdbsmi;
1595 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1596 smibuf->smi_msgnum = msgnum;
1597 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1599 /* Use the negative of the message number for its supp record index */
1600 TheIndex = (0L - msgnum);
1602 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1603 if (cdbsmi == NULL) {
1604 return; /* record not found; go with defaults */
1606 memcpy(smibuf, cdbsmi->ptr,
1607 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1608 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1615 * PutSuppMsgInfo() - (re)write supplementary record for a message
1617 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1621 /* Use the negative of the message number for its supp record index */
1622 TheIndex = (0L - smibuf->smi_msgnum);
1624 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1625 smibuf->smi_msgnum, smibuf->smi_refcount);
1627 cdb_store(CDB_MSGMAIN,
1628 &TheIndex, sizeof(long),
1629 smibuf, sizeof(struct SuppMsgInfo));
1636 * AdjRefCount - change the reference count for a message;
1637 * delete the message if it reaches zero
1639 void AdjRefCount(long msgnum, int incr)
1642 struct SuppMsgInfo smi;
1645 /* This is a *tight* critical section; please keep it that way, as
1646 * it may get called while nested in other critical sections.
1647 * Complicating this any further will surely cause deadlock!
1649 begin_critical_section(S_SUPPMSGMAIN);
1650 GetSuppMsgInfo(&smi, msgnum);
1651 smi.smi_refcount += incr;
1652 PutSuppMsgInfo(&smi);
1653 end_critical_section(S_SUPPMSGMAIN);
1655 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1656 msgnum, smi.smi_refcount);
1658 /* If the reference count is now zero, delete the message
1659 * (and its supplementary record as well).
1661 if (smi.smi_refcount == 0) {
1662 lprintf(9, "Deleting message <%ld>\n", msgnum);
1664 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1665 delnum = (0L - msgnum);
1666 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1672 * Write a generic object to this room
1674 void CtdlWriteObject( char *req_room, /* Room to stuff it in */
1675 char *content_type, /* MIME type of this object */
1676 char *tempfilename, /* Where to fetch it from */
1677 int is_mailbox, /* Private mailbox room? */
1678 int is_binary, /* Is encoding necessary? */
1679 int is_unique /* Del others of this type? */
1683 char filename[PATH_MAX];
1686 struct quickroom qrbuf;
1687 char roomname[ROOMNAMELEN];
1689 if (is_mailbox) MailboxName(roomname, &CC->usersupp, req_room);
1690 else safestrncpy(roomname, req_room, sizeof(roomname));
1692 strcpy(filename, tmpnam(NULL));
1693 fp = fopen(filename, "w");
1694 if (fp == NULL) return;
1696 fprintf(fp, "%c%c%c", 0xFF, MES_NORMAL, 4);
1697 fprintf(fp, "T%ld%c", time(NULL), 0);
1698 fprintf(fp, "A%s%c", CC->usersupp.fullname, 0);
1699 fprintf(fp, "O%s%c", roomname, 0);
1700 fprintf(fp, "N%s%c", config.c_nodename, 0);
1701 fprintf(fp, "MContent-type: %s\n", content_type);
1703 tempfp = fopen(tempfilename, "r");
1704 if (tempfp == NULL) {
1710 if (is_binary == 0) {
1711 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
1712 while (ch=getc(tempfp), ch>0) putc(ch, fp);
1717 fprintf(fp, "Content-transfer-encoding: base64\n\n");
1720 sprintf(cmdbuf, "./base64 -e <%s >>%s",
1721 tempfilename, filename);
1725 /* Create the requested room if we have to. */
1726 if (getroom(&qrbuf, roomname) != 0) {
1727 create_room(roomname, 4, "", 0);
1730 /* If the caller specified this object as unique, delete all
1731 * other objects of this type that are currently in the room.
1734 lprintf(9, "Deleted %d other msgs of this type\n",
1735 CtdlDeleteMessages(roomname, 0L, content_type) );
1738 /* Now write the data */
1739 save_message(filename, "", roomname, MES_LOCAL, 1);