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) ) {
202 get_msglist(&CC->quickroom);
203 getuser(&CC->usersupp, CC->curr_user);
204 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
206 if (CC->num_msgs != 0) for (a = 0; a < (CC->num_msgs); ++a) {
207 if ((MessageFromList(a) >= 0)
211 || ((mode == MSGS_OLD) && (MessageFromList(a) <= vbuf.v_lastseen))
212 || ((mode == MSGS_NEW) && (MessageFromList(a) > vbuf.v_lastseen))
213 || ((mode == MSGS_NEW) && (MessageFromList(a) >= vbuf.v_lastseen)
214 && (CC->usersupp.flags & US_LASTOLD))
215 || ((mode == MSGS_LAST) && (a >= (CC->num_msgs - ref)))
216 || ((mode == MSGS_FIRST) && (a < ref))
217 || ((mode == MSGS_GT) && (MessageFromList(a) > ref))
220 CallBack(MessageFromList(a));
228 * cmd_msgs() - get list of message #'s in this room
229 * implements the MSGS server command using CtdlForEachMessage()
231 void cmd_msgs(char *cmdbuf)
237 extract(which, cmdbuf, 0);
238 cm_ref = extract_int(cmdbuf, 1);
242 if (!strncasecmp(which, "OLD", 3))
244 else if (!strncasecmp(which, "NEW", 3))
246 else if (!strncasecmp(which, "FIRST", 5))
248 else if (!strncasecmp(which, "LAST", 4))
250 else if (!strncasecmp(which, "GT", 2))
253 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
254 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
258 cprintf("%d Message list...\n", LISTING_FOLLOWS);
259 CtdlForEachMessage(mode, cm_ref, simple_listing);
267 * help_subst() - support routine for help file viewer
269 void help_subst(char *strbuf, char *source, char *dest)
274 while (p = pattern2(strbuf, source), (p >= 0)) {
275 strcpy(workbuf, &strbuf[p + strlen(source)]);
276 strcpy(&strbuf[p], dest);
277 strcat(strbuf, workbuf);
282 void do_help_subst(char *buffer)
286 help_subst(buffer, "^nodename", config.c_nodename);
287 help_subst(buffer, "^humannode", config.c_humannode);
288 help_subst(buffer, "^fqdn", config.c_fqdn);
289 help_subst(buffer, "^username", CC->usersupp.fullname);
290 sprintf(buf2, "%ld", CC->usersupp.usernum);
291 help_subst(buffer, "^usernum", buf2);
292 help_subst(buffer, "^sysadm", config.c_sysadm);
293 help_subst(buffer, "^variantname", CITADEL);
294 sprintf(buf2, "%d", config.c_maxsessions);
295 help_subst(buffer, "^maxsessions", buf2);
301 * memfmout() - Citadel text formatter and paginator.
302 * Although the original purpose of this routine was to format
303 * text to the reader's screen width, all we're really using it
304 * for here is to format text out to 80 columns before sending it
305 * to the client. The client software may reformat it again.
307 void memfmout(int width, char *mptr, char subst)
308 /* screen width to use */
309 /* where are we going to get our text from? */
310 /* nonzero if we should use hypertext mode */
322 c = 1; /* c is the current pos */
325 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
327 buffer[strlen(buffer) + 1] = 0;
328 buffer[strlen(buffer)] = ch;
331 if (buffer[0] == '^')
332 do_help_subst(buffer);
334 buffer[strlen(buffer) + 1] = 0;
336 strcpy(buffer, &buffer[1]);
345 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
347 if (((old == 13) || (old == 10)) && (isspace(real))) {
355 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
356 cprintf("\n%s", aaa);
365 if ((strlen(aaa) + c) > (width - 5)) {
375 if ((ch == 13) || (ch == 10)) {
376 cprintf("%s\n", aaa);
383 FMTEND: cprintf("%s\n", aaa);
389 * Callback function for mime parser that simply lists the part
391 void list_this_part(char *name, char *filename, char *partnum, char *disp,
392 void *content, char *cbtype, size_t length)
395 cprintf("part=%s|%s|%s|%s|%s|%d\n",
396 name, filename, partnum, disp, cbtype, length);
401 * Callback function for mime parser that wants to display text
403 void fixed_output(char *name, char *filename, char *partnum, char *disp,
404 void *content, char *cbtype, size_t length)
407 if (!strcasecmp(cbtype, "text/plain")) {
408 client_write(content, length);
410 cprintf("Part %s: %s (%s) (%d bytes)\n",
411 partnum, filename, cbtype, length);
417 * Callback function for mime parser that opens a section for downloading
419 void mime_download(char *name, char *filename, char *partnum, char *disp,
420 void *content, char *cbtype, size_t length)
423 /* Silently go away if there's already a download open... */
424 if (CC->download_fp != NULL)
427 /* ...or if this is not the desired section */
428 if (strcasecmp(desired_section, partnum))
431 CC->download_fp = tmpfile();
432 if (CC->download_fp == NULL)
435 fwrite(content, length, 1, CC->download_fp);
436 fflush(CC->download_fp);
437 rewind(CC->download_fp);
439 OpenCmdResult(filename, cbtype);
445 * Get a message off disk. (return value is the message's timestamp)
448 time_t output_message(char *msgid, int mode, int headers_only)
453 CIT_UBYTE format_type, anon_flag;
458 struct cdbdata *dmsgtext;
461 /* buffers needed for RFC822 translation */
470 msg_num = atol(msgid);
473 if ((!(CC->logged_in)) && (!(CC->internal_pgm)) && (mode != MT_DATE)) {
474 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
477 /* We used to need to check in the current room's message list
478 * to determine where the message's disk position. We no longer need
479 * to do this, but we do it anyway as a security measure, in order to
480 * prevent rogue clients from reading messages not in the current room.
484 if (CC->num_msgs > 0) {
485 for (a = 0; a < CC->num_msgs; ++a) {
486 if (MessageFromList(a) == msg_num) {
493 cprintf("%d Message %ld is not in this room.\n",
497 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msg_num, sizeof(long));
499 if (dmsgtext == NULL) {
501 cprintf("%d Can't find message %ld\n",
502 ERROR + INTERNAL_ERROR);
505 msg_len = (long) dmsgtext->len;
506 mptr = dmsgtext->ptr;
508 /* this loop spews out the whole message if we're doing raw format */
509 if (mode == MT_RAW) {
510 cprintf("%d %ld\n", BINARY_FOLLOWS, msg_len);
511 client_write(dmsgtext->ptr, (int) msg_len);
515 /* Otherwise, we'll start parsing it field by field... */
518 cprintf("%d Illegal message format on disk\n",
519 ERROR + INTERNAL_ERROR);
524 format_type = *mptr++;
526 /* Are we downloading a MIME component? */
527 if (mode == MT_DOWNLOAD) {
528 if (format_type != 4) {
529 cprintf("%d This is not a MIME message.\n",
531 } else if (CC->download_fp != NULL) {
532 cprintf("%d You already have a download open.\n",
535 /* Skip to the message body */
536 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
539 buf[strlen(buf) + 1] = 0;
541 buf[strlen(buf)] = rch;
545 mime_parser(mptr, NULL, *mime_download);
546 /* If there's no file open by this time, the requested
547 * section wasn't found, so print an error
549 if (CC->download_fp == NULL) {
550 cprintf("%d Section %s not found.\n",
551 ERROR + FILE_NOT_FOUND,
558 /* Are we just looking for the message date? */
560 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
563 buf[strlen(buf) + 1] = 0;
565 buf[strlen(buf)] = rch;
574 /* now for the user-mode message reading loops */
575 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
577 if (mode == MT_CITADEL)
578 cprintf("type=%d\n", format_type);
580 if ((anon_flag == MES_ANON) && (mode == MT_CITADEL)) {
581 cprintf("nhdr=yes\n");
583 /* begin header processing loop for Citadel message format */
585 if ((mode == MT_CITADEL) || (mode == MT_MIME))
586 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
589 buf[strlen(buf) + 1] = 0;
591 buf[strlen(buf)] = rch;
595 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
596 if (anon_flag == MES_ANON)
597 cprintf("from=****");
598 else if (anon_flag == MES_AN2)
599 cprintf("from=anonymous");
601 cprintf("from=%s", buf);
602 if ((is_room_aide()) && ((anon_flag == MES_ANON)
603 || (anon_flag == MES_AN2)))
604 cprintf(" [%s]", buf);
606 } else if (ch == 'P')
607 cprintf("path=%s\n", buf);
609 cprintf("subj=%s\n", buf);
611 cprintf("msgn=%s\n", buf);
613 cprintf("hnod=%s\n", buf);
615 cprintf("room=%s\n", buf);
617 cprintf("node=%s\n", buf);
619 cprintf("rcpt=%s\n", buf);
621 cprintf("time=%s\n", buf);
622 /* else cprintf("fld%c=%s\n",ch,buf); */
624 /* begin header processing loop for RFC822 transfer format */
628 strcpy(snode, NODENAME);
629 strcpy(lnode, HUMANNODE);
630 if (mode == MT_RFC822)
631 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
634 buf[strlen(buf) + 1] = 0;
636 buf[strlen(buf)] = rch;
641 else if (ch == 'P') {
642 cprintf("Path: %s\n", buf);
643 for (a = 0; a < strlen(buf); ++a) {
645 strcpy(buf, &buf[a + 1]);
650 } else if (ch == 'U')
651 cprintf("Subject: %s\n", buf);
657 cprintf("X-Citadel-Room: %s\n", buf);
661 cprintf("To: %s\n", buf);
662 else if (ch == 'T') {
664 cprintf("Date: %s", asctime(localtime(&xtime)));
667 if (mode == MT_RFC822) {
668 if (!strcasecmp(snode, NODENAME)) {
671 cprintf("Message-ID: <%s@%s>\n", mid, snode);
672 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
673 cprintf("From: %s@%s (%s)\n",
674 suser, snode, luser);
675 cprintf("Organization: %s\n", lnode);
677 /* end header processing loop ... at this point, we're in the text */
680 cprintf("text\n*** ?Message truncated\n000\n");
684 /* do some sort of MIME output */
685 if (format_type == 4) {
686 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
687 mime_parser(mptr, NULL, *list_this_part);
689 if (mode == MT_MIME) { /* If MT_MIME then it's parts only */
696 /* give 'em a length */
698 while (ch = *mptr++, ch > 0) {
701 cprintf("mlen=%ld\n", msg_len);
706 /* signify start of msg text */
707 if (mode == MT_CITADEL)
709 if ((mode == MT_RFC822) && (format_type != 4))
712 /* If the format type on disk is 1 (fixed-format), then we want
713 * everything to be output completely literally ... regardless of
714 * what message transfer format is in use.
716 if (format_type == 1) {
718 while (ch = *mptr++, ch > 0) {
721 if ((ch == 10) || (strlen(buf) > 250)) {
722 cprintf("%s\n", buf);
725 buf[strlen(buf) + 1] = 0;
726 buf[strlen(buf)] = ch;
730 cprintf("%s\n", buf);
732 /* If the message on disk is format 0 (Citadel vari-format), we
733 * output using the formatter at 80 columns. This is the final output
734 * form if the transfer format is RFC822, but if the transfer format
735 * is Citadel proprietary, it'll still work, because the indentation
736 * for new paragraphs is correct and the client will reformat the
737 * message to the reader's screen width.
739 if (format_type == 0) {
740 memfmout(80, mptr, 0);
742 /* If the message on disk is format 4 (MIME), we've gotta hand it
743 * off to the MIME parser. The client has already been told that
744 * this message is format 1 (fixed format), so the callback function
745 * we use will display those parts as-is.
747 if (format_type == 4) {
748 mime_parser(mptr, NULL, *fixed_output);
758 * display a message (mode 0 - Citadel proprietary)
760 void cmd_msg0(char *cmdbuf)
763 int headers_only = 0;
765 extract(msgid, cmdbuf, 0);
766 headers_only = extract_int(cmdbuf, 1);
768 output_message(msgid, MT_CITADEL, headers_only);
774 * display a message (mode 2 - RFC822)
776 void cmd_msg2(char *cmdbuf)
779 int headers_only = 0;
781 extract(msgid, cmdbuf, 0);
782 headers_only = extract_int(cmdbuf, 1);
784 output_message(msgid, MT_RFC822, headers_only);
788 * display a message (mode 3 - IGnet raw format - internal programs only)
790 void cmd_msg3(char *cmdbuf)
793 int headers_only = 0;
795 if (CC->internal_pgm == 0) {
796 cprintf("%d This command is for internal programs only.\n",
800 extract(msgid, cmdbuf, 0);
801 headers_only = extract_int(cmdbuf, 1);
803 output_message(msgid, MT_RAW, headers_only);
807 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
809 void cmd_msg4(char *cmdbuf)
813 extract(msgid, cmdbuf, 0);
815 output_message(msgid, MT_MIME, 0);
821 * Open a component of a MIME message as a download file
823 void cmd_opna(char *cmdbuf)
827 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
829 extract(msgid, cmdbuf, 0);
830 extract(desired_section, cmdbuf, 1);
832 output_message(msgid, MT_DOWNLOAD, 0);
838 * Message base operation to send a message to the master file
839 * (returns new message number)
841 long send_message(char *message_in_memory, /* pointer to buffer */
842 size_t message_length, /* length of buffer */
844 { /* 1 to generate an I field */
847 char *actual_message;
848 size_t actual_length;
852 /* Get a new message number */
853 newmsgid = get_new_message_number();
856 sprintf(msgidbuf, "I%ld", newmsgid);
857 actual_length = message_length + strlen(msgidbuf) + 1;
858 actual_message = mallok(actual_length);
859 memcpy(actual_message, message_in_memory, 3);
860 memcpy(&actual_message[3], msgidbuf, (strlen(msgidbuf) + 1));
861 memcpy(&actual_message[strlen(msgidbuf) + 4],
862 &message_in_memory[3], message_length - 3);
864 actual_message = message_in_memory;
865 actual_length = message_length;
868 /* Write our little bundle of joy into the message base */
869 begin_critical_section(S_MSGMAIN);
870 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
871 actual_message, actual_length) < 0) {
872 lprintf(2, "Can't store message\n");
877 end_critical_section(S_MSGMAIN);
880 phree(actual_message);
882 /* Finally, return the pointers */
889 * this is a simple file copy routine.
891 void copy_file(char *from, char *to)
896 ffp = fopen(from, "r");
899 tfp = fopen(to, "w");
904 while (a = getc(ffp), a >= 0) {
915 * message base operation to save a message and install its pointers
917 void save_message(char *mtmp, /* file containing proper message */
918 char *rec, /* Recipient (if mail) */
919 char *force, /* if non-zero length, force a room */
920 int mailtype, /* local or remote type, see citadel.h */
922 { /* set to 1 to generate an 'I' field */
924 char hold_rm[ROOMNAMELEN];
925 char actual_rm[ROOMNAMELEN];
926 char force_room[ROOMNAMELEN];
927 char content_type[256]; /* We have to learn this */
931 char *message_in_memory;
936 struct usersupp userbuf;
938 static int seqnum = 0;
939 int successful_local_recipients = 0;
940 struct quickroom qtemp;
941 struct SuppMsgInfo smi;
943 lprintf(9, "save_message(%s,%s,%s,%d,%d)\n",
944 mtmp, rec, force, mailtype, generate_id);
946 strcpy(force_room, force);
948 /* Strip non-printable characters out of the recipient name */
949 strcpy(recipient, rec);
950 for (a = 0; a < strlen(recipient); ++a)
951 if (!isprint(recipient[a]))
952 strcpy(&recipient[a], &recipient[a + 1]);
954 /* Measure the message */
955 stat(mtmp, &statbuf);
956 templen = statbuf.st_size;
958 /* Now read it into memory */
959 message_in_memory = (char *) mallok(templen);
960 if (message_in_memory == NULL) {
961 lprintf(2, "Can't allocate memory to save message!\n");
964 fp = fopen(mtmp, "rb");
965 fread(message_in_memory, templen, 1, fp);
968 /* Learn about what's inside, because it's what's inside that counts */
969 mptr = message_in_memory;
970 ++mptr; /* advance past 0xFF header */
971 ++mptr; /* advance past anon flag */
975 strcpy(content_type, "text/x-citadel-variformat");
978 strcpy(content_type, "text/plain");
981 strcpy(content_type, "text/plain");
982 /* advance past header fields */
983 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
990 if (!strncasecmp(mptr, "Content-type: ", 14)) {
991 safestrncpy(content_type, mptr,
992 sizeof(content_type));
993 lprintf(9, "%s\n", content_type);
994 strcpy(content_type, &content_type[14]);
995 for (a=0; a<strlen(content_type); ++a)
996 if ( (content_type[a]==';')
997 || (content_type[a]==' ')
998 || (content_type[a]==13)
999 || (content_type[a]==10) )
1000 content_type[a] = 0;
1006 lprintf(9, "Content type is <%s>\n", content_type);
1008 /* Save it to disk */
1009 newmsgid = send_message(message_in_memory, templen, generate_id);
1010 phree(message_in_memory);
1014 strcpy(actual_rm, CC->quickroom.QRname);
1015 strcpy(hold_rm, "");
1017 /* If this is being done by the networker delivering a private
1018 * message, we want to BYPASS saving the sender's copy (because there
1019 * is no local sender; it would otherwise go to the Trashcan).
1021 if (!CC->internal_pgm) {
1022 /* If the user is a twit, move to the twit room for posting */
1024 if (CC->usersupp.axlevel == 2) {
1025 strcpy(hold_rm, actual_rm);
1026 strcpy(actual_rm, config.c_twitroom);
1028 /* ...or if this message is destined for Aide> then go there. */
1029 lprintf(9, "actual room forcing loop\n");
1030 if (strlen(force_room) > 0) {
1031 strcpy(hold_rm, actual_rm);
1032 strcpy(actual_rm, force_room);
1034 /* This call to usergoto() changes rooms if necessary. It also
1035 * causes the latest message list to be read into memory.
1037 usergoto(actual_rm, 0);
1039 /* read in the quickroom record, obtaining a lock... */
1040 lgetroom(&CC->quickroom, actual_rm);
1042 /* Fix an obscure bug */
1043 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1044 CC->quickroom.QRflags =
1045 CC->quickroom.QRflags & ~QR_MAILBOX;
1048 /* Add the message pointer to the room */
1049 CC->quickroom.QRhighest =
1050 AddMessageToRoom(&CC->quickroom, newmsgid);
1052 /* update quickroom */
1053 lputroom(&CC->quickroom);
1054 ++successful_local_recipients;
1057 /* Network mail - send a copy to the network program. */
1058 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1059 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1060 (long) getpid(), CC->cs_pid, ++seqnum);
1061 copy_file(mtmp, aaa);
1062 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1064 /* Bump this user's messages posted counter. */
1065 lgetuser(&CC->usersupp, CC->curr_user);
1066 CC->usersupp.posted = CC->usersupp.posted + 1;
1067 lputuser(&CC->usersupp);
1069 /* If this is private, local mail, make a copy in the
1070 * recipient's mailbox and bump the reference count.
1072 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1073 if (getuser(&userbuf, recipient) == 0) {
1074 MailboxName(actual_rm, &userbuf, MAILROOM);
1075 lprintf(9, "Targeting mailbox: <%s>\n", actual_rm);
1076 if (lgetroom(&qtemp, actual_rm) == 0) {
1078 AddMessageToRoom(&qtemp, newmsgid);
1080 ++successful_local_recipients;
1084 /* If we've posted in a room other than the current room, then we
1085 * have to now go back to the current room...
1087 if (strlen(hold_rm) > 0) {
1088 usergoto(hold_rm, 0);
1090 unlink(mtmp); /* delete the temporary file */
1092 /* Write a supplemental message info record. This doesn't have to
1093 * be a critical section because nobody else knows about this message
1096 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1097 smi.smi_msgnum = newmsgid;
1098 smi.smi_refcount = successful_local_recipients;
1099 safestrncpy(smi.smi_content_type, content_type, 64);
1100 PutSuppMsgInfo(&smi);
1105 * Generate an administrative message and post it in the Aide> room.
1107 void aide_message(char *text)
1111 fp = fopen(CC->temp, "wb");
1112 fprintf(fp, "%c%c%c", 255, MES_NORMAL, 0);
1113 fprintf(fp, "Psysop%c", 0);
1114 fprintf(fp, "T%ld%c", (long) time(NULL), 0);
1115 fprintf(fp, "ACitadel%c", 0);
1116 fprintf(fp, "OAide%c", 0);
1117 fprintf(fp, "N%s%c", NODENAME, 0);
1118 fprintf(fp, "M%s\n%c", text, 0);
1120 save_message(CC->temp, "", AIDEROOM, MES_LOCAL, 1);
1121 syslog(LOG_NOTICE, text);
1127 * Build a binary message to be saved on disk.
1130 char *filename, /* temporary file name */
1131 struct usersupp *author, /* author's usersupp structure */
1132 char *recipient, /* NULL if it's not mail */
1133 char *room, /* room where it's going */
1134 int type, /* see MES_ types in header file */
1135 int net_type, /* see MES_ types in header file */
1136 int format_type, /* local or remote (see citadel.h) */
1138 { /* who we're masquerading as */
1146 /* Don't confuse the poor folks if it's not routed mail. */
1147 strcpy(dest_node, "");
1150 /* If net_type is MES_BINARY, split out the destination node. */
1151 if (net_type == MES_BINARY) {
1152 strcpy(dest_node, NODENAME);
1153 for (a = 0; a < strlen(recipient); ++a) {
1154 if (recipient[a] == '@') {
1156 strcpy(dest_node, &recipient[a + 1]);
1160 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1161 if (net_type == MES_INTERNET) {
1162 strcpy(dest_node, "internet");
1164 while (isspace(recipient[strlen(recipient) - 1]))
1165 recipient[strlen(recipient) - 1] = 0;
1168 fp = fopen(filename, "w");
1170 putc(type, fp); /* Normal or anonymous, see MES_ flags */
1171 putc(format_type, fp); /* Formatted or unformatted */
1172 fprintf(fp, "Pcit%ld%c", author->usernum, 0); /* path */
1173 fprintf(fp, "T%ld%c", (long) now, 0); /* date/time */
1175 fprintf(fp, "A%s%c", fake_name, 0);
1177 fprintf(fp, "A%s%c", author->fullname, 0); /* author */
1179 if (CC->quickroom.QRflags & QR_MAILBOX) { /* room */
1180 fprintf(fp, "O%s%c", &CC->quickroom.QRname[11], 0);
1182 fprintf(fp, "O%s%c", CC->quickroom.QRname, 0);
1185 fprintf(fp, "N%s%c", NODENAME, 0); /* nodename */
1186 fprintf(fp, "H%s%c", HUMANNODE, 0); /* human nodename */
1188 if (recipient[0] != 0)
1189 fprintf(fp, "R%s%c", recipient, 0);
1190 if (dest_node[0] != 0)
1191 fprintf(fp, "D%s%c", dest_node, 0);
1195 while (client_gets(buf), strcmp(buf, "000")) {
1196 fprintf(fp, "%s\n", buf);
1207 * message entry - mode 0 (normal) <bc>
1209 void cmd_ent0(char *entargs)
1212 char recipient[256];
1214 int format_type = 0;
1215 char newusername[256]; /* <bc> */
1220 struct usersupp tempUS;
1223 post = extract_int(entargs, 0);
1224 extract(recipient, entargs, 1);
1225 anon_flag = extract_int(entargs, 2);
1226 format_type = extract_int(entargs, 3);
1228 /* first check to make sure the request is valid. */
1230 if (!(CC->logged_in)) {
1231 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1234 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1235 cprintf("%d Need to be validated to enter ",
1236 ERROR + HIGHER_ACCESS_REQUIRED);
1237 cprintf("(except in %s> to sysop)\n", MAILROOM);
1240 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1241 cprintf("%d Need net privileges to enter here.\n",
1242 ERROR + HIGHER_ACCESS_REQUIRED);
1245 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1246 cprintf("%d Sorry, this is a read-only room.\n",
1247 ERROR + HIGHER_ACCESS_REQUIRED);
1253 if (post == 2) { /* <bc> */
1254 if (CC->usersupp.axlevel < 6) {
1255 cprintf("%d You don't have permission to do an aide post.\n",
1256 ERROR + HIGHER_ACCESS_REQUIRED);
1259 extract(newusername, entargs, 4);
1260 memset(CC->fake_postname, 0, 32);
1261 strcpy(CC->fake_postname, newusername);
1262 cprintf("%d Ok\n", OK);
1265 CC->cs_flags |= CS_POSTING;
1268 if (CC->quickroom.QRflags & QR_MAILBOX) {
1269 if (CC->usersupp.axlevel >= 2) {
1270 strcpy(buf, recipient);
1272 strcpy(buf, "sysop");
1273 lprintf(9, "calling alias()\n");
1274 e = alias(buf); /* alias and mail type */
1275 lprintf(9, "alias() returned %d\n", e);
1276 if ((buf[0] == 0) || (e == MES_ERROR)) {
1277 cprintf("%d Unknown address - cannot send message.\n",
1278 ERROR + NO_SUCH_USER);
1281 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1282 cprintf("%d Net privileges required for network mail.\n",
1283 ERROR + HIGHER_ACCESS_REQUIRED);
1286 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1287 && ((CC->usersupp.flags & US_INTERNET) == 0)
1288 && (!CC->internal_pgm)) {
1289 cprintf("%d You don't have access to Internet mail.\n",
1290 ERROR + HIGHER_ACCESS_REQUIRED);
1293 if (!strcasecmp(buf, "sysop")) {
1298 goto SKFALL; /* don't search local file */
1299 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1300 cprintf("%d Can't send mail to yourself!\n",
1301 ERROR + NO_SUCH_USER);
1304 /* Check to make sure the user exists; also get the correct
1305 * upper/lower casing of the name.
1307 a = getuser(&tempUS, buf);
1309 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1312 strcpy(buf, tempUS.fullname);
1314 SKFALL:b = MES_NORMAL;
1315 if (CC->quickroom.QRflags & QR_ANONONLY)
1317 if (CC->quickroom.QRflags & QR_ANONOPT) {
1321 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1324 /* If we're only checking the validity of the request, return
1325 * success without creating the message.
1328 cprintf("%d %s\n", OK, buf);
1331 cprintf("%d send message\n", SEND_LISTING);
1332 if (CC->fake_postname[0])
1333 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, CC->fake_postname);
1334 else if (CC->fake_username[0])
1335 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, CC->fake_username);
1337 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, "");
1338 save_message(CC->temp, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1339 CC->fake_postname[0] = '\0';
1346 * message entry - mode 3 (raw)
1348 void cmd_ent3(char *entargs)
1354 struct usersupp tempUS;
1359 if (CC->internal_pgm == 0) {
1360 cprintf("%d This command is for internal programs only.\n",
1364 /* See if there's a recipient, but make sure it's a real one */
1365 extract(recp, entargs, 1);
1366 for (a = 0; a < strlen(recp); ++a)
1367 if (!isprint(recp[a]))
1368 strcpy(&recp[a], &recp[a + 1]);
1369 while (isspace(recp[0]))
1370 strcpy(recp, &recp[1]);
1371 while (isspace(recp[strlen(recp) - 1]))
1372 recp[strlen(recp) - 1] = 0;
1374 /* If we're in Mail, check the recipient */
1375 if (strlen(recp) > 0) {
1376 e = alias(recp); /* alias and mail type */
1377 if ((recp[0] == 0) || (e == MES_ERROR)) {
1378 cprintf("%d Unknown address - cannot send message.\n",
1379 ERROR + NO_SUCH_USER);
1382 if (e == MES_LOCAL) {
1383 a = getuser(&tempUS, recp);
1385 cprintf("%d No such user.\n",
1386 ERROR + NO_SUCH_USER);
1391 /* At this point, message has been approved. */
1392 if (extract_int(entargs, 0) == 0) {
1393 cprintf("%d OK to send\n", OK);
1396 /* open a temp file to hold the message */
1397 fp = fopen(CC->temp, "wb");
1399 cprintf("%d Cannot open %s: %s\n",
1400 ERROR + INTERNAL_ERROR,
1401 CC->temp, strerror(errno));
1404 msglen = extract_long(entargs, 2);
1405 cprintf("%d %ld\n", SEND_BINARY, msglen);
1406 while (msglen > 0L) {
1407 bloklen = ((msglen >= 255L) ? 255 : msglen);
1408 client_read(buf, (int) bloklen);
1409 fwrite(buf, (int) bloklen, 1, fp);
1410 msglen = msglen - bloklen;
1414 save_message(CC->temp, recp, "", e, 0);
1419 * API function to delete messages which match a set of criteria
1420 * (returns the actual number of messages deleted)
1421 * FIX ... still need to implement delete by content type
1423 int CtdlDeleteMessages( char *room_name, /* which room */
1424 long dmsgnum, /* or "0" for any */
1425 char *content_type /* or NULL for any */
1428 struct quickroom qrbuf;
1429 struct cdbdata *cdbfr;
1430 long *msglist = NULL;
1433 int num_deleted = 0;
1435 struct SuppMsgInfo smi;
1437 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1438 room_name, dmsgnum, content_type);
1440 /* get room record, obtaining a lock... */
1441 if (lgetroom(&qrbuf, room_name) != 0) {
1442 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1444 return(0); /* room not found */
1447 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1449 lprintf(9, "doing mallok/memcpy loop\n");
1450 if (cdbfr != NULL) {
1451 msglist = mallok(cdbfr->len);
1452 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1453 num_msgs = cdbfr->len / sizeof(long);
1458 for (i=0; i<num_msgs; ++i) {
1459 lprintf(9, "Evaluating message d\n", i);
1462 /* Set/clear a bit for each criterion */
1464 lprintf(9, "Message number is <%ld>\n", msglist[i]);
1465 if ( (dmsgnum == 0L) || (msglist[i]==dmsgnum) ) {
1466 delete_this |= 0x01;
1469 if (content_type == NULL) {
1470 delete_this |= 0x02;
1472 GetSuppMsgInfo(&smi, msglist[i]);
1473 lprintf(9, "Content type is <%s>\n",
1474 smi.smi_content_type);
1475 if (!strcasecmp(smi.smi_content_type,
1477 delete_this |= 0x02;
1481 /* Delete message only if all bits are set */
1482 if (delete_this == 0x03) {
1483 AdjRefCount(msglist[i], -1);
1489 num_msgs = sort_msglist(msglist, num_msgs);
1490 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1491 msglist, (num_msgs * sizeof(long)) );
1493 qrbuf.QRhighest = msglist[num_msgs - 1];
1497 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1498 return(num_deleted);
1504 * Delete message from current room
1506 void cmd_dele(char *delstr)
1511 getuser(&CC->usersupp, CC->curr_user);
1512 if ((CC->usersupp.axlevel < 6)
1513 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1514 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1515 cprintf("%d Higher access required.\n",
1516 ERROR + HIGHER_ACCESS_REQUIRED);
1519 delnum = extract_long(delstr, 0);
1521 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1524 cprintf("%d %d message%s deleted.\n", OK,
1525 num_deleted, ((num_deleted!=1) ? "s" : "") );
1527 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1533 * move a message to another room
1535 void cmd_move(char *args)
1540 struct quickroom qtemp;
1543 num = extract_long(args, 0);
1544 extract(targ, args, 1);
1546 getuser(&CC->usersupp, CC->curr_user);
1547 if ((CC->usersupp.axlevel < 6)
1548 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1549 cprintf("%d Higher access required.\n",
1550 ERROR + HIGHER_ACCESS_REQUIRED);
1553 if (getroom(&qtemp, targ) != 0) {
1554 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1557 /* yank the message out of the current room... */
1558 lgetroom(&CC->quickroom, CC->quickroom.QRname);
1559 get_msglist(&CC->quickroom);
1562 for (a = 0; a < (CC->num_msgs); ++a) {
1563 if (MessageFromList(a) == num) {
1565 SetMessageInList(a, 0L);
1569 CC->num_msgs = sort_msglist(CC->msglist, CC->num_msgs);
1570 put_msglist(&CC->quickroom);
1571 CC->quickroom.QRhighest = MessageFromList((CC->num_msgs) - 1);
1573 lputroom(&CC->quickroom);
1575 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1578 /* put the message into the target room */
1579 lgetroom(&qtemp, targ);
1580 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1583 cprintf("%d Message moved.\n", OK);
1589 * GetSuppMsgInfo() - Get the supplementary record for a message
1591 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1594 struct cdbdata *cdbsmi;
1597 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1598 smibuf->smi_msgnum = msgnum;
1599 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1601 /* Use the negative of the message number for its supp record index */
1602 TheIndex = (0L - msgnum);
1604 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1605 if (cdbsmi == NULL) {
1606 return; /* record not found; go with defaults */
1608 memcpy(smibuf, cdbsmi->ptr,
1609 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1610 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1617 * PutSuppMsgInfo() - (re)write supplementary record for a message
1619 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1623 /* Use the negative of the message number for its supp record index */
1624 TheIndex = (0L - smibuf->smi_msgnum);
1626 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1627 smibuf->smi_msgnum, smibuf->smi_refcount);
1629 cdb_store(CDB_MSGMAIN,
1630 &TheIndex, sizeof(long),
1631 smibuf, sizeof(struct SuppMsgInfo));
1638 * AdjRefCount - change the reference count for a message;
1639 * delete the message if it reaches zero
1641 void AdjRefCount(long msgnum, int incr)
1644 struct SuppMsgInfo smi;
1647 /* This is a *tight* critical section; please keep it that way, as
1648 * it may get called while nested in other critical sections.
1649 * Complicating this any further will surely cause deadlock!
1651 begin_critical_section(S_SUPPMSGMAIN);
1652 GetSuppMsgInfo(&smi, msgnum);
1653 smi.smi_refcount += incr;
1654 PutSuppMsgInfo(&smi);
1655 end_critical_section(S_SUPPMSGMAIN);
1657 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1658 msgnum, smi.smi_refcount);
1660 /* If the reference count is now zero, delete the message
1661 * (and its supplementary record as well).
1663 if (smi.smi_refcount == 0) {
1664 lprintf(9, "Deleting message <%ld>\n", msgnum);
1666 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1667 delnum = (0L - msgnum);
1668 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1674 * Write a generic object to this room
1676 void CtdlWriteObject( char *req_room, /* Room to stuff it in */
1677 char *content_type, /* MIME type of this object */
1678 char *tempfilename, /* Where to fetch it from */
1679 int is_mailbox, /* Private mailbox room? */
1680 int is_binary, /* Is encoding necessary? */
1681 int is_unique /* Del others of this type? */
1685 char filename[PATH_MAX];
1688 struct quickroom qrbuf;
1689 char roomname[ROOMNAMELEN];
1691 if (is_mailbox) MailboxName(roomname, &CC->usersupp, req_room);
1692 else safestrncpy(roomname, req_room, sizeof(roomname));
1694 strcpy(filename, tmpnam(NULL));
1695 fp = fopen(filename, "w");
1696 if (fp == NULL) return;
1698 fprintf(fp, "%c%c%c", 0xFF, MES_NORMAL, 4);
1699 fprintf(fp, "T%ld%c", time(NULL), 0);
1700 fprintf(fp, "A%s%c", CC->usersupp.fullname, 0);
1701 fprintf(fp, "O%s%c", roomname, 0);
1702 fprintf(fp, "N%s%c", config.c_nodename, 0);
1703 fprintf(fp, "MContent-type: %s\n", content_type);
1705 tempfp = fopen(tempfilename, "r");
1706 if (tempfp == NULL) {
1712 if (is_binary == 0) {
1713 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
1714 while (ch=getc(tempfp), ch>0) putc(ch, fp);
1719 fprintf(fp, "Content-transfer-encoding: base64\n\n");
1722 sprintf(cmdbuf, "./base64 -e <%s >>%s",
1723 tempfilename, filename);
1727 /* Create the requested room if we have to. */
1728 if (getroom(&qrbuf, roomname) != 0) {
1729 create_room(roomname, 4, "", 0);
1732 /* If the caller specified this object as unique, delete all
1733 * other objects of this type that are currently in the room.
1735 CtdlDeleteMessages(roomname, 0L, content_type);
1737 /* Now write the data */
1738 save_message(filename, "", roomname, MES_LOCAL, 1);