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