3 * Revision 1.77 1999/07/24 21:25:15 ajc
4 * test of CVS log function
26 #include "sysdep_decls.h"
27 #include "citserver.h"
32 #include "dynloader.h"
34 #include "mime_parser.h"
43 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
45 extern struct config config;
49 * This function is self explanatory.
50 * (What can I say, I'm in a weird mood today...)
52 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
56 for (i = 0; i < strlen(name); ++i)
59 if (isspace(name[i - 1])) {
60 strcpy(&name[i - 1], &name[i]);
63 while (isspace(name[i + 1])) {
64 strcpy(&name[i + 1], &name[i + 2]);
71 * Aliasing for network mail.
72 * (Error messages have been commented out, because this is a server.)
75 { /* process alias and routing info for mail */
78 char aaa[300], bbb[300];
80 lprintf(9, "alias() called for <%s>\n", name);
82 remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
84 fp = fopen("network/mail.aliases", "r");
86 fp = fopen("/dev/null", "r");
91 while (fgets(aaa, sizeof aaa, fp) != NULL) {
92 while (isspace(name[0]))
93 strcpy(name, &name[1]);
94 aaa[strlen(aaa) - 1] = 0;
96 for (a = 0; a < strlen(aaa); ++a) {
98 strcpy(bbb, &aaa[a + 1]);
102 if (!strcasecmp(name, aaa))
106 lprintf(7, "Mail is being forwarded to %s\n", name);
108 /* determine local or remote type, see citadel.h */
109 for (a = 0; a < strlen(name); ++a)
111 return (MES_INTERNET);
112 for (a = 0; a < strlen(name); ++a)
114 for (b = a; b < strlen(name); ++b)
116 return (MES_INTERNET);
118 for (a = 0; a < strlen(name); ++a)
122 lprintf(7, "Too many @'s in address\n");
126 for (a = 0; a < strlen(name); ++a)
128 strcpy(bbb, &name[a + 1]);
130 strcpy(bbb, &bbb[1]);
131 fp = fopen("network/mail.sysinfo", "r");
135 a = getstring(fp, aaa);
136 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
137 a = getstring(fp, aaa);
138 if (!strncmp(aaa, "use ", 4)) {
139 strcpy(bbb, &aaa[4]);
144 if (!strncmp(aaa, "uum", 3)) {
146 for (a = 0; a < strlen(bbb); ++a) {
152 while (bbb[strlen(bbb) - 1] == '_')
153 bbb[strlen(bbb) - 1] = 0;
154 sprintf(name, &aaa[4], bbb);
155 return (MES_INTERNET);
157 if (!strncmp(aaa, "bin", 3)) {
160 while (aaa[strlen(aaa) - 1] != '@')
161 aaa[strlen(aaa) - 1] = 0;
162 aaa[strlen(aaa) - 1] = 0;
163 while (aaa[strlen(aaa) - 1] == ' ')
164 aaa[strlen(aaa) - 1] = 0;
165 while (bbb[0] != '@')
166 strcpy(bbb, &bbb[1]);
167 strcpy(bbb, &bbb[1]);
168 while (bbb[0] == ' ')
169 strcpy(bbb, &bbb[1]);
170 sprintf(name, "%s @%s", aaa, bbb);
183 fp = fopen("citadel.control", "r");
184 fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
190 void simple_listing(long msgnum) {
191 cprintf("%ld\n", msgnum);
196 * API function to perform an operation for each qualifying message in the
199 void CtdlForEachMessage(int mode, long ref,
200 void (*CallBack) (long msgnum) ) {
206 get_msglist(&CC->quickroom);
207 getuser(&CC->usersupp, CC->curr_user);
208 CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
210 if (CC->num_msgs != 0) for (a = 0; a < (CC->num_msgs); ++a) {
211 if ((MessageFromList(a) >= 0)
215 || ((mode == MSGS_OLD) && (MessageFromList(a) <= vbuf.v_lastseen))
216 || ((mode == MSGS_NEW) && (MessageFromList(a) > vbuf.v_lastseen))
217 || ((mode == MSGS_NEW) && (MessageFromList(a) >= vbuf.v_lastseen)
218 && (CC->usersupp.flags & US_LASTOLD))
219 || ((mode == MSGS_LAST) && (a >= (CC->num_msgs - ref)))
220 || ((mode == MSGS_FIRST) && (a < ref))
221 || ((mode == MSGS_GT) && (MessageFromList(a) > ref))
224 CallBack(MessageFromList(a));
232 * cmd_msgs() - get list of message #'s in this room
233 * implements the MSGS server command using CtdlForEachMessage()
235 void cmd_msgs(char *cmdbuf)
241 extract(which, cmdbuf, 0);
242 cm_ref = extract_int(cmdbuf, 1);
246 if (!strncasecmp(which, "OLD", 3))
248 else if (!strncasecmp(which, "NEW", 3))
250 else if (!strncasecmp(which, "FIRST", 5))
252 else if (!strncasecmp(which, "LAST", 4))
254 else if (!strncasecmp(which, "GT", 2))
257 if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
258 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
262 cprintf("%d Message list...\n", LISTING_FOLLOWS);
263 CtdlForEachMessage(mode, cm_ref, simple_listing);
271 * help_subst() - support routine for help file viewer
273 void help_subst(char *strbuf, char *source, char *dest)
278 while (p = pattern2(strbuf, source), (p >= 0)) {
279 strcpy(workbuf, &strbuf[p + strlen(source)]);
280 strcpy(&strbuf[p], dest);
281 strcat(strbuf, workbuf);
286 void do_help_subst(char *buffer)
290 help_subst(buffer, "^nodename", config.c_nodename);
291 help_subst(buffer, "^humannode", config.c_humannode);
292 help_subst(buffer, "^fqdn", config.c_fqdn);
293 help_subst(buffer, "^username", CC->usersupp.fullname);
294 sprintf(buf2, "%ld", CC->usersupp.usernum);
295 help_subst(buffer, "^usernum", buf2);
296 help_subst(buffer, "^sysadm", config.c_sysadm);
297 help_subst(buffer, "^variantname", CITADEL);
298 sprintf(buf2, "%d", config.c_maxsessions);
299 help_subst(buffer, "^maxsessions", buf2);
305 * memfmout() - Citadel text formatter and paginator.
306 * Although the original purpose of this routine was to format
307 * text to the reader's screen width, all we're really using it
308 * for here is to format text out to 80 columns before sending it
309 * to the client. The client software may reformat it again.
311 void memfmout(int width, char *mptr, char subst)
312 /* screen width to use */
313 /* where are we going to get our text from? */
314 /* nonzero if we should use hypertext mode */
326 c = 1; /* c is the current pos */
329 while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
331 buffer[strlen(buffer) + 1] = 0;
332 buffer[strlen(buffer)] = ch;
335 if (buffer[0] == '^')
336 do_help_subst(buffer);
338 buffer[strlen(buffer) + 1] = 0;
340 strcpy(buffer, &buffer[1]);
349 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
351 if (((old == 13) || (old == 10)) && (isspace(real))) {
359 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
360 cprintf("\n%s", aaa);
369 if ((strlen(aaa) + c) > (width - 5)) {
379 if ((ch == 13) || (ch == 10)) {
380 cprintf("%s\n", aaa);
387 FMTEND: cprintf("%s\n", aaa);
393 * Callback function for mime parser that simply lists the part
395 void list_this_part(char *name, char *filename, char *partnum, char *disp,
396 void *content, char *cbtype, size_t length)
399 cprintf("part=%s|%s|%s|%s|%s|%d\n",
400 name, filename, partnum, disp, cbtype, length);
405 * Callback function for mime parser that wants to display text
407 void fixed_output(char *name, char *filename, char *partnum, char *disp,
408 void *content, char *cbtype, size_t length)
411 if (!strcasecmp(cbtype, "text/plain")) {
412 client_write(content, length);
414 cprintf("Part %s: %s (%s) (%d bytes)\n",
415 partnum, filename, cbtype, length);
421 * Callback function for mime parser that opens a section for downloading
423 void mime_download(char *name, char *filename, char *partnum, char *disp,
424 void *content, char *cbtype, size_t length)
427 /* Silently go away if there's already a download open... */
428 if (CC->download_fp != NULL)
431 /* ...or if this is not the desired section */
432 if (strcasecmp(desired_section, partnum))
435 CC->download_fp = tmpfile();
436 if (CC->download_fp == NULL)
439 fwrite(content, length, 1, CC->download_fp);
440 fflush(CC->download_fp);
441 rewind(CC->download_fp);
443 OpenCmdResult(filename, cbtype);
449 * Get a message off disk. (return value is the message's timestamp)
452 time_t output_message(char *msgid, int mode, int headers_only)
457 CIT_UBYTE format_type, anon_flag;
462 struct cdbdata *dmsgtext;
465 /* buffers needed for RFC822 translation */
474 msg_num = atol(msgid);
477 if ((!(CC->logged_in)) && (!(CC->internal_pgm)) && (mode != MT_DATE)) {
478 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
481 /* We used to need to check in the current room's message list
482 * to determine where the message's disk position. We no longer need
483 * to do this, but we do it anyway as a security measure, in order to
484 * prevent rogue clients from reading messages not in the current room.
488 if (CC->num_msgs > 0) {
489 for (a = 0; a < CC->num_msgs; ++a) {
490 if (MessageFromList(a) == msg_num) {
497 cprintf("%d Message %ld is not in this room.\n",
501 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msg_num, sizeof(long));
503 if (dmsgtext == NULL) {
505 cprintf("%d Can't find message %ld\n",
506 ERROR + INTERNAL_ERROR);
509 msg_len = (long) dmsgtext->len;
510 mptr = dmsgtext->ptr;
512 /* this loop spews out the whole message if we're doing raw format */
513 if (mode == MT_RAW) {
514 cprintf("%d %ld\n", BINARY_FOLLOWS, msg_len);
515 client_write(dmsgtext->ptr, (int) msg_len);
519 /* Otherwise, we'll start parsing it field by field... */
522 cprintf("%d Illegal message format on disk\n",
523 ERROR + INTERNAL_ERROR);
528 format_type = *mptr++;
530 /* Are we downloading a MIME component? */
531 if (mode == MT_DOWNLOAD) {
532 if (format_type != 4) {
533 cprintf("%d This is not a MIME message.\n",
535 } else if (CC->download_fp != NULL) {
536 cprintf("%d You already have a download open.\n",
539 /* Skip to the message body */
540 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
543 buf[strlen(buf) + 1] = 0;
545 buf[strlen(buf)] = rch;
549 mime_parser(mptr, NULL, *mime_download);
550 /* If there's no file open by this time, the requested
551 * section wasn't found, so print an error
553 if (CC->download_fp == NULL) {
554 cprintf("%d Section %s not found.\n",
555 ERROR + FILE_NOT_FOUND,
562 /* Are we just looking for the message date? */
564 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
567 buf[strlen(buf) + 1] = 0;
569 buf[strlen(buf)] = rch;
578 /* now for the user-mode message reading loops */
579 cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
581 if (mode == MT_CITADEL)
582 cprintf("type=%d\n", format_type);
584 if ((anon_flag == MES_ANON) && (mode == MT_CITADEL)) {
585 cprintf("nhdr=yes\n");
587 /* begin header processing loop for Citadel message format */
589 if ((mode == MT_CITADEL) || (mode == MT_MIME))
590 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
593 buf[strlen(buf) + 1] = 0;
595 buf[strlen(buf)] = rch;
599 PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
600 if (anon_flag == MES_ANON)
601 cprintf("from=****");
602 else if (anon_flag == MES_AN2)
603 cprintf("from=anonymous");
605 cprintf("from=%s", buf);
606 if ((is_room_aide()) && ((anon_flag == MES_ANON)
607 || (anon_flag == MES_AN2)))
608 cprintf(" [%s]", buf);
610 } else if (ch == 'P')
611 cprintf("path=%s\n", buf);
613 cprintf("subj=%s\n", buf);
615 cprintf("msgn=%s\n", buf);
617 cprintf("hnod=%s\n", buf);
619 cprintf("room=%s\n", buf);
621 cprintf("node=%s\n", buf);
623 cprintf("rcpt=%s\n", buf);
625 cprintf("time=%s\n", buf);
626 /* else cprintf("fld%c=%s\n",ch,buf); */
628 /* begin header processing loop for RFC822 transfer format */
632 strcpy(snode, NODENAME);
633 strcpy(lnode, HUMANNODE);
634 if (mode == MT_RFC822)
635 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
638 buf[strlen(buf) + 1] = 0;
640 buf[strlen(buf)] = rch;
645 else if (ch == 'P') {
646 cprintf("Path: %s\n", buf);
647 for (a = 0; a < strlen(buf); ++a) {
649 strcpy(buf, &buf[a + 1]);
654 } else if (ch == 'U')
655 cprintf("Subject: %s\n", buf);
661 cprintf("X-Citadel-Room: %s\n", buf);
665 cprintf("To: %s\n", buf);
666 else if (ch == 'T') {
668 cprintf("Date: %s", asctime(localtime(&xtime)));
671 if (mode == MT_RFC822) {
672 if (!strcasecmp(snode, NODENAME)) {
675 cprintf("Message-ID: <%s@%s>\n", mid, snode);
676 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
677 cprintf("From: %s@%s (%s)\n",
678 suser, snode, luser);
679 cprintf("Organization: %s\n", lnode);
681 /* end header processing loop ... at this point, we're in the text */
684 cprintf("text\n*** ?Message truncated\n000\n");
688 /* do some sort of MIME output */
689 if (format_type == 4) {
690 if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
691 mime_parser(mptr, NULL, *list_this_part);
693 if (mode == MT_MIME) { /* If MT_MIME then it's parts only */
700 /* give 'em a length */
702 while (ch = *mptr++, ch > 0) {
705 cprintf("mlen=%ld\n", msg_len);
710 /* signify start of msg text */
711 if (mode == MT_CITADEL)
713 if ((mode == MT_RFC822) && (format_type != 4))
716 /* If the format type on disk is 1 (fixed-format), then we want
717 * everything to be output completely literally ... regardless of
718 * what message transfer format is in use.
720 if (format_type == 1) {
722 while (ch = *mptr++, ch > 0) {
725 if ((ch == 10) || (strlen(buf) > 250)) {
726 cprintf("%s\n", buf);
729 buf[strlen(buf) + 1] = 0;
730 buf[strlen(buf)] = ch;
734 cprintf("%s\n", buf);
736 /* If the message on disk is format 0 (Citadel vari-format), we
737 * output using the formatter at 80 columns. This is the final output
738 * form if the transfer format is RFC822, but if the transfer format
739 * is Citadel proprietary, it'll still work, because the indentation
740 * for new paragraphs is correct and the client will reformat the
741 * message to the reader's screen width.
743 if (format_type == 0) {
744 memfmout(80, mptr, 0);
746 /* If the message on disk is format 4 (MIME), we've gotta hand it
747 * off to the MIME parser. The client has already been told that
748 * this message is format 1 (fixed format), so the callback function
749 * we use will display those parts as-is.
751 if (format_type == 4) {
752 mime_parser(mptr, NULL, *fixed_output);
762 * display a message (mode 0 - Citadel proprietary)
764 void cmd_msg0(char *cmdbuf)
767 int headers_only = 0;
769 extract(msgid, cmdbuf, 0);
770 headers_only = extract_int(cmdbuf, 1);
772 output_message(msgid, MT_CITADEL, headers_only);
778 * display a message (mode 2 - RFC822)
780 void cmd_msg2(char *cmdbuf)
783 int headers_only = 0;
785 extract(msgid, cmdbuf, 0);
786 headers_only = extract_int(cmdbuf, 1);
788 output_message(msgid, MT_RFC822, headers_only);
792 * display a message (mode 3 - IGnet raw format - internal programs only)
794 void cmd_msg3(char *cmdbuf)
797 int headers_only = 0;
799 if (CC->internal_pgm == 0) {
800 cprintf("%d This command is for internal programs only.\n",
804 extract(msgid, cmdbuf, 0);
805 headers_only = extract_int(cmdbuf, 1);
807 output_message(msgid, MT_RAW, headers_only);
811 * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
813 void cmd_msg4(char *cmdbuf)
817 extract(msgid, cmdbuf, 0);
819 output_message(msgid, MT_MIME, 0);
825 * Open a component of a MIME message as a download file
827 void cmd_opna(char *cmdbuf)
831 CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
833 extract(msgid, cmdbuf, 0);
834 extract(desired_section, cmdbuf, 1);
836 output_message(msgid, MT_DOWNLOAD, 0);
842 * Message base operation to send a message to the master file
843 * (returns new message number)
845 long send_message(char *message_in_memory, /* pointer to buffer */
846 size_t message_length, /* length of buffer */
848 { /* 1 to generate an I field */
851 char *actual_message;
852 size_t actual_length;
856 /* Get a new message number */
857 newmsgid = get_new_message_number();
860 sprintf(msgidbuf, "I%ld", newmsgid);
861 actual_length = message_length + strlen(msgidbuf) + 1;
862 actual_message = mallok(actual_length);
863 memcpy(actual_message, message_in_memory, 3);
864 memcpy(&actual_message[3], msgidbuf, (strlen(msgidbuf) + 1));
865 memcpy(&actual_message[strlen(msgidbuf) + 4],
866 &message_in_memory[3], message_length - 3);
868 actual_message = message_in_memory;
869 actual_length = message_length;
872 /* Write our little bundle of joy into the message base */
873 begin_critical_section(S_MSGMAIN);
874 if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
875 actual_message, actual_length) < 0) {
876 lprintf(2, "Can't store message\n");
881 end_critical_section(S_MSGMAIN);
884 phree(actual_message);
886 /* Finally, return the pointers */
893 * this is a simple file copy routine.
895 void copy_file(char *from, char *to)
900 ffp = fopen(from, "r");
903 tfp = fopen(to, "w");
908 while (a = getc(ffp), a >= 0) {
919 * message base operation to save a message and install its pointers
921 void save_message(char *mtmp, /* file containing proper message */
922 char *rec, /* Recipient (if mail) */
923 char *force, /* if non-zero length, force a room */
924 int mailtype, /* local or remote type, see citadel.h */
926 { /* set to 1 to generate an 'I' field */
928 char hold_rm[ROOMNAMELEN];
929 char actual_rm[ROOMNAMELEN];
930 char force_room[ROOMNAMELEN];
931 char content_type[256]; /* We have to learn this */
935 char *message_in_memory;
940 struct usersupp userbuf;
942 static int seqnum = 0;
943 int successful_local_recipients = 0;
944 struct quickroom qtemp;
945 struct SuppMsgInfo smi;
947 lprintf(9, "save_message(%s,%s,%s,%d,%d)\n",
948 mtmp, rec, force, mailtype, generate_id);
950 strcpy(force_room, force);
952 /* Strip non-printable characters out of the recipient name */
953 strcpy(recipient, rec);
954 for (a = 0; a < strlen(recipient); ++a)
955 if (!isprint(recipient[a]))
956 strcpy(&recipient[a], &recipient[a + 1]);
958 /* Measure the message */
959 stat(mtmp, &statbuf);
960 templen = statbuf.st_size;
962 /* Now read it into memory */
963 message_in_memory = (char *) mallok(templen);
964 if (message_in_memory == NULL) {
965 lprintf(2, "Can't allocate memory to save message!\n");
968 fp = fopen(mtmp, "rb");
969 fread(message_in_memory, templen, 1, fp);
972 /* Learn about what's inside, because it's what's inside that counts */
973 mptr = message_in_memory;
974 ++mptr; /* advance past 0xFF header */
975 ++mptr; /* advance past anon flag */
979 strcpy(content_type, "text/x-citadel-variformat");
982 strcpy(content_type, "text/plain");
985 strcpy(content_type, "text/plain");
986 /* advance past header fields */
987 while (ch = *mptr++, (ch != 'M' && ch != 0)) {
994 if (!strncasecmp(mptr, "Content-type: ", 14)) {
995 safestrncpy(content_type, mptr,
996 sizeof(content_type));
997 lprintf(9, "%s\n", content_type);
998 strcpy(content_type, &content_type[14]);
999 for (a=0; a<strlen(content_type); ++a)
1000 if ( (content_type[a]==';')
1001 || (content_type[a]==' ')
1002 || (content_type[a]==13)
1003 || (content_type[a]==10) )
1004 content_type[a] = 0;
1010 lprintf(9, "Content type is <%s>\n", content_type);
1012 /* Save it to disk */
1013 newmsgid = send_message(message_in_memory, templen, generate_id);
1014 phree(message_in_memory);
1018 strcpy(actual_rm, CC->quickroom.QRname);
1019 strcpy(hold_rm, "");
1021 /* If this is being done by the networker delivering a private
1022 * message, we want to BYPASS saving the sender's copy (because there
1023 * is no local sender; it would otherwise go to the Trashcan).
1025 if ( (!CC->internal_pgm) || (strlen(recipient)==0) ) {
1026 /* If the user is a twit, move to the twit room for posting */
1028 if (CC->usersupp.axlevel == 2) {
1029 strcpy(hold_rm, actual_rm);
1030 strcpy(actual_rm, config.c_twitroom);
1032 /* ...or if this message is destined for Aide> then go there. */
1033 lprintf(9, "actual room forcing loop\n");
1034 if (strlen(force_room) > 0) {
1035 strcpy(hold_rm, actual_rm);
1036 strcpy(actual_rm, force_room);
1038 /* This call to usergoto() changes rooms if necessary. It also
1039 * causes the latest message list to be read into memory.
1041 usergoto(actual_rm, 0);
1043 /* read in the quickroom record, obtaining a lock... */
1044 lgetroom(&CC->quickroom, actual_rm);
1046 /* Fix an obscure bug */
1047 if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
1048 CC->quickroom.QRflags =
1049 CC->quickroom.QRflags & ~QR_MAILBOX;
1052 /* Add the message pointer to the room */
1053 CC->quickroom.QRhighest =
1054 AddMessageToRoom(&CC->quickroom, newmsgid);
1056 /* update quickroom */
1057 lputroom(&CC->quickroom);
1058 ++successful_local_recipients;
1061 /* Network mail - send a copy to the network program. */
1062 if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
1063 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1064 (long) getpid(), CC->cs_pid, ++seqnum);
1065 copy_file(mtmp, aaa);
1066 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
1068 /* Bump this user's messages posted counter. */
1069 lgetuser(&CC->usersupp, CC->curr_user);
1070 CC->usersupp.posted = CC->usersupp.posted + 1;
1071 lputuser(&CC->usersupp);
1073 /* If this is private, local mail, make a copy in the
1074 * recipient's mailbox and bump the reference count.
1076 if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1077 if (getuser(&userbuf, recipient) == 0) {
1078 MailboxName(actual_rm, &userbuf, MAILROOM);
1079 lprintf(9, "Targeting mailbox: <%s>\n", actual_rm);
1080 if (lgetroom(&qtemp, actual_rm) == 0) {
1082 AddMessageToRoom(&qtemp, newmsgid);
1084 ++successful_local_recipients;
1088 /* If we've posted in a room other than the current room, then we
1089 * have to now go back to the current room...
1091 if (strlen(hold_rm) > 0) {
1092 usergoto(hold_rm, 0);
1094 unlink(mtmp); /* delete the temporary file */
1096 /* Write a supplemental message info record. This doesn't have to
1097 * be a critical section because nobody else knows about this message
1100 memset(&smi, 0, sizeof(struct SuppMsgInfo));
1101 smi.smi_msgnum = newmsgid;
1102 smi.smi_refcount = successful_local_recipients;
1103 safestrncpy(smi.smi_content_type, content_type, 64);
1104 PutSuppMsgInfo(&smi);
1109 * Generate an administrative message and post it in the Aide> room.
1111 void aide_message(char *text)
1115 fp = fopen(CC->temp, "wb");
1116 fprintf(fp, "%c%c%c", 255, MES_NORMAL, 0);
1117 fprintf(fp, "Psysop%c", 0);
1118 fprintf(fp, "T%ld%c", (long) time(NULL), 0);
1119 fprintf(fp, "ACitadel%c", 0);
1120 fprintf(fp, "OAide%c", 0);
1121 fprintf(fp, "N%s%c", NODENAME, 0);
1122 fprintf(fp, "M%s\n%c", text, 0);
1124 save_message(CC->temp, "", AIDEROOM, MES_LOCAL, 1);
1125 syslog(LOG_NOTICE, text);
1131 * Build a binary message to be saved on disk.
1134 char *filename, /* temporary file name */
1135 struct usersupp *author, /* author's usersupp structure */
1136 char *recipient, /* NULL if it's not mail */
1137 char *room, /* room where it's going */
1138 int type, /* see MES_ types in header file */
1139 int net_type, /* see MES_ types in header file */
1140 int format_type, /* local or remote (see citadel.h) */
1142 { /* who we're masquerading as */
1150 /* Don't confuse the poor folks if it's not routed mail. */
1151 strcpy(dest_node, "");
1154 /* If net_type is MES_BINARY, split out the destination node. */
1155 if (net_type == MES_BINARY) {
1156 strcpy(dest_node, NODENAME);
1157 for (a = 0; a < strlen(recipient); ++a) {
1158 if (recipient[a] == '@') {
1160 strcpy(dest_node, &recipient[a + 1]);
1164 /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1165 if (net_type == MES_INTERNET) {
1166 strcpy(dest_node, "internet");
1168 while (isspace(recipient[strlen(recipient) - 1]))
1169 recipient[strlen(recipient) - 1] = 0;
1172 fp = fopen(filename, "w");
1174 putc(type, fp); /* Normal or anonymous, see MES_ flags */
1175 putc(format_type, fp); /* Formatted or unformatted */
1176 fprintf(fp, "Pcit%ld%c", author->usernum, 0); /* path */
1177 fprintf(fp, "T%ld%c", (long) now, 0); /* date/time */
1179 fprintf(fp, "A%s%c", fake_name, 0);
1181 fprintf(fp, "A%s%c", author->fullname, 0); /* author */
1183 if (CC->quickroom.QRflags & QR_MAILBOX) { /* room */
1184 fprintf(fp, "O%s%c", &CC->quickroom.QRname[11], 0);
1186 fprintf(fp, "O%s%c", CC->quickroom.QRname, 0);
1189 fprintf(fp, "N%s%c", NODENAME, 0); /* nodename */
1190 fprintf(fp, "H%s%c", HUMANNODE, 0); /* human nodename */
1192 if (recipient[0] != 0)
1193 fprintf(fp, "R%s%c", recipient, 0);
1194 if (dest_node[0] != 0)
1195 fprintf(fp, "D%s%c", dest_node, 0);
1199 while (client_gets(buf), strcmp(buf, "000")) {
1200 fprintf(fp, "%s\n", buf);
1211 * message entry - mode 0 (normal) <bc>
1213 void cmd_ent0(char *entargs)
1216 char recipient[256];
1218 int format_type = 0;
1219 char newusername[256]; /* <bc> */
1224 struct usersupp tempUS;
1227 post = extract_int(entargs, 0);
1228 extract(recipient, entargs, 1);
1229 anon_flag = extract_int(entargs, 2);
1230 format_type = extract_int(entargs, 3);
1232 /* first check to make sure the request is valid. */
1234 if (!(CC->logged_in)) {
1235 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
1238 if ((CC->usersupp.axlevel < 2) && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1239 cprintf("%d Need to be validated to enter ",
1240 ERROR + HIGHER_ACCESS_REQUIRED);
1241 cprintf("(except in %s> to sysop)\n", MAILROOM);
1244 if ((CC->usersupp.axlevel < 4) && (CC->quickroom.QRflags & QR_NETWORK)) {
1245 cprintf("%d Need net privileges to enter here.\n",
1246 ERROR + HIGHER_ACCESS_REQUIRED);
1249 if ((CC->usersupp.axlevel < 6) && (CC->quickroom.QRflags & QR_READONLY)) {
1250 cprintf("%d Sorry, this is a read-only room.\n",
1251 ERROR + HIGHER_ACCESS_REQUIRED);
1257 if (post == 2) { /* <bc> */
1258 if (CC->usersupp.axlevel < 6) {
1259 cprintf("%d You don't have permission to do an aide post.\n",
1260 ERROR + HIGHER_ACCESS_REQUIRED);
1263 extract(newusername, entargs, 4);
1264 memset(CC->fake_postname, 0, 32);
1265 strcpy(CC->fake_postname, newusername);
1266 cprintf("%d Ok\n", OK);
1269 CC->cs_flags |= CS_POSTING;
1272 if (CC->quickroom.QRflags & QR_MAILBOX) {
1273 if (CC->usersupp.axlevel >= 2) {
1274 strcpy(buf, recipient);
1276 strcpy(buf, "sysop");
1277 lprintf(9, "calling alias()\n");
1278 e = alias(buf); /* alias and mail type */
1279 lprintf(9, "alias() returned %d\n", e);
1280 if ((buf[0] == 0) || (e == MES_ERROR)) {
1281 cprintf("%d Unknown address - cannot send message.\n",
1282 ERROR + NO_SUCH_USER);
1285 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
1286 cprintf("%d Net privileges required for network mail.\n",
1287 ERROR + HIGHER_ACCESS_REQUIRED);
1290 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
1291 && ((CC->usersupp.flags & US_INTERNET) == 0)
1292 && (!CC->internal_pgm)) {
1293 cprintf("%d You don't have access to Internet mail.\n",
1294 ERROR + HIGHER_ACCESS_REQUIRED);
1297 if (!strcasecmp(buf, "sysop")) {
1302 goto SKFALL; /* don't search local file */
1303 if (!strcasecmp(buf, CC->usersupp.fullname)) {
1304 cprintf("%d Can't send mail to yourself!\n",
1305 ERROR + NO_SUCH_USER);
1308 /* Check to make sure the user exists; also get the correct
1309 * upper/lower casing of the name.
1311 a = getuser(&tempUS, buf);
1313 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1316 strcpy(buf, tempUS.fullname);
1318 SKFALL:b = MES_NORMAL;
1319 if (CC->quickroom.QRflags & QR_ANONONLY)
1321 if (CC->quickroom.QRflags & QR_ANONOPT) {
1325 if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
1328 /* If we're only checking the validity of the request, return
1329 * success without creating the message.
1332 cprintf("%d %s\n", OK, buf);
1335 cprintf("%d send message\n", SEND_LISTING);
1336 if (CC->fake_postname[0])
1337 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, CC->fake_postname);
1338 else if (CC->fake_username[0])
1339 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, CC->fake_username);
1341 make_message(CC->temp, &CC->usersupp, buf, CC->quickroom.QRname, b, e, format_type, "");
1342 save_message(CC->temp, buf, (mtsflag ? AIDEROOM : ""), e, 1);
1343 CC->fake_postname[0] = '\0';
1350 * message entry - mode 3 (raw)
1352 void cmd_ent3(char *entargs)
1358 struct usersupp tempUS;
1363 if (CC->internal_pgm == 0) {
1364 cprintf("%d This command is for internal programs only.\n",
1368 /* See if there's a recipient, but make sure it's a real one */
1369 extract(recp, entargs, 1);
1370 for (a = 0; a < strlen(recp); ++a)
1371 if (!isprint(recp[a]))
1372 strcpy(&recp[a], &recp[a + 1]);
1373 while (isspace(recp[0]))
1374 strcpy(recp, &recp[1]);
1375 while (isspace(recp[strlen(recp) - 1]))
1376 recp[strlen(recp) - 1] = 0;
1378 /* If we're in Mail, check the recipient */
1379 if (strlen(recp) > 0) {
1380 e = alias(recp); /* alias and mail type */
1381 if ((recp[0] == 0) || (e == MES_ERROR)) {
1382 cprintf("%d Unknown address - cannot send message.\n",
1383 ERROR + NO_SUCH_USER);
1386 if (e == MES_LOCAL) {
1387 a = getuser(&tempUS, recp);
1389 cprintf("%d No such user.\n",
1390 ERROR + NO_SUCH_USER);
1395 /* At this point, message has been approved. */
1396 if (extract_int(entargs, 0) == 0) {
1397 cprintf("%d OK to send\n", OK);
1400 /* open a temp file to hold the message */
1401 fp = fopen(CC->temp, "wb");
1403 cprintf("%d Cannot open %s: %s\n",
1404 ERROR + INTERNAL_ERROR,
1405 CC->temp, strerror(errno));
1408 msglen = extract_long(entargs, 2);
1409 cprintf("%d %ld\n", SEND_BINARY, msglen);
1410 while (msglen > 0L) {
1411 bloklen = ((msglen >= 255L) ? 255 : msglen);
1412 client_read(buf, (int) bloklen);
1413 fwrite(buf, (int) bloklen, 1, fp);
1414 msglen = msglen - bloklen;
1418 save_message(CC->temp, recp, "", e, 0);
1423 * API function to delete messages which match a set of criteria
1424 * (returns the actual number of messages deleted)
1425 * FIX ... still need to implement delete by content type
1427 int CtdlDeleteMessages( char *room_name, /* which room */
1428 long dmsgnum, /* or "0" for any */
1429 char *content_type /* or NULL for any */
1432 struct quickroom qrbuf;
1433 struct cdbdata *cdbfr;
1434 long *msglist = NULL;
1437 int num_deleted = 0;
1439 struct SuppMsgInfo smi;
1441 lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
1442 room_name, dmsgnum, content_type);
1444 /* get room record, obtaining a lock... */
1445 if (lgetroom(&qrbuf, room_name) != 0) {
1446 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
1448 return(0); /* room not found */
1451 cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
1453 lprintf(9, "doing mallok/memcpy loop\n");
1454 if (cdbfr != NULL) {
1455 msglist = mallok(cdbfr->len);
1456 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1457 num_msgs = cdbfr->len / sizeof(long);
1462 for (i=0; i<num_msgs; ++i) {
1463 lprintf(9, "Evaluating message d\n", i);
1466 /* Set/clear a bit for each criterion */
1468 lprintf(9, "Message number is <%ld>\n", msglist[i]);
1469 if ( (dmsgnum == 0L) || (msglist[i]==dmsgnum) ) {
1470 delete_this |= 0x01;
1473 if (content_type == NULL) {
1474 delete_this |= 0x02;
1476 GetSuppMsgInfo(&smi, msglist[i]);
1477 lprintf(9, "Content type is <%s>\n",
1478 smi.smi_content_type);
1479 if (!strcasecmp(smi.smi_content_type,
1481 delete_this |= 0x02;
1485 /* Delete message only if all bits are set */
1486 if (delete_this == 0x03) {
1487 AdjRefCount(msglist[i], -1);
1493 num_msgs = sort_msglist(msglist, num_msgs);
1494 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
1495 msglist, (num_msgs * sizeof(long)) );
1497 qrbuf.QRhighest = msglist[num_msgs - 1];
1501 lprintf(9, "%d message(s) deleted.\n", num_deleted);
1502 return(num_deleted);
1508 * Delete message from current room
1510 void cmd_dele(char *delstr)
1515 getuser(&CC->usersupp, CC->curr_user);
1516 if ((CC->usersupp.axlevel < 6)
1517 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1518 && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1519 cprintf("%d Higher access required.\n",
1520 ERROR + HIGHER_ACCESS_REQUIRED);
1523 delnum = extract_long(delstr, 0);
1525 num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, NULL);
1528 cprintf("%d %d message%s deleted.\n", OK,
1529 num_deleted, ((num_deleted!=1) ? "s" : "") );
1531 cprintf("%d Message %ld not found.\n", ERROR, delnum);
1537 * move a message to another room
1539 void cmd_move(char *args)
1544 struct quickroom qtemp;
1547 num = extract_long(args, 0);
1548 extract(targ, args, 1);
1550 getuser(&CC->usersupp, CC->curr_user);
1551 if ((CC->usersupp.axlevel < 6)
1552 && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1553 cprintf("%d Higher access required.\n",
1554 ERROR + HIGHER_ACCESS_REQUIRED);
1557 if (getroom(&qtemp, targ) != 0) {
1558 cprintf("%d '%s' does not exist.\n", ERROR, targ);
1561 /* yank the message out of the current room... */
1562 lgetroom(&CC->quickroom, CC->quickroom.QRname);
1563 get_msglist(&CC->quickroom);
1566 for (a = 0; a < (CC->num_msgs); ++a) {
1567 if (MessageFromList(a) == num) {
1569 SetMessageInList(a, 0L);
1573 CC->num_msgs = sort_msglist(CC->msglist, CC->num_msgs);
1574 put_msglist(&CC->quickroom);
1575 CC->quickroom.QRhighest = MessageFromList((CC->num_msgs) - 1);
1577 lputroom(&CC->quickroom);
1579 cprintf("%d msg %ld does not exist.\n", ERROR, num);
1582 /* put the message into the target room */
1583 lgetroom(&qtemp, targ);
1584 qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1587 cprintf("%d Message moved.\n", OK);
1593 * GetSuppMsgInfo() - Get the supplementary record for a message
1595 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
1598 struct cdbdata *cdbsmi;
1601 memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1602 smibuf->smi_msgnum = msgnum;
1603 smibuf->smi_refcount = 1; /* Default reference count is 1 */
1605 /* Use the negative of the message number for its supp record index */
1606 TheIndex = (0L - msgnum);
1608 cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
1609 if (cdbsmi == NULL) {
1610 return; /* record not found; go with defaults */
1612 memcpy(smibuf, cdbsmi->ptr,
1613 ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1614 sizeof(struct SuppMsgInfo) : cdbsmi->len));
1621 * PutSuppMsgInfo() - (re)write supplementary record for a message
1623 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1627 /* Use the negative of the message number for its supp record index */
1628 TheIndex = (0L - smibuf->smi_msgnum);
1630 lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
1631 smibuf->smi_msgnum, smibuf->smi_refcount);
1633 cdb_store(CDB_MSGMAIN,
1634 &TheIndex, sizeof(long),
1635 smibuf, sizeof(struct SuppMsgInfo));
1642 * AdjRefCount - change the reference count for a message;
1643 * delete the message if it reaches zero
1645 void AdjRefCount(long msgnum, int incr)
1648 struct SuppMsgInfo smi;
1651 /* This is a *tight* critical section; please keep it that way, as
1652 * it may get called while nested in other critical sections.
1653 * Complicating this any further will surely cause deadlock!
1655 begin_critical_section(S_SUPPMSGMAIN);
1656 GetSuppMsgInfo(&smi, msgnum);
1657 smi.smi_refcount += incr;
1658 PutSuppMsgInfo(&smi);
1659 end_critical_section(S_SUPPMSGMAIN);
1661 lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1662 msgnum, smi.smi_refcount);
1664 /* If the reference count is now zero, delete the message
1665 * (and its supplementary record as well).
1667 if (smi.smi_refcount == 0) {
1668 lprintf(9, "Deleting message <%ld>\n", msgnum);
1670 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1671 delnum = (0L - msgnum);
1672 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
1678 * Write a generic object to this room
1680 void CtdlWriteObject( char *req_room, /* Room to stuff it in */
1681 char *content_type, /* MIME type of this object */
1682 char *tempfilename, /* Where to fetch it from */
1683 int is_mailbox, /* Private mailbox room? */
1684 int is_binary, /* Is encoding necessary? */
1685 int is_unique /* Del others of this type? */
1689 char filename[PATH_MAX];
1692 struct quickroom qrbuf;
1693 char roomname[ROOMNAMELEN];
1695 if (is_mailbox) MailboxName(roomname, &CC->usersupp, req_room);
1696 else safestrncpy(roomname, req_room, sizeof(roomname));
1698 strcpy(filename, tmpnam(NULL));
1699 fp = fopen(filename, "w");
1700 if (fp == NULL) return;
1702 fprintf(fp, "%c%c%c", 0xFF, MES_NORMAL, 4);
1703 fprintf(fp, "T%ld%c", time(NULL), 0);
1704 fprintf(fp, "A%s%c", CC->usersupp.fullname, 0);
1705 fprintf(fp, "O%s%c", roomname, 0);
1706 fprintf(fp, "N%s%c", config.c_nodename, 0);
1707 fprintf(fp, "MContent-type: %s\n", content_type);
1709 tempfp = fopen(tempfilename, "r");
1710 if (tempfp == NULL) {
1716 if (is_binary == 0) {
1717 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
1718 while (ch=getc(tempfp), ch>0) putc(ch, fp);
1723 fprintf(fp, "Content-transfer-encoding: base64\n\n");
1726 sprintf(cmdbuf, "./base64 -e <%s >>%s",
1727 tempfilename, filename);
1731 /* Create the requested room if we have to. */
1732 if (getroom(&qrbuf, roomname) != 0) {
1733 create_room(roomname, 4, "", 0);
1736 /* If the caller specified this object as unique, delete all
1737 * other objects of this type that are currently in the room.
1739 CtdlDeleteMessages(roomname, 0L, content_type);
1741 /* Now write the data */
1742 save_message(filename, "", roomname, MES_LOCAL, 1);