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