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);
493 /* We used to need to check in the current room's message list
494 * to determine where the message's disk position. We no longer need
495 * to do this, but we do it anyway as a security measure, in order to
496 * prevent rogue clients from reading messages not in the current room.
500 if (CC->num_msgs > 0) {
501 for (a = 0; a < CC->num_msgs; ++a) {
502 if (MessageFromList(a) == msg_num) {
509 cprintf("%d Message %ld is not in this room.\n",
513 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msg_num, sizeof(long));
515 if (dmsgtext == NULL) {
517 cprintf("%d Can't find message %ld\n",
518 ERROR + INTERNAL_ERROR);
521 msg_len = (long) dmsgtext->len;
522 mptr = dmsgtext->ptr;
524 /* this loop spews out the whole message if we're doing raw format */
525 if (mode == MT_RAW) {
526 cprintf("%d %ld\n", BINARY_FOLLOWS, msg_len);
527 client_write(dmsgtext->ptr, (int) msg_len);
531 /* Otherwise, we'll start parsing it field by field... */
534 cprintf("%d Illegal message format on disk\n",
535 ERROR + INTERNAL_ERROR);
540 format_type = *mptr++;
542 /* Are we downloading a MIME component? */
543 if (mode == MT_DOWNLOAD) {
544 if (format_type != 4) {
545 cprintf("%d This is not a MIME message.\n",
547 } else if (CC->download_fp != NULL) {
548 cprintf("%d You already have a download open.\n",
551 /* Skip to the message body */
552 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
555 buf[strlen(buf) + 1] = 0;
557 buf[strlen(buf)] = rch;
561 mime_parser(mptr, NULL, *mime_download);
562 /* If there's no file open by this time, the requested
563 * section wasn't found, so print an error
565 if (CC->download_fp == NULL) {
566 cprintf("%d Section %s not found.\n",
567 ERROR + FILE_NOT_FOUND,
574 /* Are we just looking for the message date? */
576 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
579 buf[strlen(buf) + 1] = 0;
581 buf[strlen(buf)] = rch;
590 /* now for the user-mode message reading loops */
591 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
593 if (mode == MT_CITADEL)
594 cprintf("type=%d\n", format_type);
596 if ((anon_flag == MES_ANON) && (mode == MT_CITADEL)) {
597 cprintf("nhdr=yes\n");
599 /* begin header processing loop for Citadel message format */
601 if ((mode == MT_CITADEL) || (mode == MT_MIME))
602 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
605 buf[strlen(buf) + 1] = 0;
607 buf[strlen(buf)] = rch;
611 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
612 if (anon_flag == MES_ANON)
613 cprintf("from=****");
614 else if (anon_flag == MES_AN2)
615 cprintf("from=anonymous");
617 cprintf("from=%s", buf);
618 if ((is_room_aide()) && ((anon_flag == MES_ANON)
619 || (anon_flag == MES_AN2)))
620 cprintf(" [%s]", buf);
622 } else if (ch == 'P')
623 cprintf("path=%s\n", buf);
625 cprintf("subj=%s\n", buf);
627 cprintf("msgn=%s\n", buf);
629 cprintf("hnod=%s\n", buf);
631 cprintf("room=%s\n", buf);
633 cprintf("node=%s\n", buf);
635 cprintf("rcpt=%s\n", buf);
637 cprintf("time=%s\n", buf);
638 /* else cprintf("fld%c=%s\n",ch,buf); */
640 /* begin header processing loop for RFC822 transfer format */
644 strcpy(snode, NODENAME);
645 strcpy(lnode, HUMANNODE);
646 if (mode == MT_RFC822)
647 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
650 buf[strlen(buf) + 1] = 0;
652 buf[strlen(buf)] = rch;
657 else if (ch == 'P') {
658 cprintf("Path: %s\n", buf);
659 for (a = 0; a < strlen(buf); ++a) {
661 strcpy(buf, &buf[a + 1]);
666 } else if (ch == 'U')
667 cprintf("Subject: %s\n", buf);
673 cprintf("X-Citadel-Room: %s\n", buf);
677 cprintf("To: %s\n", buf);
678 else if (ch == 'T') {
680 cprintf("Date: %s", asctime(localtime(&xtime)));
683 if (mode == MT_RFC822) {
684 if (!strcasecmp(snode, NODENAME)) {
687 cprintf("Message-ID: <%s@%s>\n", mid, snode);
688 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
689 cprintf("From: %s@%s (%s)\n",
690 suser, snode, luser);
691 cprintf("Organization: %s\n", lnode);
693 /* end header processing loop ... at this point, we're in the text */
696 cprintf("text\n*** ?Message truncated\n000\n");
700 /* do some sort of MIME output */
701 if (format_type == 4) {
702 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
703 mime_parser(mptr, NULL, *list_this_part);
705 if (mode == MT_MIME) { /* If MT_MIME then it's parts only */
712 /* give 'em a length */
714 while (ch = *mptr++, ch > 0) {
717 cprintf("mlen=%ld\n", msg_len);
722 /* signify start of msg text */
723 if (mode == MT_CITADEL)
725 if ((mode == MT_RFC822) && (format_type != 4))
728 /* If the format type on disk is 1 (fixed-format), then we want
729 * everything to be output completely literally ... regardless of
730 * what message transfer format is in use.
732 if (format_type == 1) {
734 while (ch = *mptr++, ch > 0) {
737 if ((ch == 10) || (strlen(buf) > 250)) {
738 cprintf("%s\n", buf);
741 buf[strlen(buf) + 1] = 0;
742 buf[strlen(buf)] = ch;
746 cprintf("%s\n", buf);
748 /* If the message on disk is format 0 (Citadel vari-format), we
749 * output using the formatter at 80 columns. This is the final output
750 * form if the transfer format is RFC822, but if the transfer format
751 * is Citadel proprietary, it'll still work, because the indentation
752 * for new paragraphs is correct and the client will reformat the
753 * message to the reader's screen width.
755 if (format_type == 0) {
756 memfmout(80, mptr, 0);
758 /* If the message on disk is format 4 (MIME), we've gotta hand it
759 * off to the MIME parser. The client has already been told that
760 * this message is format 1 (fixed format), so the callback function
761 * we use will display those parts as-is.
763 if (format_type == 4) {
764 mime_parser(mptr, NULL, *fixed_output);
774 * display a message (mode 0 - Citadel proprietary)
776 void cmd_msg0(char *cmdbuf)
779 int headers_only = 0;
781 extract(msgid, cmdbuf, 0);
782 headers_only = extract_int(cmdbuf, 1);
784 output_message(msgid, MT_CITADEL, headers_only);
790 * display a message (mode 2 - RFC822)
792 void cmd_msg2(char *cmdbuf)
795 int headers_only = 0;
797 extract(msgid, cmdbuf, 0);
798 headers_only = extract_int(cmdbuf, 1);
800 output_message(msgid, MT_RFC822, headers_only);
804 * display a message (mode 3 - IGnet raw format - internal programs only)
806 void cmd_msg3(char *cmdbuf)
809 int headers_only = 0;
811 if (CC->internal_pgm == 0) {
812 cprintf("%d This command is for internal programs only.\n",
816 extract(msgid, cmdbuf, 0);
817 headers_only = extract_int(cmdbuf, 1);
819 output_message(msgid, MT_RAW, headers_only);
823 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
825 void cmd_msg4(char *cmdbuf)
829 extract(msgid, cmdbuf, 0);
831 output_message(msgid, MT_MIME, 0);
837 * Open a component of a MIME message as a download file
839 void cmd_opna(char *cmdbuf)
843 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
845 extract(msgid, cmdbuf, 0);
846 extract(desired_section, cmdbuf, 1);
848 output_message(msgid, MT_DOWNLOAD, 0);
854 * Message base operation to send a message to the master file
855 * (returns new message number)
857 long send_message(char *message_in_memory, /* pointer to buffer */
858 size_t message_length, /* length of buffer */
860 { /* 1 to generate an I field */
863 char *actual_message;
864 size_t actual_length;
868 /* Get a new message number */
869 newmsgid = get_new_message_number();
872 sprintf(msgidbuf, "I%ld", newmsgid);
873 actual_length = message_length + strlen(msgidbuf) + 1;
874 actual_message = mallok(actual_length);
875 memcpy(actual_message, message_in_memory, 3);
876 memcpy(&actual_message[3], msgidbuf, (strlen(msgidbuf) + 1));
877 memcpy(&actual_message[strlen(msgidbuf) + 4],
878 &message_in_memory[3], message_length - 3);
880 actual_message = message_in_memory;
881 actual_length = message_length;
884 /* Write our little bundle of joy into the message base */
885 begin_critical_section(S_MSGMAIN);
886 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
887 actual_message, actual_length) < 0) {
888 lprintf(2, "Can't store message\n");
893 end_critical_section(S_MSGMAIN);
896 phree(actual_message);
898 /* Finally, return the pointers */
905 * this is a simple file copy routine.
907 void copy_file(char *from, char *to)
912 ffp = fopen(from, "r");
915 tfp = fopen(to, "w");
920 while (a = getc(ffp), a >= 0) {
931 * message base operation to save a message and install its pointers
933 void save_message(char *mtmp, /* file containing proper message */
934 char *rec, /* Recipient (if mail) */
935 char *force, /* if non-zero length, force a room */
936 int mailtype, /* local or remote type, see citadel.h */
938 { /* set to 1 to generate an 'I' field */
940 char hold_rm[ROOMNAMELEN];
941 char actual_rm[ROOMNAMELEN];
942 char force_room[ROOMNAMELEN];
943 char content_type[256]; /* We have to learn this */
947 char *message_in_memory;
952 struct usersupp userbuf;
954 static int seqnum = 0;
955 int successful_local_recipients = 0;
956 struct quickroom qtemp;
957 struct SuppMsgInfo smi;
959 lprintf(9, "save_message(%s,%s,%s,%d,%d)\n",
960 mtmp, rec, force, mailtype, generate_id);
962 strcpy(force_room, force);
964 /* Strip non-printable characters out of the recipient name */
965 strcpy(recipient, rec);
966 for (a = 0; a < strlen(recipient); ++a)
967 if (!isprint(recipient[a]))
968 strcpy(&recipient[a], &recipient[a + 1]);
970 /* Measure the message */
971 stat(mtmp, &statbuf);
972 templen = statbuf.st_size;
974 /* Now read it into memory */
975 message_in_memory = (char *) mallok(templen);
976 if (message_in_memory == NULL) {
977 lprintf(2, "Can't allocate memory to save message!\n");
980 fp = fopen(mtmp, "rb");
981 fread(message_in_memory, templen, 1, fp);
984 /* Learn about what's inside, because it's what's inside that counts */
985 mptr = message_in_memory;
986 ++mptr; /* advance past 0xFF header */
987 ++mptr; /* advance past anon flag */
991 strcpy(content_type, "text/x-citadel-variformat");
994 strcpy(content_type, "text/plain");
997 strcpy(content_type, "text/plain");
998 /* advance past header fields */
999 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
1006 if (!strncasecmp(mptr, "Content-type: ", 14)) {
1007 safestrncpy(content_type, mptr,
1008 sizeof(content_type));
1009 lprintf(9, "%s\n", content_type);
1010 strcpy(content_type, &content_type[14]);
1011 for (a=0; a<strlen(content_type); ++a)
1012 if ( (content_type[a]==';')
1013 || (content_type[a]==' ')
1014 || (content_type[a]==13)
1015 || (content_type[a]==10) )
1016 content_type[a] = 0;
1022 lprintf(9, "Content type is <%s>\n", content_type);
1024 /* Save it to disk */
1025 newmsgid = send_message(message_in_memory, templen, generate_id);
1026 phree(message_in_memory);
1030 strcpy(actual_rm, CC->quickroom.QRname);
1031 strcpy(hold_rm, "");
1033 /* If this is being done by the networker delivering a private
1034 * message, we want to BYPASS saving the sender's copy (because there
1035 * is no local sender; it would otherwise go to the Trashcan).
1037 if ( (!CC->internal_pgm) || (strlen(recipient)==0) ) {
1038 /* If the user is a twit, move to the twit room for posting */
1040 if (CC->usersupp.axlevel == 2) {
1041 strcpy(hold_rm, actual_rm);
1042 strcpy(actual_rm, config.c_twitroom);
1044 /* ...or if this message is destined for Aide> then go there. */
1045 lprintf(9, "actual room forcing loop\n");
1046 if (strlen(force_room) > 0) {
1047 strcpy(hold_rm, actual_rm);
1048 strcpy(actual_rm, force_room);
1050 /* This call to usergoto() changes rooms if necessary. It also
1051 * causes the latest message list to be read into memory.
1053 usergoto(actual_rm, 0);
1055 /* read in the quickroom record, obtaining a lock... */
1056 lgetroom(&CC->quickroom, actual_rm);
1058 /* Fix an obscure bug */
1059 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1060 CC->quickroom.QRflags =
1061 CC->quickroom.QRflags & ~QR_MAILBOX;
1064 /* Add the message pointer to the room */
1065 CC->quickroom.QRhighest =
1066 AddMessageToRoom(&CC->quickroom, newmsgid);
1068 /* update quickroom */
1069 lputroom(&CC->quickroom);
1070 ++successful_local_recipients;
1073 /* Network mail - send a copy to the network program. */
1074 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1075 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1076 (long) getpid(), CC->cs_pid, ++seqnum);
1077 copy_file(mtmp, aaa);
1078 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1080 /* Bump this user's messages posted counter. */
1081 lgetuser(&CC->usersupp, CC->curr_user);
1082 CC->usersupp.posted = CC->usersupp.posted + 1;
1083 lputuser(&CC->usersupp);
1085 /* If this is private, local mail, make a copy in the
1086 * recipient's mailbox and bump the reference count.
1088 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1089 if (getuser(&userbuf, recipient) == 0) {
1090 MailboxName(actual_rm, &userbuf, MAILROOM);
1091 lprintf(9, "Targeting mailbox: <%s>\n", actual_rm);
1092 if (lgetroom(&qtemp, actual_rm) == 0) {
1094 AddMessageToRoom(&qtemp, newmsgid);
1096 ++successful_local_recipients;
1100 /* If we've posted in a room other than the current room, then we
1101 * have to now go back to the current room...
1103 if (strlen(hold_rm) > 0) {
1104 usergoto(hold_rm, 0);
1106 unlink(mtmp); /* delete the temporary file */
1108 /* Write a supplemental message info record. This doesn't have to
1109 * be a critical section because nobody else knows about this message
1112 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1113 smi.smi_msgnum = newmsgid;
1114 smi.smi_refcount = successful_local_recipients;
1115 safestrncpy(smi.smi_content_type, content_type, 64);
1116 PutSuppMsgInfo(&smi);
1121 * Generate an administrative message and post it in the Aide> room.
1123 void aide_message(char *text)
1127 fp = fopen(CC->temp, "wb");
1128 fprintf(fp, "%c%c%c", 255, MES_NORMAL, 0);
1129 fprintf(fp, "Psysop%c", 0);
1130 fprintf(fp, "T%ld%c", (long) time(NULL), 0);
1131 fprintf(fp, "ACitadel%c", 0);
1132 fprintf(fp, "OAide%c", 0);
1133 fprintf(fp, "N%s%c", NODENAME, 0);
1134 fprintf(fp, "M%s\n%c", text, 0);
1136 save_message(CC->temp, "", AIDEROOM, MES_LOCAL, 1);
1137 syslog(LOG_NOTICE, text);
1143 * Build a binary message to be saved on disk.
1146 char *filename, /* temporary file name */
1147 struct usersupp *author, /* author's usersupp structure */
1148 char *recipient, /* NULL if it's not mail */
1149 char *room, /* room where it's going */
1150 int type, /* see MES_ types in header file */
1151 int net_type, /* see MES_ types in header file */
1152 int format_type, /* local or remote (see citadel.h) */
1154 { /* who we're masquerading as */
1162 /* Don't confuse the poor folks if it's not routed mail. */
1163 strcpy(dest_node, "");
1166 /* If net_type is MES_BINARY, split out the destination node. */
1167 if (net_type == MES_BINARY) {
1168 strcpy(dest_node, NODENAME);
1169 for (a = 0; a < strlen(recipient); ++a) {
1170 if (recipient[a] == '@') {
1172 strcpy(dest_node, &recipient[a + 1]);
1176 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1177 if (net_type == MES_INTERNET) {
1178 strcpy(dest_node, "internet");
1180 while (isspace(recipient[strlen(recipient) - 1]))
1181 recipient[strlen(recipient) - 1] = 0;
1184 fp = fopen(filename, "w");
1186 putc(type, fp); /* Normal or anonymous, see MES_ flags */
1187 putc(format_type, fp); /* Formatted or unformatted */
1188 fprintf(fp, "Pcit%ld%c", author->usernum, 0); /* path */
1189 fprintf(fp, "T%ld%c", (long) now, 0); /* date/time */
1191 fprintf(fp, "A%s%c", fake_name, 0);
1193 fprintf(fp, "A%s%c", author->fullname, 0); /* author */
1195 if (CC->quickroom.QRflags & QR_MAILBOX) { /* room */
1196 fprintf(fp, "O%s%c", &CC->quickroom.QRname[11], 0);
1198 fprintf(fp, "O%s%c", CC->quickroom.QRname, 0);
1201 fprintf(fp, "N%s%c", NODENAME, 0); /* nodename */
1202 fprintf(fp, "H%s%c", HUMANNODE, 0); /* human nodename */
1204 if (recipient[0] != 0)
1205 fprintf(fp, "R%s%c", recipient, 0);
1206 if (dest_node[0] != 0)
1207 fprintf(fp, "D%s%c", dest_node, 0);
1211 while (client_gets(buf), strcmp(buf, "000")) {
1212 fprintf(fp, "%s\n", buf);
1223 * message entry - mode 0 (normal) <bc>
1225 void cmd_ent0(char *entargs)
1228 char recipient[256];
1230 int format_type = 0;
1231 char newusername[256]; /* <bc> */
1236 struct usersupp tempUS;
1239 post = extract_int(entargs, 0);
1240 extract(recipient, entargs, 1);
1241 anon_flag = extract_int(entargs, 2);
1242 format_type = extract_int(entargs, 3);
1244 /* first check to make sure the request is valid. */
1246 if (!(CC->logged_in)) {
1247 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1250 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1251 cprintf("%d Need to be validated to enter ",
1252 ERROR + HIGHER_ACCESS_REQUIRED);
1253 cprintf("(except in %s> to sysop)\n", MAILROOM);
1256 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1257 cprintf("%d Need net privileges to enter here.\n",
1258 ERROR + HIGHER_ACCESS_REQUIRED);
1261 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1262 cprintf("%d Sorry, this is a read-only room.\n",
1263 ERROR + HIGHER_ACCESS_REQUIRED);
1269 if (post == 2) { /* <bc> */
1270 if (CC->usersupp.axlevel < 6) {
1271 cprintf("%d You don't have permission to do an aide post.\n",
1272 ERROR + HIGHER_ACCESS_REQUIRED);
1275 extract(newusername, entargs, 4);
1276 memset(CC->fake_postname, 0, 32);
1277 strcpy(CC->fake_postname, newusername);
1278 cprintf("%d Ok\n", OK);
1281 CC->cs_flags |= CS_POSTING;
1284 if (CC->quickroom.QRflags & QR_MAILBOX) {
1285 if (CC->usersupp.axlevel >= 2) {
1286 strcpy(buf, recipient);
1288 strcpy(buf, "sysop");
1289 lprintf(9, "calling alias()\n");
1290 e = alias(buf); /* alias and mail type */
1291 lprintf(9, "alias() returned %d\n", e);
1292 if ((buf[0] == 0) || (e == MES_ERROR)) {
1293 cprintf("%d Unknown address - cannot send message.\n",
1294 ERROR + NO_SUCH_USER);
1297 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1298 cprintf("%d Net privileges required for network mail.\n",
1299 ERROR + HIGHER_ACCESS_REQUIRED);
1302 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1303 && ((CC->usersupp.flags & US_INTERNET) == 0)
1304 && (!CC->internal_pgm)) {
1305 cprintf("%d You don't have access to Internet mail.\n",
1306 ERROR + HIGHER_ACCESS_REQUIRED);
1309 if (!strcasecmp(buf, "sysop")) {
1314 goto SKFALL; /* don't search local file */
1315 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1316 cprintf("%d Can't send mail to yourself!\n",
1317 ERROR + NO_SUCH_USER);
1320 /* Check to make sure the user exists; also get the correct
1321 * upper/lower casing of the name.
1323 a = getuser(&tempUS, buf);
1325 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1328 strcpy(buf, tempUS.fullname);
1330 SKFALL:b = MES_NORMAL;
1331 if (CC->quickroom.QRflags & QR_ANONONLY)
1333 if (CC->quickroom.QRflags & QR_ANONOPT) {
1337 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1340 /* If we're only checking the validity of the request, return
1341 * success without creating the message.
1344 cprintf("%d %s\n", OK, buf);
1347 cprintf("%d send message\n", SEND_LISTING);
1348 if (CC->fake_postname[0])
1349 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, CC->fake_postname);
1350 else if (CC->fake_username[0])
1351 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, CC->fake_username);
1353 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, "");
1354 save_message(CC->temp, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1355 CC->fake_postname[0] = '\0';
1362 * message entry - mode 3 (raw)
1364 void cmd_ent3(char *entargs)
1370 struct usersupp tempUS;
1375 if (CC->internal_pgm == 0) {
1376 cprintf("%d This command is for internal programs only.\n",
1380 /* See if there's a recipient, but make sure it's a real one */
1381 extract(recp, entargs, 1);
1382 for (a = 0; a < strlen(recp); ++a)
1383 if (!isprint(recp[a]))
1384 strcpy(&recp[a], &recp[a + 1]);
1385 while (isspace(recp[0]))
1386 strcpy(recp, &recp[1]);
1387 while (isspace(recp[strlen(recp) - 1]))
1388 recp[strlen(recp) - 1] = 0;
1390 /* If we're in Mail, check the recipient */
1391 if (strlen(recp) > 0) {
1392 e = alias(recp); /* alias and mail type */
1393 if ((recp[0] == 0) || (e == MES_ERROR)) {
1394 cprintf("%d Unknown address - cannot send message.\n",
1395 ERROR + NO_SUCH_USER);
1398 if (e == MES_LOCAL) {
1399 a = getuser(&tempUS, recp);
1401 cprintf("%d No such user.\n",
1402 ERROR + NO_SUCH_USER);
1407 /* At this point, message has been approved. */
1408 if (extract_int(entargs, 0) == 0) {
1409 cprintf("%d OK to send\n", OK);
1412 /* open a temp file to hold the message */
1413 fp = fopen(CC->temp, "wb");
1415 cprintf("%d Cannot open %s: %s\n",
1416 ERROR + INTERNAL_ERROR,
1417 CC->temp, strerror(errno));
1420 msglen = extract_long(entargs, 2);
1421 cprintf("%d %ld\n", SEND_BINARY, msglen);
1422 while (msglen > 0L) {
1423 bloklen = ((msglen >= 255L) ? 255 : msglen);
1424 client_read(buf, (int) bloklen);
1425 fwrite(buf, (int) bloklen, 1, fp);
1426 msglen = msglen - bloklen;
1430 save_message(CC->temp, recp, "", e, 0);
1435 * API function to delete messages which match a set of criteria
1436 * (returns the actual number of messages deleted)
1437 * FIX ... still need to implement delete by content type
1439 int CtdlDeleteMessages( char *room_name, /* which room */
1440 long dmsgnum, /* or "0" for any */
1441 char *content_type /* or NULL for any */
1444 struct quickroom qrbuf;
1445 struct cdbdata *cdbfr;
1446 long *msglist = NULL;
1449 int num_deleted = 0;
1451 struct SuppMsgInfo smi;
1453 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1454 room_name, dmsgnum, content_type);
1456 /* get room record, obtaining a lock... */
1457 if (lgetroom(&qrbuf, room_name) != 0) {
1458 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1460 return(0); /* room not found */
1463 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1465 lprintf(9, "doing mallok/memcpy loop\n");
1466 if (cdbfr != NULL) {
1467 msglist = mallok(cdbfr->len);
1468 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1469 num_msgs = cdbfr->len / sizeof(long);
1474 for (i=0; i<num_msgs; ++i) {
1477 /* Set/clear a bit for each criterion */
1479 if ( (dmsgnum == 0L) || (msglist[i]==dmsgnum) ) {
1480 delete_this |= 0x01;
1483 if (content_type == NULL) {
1484 delete_this |= 0x02;
1486 GetSuppMsgInfo(&smi, msglist[i]);
1487 if (!strcasecmp(smi.smi_content_type,
1489 delete_this |= 0x02;
1493 /* Delete message only if all bits are set */
1494 if (delete_this == 0x03) {
1495 AdjRefCount(msglist[i], -1);
1501 num_msgs = sort_msglist(msglist, num_msgs);
1502 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1503 msglist, (num_msgs * sizeof(long)) );
1505 qrbuf.QRhighest = msglist[num_msgs - 1];
1509 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1510 return(num_deleted);
1516 * Delete message from current room
1518 void cmd_dele(char *delstr)
1523 getuser(&CC->usersupp, CC->curr_user);
1524 if ((CC->usersupp.axlevel < 6)
1525 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1526 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1527 cprintf("%d Higher access required.\n",
1528 ERROR + HIGHER_ACCESS_REQUIRED);
1531 delnum = extract_long(delstr, 0);
1533 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1536 cprintf("%d %d message%s deleted.\n", OK,
1537 num_deleted, ((num_deleted!=1) ? "s" : "") );
1539 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1545 * move a message to another room
1547 void cmd_move(char *args)
1551 struct quickroom qtemp;
1554 num = extract_long(args, 0);
1555 extract(targ, args, 1);
1557 getuser(&CC->usersupp, CC->curr_user);
1558 if ((CC->usersupp.axlevel < 6)
1559 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1560 cprintf("%d Higher access required.\n",
1561 ERROR + HIGHER_ACCESS_REQUIRED);
1564 if (getroom(&qtemp, targ) != 0) {
1565 cprintf("%d '%s' does not exist.\n", ERROR, targ);
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 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1586 * GetSuppMsgInfo() - Get the supplementary record for a message
1588 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1591 struct cdbdata *cdbsmi;
1594 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1595 smibuf->smi_msgnum = msgnum;
1596 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1598 /* Use the negative of the message number for its supp record index */
1599 TheIndex = (0L - msgnum);
1601 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1602 if (cdbsmi == NULL) {
1603 return; /* record not found; go with defaults */
1605 memcpy(smibuf, cdbsmi->ptr,
1606 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1607 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1614 * PutSuppMsgInfo() - (re)write supplementary record for a message
1616 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1620 /* Use the negative of the message number for its supp record index */
1621 TheIndex = (0L - smibuf->smi_msgnum);
1623 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1624 smibuf->smi_msgnum, smibuf->smi_refcount);
1626 cdb_store(CDB_MSGMAIN,
1627 &TheIndex, sizeof(long),
1628 smibuf, sizeof(struct SuppMsgInfo));
1635 * AdjRefCount - change the reference count for a message;
1636 * delete the message if it reaches zero
1638 void AdjRefCount(long msgnum, int incr)
1641 struct SuppMsgInfo smi;
1644 /* This is a *tight* critical section; please keep it that way, as
1645 * it may get called while nested in other critical sections.
1646 * Complicating this any further will surely cause deadlock!
1648 begin_critical_section(S_SUPPMSGMAIN);
1649 GetSuppMsgInfo(&smi, msgnum);
1650 smi.smi_refcount += incr;
1651 PutSuppMsgInfo(&smi);
1652 end_critical_section(S_SUPPMSGMAIN);
1654 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1655 msgnum, smi.smi_refcount);
1657 /* If the reference count is now zero, delete the message
1658 * (and its supplementary record as well).
1660 if (smi.smi_refcount == 0) {
1661 lprintf(9, "Deleting message <%ld>\n", msgnum);
1663 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1664 delnum = (0L - msgnum);
1665 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1671 * Write a generic object to this room
1673 void CtdlWriteObject( char *req_room, /* Room to stuff it in */
1674 char *content_type, /* MIME type of this object */
1675 char *tempfilename, /* Where to fetch it from */
1676 int is_mailbox, /* Private mailbox room? */
1677 int is_binary, /* Is encoding necessary? */
1678 int is_unique /* Del others of this type? */
1682 char filename[PATH_MAX];
1685 struct quickroom qrbuf;
1686 char roomname[ROOMNAMELEN];
1688 if (is_mailbox) MailboxName(roomname, &CC->usersupp, req_room);
1689 else safestrncpy(roomname, req_room, sizeof(roomname));
1691 strcpy(filename, tmpnam(NULL));
1692 fp = fopen(filename, "w");
1693 if (fp == NULL) return;
1695 fprintf(fp, "%c%c%c", 0xFF, MES_NORMAL, 4);
1696 fprintf(fp, "T%ld%c", time(NULL), 0);
1697 fprintf(fp, "A%s%c", CC->usersupp.fullname, 0);
1698 fprintf(fp, "O%s%c", roomname, 0);
1699 fprintf(fp, "N%s%c", config.c_nodename, 0);
1700 fprintf(fp, "MContent-type: %s\n", content_type);
1702 tempfp = fopen(tempfilename, "r");
1703 if (tempfp == NULL) {
1709 if (is_binary == 0) {
1710 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
1711 while (ch=getc(tempfp), ch>0) putc(ch, fp);
1716 fprintf(fp, "Content-transfer-encoding: base64\n\n");
1719 sprintf(cmdbuf, "./base64 -e <%s >>%s",
1720 tempfilename, filename);
1724 /* Create the requested room if we have to. */
1725 if (getroom(&qrbuf, roomname) != 0) {
1726 create_room(roomname, 4, "", 0);
1729 /* If the caller specified this object as unique, delete all
1730 * other objects of this type that are currently in the room.
1733 lprintf(9, "Deleted %d other msgs of this type\n",
1734 CtdlDeleteMessages(roomname, 0L, content_type) );
1737 /* Now write the data */
1738 save_message(filename, "", roomname, MES_LOCAL, 1);