]> code.citadel.org Git - citadel.git/blob - citadel/msgbase.c
* Bugfix for MSG0 command to properly handle multipart/alternative
[citadel.git] / citadel / msgbase.c
1 /*
2  * $Id$
3  *
4  * Implements the message store.
5  *
6  */
7
8 #ifdef DLL_EXPORT
9 #define IN_LIBCIT
10 #endif
11
12 #include "sysdep.h"
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
20 # include <time.h>
21 #else
22 # if HAVE_SYS_TIME_H
23 #  include <sys/time.h>
24 # else
25 #  include <time.h>
26 # endif
27 #endif
28
29 #include <ctype.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <limits.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <sys/stat.h>
36 #include "citadel.h"
37 #include "server.h"
38 #include "dynloader.h"
39 #include "database.h"
40 #include "msgbase.h"
41 #include "support.h"
42 #include "sysdep_decls.h"
43 #include "citserver.h"
44 #include "room_ops.h"
45 #include "user_ops.h"
46 #include "file_ops.h"
47 #include "control.h"
48 #include "tools.h"
49 #include "mime_parser.h"
50 #include "html.h"
51 #include "genstamp.h"
52 #include "internet_addressing.h"
53
54 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
55 #define ma ((struct ma_info *)CtdlGetUserData(SYM_MA_INFO))
56 #define msg_repl ((struct repl *)CtdlGetUserData(SYM_REPL))
57
58 extern struct config config;
59 long config_msgnum;
60
61 char *msgkeys[] = {
62         "", "", "", "", "", "", "", "", 
63         "", "", "", "", "", "", "", "", 
64         "", "", "", "", "", "", "", "", 
65         "", "", "", "", "", "", "", "", 
66         "", "", "", "", "", "", "", "", 
67         "", "", "", "", "", "", "", "", 
68         "", "", "", "", "", "", "", "", 
69         "", "", "", "", "", "", "", "", 
70         "", 
71         "from",
72         "", "", "",
73         "exti",
74         "rfca",
75         "", 
76         "hnod",
77         "msgn",
78         "", "", "",
79         "text",
80         "node",
81         "room",
82         "path",
83         "",
84         "rcpt",
85         "spec",
86         "time",
87         "subj",
88         "",
89         "",
90         "",
91         "",
92         ""
93 };
94
95 /*
96  * This function is self explanatory.
97  * (What can I say, I'm in a weird mood today...)
98  */
99 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name)
100 {
101         int i;
102
103         for (i = 0; i < strlen(name); ++i) {
104                 if (name[i] == '@') {
105                         while (isspace(name[i - 1]) && i > 0) {
106                                 strcpy(&name[i - 1], &name[i]);
107                                 --i;
108                         }
109                         while (isspace(name[i + 1])) {
110                                 strcpy(&name[i + 1], &name[i + 2]);
111                         }
112                 }
113         }
114 }
115
116
117 /*
118  * Aliasing for network mail.
119  * (Error messages have been commented out, because this is a server.)
120  */
121 int alias(char *name)
122 {                               /* process alias and routing info for mail */
123         FILE *fp;
124         int a, b;
125         char aaa[300], bbb[300];
126
127         remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
128
129         fp = fopen("network/mail.aliases", "r");
130         if (fp == NULL)
131                 fp = fopen("/dev/null", "r");
132         if (fp == NULL)
133                 return (MES_ERROR);
134         strcpy(aaa, "");
135         strcpy(bbb, "");
136         while (fgets(aaa, sizeof aaa, fp) != NULL) {
137                 while (isspace(name[0]))
138                         strcpy(name, &name[1]);
139                 aaa[strlen(aaa) - 1] = 0;
140                 strcpy(bbb, "");
141                 for (a = 0; a < strlen(aaa); ++a) {
142                         if (aaa[a] == ',') {
143                                 strcpy(bbb, &aaa[a + 1]);
144                                 aaa[a] = 0;
145                         }
146                 }
147                 if (!strcasecmp(name, aaa))
148                         strcpy(name, bbb);
149         }
150         fclose(fp);
151         lprintf(7, "Mail is being forwarded to %s\n", name);
152
153         /* Change "user @ xxx" to "user" if xxx is an alias for this host */
154         for (a=0; a<strlen(name); ++a) {
155                 if (name[a] == '@') {
156                         if (CtdlHostAlias(&name[a+1]) == hostalias_localhost) {
157                                 name[a] = 0;
158                                 lprintf(7, "Changed to <%s>\n", name);
159                         }
160                 }
161         }
162
163         /* determine local or remote type, see citadel.h */
164         for (a = 0; a < strlen(name); ++a)
165                 if (name[a] == '!')
166                         return (MES_INTERNET);
167         for (a = 0; a < strlen(name); ++a)
168                 if (name[a] == '@')
169                         for (b = a; b < strlen(name); ++b)
170                                 if (name[b] == '.')
171                                         return (MES_INTERNET);
172         b = 0;
173         for (a = 0; a < strlen(name); ++a)
174                 if (name[a] == '@')
175                         ++b;
176         if (b > 1) {
177                 lprintf(7, "Too many @'s in address\n");
178                 return (MES_ERROR);
179         }
180         if (b == 1) {
181                 for (a = 0; a < strlen(name); ++a)
182                         if (name[a] == '@')
183                                 strcpy(bbb, &name[a + 1]);
184                 while (bbb[0] == 32)
185                         strcpy(bbb, &bbb[1]);
186                 fp = fopen("network/mail.sysinfo", "r");
187                 if (fp == NULL)
188                         return (MES_ERROR);    
189 GETSN:          do {
190                         a = getstring(fp, aaa);
191                 } while ((a >= 0) && (strcasecmp(aaa, bbb)));
192                 a = getstring(fp, aaa);
193                 if (!strncmp(aaa, "use ", 4)) {
194                         strcpy(bbb, &aaa[4]);
195                         fseek(fp, 0L, 0);
196                         goto GETSN;
197                 }
198                 fclose(fp);
199                 if (!strncmp(aaa, "uum", 3)) {
200                         strcpy(bbb, name);
201                         for (a = 0; a < strlen(bbb); ++a) {
202                                 if (bbb[a] == '@')
203                                         bbb[a] = 0;
204                                 if (bbb[a] == ' ')
205                                         bbb[a] = '_';
206                         }
207                         while (bbb[strlen(bbb) - 1] == '_')
208                                 bbb[strlen(bbb) - 1] = 0;
209                         sprintf(name, &aaa[4], bbb);
210                         lprintf(9, "returning MES_INTERNET\n");
211                         return (MES_INTERNET);
212                 }
213                 if (!strncmp(aaa, "bin", 3)) {
214                         strcpy(aaa, name);
215                         strcpy(bbb, name);
216                         while (aaa[strlen(aaa) - 1] != '@')
217                                 aaa[strlen(aaa) - 1] = 0;
218                         aaa[strlen(aaa) - 1] = 0;
219                         while (aaa[strlen(aaa) - 1] == ' ')
220                                 aaa[strlen(aaa) - 1] = 0;
221                         while (bbb[0] != '@')
222                                 strcpy(bbb, &bbb[1]);
223                         strcpy(bbb, &bbb[1]);
224                         while (bbb[0] == ' ')
225                                 strcpy(bbb, &bbb[1]);
226                         sprintf(name, "%s @%s", aaa, bbb);
227                         lprintf(9, "returning MES_BINARY\n");
228                         return (MES_BINARY);
229                 }
230                 return (MES_ERROR);
231         }
232         lprintf(9, "returning MES_LOCAL\n");
233         return (MES_LOCAL);
234 }
235
236
237 void get_mm(void)
238 {
239         FILE *fp;
240
241         fp = fopen("citadel.control", "r");
242         fread((char *) &CitControl, sizeof(struct CitControl), 1, fp);
243         fclose(fp);
244 }
245
246
247
248 void simple_listing(long msgnum, void *userdata)
249 {
250         cprintf("%ld\n", msgnum);
251 }
252
253
254
255 /* Determine if a given message matches the fields in a message template.
256  * Return 0 for a successful match.
257  */
258 int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
259         int i;
260
261         /* If there aren't any fields in the template, all messages will
262          * match.
263          */
264         if (template == NULL) return(0);
265
266         /* Null messages are bogus. */
267         if (msg == NULL) return(1);
268
269         for (i='A'; i<='Z'; ++i) {
270                 if (template->cm_fields[i] != NULL) {
271                         if (msg->cm_fields[i] == NULL) {
272                                 return 1;
273                         }
274                         if (strcasecmp(msg->cm_fields[i],
275                                 template->cm_fields[i])) return 1;
276                 }
277         }
278
279         /* All compares succeeded: we have a match! */
280         return 0;
281 }
282
283
284 /*
285  * Manipulate the "seen msgs" string.
286  */
287 void CtdlSetSeen(long target_msgnum, int target_setting) {
288         char newseen[SIZ];
289         struct cdbdata *cdbfr;
290         int i;
291         int is_seen = 0;
292         int was_seen = 1;
293         long lo = (-1L);
294         long hi = (-1L);
295         struct visit vbuf;
296         long *msglist;
297         int num_msgs = 0;
298
299         /* Learn about the user and room in question */
300         get_mm();
301         getuser(&CC->usersupp, CC->curr_user);
302         CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
303
304         /* Load the message list */
305         cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
306         if (cdbfr != NULL) {
307                 msglist = mallok(cdbfr->len);
308                 memcpy(msglist, cdbfr->ptr, cdbfr->len);
309                 num_msgs = cdbfr->len / sizeof(long);
310                 cdb_free(cdbfr);
311         } else {
312                 return; /* No messages at all?  No further action. */
313         }
314
315         lprintf(9, "before optimize: %s\n", vbuf.v_seen);
316         strcpy(newseen, "");
317
318         for (i=0; i<num_msgs; ++i) {
319                 is_seen = 0;
320
321                 if (msglist[i] == target_msgnum) {
322                         is_seen = target_setting;
323                 }
324                 else {
325                         if (is_msg_in_mset(vbuf.v_seen, msglist[i])) {
326                                 is_seen = 1;
327                         }
328                 }
329
330                 if (is_seen == 1) {
331                         if (lo < 0L) lo = msglist[i];
332                         hi = msglist[i];
333                 }
334                 if (  ((is_seen == 0) && (was_seen == 1))
335                    || ((is_seen == 1) && (i == num_msgs-1)) ) {
336                         if ( (strlen(newseen) + 20) > SIZ) {
337                                 strcpy(newseen, &newseen[20]);
338                                 newseen[0] = '*';
339                         }
340                         if (strlen(newseen) > 0) strcat(newseen, ",");
341                         if (lo == hi) {
342                                 sprintf(&newseen[strlen(newseen)], "%ld", lo);
343                         }
344                         else {
345                                 sprintf(&newseen[strlen(newseen)], "%ld:%ld",
346                                         lo, hi);
347                         }
348                         lo = (-1L);
349                         hi = (-1L);
350                 }
351                 was_seen = is_seen;
352         }
353
354         safestrncpy(vbuf.v_seen, newseen, SIZ);
355         lprintf(9, " after optimize: %s\n", vbuf.v_seen);
356         phree(msglist);
357         CtdlSetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
358 }
359
360
361 /*
362  * API function to perform an operation for each qualifying message in the
363  * current room.  (Returns the number of messages processed.)
364  */
365 int CtdlForEachMessage(int mode, long ref,
366                         int moderation_level,
367                         char *content_type,
368                         struct CtdlMessage *compare,
369                         void (*CallBack) (long, void *),
370                         void *userdata)
371 {
372
373         int a;
374         struct visit vbuf;
375         struct cdbdata *cdbfr;
376         long *msglist = NULL;
377         int num_msgs = 0;
378         int num_processed = 0;
379         long thismsg;
380         struct SuppMsgInfo smi;
381         struct CtdlMessage *msg;
382         int is_seen;
383         long lastold = 0L;
384         int printed_lastold = 0;
385
386         /* Learn about the user and room in question */
387         get_mm();
388         getuser(&CC->usersupp, CC->curr_user);
389         CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
390
391         /* Load the message list */
392         cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
393         if (cdbfr != NULL) {
394                 msglist = mallok(cdbfr->len);
395                 memcpy(msglist, cdbfr->ptr, cdbfr->len);
396                 num_msgs = cdbfr->len / sizeof(long);
397                 cdb_free(cdbfr);
398         } else {
399                 return 0;       /* No messages at all?  No further action. */
400         }
401
402
403         /*
404          * Now begin the traversal.
405          */
406         if (num_msgs > 0) for (a = 0; a < num_msgs; ++a) {
407                 GetSuppMsgInfo(&smi, msglist[a]);
408
409                 /* Filter out messages that are moderated below the level
410                  * currently being viewed at.
411                  */
412                 if (smi.smi_mod < moderation_level) {
413                         msglist[a] = 0L;
414                 }
415
416                 /* If the caller is looking for a specific MIME type, filter
417                  * out all messages which are not of the type requested.
418                  */
419                 if (content_type != NULL) if (strlen(content_type) > 0) {
420                         if (strcasecmp(smi.smi_content_type, content_type)) {
421                                 msglist[a] = 0L;
422                         }
423                 }
424         }
425
426         num_msgs = sort_msglist(msglist, num_msgs);
427
428         /* If a template was supplied, filter out the messages which
429          * don't match.  (This could induce some delays!)
430          */
431         if (num_msgs > 0) {
432                 if (compare != NULL) {
433                         for (a = 0; a < num_msgs; ++a) {
434                                 msg = CtdlFetchMessage(msglist[a]);
435                                 if (msg != NULL) {
436                                         if (CtdlMsgCmp(msg, compare)) {
437                                                 msglist[a] = 0L;
438                                         }
439                                         CtdlFreeMessage(msg);
440                                 }
441                         }
442                 }
443         }
444
445         
446         /*
447          * Now iterate through the message list, according to the
448          * criteria supplied by the caller.
449          */
450         if (num_msgs > 0)
451                 for (a = 0; a < num_msgs; ++a) {
452                         thismsg = msglist[a];
453                         is_seen = is_msg_in_mset(vbuf.v_seen, thismsg);
454                         if (is_seen) lastold = thismsg;
455                         if ((thismsg > 0L)
456                             && (
457
458                                        (mode == MSGS_ALL)
459                                        || ((mode == MSGS_OLD) && (is_seen))
460                                        || ((mode == MSGS_NEW) && (!is_seen))
461                                        || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
462                                    || ((mode == MSGS_FIRST) && (a < ref))
463                                 || ((mode == MSGS_GT) && (thismsg > ref))
464                                 || ((mode == MSGS_EQ) && (thismsg == ref))
465                             )
466                             ) {
467                                 if ((mode == MSGS_NEW) && (CC->usersupp.flags & US_LASTOLD) && (lastold > 0L) && (printed_lastold == 0) && (!is_seen)) {
468                                         if (CallBack)
469                                                 CallBack(lastold, userdata);
470                                         printed_lastold = 1;
471                                         ++num_processed;
472                                 }
473                                 if (CallBack) CallBack(thismsg, userdata);
474                                 ++num_processed;
475                         }
476                 }
477         phree(msglist);         /* Clean up */
478         return num_processed;
479 }
480
481
482
483 /*
484  * cmd_msgs()  -  get list of message #'s in this room
485  *                implements the MSGS server command using CtdlForEachMessage()
486  */
487 void cmd_msgs(char *cmdbuf)
488 {
489         int mode = 0;
490         char which[SIZ];
491         char buf[SIZ];
492         char tfield[SIZ];
493         char tvalue[SIZ];
494         int cm_ref = 0;
495         int i;
496         int with_template = 0;
497         struct CtdlMessage *template = NULL;
498
499         extract(which, cmdbuf, 0);
500         cm_ref = extract_int(cmdbuf, 1);
501         with_template = extract_int(cmdbuf, 2);
502
503         mode = MSGS_ALL;
504         strcat(which, "   ");
505         if (!strncasecmp(which, "OLD", 3))
506                 mode = MSGS_OLD;
507         else if (!strncasecmp(which, "NEW", 3))
508                 mode = MSGS_NEW;
509         else if (!strncasecmp(which, "FIRST", 5))
510                 mode = MSGS_FIRST;
511         else if (!strncasecmp(which, "LAST", 4))
512                 mode = MSGS_LAST;
513         else if (!strncasecmp(which, "GT", 2))
514                 mode = MSGS_GT;
515
516         if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
517                 cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
518                 return;
519         }
520
521         if (with_template) {
522                 cprintf("%d Send template then receive message list\n",
523                         START_CHAT_MODE);
524                 template = (struct CtdlMessage *)
525                         mallok(sizeof(struct CtdlMessage));
526                 memset(template, 0, sizeof(struct CtdlMessage));
527                 while(client_gets(buf), strcmp(buf,"000")) {
528                         extract(tfield, buf, 0);
529                         extract(tvalue, buf, 1);
530                         for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
531                                 if (!strcasecmp(tfield, msgkeys[i])) {
532                                         template->cm_fields[i] =
533                                                 strdoop(tvalue);
534                                 }
535                         }
536                 }
537         }
538         else {
539                 cprintf("%d Message list...\n", LISTING_FOLLOWS);
540         }
541
542         CtdlForEachMessage(mode, cm_ref,
543                 CC->usersupp.moderation_filter,
544                 NULL, template, simple_listing, NULL);
545         if (template != NULL) CtdlFreeMessage(template);
546         cprintf("000\n");
547 }
548
549
550
551
552 /* 
553  * help_subst()  -  support routine for help file viewer
554  */
555 void help_subst(char *strbuf, char *source, char *dest)
556 {
557         char workbuf[SIZ];
558         int p;
559
560         while (p = pattern2(strbuf, source), (p >= 0)) {
561                 strcpy(workbuf, &strbuf[p + strlen(source)]);
562                 strcpy(&strbuf[p], dest);
563                 strcat(strbuf, workbuf);
564         }
565 }
566
567
568 void do_help_subst(char *buffer)
569 {
570         char buf2[16];
571
572         help_subst(buffer, "^nodename", config.c_nodename);
573         help_subst(buffer, "^humannode", config.c_humannode);
574         help_subst(buffer, "^fqdn", config.c_fqdn);
575         help_subst(buffer, "^username", CC->usersupp.fullname);
576         sprintf(buf2, "%ld", CC->usersupp.usernum);
577         help_subst(buffer, "^usernum", buf2);
578         help_subst(buffer, "^sysadm", config.c_sysadm);
579         help_subst(buffer, "^variantname", CITADEL);
580         sprintf(buf2, "%d", config.c_maxsessions);
581         help_subst(buffer, "^maxsessions", buf2);
582 }
583
584
585
586 /*
587  * memfmout()  -  Citadel text formatter and paginator.
588  *             Although the original purpose of this routine was to format
589  *             text to the reader's screen width, all we're really using it
590  *             for here is to format text out to 80 columns before sending it
591  *             to the client.  The client software may reformat it again.
592  */
593 void memfmout(
594         int width,              /* screen width to use */
595         char *mptr,             /* where are we going to get our text from? */
596         char subst,             /* nonzero if we should do substitutions */
597         char *nl)               /* string to terminate lines with */
598 {
599         int a, b, c;
600         int real = 0;
601         int old = 0;
602         CIT_UBYTE ch;
603         char aaa[140];
604         char buffer[SIZ];
605
606         strcpy(aaa, "");
607         old = 255;
608         strcpy(buffer, "");
609         c = 1;                  /* c is the current pos */
610
611         do {
612                 if (subst) {
613                         while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
614                                 ch = *mptr++;
615                                 buffer[strlen(buffer) + 1] = 0;
616                                 buffer[strlen(buffer)] = ch;
617                         }
618
619                         if (buffer[0] == '^')
620                                 do_help_subst(buffer);
621
622                         buffer[strlen(buffer) + 1] = 0;
623                         a = buffer[0];
624                         strcpy(buffer, &buffer[1]);
625                 } else {
626                         ch = *mptr++;
627                 }
628
629                 old = real;
630                 real = ch;
631
632                 if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
633                         ch = 32;
634                 if (((old == 13) || (old == 10)) && (isspace(real))) {
635                         cprintf("%s", nl);
636                         c = 1;
637                 }
638                 if (ch > 126)
639                         continue;
640
641                 if (ch > 32) {
642                         if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
643                                 cprintf("%s%s", nl, aaa);
644                                 c = strlen(aaa);
645                                 aaa[0] = 0;
646                         }
647                         b = strlen(aaa);
648                         aaa[b] = ch;
649                         aaa[b + 1] = 0;
650                 }
651                 if (ch == 32) {
652                         if ((strlen(aaa) + c) > (width - 5)) {
653                                 cprintf("%s", nl);
654                                 c = 1;
655                         }
656                         cprintf("%s ", aaa);
657                         ++c;
658                         c = c + strlen(aaa);
659                         strcpy(aaa, "");
660                 }
661                 if ((ch == 13) || (ch == 10)) {
662                         cprintf("%s%s", aaa, nl);
663                         c = 1;
664                         strcpy(aaa, "");
665                 }
666
667         } while (ch > 0);
668
669         cprintf("%s%s", aaa, nl);
670 }
671
672
673
674 /*
675  * Callback function for mime parser that simply lists the part
676  */
677 void list_this_part(char *name, char *filename, char *partnum, char *disp,
678                     void *content, char *cbtype, size_t length, char *encoding,
679                     void *cbuserdata)
680 {
681
682         cprintf("part=%s|%s|%s|%s|%s|%d\n",
683                 name, filename, partnum, disp, cbtype, length);
684 }
685
686
687 /*
688  * Callback function for mime parser that opens a section for downloading
689  */
690 void mime_download(char *name, char *filename, char *partnum, char *disp,
691                    void *content, char *cbtype, size_t length, char *encoding,
692                    void *cbuserdata)
693 {
694
695         /* Silently go away if there's already a download open... */
696         if (CC->download_fp != NULL)
697                 return;
698
699         /* ...or if this is not the desired section */
700         if (strcasecmp(desired_section, partnum))
701                 return;
702
703         CC->download_fp = tmpfile();
704         if (CC->download_fp == NULL)
705                 return;
706
707         fwrite(content, length, 1, CC->download_fp);
708         fflush(CC->download_fp);
709         rewind(CC->download_fp);
710
711         OpenCmdResult(filename, cbtype);
712 }
713
714
715
716 /*
717  * Load a message from disk into memory.
718  * This is used by CtdlOutputMsg() and other fetch functions.
719  *
720  * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
721  *       using the CtdlMessageFree() function.
722  */
723 struct CtdlMessage *CtdlFetchMessage(long msgnum)
724 {
725         struct cdbdata *dmsgtext;
726         struct CtdlMessage *ret = NULL;
727         char *mptr;
728         CIT_UBYTE ch;
729         CIT_UBYTE field_header;
730         size_t field_length;
731
732         dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
733         if (dmsgtext == NULL) {
734                 return NULL;
735         }
736         mptr = dmsgtext->ptr;
737
738         /* Parse the three bytes that begin EVERY message on disk.
739          * The first is always 0xFF, the on-disk magic number.
740          * The second is the anonymous/public type byte.
741          * The third is the format type byte (vari, fixed, or MIME).
742          */
743         ch = *mptr++;
744         if (ch != 255) {
745                 lprintf(5, "Message %ld appears to be corrupted.\n", msgnum);
746                 cdb_free(dmsgtext);
747                 return NULL;
748         }
749         ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
750         memset(ret, 0, sizeof(struct CtdlMessage));
751
752         ret->cm_magic = CTDLMESSAGE_MAGIC;
753         ret->cm_anon_type = *mptr++;    /* Anon type byte */
754         ret->cm_format_type = *mptr++;  /* Format type byte */
755
756         /*
757          * The rest is zero or more arbitrary fields.  Load them in.
758          * We're done when we encounter either a zero-length field or
759          * have just processed the 'M' (message text) field.
760          */
761         do {
762                 field_length = strlen(mptr);
763                 if (field_length == 0)
764                         break;
765                 field_header = *mptr++;
766                 ret->cm_fields[field_header] = mallok(field_length);
767                 strcpy(ret->cm_fields[field_header], mptr);
768
769                 while (*mptr++ != 0);   /* advance to next field */
770
771         } while ((field_length > 0) && (field_header != 'M'));
772
773         cdb_free(dmsgtext);
774
775         /* Always make sure there's something in the msg text field */
776         if (ret->cm_fields['M'] == NULL)
777                 ret->cm_fields['M'] = strdoop("<no text>\n");
778
779         /* Perform "before read" hooks (aborting if any return nonzero) */
780         if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
781                 CtdlFreeMessage(ret);
782                 return NULL;
783         }
784
785         return (ret);
786 }
787
788
789 /*
790  * Returns 1 if the supplied pointer points to a valid Citadel message.
791  * If the pointer is NULL or the magic number check fails, returns 0.
792  */
793 int is_valid_message(struct CtdlMessage *msg) {
794         if (msg == NULL)
795                 return 0;
796         if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
797                 lprintf(3, "is_valid_message() -- self-check failed\n");
798                 return 0;
799         }
800         return 1;
801 }
802
803
804 /*
805  * 'Destructor' for struct CtdlMessage
806  */
807 void CtdlFreeMessage(struct CtdlMessage *msg)
808 {
809         int i;
810
811         if (is_valid_message(msg) == 0) return;
812
813         for (i = 0; i < 256; ++i)
814                 if (msg->cm_fields[i] != NULL) {
815                         phree(msg->cm_fields[i]);
816                 }
817
818         msg->cm_magic = 0;      /* just in case */
819         phree(msg);
820 }
821
822
823 /*
824  * Pre callback function for mime parser that wants to display text
825  */
826 void fixed_output_pre(char *name, char *filename, char *partnum, char *disp,
827                 void *content, char *cbtype, size_t length, char *encoding,
828                 void *cbuserdata)
829 {
830                 lprintf(9, "fixed_output_pre() type=<%s>\n", cbtype);   
831                 if (!strcasecmp(cbtype, "multipart/alternative")) {
832                         ma->is_ma = 1;
833                         ma->did_print = 0;
834                         lprintf(9, "multipart/alternative: <%s>\n", ma->prefix);
835                         return;
836                 }
837 }
838
839 /*
840  * Pre callback function for mime parser that wants to display text
841  */
842 void fixed_output_post(char *name, char *filename, char *partnum, char *disp,
843                 void *content, char *cbtype, size_t length, char *encoding,
844                 void *cbuserdata)
845 {
846                 lprintf(9, "fixed_output_post() type=<%s>\n", cbtype);  
847                 if (!strcasecmp(cbtype, "multipart/alternative")) {
848                         ma->is_ma = 0;
849                         ma->did_print = 0;
850                         lprintf(9, "multipart/alternative: <%s>\n", ma->prefix);
851                         return;
852                 }
853 }
854
855 /*
856  * Inline callback function for mime parser that wants to display text
857  */
858 void fixed_output(char *name, char *filename, char *partnum, char *disp,
859                 void *content, char *cbtype, size_t length, char *encoding,
860                 void *cbuserdata)
861         {
862                 char *ptr;
863                 char *wptr;
864                 size_t wlen;
865                 CIT_UBYTE ch = 0;
866
867                 lprintf(9, "fixed_output() type=<%s>\n", cbtype);       
868         
869                 if ( (ma->is_ma == 1) && (ma->did_print == 1) ) {
870                         lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype);
871                         return;
872                 }
873         
874                 ma->did_print = 1;
875         
876                 if ( (!strcasecmp(cbtype, "text/plain")) 
877                    || (strlen(cbtype)==0) ) {
878                         wlen = length;
879                         wptr = content;
880                         while (wlen--) {
881                                 ch = *wptr++;
882                                 /**********
883                                 if (ch==10) cprintf("\r\n");
884                                 else cprintf("%c", ch);
885                                  **********/
886                                 cprintf("%c", ch);
887                         }
888                         if (ch != '\n') cprintf("\n");
889                 }
890                 else if (!strcasecmp(cbtype, "text/html")) {
891                         ptr = html_to_ascii(content, 80, 0);
892                         wlen = strlen(ptr);
893                         wptr = ptr;
894                         while (wlen--) {
895                                 ch = *wptr++;
896                                 if (ch==10) cprintf("\r\n");
897                                 else cprintf("%c", ch);
898                         }
899                         phree(ptr);
900                 }
901                 else if (strncasecmp(cbtype, "multipart/", 10)) {
902                         cprintf("Part %s: %s (%s) (%d bytes)\r\n",
903                                 partnum, filename, cbtype, length);
904                 }
905         }
906
907
908 /*
909  * Get a message off disk.  (returns om_* values found in msgbase.h)
910  * 
911  */
912 int CtdlOutputMsg(long msg_num,         /* message number (local) to fetch */
913                 int mode,               /* how would you like that message? */
914                 int headers_only,       /* eschew the message body? */
915                 int do_proto,           /* do Citadel protocol responses? */
916                 int crlf                /* Use CRLF newlines instead of LF? */
917 ) {
918         struct CtdlMessage *TheMessage;
919         int retcode;
920
921         lprintf(7, "CtdlOutputMsg() msgnum=%ld, mode=%d\n", 
922                 msg_num, mode);
923
924         TheMessage = NULL;
925
926         if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
927                 if (do_proto) cprintf("%d Not logged in.\n",
928                         ERROR + NOT_LOGGED_IN);
929                 return(om_not_logged_in);
930         }
931
932         /* FIXME ... small security issue
933          * We need to check to make sure the requested message is actually
934          * in the current room, and set msg_ok to 1 only if it is.  This
935          * functionality is currently missing because I'm in a hurry to replace
936          * broken production code with nonbroken pre-beta code.  :(   -- ajc
937          *
938          if (!msg_ok) {
939          if (do_proto) cprintf("%d Message %ld is not in this room.\n",
940          ERROR, msg_num);
941          return(om_no_such_msg);
942          }
943          */
944
945         /*
946          * Fetch the message from disk
947          */
948         TheMessage = CtdlFetchMessage(msg_num);
949         if (TheMessage == NULL) {
950                 if (do_proto) cprintf("%d Can't locate msg %ld on disk\n",
951                         ERROR, msg_num);
952                 return(om_no_such_msg);
953         }
954         
955         retcode = CtdlOutputPreLoadedMsg(
956                         TheMessage, msg_num, mode,
957                         headers_only, do_proto, crlf);
958
959         CtdlFreeMessage(TheMessage);
960         return(retcode);
961 }
962
963
964 /*
965  * Get a message off disk.  (returns om_* values found in msgbase.h)
966  * 
967  */
968 int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage,
969                 long msg_num,
970                 int mode,               /* how would you like that message? */
971                 int headers_only,       /* eschew the message body? */
972                 int do_proto,           /* do Citadel protocol responses? */
973                 int crlf                /* Use CRLF newlines instead of LF? */
974 ) {
975         int i, k;
976         char buf[1024];
977         CIT_UBYTE ch;
978         char allkeys[SIZ];
979         char display_name[SIZ];
980         char *mptr;
981         char *nl;       /* newline string */
982
983         /* buffers needed for RFC822 translation */
984         char suser[SIZ];
985         char luser[SIZ];
986         char fuser[SIZ];
987         char snode[SIZ];
988         char lnode[SIZ];
989         char mid[SIZ];
990         char datestamp[SIZ];
991         /*                                       */
992
993         sprintf(mid, "%ld", msg_num);
994         nl = (crlf ? "\r\n" : "\n");
995
996         if (!is_valid_message(TheMessage)) {
997                 lprintf(1, "ERROR: invalid preloaded message for output\n");
998                 return(om_no_such_msg);
999         }
1000
1001         /* Are we downloading a MIME component? */
1002         if (mode == MT_DOWNLOAD) {
1003                 if (TheMessage->cm_format_type != FMT_RFC822) {
1004                         if (do_proto)
1005                                 cprintf("%d This is not a MIME message.\n",
1006                                 ERROR);
1007                 } else if (CC->download_fp != NULL) {
1008                         if (do_proto) cprintf(
1009                                 "%d You already have a download open.\n",
1010                                 ERROR);
1011                 } else {
1012                         /* Parse the message text component */
1013                         mptr = TheMessage->cm_fields['M'];
1014                         mime_parser(mptr, NULL,
1015                                 *mime_download, NULL, NULL,
1016                                 NULL, 0);
1017                         /* If there's no file open by this time, the requested
1018                          * section wasn't found, so print an error
1019                          */
1020                         if (CC->download_fp == NULL) {
1021                                 if (do_proto) cprintf(
1022                                         "%d Section %s not found.\n",
1023                                         ERROR + FILE_NOT_FOUND,
1024                                         desired_section);
1025                         }
1026                 }
1027                 return((CC->download_fp != NULL) ? om_ok : om_mime_error);
1028         }
1029
1030         /* now for the user-mode message reading loops */
1031         if (do_proto) cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num);
1032
1033         /* Tell the client which format type we're using.  If this is a
1034          * MIME message, *lie* about it and tell the user it's fixed-format.
1035          */
1036         if (mode == MT_CITADEL) {
1037                 if (TheMessage->cm_format_type == FMT_RFC822) {
1038                         if (do_proto) cprintf("type=1\n");
1039                 }
1040                 else {
1041                         if (do_proto) cprintf("type=%d\n",
1042                                 TheMessage->cm_format_type);
1043                 }
1044         }
1045
1046         /* nhdr=yes means that we're only displaying headers, no body */
1047         if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
1048                 if (do_proto) cprintf("nhdr=yes\n");
1049         }
1050
1051         /* begin header processing loop for Citadel message format */
1052
1053         if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
1054
1055                 strcpy(display_name, "<unknown>");
1056                 if (TheMessage->cm_fields['A']) {
1057                         strcpy(buf, TheMessage->cm_fields['A']);
1058                         PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
1059                         if (TheMessage->cm_anon_type == MES_ANON)
1060                                 strcpy(display_name, "****");
1061                         else if (TheMessage->cm_anon_type == MES_AN2)
1062                                 strcpy(display_name, "anonymous");
1063                         else
1064                                 strcpy(display_name, buf);
1065                         if ((is_room_aide())
1066                             && ((TheMessage->cm_anon_type == MES_ANON)
1067                              || (TheMessage->cm_anon_type == MES_AN2))) {
1068                                 sprintf(&display_name[strlen(display_name)],
1069                                         " [%s]", buf);
1070                         }
1071                 }
1072
1073                 strcpy(allkeys, FORDER);
1074                 for (i=0; i<strlen(allkeys); ++i) {
1075                         k = (int) allkeys[i];
1076                         if (k != 'M') {
1077                                 if (TheMessage->cm_fields[k] != NULL) {
1078                                         if (k == 'A') {
1079                                                 if (do_proto) cprintf("%s=%s\n",
1080                                                         msgkeys[k],
1081                                                         display_name);
1082                                         }
1083                                         else {
1084                                                 if (do_proto) cprintf("%s=%s\n",
1085                                                         msgkeys[k],
1086                                                         TheMessage->cm_fields[k]
1087                                         );
1088                                         }
1089                                 }
1090                         }
1091                 }
1092
1093         }
1094
1095         /* begin header processing loop for RFC822 transfer format */
1096
1097         strcpy(suser, "");
1098         strcpy(luser, "");
1099         strcpy(fuser, "");
1100         strcpy(snode, NODENAME);
1101         strcpy(lnode, HUMANNODE);
1102         if (mode == MT_RFC822) {
1103                 cprintf("X-UIDL: %ld%s", msg_num, nl);
1104                 for (i = 0; i < 256; ++i) {
1105                         if (TheMessage->cm_fields[i]) {
1106                                 mptr = TheMessage->cm_fields[i];
1107
1108                                 if (i == 'A') {
1109                                         strcpy(luser, mptr);
1110                                         strcpy(suser, mptr);
1111                                 }
1112 /****
1113  "Path:" removed for now because it confuses brain-dead Microsoft shitware
1114  into thinking that mail messages are newsgroup messages instead.  When we
1115  add NNTP support back into Citadel we'll have to add code to only output
1116  this field when appropriate.
1117                                 else if (i == 'P') {
1118                                         cprintf("Path: %s%s", mptr, nl);
1119                                 }
1120  ****/
1121                                 else if (i == 'U')
1122                                         cprintf("Subject: %s%s", mptr, nl);
1123                                 else if (i == 'I')
1124                                         strcpy(mid, mptr);
1125                                 else if (i == 'H')
1126                                         strcpy(lnode, mptr);
1127                                 else if (i == 'O')
1128                                         cprintf("X-Citadel-Room: %s%s",
1129                                                 mptr, nl);
1130                                 else if (i == 'N')
1131                                         strcpy(snode, mptr);
1132                                 else if (i == 'R')
1133                                         cprintf("To: %s%s", mptr, nl);
1134                                 else if (i == 'T') {
1135                                         datestring(datestamp, atol(mptr),
1136                                                 DATESTRING_RFC822 );
1137                                         cprintf("Date: %s%s", datestamp, nl);
1138                                 }
1139                         }
1140                 }
1141         }
1142
1143         for (i=0; i<strlen(suser); ++i) {
1144                 suser[i] = tolower(suser[i]);
1145                 if (!isalnum(suser[i])) suser[i]='_';
1146         }
1147
1148         if (mode == MT_RFC822) {
1149                 if (!strcasecmp(snode, NODENAME)) {
1150                         strcpy(snode, FQDN);
1151                 }
1152
1153                 /* Construct a fun message id */
1154                 cprintf("Message-ID: <%s", mid);
1155                 if (strchr(mid, '@')==NULL) {
1156                         cprintf("@%s", snode);
1157                 }
1158                 cprintf(">%s", nl);
1159
1160                 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
1161
1162                 if (strlen(fuser) > 0) {
1163                         cprintf("From: %s (%s)%s", fuser, luser, nl);
1164                 }
1165                 else {
1166                         cprintf("From: %s@%s (%s)%s", suser, snode, luser, nl);
1167                 }
1168
1169                 cprintf("Organization: %s%s", lnode, nl);
1170         }
1171
1172         /* end header processing loop ... at this point, we're in the text */
1173
1174         mptr = TheMessage->cm_fields['M'];
1175
1176         /* Tell the client about the MIME parts in this message */
1177         if (TheMessage->cm_format_type == FMT_RFC822) {
1178                 if (mode == MT_CITADEL) {
1179                         mime_parser(mptr, NULL,
1180                                 *list_this_part, NULL, NULL,
1181                                 NULL, 0);
1182                 }
1183                 else if (mode == MT_MIME) {     /* list parts only */
1184                         mime_parser(mptr, NULL,
1185                                 *list_this_part, NULL, NULL,
1186                                 NULL, 0);
1187                         if (do_proto) cprintf("000\n");
1188                         return(om_ok);
1189                 }
1190                 else if (mode == MT_RFC822) {   /* unparsed RFC822 dump */
1191                         /* FIXME ... we have to put some code in here to avoid
1192                          * printing duplicate header information when both
1193                          * Citadel and RFC822 headers exist.  Preference should
1194                          * probably be given to the RFC822 headers.
1195                          */
1196                         while (ch=*(mptr++), ch!=0) {
1197                                 if (ch==13) ;
1198                                 else if (ch==10) cprintf("%s", nl);
1199                                 else cprintf("%c", ch);
1200                         }
1201                         if (do_proto) cprintf("000\n");
1202                         return(om_ok);
1203                 }
1204         }
1205
1206         if (headers_only) {
1207                 if (do_proto) cprintf("000\n");
1208                 return(om_ok);
1209         }
1210
1211         /* signify start of msg text */
1212         if (mode == MT_CITADEL)
1213                 if (do_proto) cprintf("text\n");
1214         if (mode == MT_RFC822) {
1215                 if (TheMessage->cm_fields['U'] == NULL) {
1216                         cprintf("Subject: (no subject)%s", nl);
1217                 }
1218                 cprintf("%s", nl);
1219         }
1220
1221         /* If the format type on disk is 1 (fixed-format), then we want
1222          * everything to be output completely literally ... regardless of
1223          * what message transfer format is in use.
1224          */
1225         if (TheMessage->cm_format_type == FMT_FIXED) {
1226                 strcpy(buf, "");
1227                 while (ch = *mptr++, ch > 0) {
1228                         if (ch == 13)
1229                                 ch = 10;
1230                         if ((ch == 10) || (strlen(buf) > 250)) {
1231                                 cprintf("%s%s", buf, nl);
1232                                 strcpy(buf, "");
1233                         } else {
1234                                 buf[strlen(buf) + 1] = 0;
1235                                 buf[strlen(buf)] = ch;
1236                         }
1237                 }
1238                 if (strlen(buf) > 0)
1239                         cprintf("%s%s", buf, nl);
1240         }
1241
1242         /* If the message on disk is format 0 (Citadel vari-format), we
1243          * output using the formatter at 80 columns.  This is the final output
1244          * form if the transfer format is RFC822, but if the transfer format
1245          * is Citadel proprietary, it'll still work, because the indentation
1246          * for new paragraphs is correct and the client will reformat the
1247          * message to the reader's screen width.
1248          */
1249         if (TheMessage->cm_format_type == FMT_CITADEL) {
1250                 memfmout(80, mptr, 0, nl);
1251         }
1252
1253         /* If the message on disk is format 4 (MIME), we've gotta hand it
1254          * off to the MIME parser.  The client has already been told that
1255          * this message is format 1 (fixed format), so the callback function
1256          * we use will display those parts as-is.
1257          */
1258         if (TheMessage->cm_format_type == FMT_RFC822) {
1259                 CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info));
1260                 memset(ma, 0, sizeof(struct ma_info));
1261                 mime_parser(mptr, NULL,
1262                         *fixed_output, *fixed_output_pre, *fixed_output_post,
1263                         NULL, 0);
1264         }
1265
1266         /* now we're done */
1267         if (do_proto) cprintf("000\n");
1268         return(om_ok);
1269 }
1270
1271
1272
1273 /*
1274  * display a message (mode 0 - Citadel proprietary)
1275  */
1276 void cmd_msg0(char *cmdbuf)
1277 {
1278         long msgid;
1279         int headers_only = 0;
1280
1281         msgid = extract_long(cmdbuf, 0);
1282         headers_only = extract_int(cmdbuf, 1);
1283
1284         CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0);
1285         return;
1286 }
1287
1288
1289 /*
1290  * display a message (mode 2 - RFC822)
1291  */
1292 void cmd_msg2(char *cmdbuf)
1293 {
1294         long msgid;
1295         int headers_only = 0;
1296
1297         msgid = extract_long(cmdbuf, 0);
1298         headers_only = extract_int(cmdbuf, 1);
1299
1300         CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1);
1301 }
1302
1303
1304
1305 /* 
1306  * display a message (mode 3 - IGnet raw format - internal programs only)
1307  */
1308 void cmd_msg3(char *cmdbuf)
1309 {
1310         long msgnum;
1311         struct CtdlMessage *msg;
1312         struct ser_ret smr;
1313
1314         if (CC->internal_pgm == 0) {
1315                 cprintf("%d This command is for internal programs only.\n",
1316                         ERROR);
1317                 return;
1318         }
1319
1320         msgnum = extract_long(cmdbuf, 0);
1321         msg = CtdlFetchMessage(msgnum);
1322         if (msg == NULL) {
1323                 cprintf("%d Message %ld not found.\n", 
1324                         ERROR, msgnum);
1325                 return;
1326         }
1327
1328         serialize_message(&smr, msg);
1329         CtdlFreeMessage(msg);
1330
1331         if (smr.len == 0) {
1332                 cprintf("%d Unable to serialize message\n",
1333                         ERROR+INTERNAL_ERROR);
1334                 return;
1335         }
1336
1337         cprintf("%d %ld\n", BINARY_FOLLOWS, smr.len);
1338         client_write(smr.ser, smr.len);
1339         phree(smr.ser);
1340 }
1341
1342
1343
1344 /* 
1345  * display a message (mode 4 - MIME) (FIXME ... still evolving, not complete)
1346  */
1347 void cmd_msg4(char *cmdbuf)
1348 {
1349         long msgid;
1350
1351         msgid = extract_long(cmdbuf, 0);
1352         CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0);
1353 }
1354
1355 /*
1356  * Open a component of a MIME message as a download file 
1357  */
1358 void cmd_opna(char *cmdbuf)
1359 {
1360         long msgid;
1361
1362         CtdlAllocUserData(SYM_DESIRED_SECTION, SIZ);
1363
1364         msgid = extract_long(cmdbuf, 0);
1365         extract(desired_section, cmdbuf, 1);
1366
1367         CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1);
1368 }                       
1369
1370
1371 /*
1372  * Save a message pointer into a specified room
1373  * (Returns 0 for success, nonzero for failure)
1374  * roomname may be NULL to use the current room
1375  */
1376 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
1377         int i;
1378         char hold_rm[ROOMNAMELEN];
1379         struct cdbdata *cdbfr;
1380         int num_msgs;
1381         long *msglist;
1382         long highest_msg = 0L;
1383         struct CtdlMessage *msg = NULL;
1384
1385         lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
1386                 roomname, msgid, flags);
1387
1388         strcpy(hold_rm, CC->quickroom.QRname);
1389
1390         /* We may need to check to see if this message is real */
1391         if (  (flags & SM_VERIFY_GOODNESS)
1392            || (flags & SM_DO_REPL_CHECK)
1393            ) {
1394                 msg = CtdlFetchMessage(msgid);
1395                 if (msg == NULL) return(ERROR + ILLEGAL_VALUE);
1396         }
1397
1398         /* Perform replication checks if necessary */
1399         if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) {
1400
1401                 if (getroom(&CC->quickroom,
1402                    ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1403                    != 0) {
1404                         lprintf(9, "No such room <%s>\n", roomname);
1405                         if (msg != NULL) CtdlFreeMessage(msg);
1406                         return(ERROR + ROOM_NOT_FOUND);
1407                 }
1408
1409                 if (ReplicationChecks(msg) != 0) {
1410                         getroom(&CC->quickroom, hold_rm);
1411                         if (msg != NULL) CtdlFreeMessage(msg);
1412                         lprintf(9, "Did replication, and newer exists\n");
1413                         return(0);
1414                 }
1415         }
1416
1417         /* Now the regular stuff */
1418         if (lgetroom(&CC->quickroom,
1419            ((roomname != NULL) ? roomname : CC->quickroom.QRname) )
1420            != 0) {
1421                 lprintf(9, "No such room <%s>\n", roomname);
1422                 if (msg != NULL) CtdlFreeMessage(msg);
1423                 return(ERROR + ROOM_NOT_FOUND);
1424         }
1425
1426         cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long));
1427         if (cdbfr == NULL) {
1428                 msglist = NULL;
1429                 num_msgs = 0;
1430         } else {
1431                 msglist = mallok(cdbfr->len);
1432                 if (msglist == NULL)
1433                         lprintf(3, "ERROR malloc msglist!\n");
1434                 num_msgs = cdbfr->len / sizeof(long);
1435                 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1436                 cdb_free(cdbfr);
1437         }
1438
1439
1440         /* Make sure the message doesn't already exist in this room.  It
1441          * is absolutely taboo to have more than one reference to the same
1442          * message in a room.
1443          */
1444         if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
1445                 if (msglist[i] == msgid) {
1446                         lputroom(&CC->quickroom);       /* unlock the room */
1447                         getroom(&CC->quickroom, hold_rm);
1448                         if (msg != NULL) CtdlFreeMessage(msg);
1449                         return(ERROR + ALREADY_EXISTS);
1450                 }
1451         }
1452
1453         /* Now add the new message */
1454         ++num_msgs;
1455         msglist = reallok(msglist,
1456                           (num_msgs * sizeof(long)));
1457
1458         if (msglist == NULL) {
1459                 lprintf(3, "ERROR: can't realloc message list!\n");
1460         }
1461         msglist[num_msgs - 1] = msgid;
1462
1463         /* Sort the message list, so all the msgid's are in order */
1464         num_msgs = sort_msglist(msglist, num_msgs);
1465
1466         /* Determine the highest message number */
1467         highest_msg = msglist[num_msgs - 1];
1468
1469         /* Write it back to disk. */
1470         cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long),
1471                   msglist, num_msgs * sizeof(long));
1472
1473         /* Free up the memory we used. */
1474         phree(msglist);
1475
1476         /* Update the highest-message pointer and unlock the room. */
1477         CC->quickroom.QRhighest = highest_msg;
1478         lputroom(&CC->quickroom);
1479         getroom(&CC->quickroom, hold_rm);
1480
1481         /* Bump the reference count for this message. */
1482         if ((flags & SM_DONT_BUMP_REF)==0) {
1483                 AdjRefCount(msgid, +1);
1484         }
1485
1486         /* Return success. */
1487         if (msg != NULL) CtdlFreeMessage(msg);
1488         return (0);
1489 }
1490
1491
1492
1493 /*
1494  * Message base operation to send a message to the master file
1495  * (returns new message number)
1496  *
1497  * This is the back end for CtdlSaveMsg() and should not be directly
1498  * called by server-side modules.
1499  *
1500  */
1501 long send_message(struct CtdlMessage *msg,      /* pointer to buffer */
1502                 FILE *save_a_copy)              /* save a copy to disk? */
1503 {
1504         long newmsgid;
1505         long retval;
1506         char msgidbuf[SIZ];
1507         struct ser_ret smr;
1508
1509         /* Get a new message number */
1510         newmsgid = get_new_message_number();
1511         sprintf(msgidbuf, "%ld@%s", newmsgid, config.c_fqdn);
1512
1513         /* Generate an ID if we don't have one already */
1514         if (msg->cm_fields['I']==NULL) {
1515                 msg->cm_fields['I'] = strdoop(msgidbuf);
1516         }
1517         
1518         serialize_message(&smr, msg);
1519
1520         if (smr.len == 0) {
1521                 cprintf("%d Unable to serialize message\n",
1522                         ERROR+INTERNAL_ERROR);
1523                 return (-1L);
1524         }
1525
1526         /* Write our little bundle of joy into the message base */
1527         if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
1528                       smr.ser, smr.len) < 0) {
1529                 lprintf(2, "Can't store message\n");
1530                 retval = 0L;
1531         } else {
1532                 retval = newmsgid;
1533         }
1534
1535         /* If the caller specified that a copy should be saved to a particular
1536          * file handle, do that now too.
1537          */
1538         if (save_a_copy != NULL) {
1539                 fwrite(smr.ser, smr.len, 1, save_a_copy);
1540         }
1541
1542         /* Free the memory we used for the serialized message */
1543         phree(smr.ser);
1544
1545         /* Return the *local* message ID to the caller
1546          * (even if we're storing an incoming network message)
1547          */
1548         return(retval);
1549 }
1550
1551
1552
1553 /*
1554  * Serialize a struct CtdlMessage into the format used on disk and network.
1555  * 
1556  * This function loads up a "struct ser_ret" (defined in server.h) which
1557  * contains the length of the serialized message and a pointer to the
1558  * serialized message in memory.  THE LATTER MUST BE FREED BY THE CALLER.
1559  */
1560 void serialize_message(struct ser_ret *ret,             /* return values */
1561                         struct CtdlMessage *msg)        /* unserialized msg */
1562 {
1563         size_t wlen;
1564         int i;
1565         static char *forder = FORDER;
1566
1567         if (is_valid_message(msg) == 0) return;         /* self check */
1568
1569         ret->len = 3;
1570         for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
1571                 ret->len = ret->len +
1572                         strlen(msg->cm_fields[(int)forder[i]]) + 2;
1573
1574         lprintf(9, "serialize_message() calling malloc(%d)\n", ret->len);
1575         ret->ser = mallok(ret->len);
1576         if (ret->ser == NULL) {
1577                 ret->len = 0;
1578                 return;
1579         }
1580
1581         ret->ser[0] = 0xFF;
1582         ret->ser[1] = msg->cm_anon_type;
1583         ret->ser[2] = msg->cm_format_type;
1584         wlen = 3;
1585
1586         for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
1587                 ret->ser[wlen++] = (char)forder[i];
1588                 strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
1589                 wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1;
1590         }
1591         if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
1592                 ret->len, wlen);
1593
1594         return;
1595 }
1596
1597
1598
1599 /*
1600  * Back end for the ReplicationChecks() function
1601  */
1602 void check_repl(long msgnum, void *userdata) {
1603         struct CtdlMessage *msg;
1604         time_t timestamp = (-1L);
1605
1606         lprintf(9, "check_repl() found message %ld\n", msgnum);
1607         msg = CtdlFetchMessage(msgnum);
1608         if (msg == NULL) return;
1609         if (msg->cm_fields['T'] != NULL) {
1610                 timestamp = atol(msg->cm_fields['T']);
1611         }
1612         CtdlFreeMessage(msg);
1613
1614         if (timestamp > msg_repl->highest) {
1615                 msg_repl->highest = timestamp;  /* newer! */
1616                 lprintf(9, "newer!\n");
1617                 return;
1618         }
1619         lprintf(9, "older!\n");
1620
1621         /* Existing isn't newer?  Then delete the old one(s). */
1622         CtdlDeleteMessages(CC->quickroom.QRname, msgnum, "");
1623 }
1624
1625
1626 /*
1627  * Check to see if any messages already exist which carry the same Extended ID
1628  * as this one.  
1629  *
1630  * If any are found:
1631  * -> With older timestamps: delete them and return 0.  Message will be saved.
1632  * -> With newer timestamps: return 1.  Message save will be aborted.
1633  */
1634 int ReplicationChecks(struct CtdlMessage *msg) {
1635         struct CtdlMessage *template;
1636         int abort_this = 0;
1637
1638         lprintf(9, "ReplicationChecks() started\n");
1639         /* No extended id?  Don't do anything. */
1640         if (msg->cm_fields['E'] == NULL) return 0;
1641         if (strlen(msg->cm_fields['E']) == 0) return 0;
1642         lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']);
1643
1644         CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
1645         strcpy(msg_repl->extended_id, msg->cm_fields['E']);
1646         msg_repl->highest = atol(msg->cm_fields['T']);
1647
1648         template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1649         memset(template, 0, sizeof(struct CtdlMessage));
1650         template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
1651
1652         CtdlForEachMessage(MSGS_ALL, 0L, (-127), NULL, template,
1653                 check_repl, NULL);
1654
1655         /* If a newer message exists with the same Extended ID, abort
1656          * this save.
1657          */
1658         if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
1659                 abort_this = 1;
1660                 }
1661
1662         CtdlFreeMessage(template);
1663         lprintf(9, "ReplicationChecks() returning %d\n", abort_this);
1664         return(abort_this);
1665 }
1666
1667
1668
1669
1670 /*
1671  * Save a message to disk
1672  */
1673 long CtdlSaveMsg(struct CtdlMessage *msg,       /* message to save */
1674                 char *rec,                      /* Recipient (mail) */
1675                 char *force,                    /* force a particular room? */
1676                 int supplied_mailtype)          /* local or remote type */
1677 {
1678         char aaa[100];
1679         char hold_rm[ROOMNAMELEN];
1680         char actual_rm[ROOMNAMELEN];
1681         char force_room[ROOMNAMELEN];
1682         char content_type[SIZ];                 /* We have to learn this */
1683         char recipient[SIZ];
1684         long newmsgid;
1685         char *mptr = NULL;
1686         struct usersupp userbuf;
1687         int a;
1688         struct SuppMsgInfo smi;
1689         FILE *network_fp = NULL;
1690         static int seqnum = 1;
1691         struct CtdlMessage *imsg;
1692         char *instr;
1693         int mailtype;
1694
1695         lprintf(9, "CtdlSaveMsg() called\n");
1696         if (is_valid_message(msg) == 0) return(-1);     /* self check */
1697         mailtype = supplied_mailtype;
1698
1699         /* If this message has no timestamp, we take the liberty of
1700          * giving it one, right now.
1701          */
1702         if (msg->cm_fields['T'] == NULL) {
1703                 lprintf(9, "Generating timestamp\n");
1704                 sprintf(aaa, "%ld", time(NULL));
1705                 msg->cm_fields['T'] = strdoop(aaa);
1706         }
1707
1708         /* If this message has no path, we generate one.
1709          */
1710         if (msg->cm_fields['P'] == NULL) {
1711                 lprintf(9, "Generating path\n");
1712                 if (msg->cm_fields['A'] != NULL) {
1713                         msg->cm_fields['P'] = strdoop(msg->cm_fields['A']);
1714                         for (a=0; a<strlen(msg->cm_fields['P']); ++a) {
1715                                 if (isspace(msg->cm_fields['P'][a])) {
1716                                         msg->cm_fields['P'][a] = ' ';
1717                                 }
1718                         }
1719                 }
1720                 else {
1721                         msg->cm_fields['P'] = strdoop("unknown");
1722                 }
1723         }
1724
1725         strcpy(force_room, force);
1726
1727         /* Strip non-printable characters out of the recipient name */
1728         lprintf(9, "Checking recipient (if present)\n");
1729         strcpy(recipient, rec);
1730         for (a = 0; a < strlen(recipient); ++a)
1731                 if (!isprint(recipient[a]))
1732                         strcpy(&recipient[a], &recipient[a + 1]);
1733
1734         /* Change "user @ xxx" to "user" if xxx is an alias for this host */
1735         for (a=0; a<strlen(recipient); ++a) {
1736                 if (recipient[a] == '@') {
1737                         if (CtdlHostAlias(&recipient[a+1]) 
1738                            == hostalias_localhost) {
1739                                 recipient[a] = 0;
1740                                 lprintf(7, "Changed to <%s>\n", recipient);
1741                                 mailtype = MES_LOCAL;
1742                         }
1743                 }
1744         }
1745
1746         lprintf(9, "Recipient is <%s>\n", recipient);
1747
1748         /* Learn about what's inside, because it's what's inside that counts */
1749         lprintf(9, "Learning what's inside\n");
1750         if (msg->cm_fields['M'] == NULL) {
1751                 lprintf(1, "ERROR: attempt to save message with NULL body\n");
1752         }
1753
1754         switch (msg->cm_format_type) {
1755         case 0:
1756                 strcpy(content_type, "text/x-citadel-variformat");
1757                 break;
1758         case 1:
1759                 strcpy(content_type, "text/plain");
1760                 break;
1761         case 4:
1762                 strcpy(content_type, "text/plain");
1763                 /* advance past header fields */
1764                 mptr = msg->cm_fields['M'];
1765                 a = strlen(mptr);
1766                 while ((--a) > 0) {
1767                         if (!strncasecmp(mptr, "Content-type: ", 14)) {
1768                                 safestrncpy(content_type, mptr,
1769                                             sizeof(content_type));
1770                                 strcpy(content_type, &content_type[14]);
1771                                 for (a = 0; a < strlen(content_type); ++a)
1772                                         if ((content_type[a] == ';')
1773                                             || (content_type[a] == ' ')
1774                                             || (content_type[a] == 13)
1775                                             || (content_type[a] == 10))
1776                                                 content_type[a] = 0;
1777                                 break;
1778                         }
1779                         ++mptr;
1780                 }
1781         }
1782
1783         /* Goto the correct room */
1784         lprintf(9, "Switching rooms\n");
1785         strcpy(hold_rm, CC->quickroom.QRname);
1786         strcpy(actual_rm, CC->quickroom.QRname);
1787
1788         /* If the user is a twit, move to the twit room for posting */
1789         lprintf(9, "Handling twit stuff\n");
1790         if (TWITDETECT) {
1791                 if (CC->usersupp.axlevel == 2) {
1792                         strcpy(hold_rm, actual_rm);
1793                         strcpy(actual_rm, config.c_twitroom);
1794                 }
1795         }
1796
1797         /* ...or if this message is destined for Aide> then go there. */
1798         if (strlen(force_room) > 0) {
1799                 strcpy(actual_rm, force_room);
1800         }
1801
1802         lprintf(9, "Possibly relocating\n");
1803         if (strcasecmp(actual_rm, CC->quickroom.QRname)) {
1804                 getroom(&CC->quickroom, actual_rm);
1805         }
1806
1807         /*
1808          * If this message has no O (room) field, generate one.
1809          */
1810         if (msg->cm_fields['O'] == NULL) {
1811                 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
1812         }
1813
1814         /* Perform "before save" hooks (aborting if any return nonzero) */
1815         lprintf(9, "Performing before-save hooks\n");
1816         if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return(-1);
1817
1818         /* If this message has an Extended ID, perform replication checks */
1819         lprintf(9, "Performing replication checks\n");
1820         if (ReplicationChecks(msg) > 0) return(-1);
1821
1822         /* Network mail - send a copy to the network program. */
1823         if ((strlen(recipient) > 0) && (mailtype == MES_BINARY)) {
1824                 lprintf(9, "Sending network spool\n");
1825                 sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
1826                         (long) getpid(), CC->cs_pid, ++seqnum);
1827                 lprintf(9, "Saving a copy to %s\n", aaa);
1828                 network_fp = fopen(aaa, "ab+");
1829                 if (network_fp == NULL)
1830                         lprintf(2, "ERROR: %s\n", strerror(errno));
1831         }
1832
1833         /* Save it to disk */
1834         lprintf(9, "Saving to disk\n");
1835         newmsgid = send_message(msg, network_fp);
1836         if (network_fp != NULL) {
1837                 fclose(network_fp);
1838                 /* FIXME start a network run here */
1839         }
1840
1841         if (newmsgid <= 0L) return(-1);
1842
1843         /* Write a supplemental message info record.  This doesn't have to
1844          * be a critical section because nobody else knows about this message
1845          * yet.
1846          */
1847         lprintf(9, "Creating SuppMsgInfo record\n");
1848         memset(&smi, 0, sizeof(struct SuppMsgInfo));
1849         smi.smi_msgnum = newmsgid;
1850         smi.smi_refcount = 0;
1851         safestrncpy(smi.smi_content_type, content_type, 64);
1852         PutSuppMsgInfo(&smi);
1853
1854         /* Now figure out where to store the pointers */
1855         lprintf(9, "Storing pointers\n");
1856
1857         /* If this is being done by the networker delivering a private
1858          * message, we want to BYPASS saving the sender's copy (because there
1859          * is no local sender; it would otherwise go to the Trashcan).
1860          */
1861         if ((!CC->internal_pgm) || (strlen(recipient) == 0)) {
1862                 if (CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0) != 0) {
1863                         lprintf(3, "ERROR saving message pointer!\n");
1864                         CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1865                 }
1866         }
1867
1868         /* For internet mail, drop a copy in the outbound queue room */
1869         if (mailtype == MES_INTERNET) {
1870                 CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0);
1871         }
1872
1873         /* Bump this user's messages posted counter. */
1874         lprintf(9, "Updating user\n");
1875         lgetuser(&CC->usersupp, CC->curr_user);
1876         CC->usersupp.posted = CC->usersupp.posted + 1;
1877         lputuser(&CC->usersupp);
1878
1879         /* If this is private, local mail, make a copy in the
1880          * recipient's mailbox and bump the reference count.
1881          */
1882         if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
1883                 if (getuser(&userbuf, recipient) == 0) {
1884                         lprintf(9, "Delivering private mail\n");
1885                         MailboxName(actual_rm, &userbuf, MAILROOM);
1886                         CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
1887                 }
1888                 else {
1889                         lprintf(9, "No user <%s>, saving in %s> instead\n",
1890                                 recipient, AIDEROOM);
1891                         CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
1892                 }
1893         }
1894
1895         /* Perform "after save" hooks */
1896         lprintf(9, "Performing after-save hooks\n");
1897         PerformMessageHooks(msg, EVT_AFTERSAVE);
1898
1899         /* */
1900         lprintf(9, "Returning to original room\n");
1901         if (strcasecmp(hold_rm, CC->quickroom.QRname))
1902                 getroom(&CC->quickroom, hold_rm);
1903
1904         /* For internet mail, generate delivery instructions 
1905          * (Yes, this is recursive!   Deal with it!)
1906          */
1907         if (mailtype == MES_INTERNET) {
1908                 lprintf(9, "Generating delivery instructions\n");
1909                 instr = mallok(2048);
1910                 sprintf(instr,
1911                         "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
1912                         "bounceto|%s@%s\n"
1913                         "remote|%s|0||\n",
1914                         SPOOLMIME, newmsgid, time(NULL),
1915                         msg->cm_fields['A'], msg->cm_fields['N'],
1916                         recipient );
1917
1918                 imsg = mallok(sizeof(struct CtdlMessage));
1919                 memset(imsg, 0, sizeof(struct CtdlMessage));
1920                 imsg->cm_magic = CTDLMESSAGE_MAGIC;
1921                 imsg->cm_anon_type = MES_NORMAL;
1922                 imsg->cm_format_type = FMT_RFC822;
1923                 imsg->cm_fields['A'] = strdoop("Citadel");
1924                 imsg->cm_fields['M'] = instr;
1925                 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
1926                 CtdlFreeMessage(imsg);
1927         }
1928
1929         return(newmsgid);
1930 }
1931
1932
1933
1934 /*
1935  * Convenience function for generating small administrative messages.
1936  */
1937 void quickie_message(char *from, char *to, char *room, char *text)
1938 {
1939         struct CtdlMessage *msg;
1940
1941         msg = mallok(sizeof(struct CtdlMessage));
1942         memset(msg, 0, sizeof(struct CtdlMessage));
1943         msg->cm_magic = CTDLMESSAGE_MAGIC;
1944         msg->cm_anon_type = MES_NORMAL;
1945         msg->cm_format_type = 0;
1946         msg->cm_fields['A'] = strdoop(from);
1947         msg->cm_fields['O'] = strdoop(room);
1948         msg->cm_fields['N'] = strdoop(NODENAME);
1949         if (to != NULL)
1950                 msg->cm_fields['R'] = strdoop(to);
1951         msg->cm_fields['M'] = strdoop(text);
1952
1953         CtdlSaveMsg(msg, "", room, MES_LOCAL);
1954         CtdlFreeMessage(msg);
1955         syslog(LOG_NOTICE, text);
1956 }
1957
1958
1959
1960 /*
1961  * Back end function used by make_message() and similar functions
1962  */
1963 char *CtdlReadMessageBody(char *terminator,     /* token signalling EOT */
1964                         size_t maxlen,          /* maximum message length */
1965                         char *exist             /* if non-null, append to it;
1966                                                    exist is ALWAYS freed  */
1967                         ) {
1968         char buf[SIZ];
1969         int linelen;
1970         size_t message_len = 0;
1971         size_t buffer_len = 0;
1972         char *ptr;
1973         char *m;
1974
1975         if (exist == NULL) {
1976                 m = mallok(4096);
1977         }
1978         else {
1979                 m = reallok(exist, strlen(exist) + 4096);
1980                 if (m == NULL) phree(exist);
1981         }
1982         if (m == NULL) {
1983                 while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;;
1984                 return(NULL);
1985         } else {
1986                 buffer_len = 4096;
1987                 m[0] = 0;
1988                 message_len = 0;
1989         }
1990         /* read in the lines of message text one by one */
1991         while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) {
1992
1993                 /* strip trailing newline type stuff */
1994                 if (buf[strlen(buf)-1]==10) buf[strlen(buf)-1]=0;
1995                 if (buf[strlen(buf)-1]==13) buf[strlen(buf)-1]=0;
1996
1997                 linelen = strlen(buf);
1998
1999                 /* augment the buffer if we have to */
2000                 if ((message_len + linelen + 2) > buffer_len) {
2001                         lprintf(9, "realloking\n");
2002                         ptr = reallok(m, (buffer_len * 2) );
2003                         if (ptr == NULL) {      /* flush if can't allocate */
2004                                 while ( (client_gets(buf)>0) &&
2005                                         strcmp(buf, terminator)) ;;
2006                                 return(m);
2007                         } else {
2008                                 buffer_len = (buffer_len * 2);
2009                                 m = ptr;
2010                                 lprintf(9, "buffer_len is %d\n", buffer_len);
2011                         }
2012                 }
2013
2014                 /* Add the new line to the buffer.  We avoid using strcat()
2015                  * because that would involve traversing the entire message
2016                  * after each line, and this function needs to run fast.
2017                  */
2018                 strcpy(&m[message_len], buf);
2019                 m[message_len + linelen] = '\n';
2020                 m[message_len + linelen + 1] = 0;
2021                 message_len = message_len + linelen + 1;
2022
2023                 /* if we've hit the max msg length, flush the rest */
2024                 if (message_len >= maxlen) {
2025                         while ( (client_gets(buf)>0)
2026                                 && strcmp(buf, terminator)) ;;
2027                         return(m);
2028                 }
2029         }
2030         return(m);
2031 }
2032
2033
2034
2035
2036 /*
2037  * Build a binary message to be saved on disk.
2038  */
2039
2040 struct CtdlMessage *make_message(
2041         struct usersupp *author,        /* author's usersupp structure */
2042         char *recipient,                /* NULL if it's not mail */
2043         char *room,                     /* room where it's going */
2044         int type,                       /* see MES_ types in header file */
2045         int net_type,                   /* see MES_ types in header file */
2046         int format_type,                /* local or remote (see citadel.h) */
2047         char *fake_name)                /* who we're masquerading as */
2048 {
2049
2050         int a;
2051         char dest_node[32];
2052         char buf[SIZ];
2053         struct CtdlMessage *msg;
2054
2055         msg = mallok(sizeof(struct CtdlMessage));
2056         memset(msg, 0, sizeof(struct CtdlMessage));
2057         msg->cm_magic = CTDLMESSAGE_MAGIC;
2058         msg->cm_anon_type = type;
2059         msg->cm_format_type = format_type;
2060
2061         /* Don't confuse the poor folks if it's not routed mail. */
2062         strcpy(dest_node, "");
2063
2064         /* If net_type is MES_BINARY, split out the destination node. */
2065         if (net_type == MES_BINARY) {
2066                 strcpy(dest_node, NODENAME);
2067                 for (a = 0; a < strlen(recipient); ++a) {
2068                         if (recipient[a] == '@') {
2069                                 recipient[a] = 0;
2070                                 strcpy(dest_node, &recipient[a + 1]);
2071                         }
2072                 }
2073         }
2074
2075         /* if net_type is MES_INTERNET, set the dest node to 'internet' */
2076         if (net_type == MES_INTERNET) {
2077                 strcpy(dest_node, "internet");
2078         }
2079
2080         while (isspace(recipient[strlen(recipient) - 1]))
2081                 recipient[strlen(recipient) - 1] = 0;
2082
2083         sprintf(buf, "cit%ld", author->usernum);                /* Path */
2084         msg->cm_fields['P'] = strdoop(buf);
2085
2086         sprintf(buf, "%ld", time(NULL));                        /* timestamp */
2087         msg->cm_fields['T'] = strdoop(buf);
2088
2089         if (fake_name[0])                                       /* author */
2090                 msg->cm_fields['A'] = strdoop(fake_name);
2091         else
2092                 msg->cm_fields['A'] = strdoop(author->fullname);
2093
2094         if (CC->quickroom.QRflags & QR_MAILBOX)                 /* room */
2095                 msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
2096         else
2097                 msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
2098
2099         msg->cm_fields['N'] = strdoop(NODENAME);                /* nodename */
2100         msg->cm_fields['H'] = strdoop(HUMANNODE);               /* hnodename */
2101
2102         if (recipient[0] != 0)
2103                 msg->cm_fields['R'] = strdoop(recipient);
2104         if (dest_node[0] != 0)
2105                 msg->cm_fields['D'] = strdoop(dest_node);
2106
2107
2108         msg->cm_fields['M'] = CtdlReadMessageBody("000",
2109                                                 config.c_maxmsglen, NULL);
2110
2111
2112         return(msg);
2113 }
2114
2115
2116 /*
2117  * Check to see whether we have permission to post a message in the current
2118  * room.  Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or
2119  * returns 0 on success.
2120  */
2121 int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) {
2122
2123         if (!(CC->logged_in)) {
2124                 sprintf(errmsgbuf, "Not logged in.");
2125                 return (ERROR + NOT_LOGGED_IN);
2126         }
2127
2128         if ((CC->usersupp.axlevel < 2)
2129             && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
2130                 sprintf(errmsgbuf, "Need to be validated to enter "
2131                                 "(except in %s> to sysop)", MAILROOM);
2132                 return (ERROR + HIGHER_ACCESS_REQUIRED);
2133         }
2134
2135         if ((CC->usersupp.axlevel < 4)
2136            && (CC->quickroom.QRflags & QR_NETWORK)) {
2137                 sprintf(errmsgbuf, "Need net privileges to enter here.");
2138                 return (ERROR + HIGHER_ACCESS_REQUIRED);
2139         }
2140
2141         if ((CC->usersupp.axlevel < 6)
2142            && (CC->quickroom.QRflags & QR_READONLY)) {
2143                 sprintf(errmsgbuf, "Sorry, this is a read-only room.");
2144                 return (ERROR + HIGHER_ACCESS_REQUIRED);
2145         }
2146
2147         strcpy(errmsgbuf, "Ok");
2148         return(0);
2149 }
2150
2151
2152
2153
2154 /*
2155  * message entry  -  mode 0 (normal)
2156  */
2157 void cmd_ent0(char *entargs)
2158 {
2159         int post = 0;
2160         char recipient[SIZ];
2161         int anon_flag = 0;
2162         int format_type = 0;
2163         char newusername[SIZ];
2164         struct CtdlMessage *msg;
2165         int a, b;
2166         int e = 0;
2167         int mtsflag = 0;
2168         struct usersupp tempUS;
2169         char buf[SIZ];
2170         int err = 0;
2171
2172         post = extract_int(entargs, 0);
2173         extract(recipient, entargs, 1);
2174         anon_flag = extract_int(entargs, 2);
2175         format_type = extract_int(entargs, 3);
2176
2177         /* first check to make sure the request is valid. */
2178
2179         err = CtdlDoIHavePermissionToPostInThisRoom(buf);
2180         if (err) {
2181                 cprintf("%d %s\n", err, buf);
2182                 return;
2183         }
2184
2185         /* Check some other permission type things. */
2186
2187         if (post == 2) {
2188                 if (CC->usersupp.axlevel < 6) {
2189                         cprintf("%d You don't have permission to masquerade.\n",
2190                                 ERROR + HIGHER_ACCESS_REQUIRED);
2191                         return;
2192                 }
2193                 extract(newusername, entargs, 4);
2194                 memset(CC->fake_postname, 0, 32);
2195                 strcpy(CC->fake_postname, newusername);
2196                 cprintf("%d Ok\n", OK);
2197                 return;
2198         }
2199         CC->cs_flags |= CS_POSTING;
2200
2201         buf[0] = 0;
2202         if (CC->quickroom.QRflags & QR_MAILBOX) {
2203                 if (CC->usersupp.axlevel >= 2) {
2204                         strcpy(buf, recipient);
2205                 } else
2206                         strcpy(buf, "sysop");
2207                 e = alias(buf); /* alias and mail type */
2208                 if ((buf[0] == 0) || (e == MES_ERROR)) {
2209                         cprintf("%d Unknown address - cannot send message.\n",
2210                                 ERROR + NO_SUCH_USER);
2211                         return;
2212                 }
2213                 if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
2214                         cprintf("%d Net privileges required for network mail.\n",
2215                                 ERROR + HIGHER_ACCESS_REQUIRED);
2216                         return;
2217                 }
2218                 if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
2219                     && ((CC->usersupp.flags & US_INTERNET) == 0)
2220                     && (!CC->internal_pgm)) {
2221                         cprintf("%d You don't have access to Internet mail.\n",
2222                                 ERROR + HIGHER_ACCESS_REQUIRED);
2223                         return;
2224                 }
2225                 if (!strcasecmp(buf, "sysop")) {
2226                         mtsflag = 1;
2227                 }
2228                 else if (e == MES_LOCAL) {      /* don't search local file */
2229                         if (!strcasecmp(buf, CC->usersupp.fullname)) {
2230                                 cprintf("%d Can't send mail to yourself!\n",
2231                                         ERROR + NO_SUCH_USER);
2232                                 return;
2233                         }
2234                         /* Check to make sure the user exists; also get the correct
2235                          * upper/lower casing of the name.
2236                          */
2237                         a = getuser(&tempUS, buf);
2238                         if (a != 0) {
2239                                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
2240                                 return;
2241                         }
2242                         strcpy(buf, tempUS.fullname);
2243                 }
2244         }
2245
2246         b = MES_NORMAL;
2247         if (CC->quickroom.QRflags & QR_ANONONLY)
2248                 b = MES_ANON;
2249         if (CC->quickroom.QRflags & QR_ANONOPT) {
2250                 if (anon_flag == 1)
2251                         b = MES_AN2;
2252         }
2253         if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2254                 buf[0] = 0;
2255
2256         /* If we're only checking the validity of the request, return
2257          * success without creating the message.
2258          */
2259         if (post == 0) {
2260                 cprintf("%d %s\n", OK, buf);
2261                 return;
2262         }
2263
2264         cprintf("%d send message\n", SEND_LISTING);
2265
2266         /* Read in the message from the client. */
2267         if (CC->fake_postname[0])
2268                 msg = make_message(&CC->usersupp, buf,
2269                         CC->quickroom.QRname, b, e, format_type,
2270                         CC->fake_postname);
2271         else if (CC->fake_username[0])
2272                 msg = make_message(&CC->usersupp, buf,
2273                         CC->quickroom.QRname, b, e, format_type,
2274                         CC->fake_username);
2275         else
2276                 msg = make_message(&CC->usersupp, buf,
2277                         CC->quickroom.QRname, b, e, format_type, "");
2278
2279         if (msg != NULL)
2280                 CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e);
2281                 CtdlFreeMessage(msg);
2282         CC->fake_postname[0] = '\0';
2283         return;
2284 }
2285
2286
2287
2288 /* 
2289  * message entry - mode 3 (raw)
2290  */
2291 void cmd_ent3(char *entargs)
2292 {
2293         char recp[SIZ];
2294         int a;
2295         int e = 0;
2296         int valid_msg = 1;
2297         unsigned char ch, which_field;
2298         struct usersupp tempUS;
2299         long msglen;
2300         struct CtdlMessage *msg;
2301         char *tempbuf;
2302
2303         if (CC->internal_pgm == 0) {
2304                 cprintf("%d This command is for internal programs only.\n",
2305                         ERROR);
2306                 return;
2307         }
2308
2309         /* See if there's a recipient, but make sure it's a real one */
2310         extract(recp, entargs, 1);
2311         for (a = 0; a < strlen(recp); ++a)
2312                 if (!isprint(recp[a]))
2313                         strcpy(&recp[a], &recp[a + 1]);
2314         while (isspace(recp[0]))
2315                 strcpy(recp, &recp[1]);
2316         while (isspace(recp[strlen(recp) - 1]))
2317                 recp[strlen(recp) - 1] = 0;
2318
2319         /* If we're in Mail, check the recipient */
2320         if (strlen(recp) > 0) {
2321                 e = alias(recp);        /* alias and mail type */
2322                 if ((recp[0] == 0) || (e == MES_ERROR)) {
2323                         cprintf("%d Unknown address - cannot send message.\n",
2324                                 ERROR + NO_SUCH_USER);
2325                         return;
2326                 }
2327                 if (e == MES_LOCAL) {
2328                         a = getuser(&tempUS, recp);
2329                         if (a != 0) {
2330                                 cprintf("%d No such user.\n",
2331                                         ERROR + NO_SUCH_USER);
2332                                 return;
2333                         }
2334                 }
2335         }
2336
2337         /* At this point, message has been approved. */
2338         if (extract_int(entargs, 0) == 0) {
2339                 cprintf("%d OK to send\n", OK);
2340                 return;
2341         }
2342
2343         msglen = extract_long(entargs, 2);
2344         msg = mallok(sizeof(struct CtdlMessage));
2345         if (msg == NULL) {
2346                 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2347                 return;
2348         }
2349
2350         memset(msg, 0, sizeof(struct CtdlMessage));
2351         tempbuf = mallok(msglen);
2352         if (tempbuf == NULL) {
2353                 cprintf("%d Out of memory\n", ERROR+INTERNAL_ERROR);
2354                 phree(msg);
2355                 return;
2356         }
2357
2358         cprintf("%d %ld\n", SEND_BINARY, msglen);
2359
2360         client_read(&ch, 1);                            /* 0xFF magic number */
2361         msg->cm_magic = CTDLMESSAGE_MAGIC;
2362         client_read(&ch, 1);                            /* anon type */
2363         msg->cm_anon_type = ch;
2364         client_read(&ch, 1);                            /* format type */
2365         msg->cm_format_type = ch;
2366         msglen = msglen - 3;
2367
2368         while (msglen > 0) {
2369                 client_read(&which_field, 1);
2370                 if (!isalpha(which_field)) valid_msg = 0;
2371                 --msglen;
2372                 tempbuf[0] = 0;
2373                 do {
2374                         client_read(&ch, 1);
2375                         --msglen;
2376                         a = strlen(tempbuf);
2377                         tempbuf[a+1] = 0;
2378                         tempbuf[a] = ch;
2379                 } while ( (ch != 0) && (msglen > 0) );
2380                 if (valid_msg)
2381                         msg->cm_fields[which_field] = strdoop(tempbuf);
2382         }
2383
2384         msg->cm_flags = CM_SKIP_HOOKS;
2385         if (valid_msg) CtdlSaveMsg(msg, recp, "", e);
2386         CtdlFreeMessage(msg);
2387         phree(tempbuf);
2388 }
2389
2390
2391 /*
2392  * API function to delete messages which match a set of criteria
2393  * (returns the actual number of messages deleted)
2394  */
2395 int CtdlDeleteMessages(char *room_name,         /* which room */
2396                        long dmsgnum,            /* or "0" for any */
2397                        char *content_type       /* or "" for any */
2398 )
2399 {
2400
2401         struct quickroom qrbuf;
2402         struct cdbdata *cdbfr;
2403         long *msglist = NULL;
2404         int num_msgs = 0;
2405         int i;
2406         int num_deleted = 0;
2407         int delete_this;
2408         struct SuppMsgInfo smi;
2409
2410         lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n",
2411                 room_name, dmsgnum, content_type);
2412
2413         /* get room record, obtaining a lock... */
2414         if (lgetroom(&qrbuf, room_name) != 0) {
2415                 lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n",
2416                         room_name);
2417                 return (0);     /* room not found */
2418         }
2419         cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
2420
2421         if (cdbfr != NULL) {
2422                 msglist = mallok(cdbfr->len);
2423                 memcpy(msglist, cdbfr->ptr, cdbfr->len);
2424                 num_msgs = cdbfr->len / sizeof(long);
2425                 cdb_free(cdbfr);
2426         }
2427         if (num_msgs > 0) {
2428                 for (i = 0; i < num_msgs; ++i) {
2429                         delete_this = 0x00;
2430
2431                         /* Set/clear a bit for each criterion */
2432
2433                         if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
2434                                 delete_this |= 0x01;
2435                         }
2436                         if (strlen(content_type) == 0) {
2437                                 delete_this |= 0x02;
2438                         } else {
2439                                 GetSuppMsgInfo(&smi, msglist[i]);
2440                                 if (!strcasecmp(smi.smi_content_type,
2441                                                 content_type)) {
2442                                         delete_this |= 0x02;
2443                                 }
2444                         }
2445
2446                         /* Delete message only if all bits are set */
2447                         if (delete_this == 0x03) {
2448                                 AdjRefCount(msglist[i], -1);
2449                                 msglist[i] = 0L;
2450                                 ++num_deleted;
2451                         }
2452                 }
2453
2454                 num_msgs = sort_msglist(msglist, num_msgs);
2455                 cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long),
2456                           msglist, (num_msgs * sizeof(long)));
2457
2458                 qrbuf.QRhighest = msglist[num_msgs - 1];
2459                 phree(msglist);
2460         }
2461         lputroom(&qrbuf);
2462         lprintf(9, "%d message(s) deleted.\n", num_deleted);
2463         return (num_deleted);
2464 }
2465
2466
2467
2468 /*
2469  * Check whether the current user has permission to delete messages from
2470  * the current room (returns 1 for yes, 0 for no)
2471  */
2472 int CtdlDoIHavePermissionToDeleteMessagesFromThisRoom(void) {
2473         getuser(&CC->usersupp, CC->curr_user);
2474         if ((CC->usersupp.axlevel < 6)
2475             && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
2476             && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
2477             && (!(CC->internal_pgm))) {
2478                 return(0);
2479         }
2480         return(1);
2481 }
2482
2483
2484
2485 /*
2486  * Delete message from current room
2487  */
2488 void cmd_dele(char *delstr)
2489 {
2490         long delnum;
2491         int num_deleted;
2492
2493         if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom() == 0) {
2494                 cprintf("%d Higher access required.\n",
2495                         ERROR + HIGHER_ACCESS_REQUIRED);
2496                 return;
2497         }
2498         delnum = extract_long(delstr, 0);
2499
2500         num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, "");
2501
2502         if (num_deleted) {
2503                 cprintf("%d %d message%s deleted.\n", OK,
2504                         num_deleted, ((num_deleted != 1) ? "s" : ""));
2505         } else {
2506                 cprintf("%d Message %ld not found.\n", ERROR, delnum);
2507         }
2508 }
2509
2510
2511 /*
2512  * Back end API function for moves and deletes
2513  */
2514 int CtdlCopyMsgToRoom(long msgnum, char *dest) {
2515         int err;
2516
2517         err = CtdlSaveMsgPointerInRoom(dest, msgnum,
2518                 (SM_VERIFY_GOODNESS | SM_DO_REPL_CHECK) );
2519         if (err != 0) return(err);
2520
2521         return(0);
2522 }
2523
2524
2525
2526 /*
2527  * move or copy a message to another room
2528  */
2529 void cmd_move(char *args)
2530 {
2531         long num;
2532         char targ[SIZ];
2533         struct quickroom qtemp;
2534         int err;
2535         int is_copy = 0;
2536
2537         num = extract_long(args, 0);
2538         extract(targ, args, 1);
2539         targ[ROOMNAMELEN - 1] = 0;
2540         is_copy = extract_int(args, 2);
2541
2542         getuser(&CC->usersupp, CC->curr_user);
2543         if ((CC->usersupp.axlevel < 6)
2544             && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
2545                 cprintf("%d Higher access required.\n",
2546                         ERROR + HIGHER_ACCESS_REQUIRED);
2547                 return;
2548         }
2549
2550         if (getroom(&qtemp, targ) != 0) {
2551                 cprintf("%d '%s' does not exist.\n", ERROR, targ);
2552                 return;
2553         }
2554
2555         err = CtdlCopyMsgToRoom(num, targ);
2556         if (err != 0) {
2557                 cprintf("%d Cannot store message in %s: error %d\n",
2558                         err, targ, err);
2559                 return;
2560         }
2561
2562         /* Now delete the message from the source room,
2563          * if this is a 'move' rather than a 'copy' operation.
2564          */
2565         if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, "");
2566
2567         cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
2568 }
2569
2570
2571
2572 /*
2573  * GetSuppMsgInfo()  -  Get the supplementary record for a message
2574  */
2575 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum)
2576 {
2577
2578         struct cdbdata *cdbsmi;
2579         long TheIndex;
2580
2581         memset(smibuf, 0, sizeof(struct SuppMsgInfo));
2582         smibuf->smi_msgnum = msgnum;
2583         smibuf->smi_refcount = 1;       /* Default reference count is 1 */
2584
2585         /* Use the negative of the message number for its supp record index */
2586         TheIndex = (0L - msgnum);
2587
2588         cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long));
2589         if (cdbsmi == NULL) {
2590                 return;         /* record not found; go with defaults */
2591         }
2592         memcpy(smibuf, cdbsmi->ptr,
2593                ((cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
2594                 sizeof(struct SuppMsgInfo) : cdbsmi->len));
2595         cdb_free(cdbsmi);
2596         return;
2597 }
2598
2599
2600 /*
2601  * PutSuppMsgInfo()  -  (re)write supplementary record for a message
2602  */
2603 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
2604 {
2605         long TheIndex;
2606
2607         /* Use the negative of the message number for its supp record index */
2608         TheIndex = (0L - smibuf->smi_msgnum);
2609
2610         lprintf(9, "PuttSuppMsgInfo(%ld) - ref count is %d\n",
2611                 smibuf->smi_msgnum, smibuf->smi_refcount);
2612
2613         cdb_store(CDB_MSGMAIN,
2614                   &TheIndex, sizeof(long),
2615                   smibuf, sizeof(struct SuppMsgInfo));
2616
2617 }
2618
2619 /*
2620  * AdjRefCount  -  change the reference count for a message;
2621  *                 delete the message if it reaches zero
2622  */
2623 void AdjRefCount(long msgnum, int incr)
2624 {
2625
2626         struct SuppMsgInfo smi;
2627         long delnum;
2628
2629         /* This is a *tight* critical section; please keep it that way, as
2630          * it may get called while nested in other critical sections.  
2631          * Complicating this any further will surely cause deadlock!
2632          */
2633         begin_critical_section(S_SUPPMSGMAIN);
2634         GetSuppMsgInfo(&smi, msgnum);
2635         lprintf(9, "Ref count for message <%ld> before write is <%d>\n",
2636                 msgnum, smi.smi_refcount);
2637         smi.smi_refcount += incr;
2638         PutSuppMsgInfo(&smi);
2639         end_critical_section(S_SUPPMSGMAIN);
2640         lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
2641                 msgnum, smi.smi_refcount);
2642
2643         /* If the reference count is now zero, delete the message
2644          * (and its supplementary record as well).
2645          */
2646         if (smi.smi_refcount == 0) {
2647                 lprintf(9, "Deleting message <%ld>\n", msgnum);
2648                 delnum = msgnum;
2649                 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2650                 delnum = (0L - msgnum);
2651                 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
2652         }
2653 }
2654
2655 /*
2656  * Write a generic object to this room
2657  *
2658  * Note: this could be much more efficient.  Right now we use two temporary
2659  * files, and still pull the message into memory as with all others.
2660  */
2661 void CtdlWriteObject(char *req_room,            /* Room to stuff it in */
2662                         char *content_type,     /* MIME type of this object */
2663                         char *tempfilename,     /* Where to fetch it from */
2664                         struct usersupp *is_mailbox,    /* Mailbox room? */
2665                         int is_binary,          /* Is encoding necessary? */
2666                         int is_unique,          /* Del others of this type? */
2667                         unsigned int flags      /* Internal save flags */
2668                         )
2669 {
2670
2671         FILE *fp, *tempfp;
2672         char filename[PATH_MAX];
2673         char cmdbuf[SIZ];
2674         char ch;
2675         struct quickroom qrbuf;
2676         char roomname[ROOMNAMELEN];
2677         struct CtdlMessage *msg;
2678         size_t len;
2679
2680         if (is_mailbox != NULL)
2681                 MailboxName(roomname, is_mailbox, req_room);
2682         else
2683                 safestrncpy(roomname, req_room, sizeof(roomname));
2684         lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
2685
2686         strcpy(filename, tmpnam(NULL));
2687         fp = fopen(filename, "w");
2688         if (fp == NULL)
2689                 return;
2690
2691         tempfp = fopen(tempfilename, "r");
2692         if (tempfp == NULL) {
2693                 fclose(fp);
2694                 unlink(filename);
2695                 return;
2696         }
2697
2698         fprintf(fp, "Content-type: %s\n", content_type);
2699         lprintf(9, "Content-type: %s\n", content_type);
2700
2701         if (is_binary == 0) {
2702                 fprintf(fp, "Content-transfer-encoding: 7bit\n\n");
2703                 while (ch = getc(tempfp), ch > 0)
2704                         putc(ch, fp);
2705                 fclose(tempfp);
2706                 putc(0, fp);
2707                 fclose(fp);
2708         } else {
2709                 fprintf(fp, "Content-transfer-encoding: base64\n\n");
2710                 fclose(tempfp);
2711                 fclose(fp);
2712                 sprintf(cmdbuf, "./base64 -e <%s >>%s",
2713                         tempfilename, filename);
2714                 system(cmdbuf);
2715         }
2716
2717         lprintf(9, "Allocating\n");
2718         msg = mallok(sizeof(struct CtdlMessage));
2719         memset(msg, 0, sizeof(struct CtdlMessage));
2720         msg->cm_magic = CTDLMESSAGE_MAGIC;
2721         msg->cm_anon_type = MES_NORMAL;
2722         msg->cm_format_type = 4;
2723         msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
2724         msg->cm_fields['O'] = strdoop(req_room);
2725         msg->cm_fields['N'] = strdoop(config.c_nodename);
2726         msg->cm_fields['H'] = strdoop(config.c_humannode);
2727         msg->cm_flags = flags;
2728         
2729         lprintf(9, "Loading\n");
2730         fp = fopen(filename, "rb");
2731         fseek(fp, 0L, SEEK_END);
2732         len = ftell(fp);
2733         rewind(fp);
2734         msg->cm_fields['M'] = mallok(len);
2735         fread(msg->cm_fields['M'], len, 1, fp);
2736         fclose(fp);
2737         unlink(filename);
2738
2739         /* Create the requested room if we have to. */
2740         if (getroom(&qrbuf, roomname) != 0) {
2741                 create_room(roomname, 
2742                         ( (is_mailbox != NULL) ? 5 : 3 ),
2743                         "", 0, 1);
2744         }
2745         /* If the caller specified this object as unique, delete all
2746          * other objects of this type that are currently in the room.
2747          */
2748         if (is_unique) {
2749                 lprintf(9, "Deleted %d other msgs of this type\n",
2750                         CtdlDeleteMessages(roomname, 0L, content_type));
2751         }
2752         /* Now write the data */
2753         CtdlSaveMsg(msg, "", roomname, MES_LOCAL);
2754         CtdlFreeMessage(msg);
2755 }
2756
2757
2758
2759
2760
2761
2762 void CtdlGetSysConfigBackend(long msgnum, void *userdata) {
2763         config_msgnum = msgnum;
2764 }
2765
2766
2767 char *CtdlGetSysConfig(char *sysconfname) {
2768         char hold_rm[ROOMNAMELEN];
2769         long msgnum;
2770         char *conf;
2771         struct CtdlMessage *msg;
2772         char buf[SIZ];
2773         
2774         strcpy(hold_rm, CC->quickroom.QRname);
2775         if (getroom(&CC->quickroom, SYSCONFIGROOM) != 0) {
2776                 getroom(&CC->quickroom, hold_rm);
2777                 return NULL;
2778         }
2779
2780
2781         /* We want the last (and probably only) config in this room */
2782         begin_critical_section(S_CONFIG);
2783         config_msgnum = (-1L);
2784         CtdlForEachMessage(MSGS_LAST, 1, (-127), sysconfname, NULL,
2785                 CtdlGetSysConfigBackend, NULL);
2786         msgnum = config_msgnum;
2787         end_critical_section(S_CONFIG);
2788
2789         if (msgnum < 0L) {
2790                 conf = NULL;
2791         }
2792         else {
2793                 msg = CtdlFetchMessage(msgnum);
2794                 if (msg != NULL) {
2795                         conf = strdoop(msg->cm_fields['M']);
2796                         CtdlFreeMessage(msg);
2797                 }
2798                 else {
2799                         conf = NULL;
2800                 }
2801         }
2802
2803         getroom(&CC->quickroom, hold_rm);
2804
2805         if (conf != NULL) do {
2806                 extract_token(buf, conf, 0, '\n');
2807                 strcpy(conf, &conf[strlen(buf)+1]);
2808         } while ( (strlen(conf)>0) && (strlen(buf)>0) );
2809
2810         return(conf);
2811 }
2812
2813 void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) {
2814         char temp[PATH_MAX];
2815         FILE *fp;
2816
2817         strcpy(temp, tmpnam(NULL));
2818
2819         fp = fopen(temp, "w");
2820         if (fp == NULL) return;
2821         fprintf(fp, "%s", sysconfdata);
2822         fclose(fp);
2823
2824         /* this handy API function does all the work for us */
2825         CtdlWriteObject(SYSCONFIGROOM, sysconfname, temp, NULL, 0, 1, 0);
2826         unlink(temp);
2827 }