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