]> code.citadel.org Git - citadel.git/blob - citadel/msgbase.c
* Added supplementary message info records for info that may change
[citadel.git] / citadel / msgbase.c
1 /* $Id$ */
2 #include "sysdep.h"
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <stdio.h>
6 #include <fcntl.h>
7 #include <time.h>
8 #include <ctype.h>
9 #include <string.h>
10 #include <syslog.h>
11 #ifdef HAVE_PTHREAD_H
12 #include <pthread.h>
13 #endif
14 #include <limits.h>
15 #include "citadel.h"
16 #include "server.h"
17 #include <errno.h>
18 #include <sys/stat.h>
19 #include "database.h"
20 #include "msgbase.h"
21 #include "support.h"
22 #include "sysdep_decls.h"
23 #include "citserver.h"
24 #include "room_ops.h"
25 #include "user_ops.h"
26 #include "file_ops.h"
27 #include "control.h"
28 #include "dynloader.h"
29 #include "tools.h"
30 #include "mime_parser.h"
31
32 #define MSGS_ALL        0
33 #define MSGS_OLD        1
34 #define MSGS_NEW        2
35 #define MSGS_FIRST      3
36 #define MSGS_LAST       4
37 #define MSGS_GT         5
38
39 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
40
41 extern struct config config;
42
43
44 /*
45  * This function is self explanatory.
46  * (What can I say, I'm in a weird mood today...)
47  */
48 void remove_any_whitespace_to_the_left_or_right_of_at_symbol(char *name) {
49         int i;
50
51         for (i=0; i<strlen(name); ++i) if (name[i]=='@') {
52                 if (i>0) if (isspace(name[i-1])) {
53                         strcpy(&name[i-1], &name[i]);
54                         i = 0;
55                         }
56                 while (isspace(name[i+1])) {
57                         strcpy(&name[i+1], &name[i+2]);
58                         }
59                 }
60         }
61
62
63 /*
64  * Aliasing for network mail.
65  * (Error messages have been commented out, because this is a server.)
66  */
67 int alias(char *name)           /* process alias and routing info for mail */
68              {
69         FILE *fp;
70         int a,b;
71         char aaa[300],bbb[300];
72
73         lprintf(9, "alias() called for <%s>\n", name);
74
75         remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
76
77         fp=fopen("network/mail.aliases","r");
78         if (fp==NULL) fp=fopen("/dev/null","r");
79         if (fp==NULL) return(MES_ERROR);
80         strcpy(aaa,""); strcpy(bbb,"");
81         while (fgets(aaa, sizeof aaa, fp)!=NULL) {
82                 while (isspace(name[0])) strcpy(name, &name[1]);
83                 aaa[strlen(aaa)-1] = 0;
84                 strcpy(bbb, "");
85                 for (a=0; a<strlen(aaa); ++a) {
86                         if (aaa[a] == ',') {
87                                 strcpy(bbb, &aaa[a+1]);
88                                 aaa[a] = 0;
89                                 }
90                         }
91                 if (!strcasecmp(name, aaa)) strcpy(name, bbb);
92                 }
93         fclose(fp);
94         lprintf(7, "Mail is being forwarded to %s\n", name);
95
96         /* determine local or remote type, see citadel.h */
97         for (a=0; a<strlen(name); ++a) if (name[a]=='!') return(MES_INTERNET);
98         for (a=0; a<strlen(name); ++a)
99                 if (name[a]=='@')
100                         for (b=a; b<strlen(name); ++b)
101                                 if (name[b]=='.') return(MES_INTERNET);
102         b=0; for (a=0; a<strlen(name); ++a) if (name[a]=='@') ++b;
103         if (b>1) {
104                 lprintf(7, "Too many @'s in address\n");
105                 return(MES_ERROR);
106                 }
107         if (b==1) {
108                 for (a=0; a<strlen(name); ++a)
109                         if (name[a]=='@') strcpy(bbb,&name[a+1]);
110                 while (bbb[0]==32) strcpy(bbb,&bbb[1]);
111                 fp = fopen("network/mail.sysinfo","r");
112                 if (fp==NULL) return(MES_ERROR);
113 GETSN:          do {
114                         a=getstring(fp,aaa);
115                         } while ((a>=0)&&(strcasecmp(aaa,bbb)));
116                 a=getstring(fp,aaa);
117                 if (!strncmp(aaa,"use ",4)) {
118                         strcpy(bbb,&aaa[4]);
119                         fseek(fp,0L,0);
120                         goto GETSN;
121                         }
122                 fclose(fp);
123                 if (!strncmp(aaa,"uum",3)) {
124                         strcpy(bbb,name);
125                         for (a=0; a<strlen(bbb); ++a) {
126                                 if (bbb[a]=='@') bbb[a]=0;
127                                 if (bbb[a]==' ') bbb[a]='_';
128                                 }
129                         while(bbb[strlen(bbb)-1]=='_') bbb[strlen(bbb)-1]=0;
130                         sprintf(name,&aaa[4],bbb);
131                         return(MES_INTERNET);
132                         }
133                 if (!strncmp(aaa,"bin",3)) {
134                         strcpy(aaa,name); strcpy(bbb,name);
135                         while (aaa[strlen(aaa)-1]!='@') aaa[strlen(aaa)-1]=0;
136                         aaa[strlen(aaa)-1]=0;
137                         while (aaa[strlen(aaa)-1]==' ') aaa[strlen(aaa)-1]=0;
138                         while (bbb[0]!='@') strcpy(bbb,&bbb[1]);
139                         strcpy(bbb,&bbb[1]);
140                         while (bbb[0]==' ') strcpy(bbb,&bbb[1]);
141                         sprintf(name,"%s @%s",aaa,bbb);
142                         return(MES_BINARY);
143                         }
144                 return(MES_ERROR);
145                 }
146         return(MES_LOCAL);
147         }
148
149
150 void get_mm(void) {
151         FILE *fp;
152
153         fp=fopen("citadel.control","r");
154         fread((char *)&CitControl,sizeof(struct CitControl),1,fp);
155         fclose(fp);
156         }
157
158 /*
159  * cmd_msgs()  -  get list of message #'s in this room
160  */
161 void cmd_msgs(char *cmdbuf)
162 {
163         int a = 0;
164         int mode = 0;
165         char which[256];
166         int cm_howmany = 0;
167         long cm_gt = 0L;
168         struct visit vbuf;
169
170         extract(which,cmdbuf,0);
171
172         mode = MSGS_ALL;
173         strcat(which,"   ");
174         if (!strncasecmp(which,"OLD",3))        mode = MSGS_OLD;
175         if (!strncasecmp(which,"NEW",3))        mode = MSGS_NEW;
176         if (!strncasecmp(which,"FIRST",5))      {
177                 mode = MSGS_FIRST;
178                 cm_howmany = extract_int(cmdbuf,1);
179                 }
180         if (!strncasecmp(which,"LAST",4))       {
181                 mode = MSGS_LAST;
182                 cm_howmany = extract_int(cmdbuf,1);
183                 }
184         if (!strncasecmp(which,"GT",2)) {
185                 mode = MSGS_GT;
186                 cm_gt = extract_long(cmdbuf,1);
187                 }
188
189         if ((!(CC->logged_in))&&(!(CC->internal_pgm))) {
190                 cprintf("%d not logged in\n",ERROR+NOT_LOGGED_IN);
191                 return;
192                 }
193         get_mm();
194         get_msglist(&CC->quickroom);
195         getuser(&CC->usersupp,CC->curr_user);
196         CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
197
198         cprintf("%d Message list...\n",LISTING_FOLLOWS);
199         if (CC->num_msgs != 0) {
200            for (a=0; a<(CC->num_msgs); ++a) 
201                if ((MessageFromList(a) >=0)
202                && ( 
203
204 (mode==MSGS_ALL)
205 || ((mode==MSGS_OLD) && (MessageFromList(a) <= vbuf.v_lastseen))
206 || ((mode==MSGS_NEW) && (MessageFromList(a) > vbuf.v_lastseen))
207 || ((mode==MSGS_NEW) && (MessageFromList(a) >= vbuf.v_lastseen)
208                      && (CC->usersupp.flags & US_LASTOLD))
209 || ((mode==MSGS_LAST)&& (a>=(CC->num_msgs-cm_howmany)))
210 || ((mode==MSGS_FIRST)&&(a<cm_howmany))
211 || ((mode==MSGS_GT) && (MessageFromList(a) > cm_gt))
212
213                         )
214                 ) {
215                         cprintf("%ld\n", MessageFromList(a));
216                         }
217            }
218         cprintf("000\n");
219         }
220
221
222
223 /* 
224  * help_subst()  -  support routine for help file viewer
225  */
226 void help_subst(char *strbuf, char *source, char *dest)
227 {
228         char workbuf[256];
229         int p;
230
231         while (p=pattern2(strbuf,source), (p>=0)) {
232                 strcpy(workbuf,&strbuf[p+strlen(source)]);
233                 strcpy(&strbuf[p],dest);
234                 strcat(strbuf,workbuf);
235                 }
236         }
237
238
239 void do_help_subst(char *buffer)
240 {
241         char buf2[16];
242
243         help_subst(buffer,"^nodename",config.c_nodename);
244         help_subst(buffer,"^humannode",config.c_humannode);
245         help_subst(buffer,"^fqdn",config.c_fqdn);
246         help_subst(buffer,"^username",CC->usersupp.fullname);
247         sprintf(buf2,"%ld",CC->usersupp.usernum);
248         help_subst(buffer,"^usernum",buf2);
249         help_subst(buffer,"^sysadm",config.c_sysadm);
250         help_subst(buffer,"^variantname",CITADEL);
251         sprintf(buf2,"%d",config.c_maxsessions);
252         help_subst(buffer,"^maxsessions",buf2);
253         }
254
255
256
257 /*
258  * memfmout()  -  Citadel text formatter and paginator.
259  *             Although the original purpose of this routine was to format
260  *             text to the reader's screen width, all we're really using it
261  *             for here is to format text out to 80 columns before sending it
262  *             to the client.  The client software may reformat it again.
263  */
264 void memfmout(int width, char *mptr, char subst)
265                         /* screen width to use */
266                         /* where are we going to get our text from? */
267                         /* nonzero if we should use hypertext mode */
268         {
269         int a,b,c;
270         int real = 0;
271         int old = 0;
272         CIT_UBYTE ch;
273         char aaa[140];
274         char buffer[256];
275         
276         strcpy(aaa,""); old=255;
277         strcpy(buffer,"");
278         c=1; /* c is the current pos */
279
280 FMTA:   if (subst) {
281                 while (ch=*mptr, ((ch!=0) && (strlen(buffer)<126) )) {
282                         ch=*mptr++;
283                         buffer[strlen(buffer)+1] = 0;
284                         buffer[strlen(buffer)] = ch;
285                         }
286
287                 if (buffer[0]=='^') do_help_subst(buffer);
288
289                 buffer[strlen(buffer)+1] = 0;
290                 a=buffer[0];
291                 strcpy(buffer,&buffer[1]);
292                 }
293         
294         else ch=*mptr++;
295
296         old=real;
297         real=ch;
298         if (ch<=0) goto FMTEND;
299         
300         if ( ((ch==13)||(ch==10)) && (old!=13) && (old!=10) ) ch=32;
301         if ( ((old==13)||(old==10)) && (isspace(real)) ) {
302                 cprintf("\n");
303                 c=1;
304                 }
305         if (ch>126) goto FMTA;
306
307         if (ch>32) {
308         if ( ((strlen(aaa)+c)>(width-5)) && (strlen(aaa)>(width-5)) )
309                 { cprintf("\n%s",aaa); c=strlen(aaa); aaa[0]=0;
310                 }
311          b=strlen(aaa); aaa[b]=ch; aaa[b+1]=0; }
312         if (ch==32) {
313                 if ((strlen(aaa)+c)>(width-5)) { 
314                         cprintf("\n");
315                         c=1;
316                         }
317                 cprintf("%s ",aaa); ++c; c=c+strlen(aaa);
318                 strcpy(aaa,"");
319                 goto FMTA;
320                 }
321         if ((ch==13)||(ch==10)) {
322                 cprintf("%s\n",aaa);
323                 c=1;
324                 strcpy(aaa,"");
325                 goto FMTA;
326                 }
327         goto FMTA;
328
329 FMTEND: cprintf("%s\n", aaa);
330         }
331
332
333
334 /*
335  * Callback function for mime parser that simply lists the part
336  */
337 void list_this_part(char *name, char *filename, char *partnum, char *disp,
338                         void *content, char *cbtype, size_t length) {
339
340         cprintf("part=%s|%s|%s|%s|%s|%d\n",
341                 name, filename, partnum, disp, cbtype, length);
342         }
343
344
345 /*
346  * Callback function for mime parser that wants to display text
347  */
348 void fixed_output(char *name, char *filename, char *partnum, char *disp,
349                         void *content, char *cbtype, size_t length) {
350
351         if (!strcasecmp(cbtype, "text/plain")) {
352                 client_write(content, length);
353                 }
354         else {
355                 cprintf("Part %s: %s (%s) (%d bytes)\n",
356                         partnum, filename, cbtype, length);
357                 }
358         }
359
360
361 /*
362  * Callback function for mime parser that opens a section for downloading
363  */
364 void mime_download(char *name, char *filename, char *partnum, char *disp,
365                         void *content, char *cbtype, size_t length) {
366
367         /* Silently go away if there's already a download open... */
368         if (CC->download_fp != NULL) return;
369
370         /* ...or if this is not the desired section */
371         if (strcasecmp(desired_section, partnum)) return;
372
373         CC->download_fp = tmpfile();
374         if (CC->download_fp == NULL) return;
375
376         fwrite(content, length, 1, CC->download_fp);
377         fflush(CC->download_fp);
378         rewind(CC->download_fp);
379
380         OpenCmdResult(filename, cbtype);
381         }
382
383
384
385 /*
386  * Get a message off disk.  (return value is the message's timestamp)
387  * 
388  */
389 time_t output_message(char *msgid, int mode, int headers_only) {
390         long msg_num;
391         int a;
392         CIT_UBYTE ch, rch;
393         CIT_UBYTE format_type,anon_flag;
394         char buf[1024];
395         long msg_len;
396         int msg_ok = 0;
397
398         struct cdbdata *dmsgtext;
399         char *mptr;
400
401         /* buffers needed for RFC822 translation */
402         char suser[256];
403         char luser[256];
404         char snode[256];
405         char lnode[256];
406         char mid[256];
407         time_t xtime = 0L;
408         /*                                       */
409
410         msg_num = atol(msgid);
411
412
413         if ((!(CC->logged_in))&&(!(CC->internal_pgm))&&(mode!=MT_DATE)) {
414                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
415                 return(xtime);
416                 }
417
418         /* We used to need to check in the current room's message list
419          * to determine where the message's disk position.  We no longer need
420          * to do this, but we do it anyway as a security measure, in order to
421          * prevent rogue clients from reading messages not in the current room.
422          */
423
424         msg_ok = 0;
425         if (CC->num_msgs > 0) {
426                 for (a=0; a<CC->num_msgs; ++a) {
427                         if (MessageFromList(a) == msg_num) {
428                                 msg_ok = 1;
429                                 }
430                         }
431                 }
432
433         if (!msg_ok) {
434                 if (mode != MT_DATE)
435                         cprintf("%d Message %ld is not in this room.\n",
436                                 ERROR, msg_num);
437                 return(xtime);
438                 }
439         
440
441         dmsgtext = cdb_fetch(CDB_MSGMAIN, &msg_num, sizeof(long));
442         
443         if (dmsgtext == NULL) {
444                 if (mode != MT_DATE)
445                         cprintf("%d Can't find message %ld\n",
446                                 ERROR+INTERNAL_ERROR);
447                 return(xtime);
448                 }
449
450         msg_len = (long) dmsgtext->len;
451         mptr = dmsgtext->ptr;
452
453         /* this loop spews out the whole message if we're doing raw format */
454         if (mode == MT_RAW) {
455                 cprintf("%d %ld\n", BINARY_FOLLOWS, msg_len);
456                 client_write(dmsgtext->ptr, (int) msg_len);
457                 cdb_free(dmsgtext);
458                 return(xtime);
459                 }
460
461         /* Otherwise, we'll start parsing it field by field... */
462         ch = *mptr++;
463         if (ch != 255) {
464                 cprintf("%d Illegal message format on disk\n",
465                         ERROR+INTERNAL_ERROR);
466                 cdb_free(dmsgtext);
467                 return(xtime);
468                 }
469
470         anon_flag = *mptr++;
471         format_type = *mptr++;
472
473         /* Are we downloading a MIME component? */
474         if (mode == MT_DOWNLOAD) {
475                 if (format_type != 4) {
476                         cprintf("%d This is not a MIME message.\n",
477                                 ERROR);
478                         }
479                 else if (CC->download_fp != NULL) {
480                         cprintf("%d You already have a download open.\n",
481                                 ERROR);
482                         }
483                 else {
484                         /* Skip to the message body */
485                         while(ch = *mptr++, (ch!='M' && ch!=0)) {
486                                 buf[0] = 0;
487                                 do {
488                                         buf[strlen(buf)+1] = 0;
489                                         rch = *mptr++;
490                                         buf[strlen(buf)] = rch;
491                                         } while (rch > 0);
492                                 }
493                         /* Now parse it */
494                         mime_parser(mptr, NULL, *mime_download);
495                         /* If there's no file open by this time, the requested
496                          * section wasn't found, so print an error
497                          */
498                         if (CC->download_fp == NULL) {
499                                 cprintf("%d Section %s not found.\n",
500                                         ERROR+FILE_NOT_FOUND,
501                                         desired_section);
502                                 }
503                         }
504                 cdb_free(dmsgtext);
505                 return(xtime);
506                 }
507
508         /* Are we just looking for the message date? */
509         if (mode == MT_DATE) while(ch = *mptr++, (ch!='M' && ch!=0)) {
510                 buf[0] = 0;
511                 do {
512                         buf[strlen(buf)+1] = 0;
513                         rch = *mptr++;
514                         buf[strlen(buf)] = rch;
515                         } while (rch > 0);
516
517                 if (ch=='T') {
518                         xtime = atol(buf);
519                         cdb_free(dmsgtext);
520                         return(xtime);
521                         }
522                 }
523
524
525         /* now for the user-mode message reading loops */
526         cprintf("%d Message %ld:\n",LISTING_FOLLOWS,msg_num);
527
528         if (mode == MT_CITADEL) cprintf("type=%d\n", format_type);
529
530         if ( (anon_flag == MES_ANON) && (mode == MT_CITADEL) ) {
531                 cprintf("nhdr=yes\n");
532                 }
533
534         /* begin header processing loop for Citadel message format */
535
536         if ((mode == MT_CITADEL)||(mode == MT_MIME))
537            while(ch = *mptr++, (ch!='M' && ch!=0)) {
538                 buf[0] = 0;
539                 do {
540                         buf[strlen(buf)+1] = 0;
541                         rch = *mptr++;
542                         buf[strlen(buf)] = rch;
543                         } while (rch > 0);
544
545                 if (ch=='A') {
546                         PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
547                         if (anon_flag==MES_ANON) cprintf("from=****");
548                         else if (anon_flag==MES_AN2) cprintf("from=anonymous");
549                         else cprintf("from=%s",buf);
550                         if ((is_room_aide()) && ((anon_flag == MES_ANON)
551                            || (anon_flag == MES_AN2)))
552                                 cprintf(" [%s]",buf);
553                         cprintf("\n");
554                         }
555                 else if (ch=='P') cprintf("path=%s\n",buf);
556                 else if (ch=='U') cprintf("subj=%s\n",buf);
557                 else if (ch=='I') cprintf("msgn=%s\n",buf);
558                 else if (ch=='H') cprintf("hnod=%s\n",buf);
559                 else if (ch=='O') cprintf("room=%s\n",buf);
560                 else if (ch=='N') cprintf("node=%s\n",buf);
561                 else if (ch=='R') cprintf("rcpt=%s\n",buf);
562                 else if (ch=='T') cprintf("time=%s\n",buf);
563                 /* else cprintf("fld%c=%s\n",ch,buf); */
564                 }
565
566         /* begin header processing loop for RFC822 transfer format */
567
568         strcpy(suser, "");
569         strcpy(luser, "");
570         strcpy(snode, NODENAME);
571         strcpy(lnode, HUMANNODE);
572         if (mode == MT_RFC822) while(ch = *mptr++, (ch!='M' && ch!=0)) {
573                 buf[0] = 0;
574                 do {
575                         buf[strlen(buf)+1] = 0;
576                         rch = *mptr++;
577                         buf[strlen(buf)] = rch;
578                         } while (rch > 0);
579
580                 if (ch=='A') strcpy(luser, buf);
581                 else if (ch=='P') {
582                         cprintf("Path: %s\n",buf);
583                         for (a=0; a<strlen(buf); ++a) {
584                                 if (buf[a] == '!') {
585                                         strcpy(buf,&buf[a+1]);
586                                         a=0;
587                                         }
588                                 }
589                         strcpy(suser, buf);
590                         }
591                 else if (ch=='U') cprintf("Subject: %s\n",buf);
592                 else if (ch=='I') strcpy(mid, buf);
593                 else if (ch=='H') strcpy(lnode, buf);
594                 else if (ch=='O') cprintf("X-Citadel-Room: %s\n",buf);
595                 else if (ch=='N') strcpy(snode, buf);
596                 else if (ch=='R') cprintf("To: %s\n",buf);
597                 else if (ch=='T')  {
598                         xtime = atol(buf);
599                         cprintf("Date: %s", asctime(localtime(&xtime)));
600                         }
601                 }
602
603         if (mode == MT_RFC822) {
604                 if (!strcasecmp(snode, NODENAME)) {
605                         strcpy(snode, FQDN);
606                         }
607                 cprintf("Message-ID: <%s@%s>\n", mid, snode);
608                 PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
609                 cprintf("From: %s@%s (%s)\n",
610                         suser, snode, luser);
611                 cprintf("Organization: %s\n", lnode);
612                 }
613
614         /* end header processing loop ... at this point, we're in the text */
615
616         if (ch==0) {
617                 cprintf("text\n*** ?Message truncated\n000\n");
618                 cdb_free(dmsgtext);
619                 return(xtime);
620                 }
621
622         /* do some sort of MIME output */
623         if (format_type == 4) {
624                 if ((mode == MT_CITADEL)||(mode == MT_MIME)) {
625                         mime_parser(mptr, NULL, *list_this_part);
626                         }
627                 if (mode == MT_MIME) { /* If MT_MIME then it's parts only */
628                         cprintf("000\n");
629                         cdb_free(dmsgtext);
630                         return(xtime);
631                         }
632                 }
633
634         if (headers_only) {
635                 /* give 'em a length */
636                 msg_len = 0L;
637                 while(ch = *mptr++, ch>0) {
638                         ++msg_len;
639                         }
640                 cprintf("mlen=%ld\n", msg_len);
641                 cprintf("000\n");
642                 cdb_free(dmsgtext);
643                 return(xtime);
644                 }
645
646         /* signify start of msg text */
647         if (mode == MT_CITADEL) cprintf("text\n");
648         if ( (mode == MT_RFC822) && (format_type != 4) ) cprintf("\n");
649
650         /* If the format type on disk is 1 (fixed-format), then we want
651          * everything to be output completely literally ... regardless of
652          * what message transfer format is in use.
653          */
654         if (format_type == 1) {
655                 strcpy(buf, "");
656                 while(ch = *mptr++, ch>0) {
657                         if (ch == 13) ch = 10;
658                         if ( (ch == 10) || (strlen(buf)>250) ) {
659                                 cprintf("%s\n", buf);
660                                 strcpy(buf, "");
661                                 }
662                         else {
663                                 buf[strlen(buf)+1] = 0;
664                                 buf[strlen(buf)] = ch;
665                                 }
666                         }
667                 if (strlen(buf)>0) cprintf("%s\n", buf);
668                 }
669         /* If the message on disk is format 0 (Citadel vari-format), we
670          * output using the formatter at 80 columns.  This is the final output
671          * form if the transfer format is RFC822, but if the transfer format
672          * is Citadel proprietary, it'll still work, because the indentation
673          * for new paragraphs is correct and the client will reformat the
674          * message to the reader's screen width.
675          */
676         if (format_type == 0) {
677                 memfmout(80,mptr,0);
678                 }
679         /* If the message on disk is format 4 (MIME), we've gotta hand it
680          * off to the MIME parser.  The client has already been told that
681          * this message is format 1 (fixed format), so the callback function
682          * we use will display those parts as-is.
683          */
684         if (format_type == 4) {
685                 mime_parser(mptr, NULL, *fixed_output);
686                 }
687
688         /* now we're done */
689         cprintf("000\n");
690         cdb_free(dmsgtext);
691         return(xtime);
692         }
693
694
695 /*
696  * display a message (mode 0 - Citadel proprietary)
697  */
698 void cmd_msg0(char *cmdbuf)
699 {
700         char msgid[256];
701         int headers_only = 0;
702
703         extract(msgid,cmdbuf,0);
704         headers_only = extract_int(cmdbuf, 1);
705
706         output_message(msgid, MT_CITADEL, headers_only);
707         return;
708         }
709
710
711 /*
712  * display a message (mode 2 - RFC822)
713  */
714 void cmd_msg2(char *cmdbuf)
715 {
716         char msgid[256];
717         int headers_only = 0;
718
719         extract(msgid,cmdbuf,0);
720         headers_only = extract_int(cmdbuf,1);
721
722         output_message(msgid,MT_RFC822,headers_only);
723         }
724
725 /* 
726  * display a message (mode 3 - IGnet raw format - internal programs only)
727  */
728 void cmd_msg3(char *cmdbuf)
729 {
730         char msgid[256];
731         int headers_only = 0;
732
733         if (CC->internal_pgm == 0) {
734                 cprintf("%d This command is for internal programs only.\n",
735                         ERROR);
736                 return;
737                 }
738
739         extract(msgid,cmdbuf,0);
740         headers_only = extract_int(cmdbuf,1);
741
742         output_message(msgid,MT_RAW,headers_only);
743         }
744
745 /* 
746  * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
747  */
748 void cmd_msg4(char *cmdbuf)
749 {
750         char msgid[256];
751
752         extract(msgid, cmdbuf, 0);
753
754         output_message(msgid, MT_MIME, 0);
755         }
756
757
758
759 /*
760  * Open a component of a MIME message as a download file 
761  */
762 void cmd_opna(char *cmdbuf)
763 {
764         char msgid[256];
765
766         CtdlAllocUserData(SYM_DESIRED_SECTION, 64);
767
768         extract(msgid, cmdbuf, 0);
769         extract(desired_section, cmdbuf, 1);
770
771         output_message(msgid, MT_DOWNLOAD, 0);
772         }
773
774
775
776 /*
777  * Message base operation to send a message to the master file
778  * (returns new message number)
779  */
780 long send_message(char *message_in_memory,      /* pointer to buffer */
781                 size_t message_length,          /* length of buffer */
782                 int generate_id) {              /* 1 to generate an I field */
783
784         long newmsgid;
785         char *actual_message;
786         size_t actual_length;
787         long retval;
788         char msgidbuf[32];
789
790         /* Get a new message number */
791         newmsgid = get_new_message_number();
792
793         if (generate_id) {
794                 sprintf(msgidbuf, "I%ld", newmsgid);
795                 actual_length = message_length + strlen(msgidbuf) + 1;
796                 actual_message = mallok(actual_length);
797                 memcpy(actual_message, message_in_memory, 3);
798                 memcpy(&actual_message[3], msgidbuf, (strlen(msgidbuf)+1) );
799                 memcpy(&actual_message[strlen(msgidbuf)+4],
800                         &message_in_memory[3], message_length - 3);
801                 }
802         
803         else {
804                 actual_message = message_in_memory;
805                 actual_length = message_length;
806                 }
807
808         /* Write our little bundle of joy into the message base */
809         begin_critical_section(S_MSGMAIN);
810         if ( cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long),
811                         actual_message, actual_length) < 0 ) {
812                 lprintf(2, "Can't store message\n");
813                 retval = 0L;
814                 }
815         else {
816                 retval = newmsgid;
817                 }
818         end_critical_section(S_MSGMAIN);
819
820         if (generate_id) {
821                 phree(actual_message);
822                 }
823
824         /* Finally, return the pointers */
825         return(retval);
826         }
827
828
829
830 /*
831  * this is a simple file copy routine.
832  */
833 void copy_file(char *from, char *to)
834 {
835         FILE *ffp,*tfp;
836         int a;
837
838         ffp=fopen(from,"r");
839         if (ffp==NULL) return;
840         tfp=fopen(to,"w");
841         if (tfp==NULL) {
842                 fclose(ffp);
843                 return;
844                 }
845         while (a=getc(ffp), a>=0) {
846                 putc(a,tfp);
847                 }
848         fclose(ffp);
849         fclose(tfp);
850         return;
851         }
852
853
854
855 /*
856  * message base operation to save a message and install its pointers
857  */
858 void save_message(char *mtmp,   /* file containing proper message */
859                 char *rec,      /* Recipient (if mail) */
860                 char *force,    /* if non-zero length, force a room */
861                 int mailtype,   /* local or remote type, see citadel.h */
862                 int generate_id) /* set to 1 to generate an 'I' field */
863 {
864         char aaa[100];
865         char hold_rm[ROOMNAMELEN];
866         char actual_rm[ROOMNAMELEN];
867         char force_room[ROOMNAMELEN];
868         char recipient[256];
869         long newmsgid;
870         char *message_in_memory;
871         struct stat statbuf;
872         size_t templen;
873         FILE *fp;
874         struct usersupp userbuf;
875         int a;
876         static int seqnum = 0;
877
878         lprintf(9, "save_message(%s,%s,%s,%d,%d)\n",
879                 mtmp, rec, force, mailtype, generate_id);
880
881         strcpy(force_room, force);
882
883         /* Strip non-printable characters out of the recipient name */
884         strcpy(recipient, rec);
885         for (a=0; a<strlen(recipient); ++a)
886                 if (!isprint(recipient[a]))
887                         strcpy(&recipient[a], &recipient[a+1]);
888
889         /* Measure the message */
890         stat(mtmp, &statbuf);
891         templen = statbuf.st_size;
892
893         /* Now read it into memory */
894         message_in_memory = (char *) mallok(templen);
895         if (message_in_memory == NULL) {
896                 lprintf(2, "Can't allocate memory to save message!\n");
897                 return;
898                 }
899
900         fp = fopen(mtmp, "rb");
901         fread(message_in_memory, templen, 1, fp);
902         fclose(fp);
903
904         newmsgid = send_message(message_in_memory, templen, generate_id);
905         phree(message_in_memory);
906         if (newmsgid <= 0L) return;
907
908         strcpy(actual_rm, CC->quickroom.QRname);
909         strcpy(hold_rm, "");
910
911         /* If the user is a twit, move to the twit room for posting... */
912         if (TWITDETECT) if (CC->usersupp.axlevel==2) {
913                 strcpy(hold_rm, actual_rm);
914                 strcpy(actual_rm, config.c_twitroom);
915                 }
916
917         /* ...or if this is a private message, go to the target mailbox. */
918         lprintf(9, "mailbox aliasing loop\n");
919         if (strlen(recipient) > 0) {
920                 /* mailtype = alias(recipient); */
921                 if (mailtype == MES_LOCAL) {
922                         if (getuser(&userbuf, recipient)!=0) {
923                                 /* User not found, goto Aide */
924                                 strcpy(force_room, AIDEROOM);
925                                 }
926                         else {
927                                 strcpy(hold_rm, actual_rm);
928                                 MailboxName(actual_rm, &userbuf, MAILROOM);
929                                 }
930                         }
931                 }
932
933         /* ...or if this message is destined for Aide> then go there. */
934         lprintf(9, "actual room forcing loop\n");
935         if (strlen(force_room) > 0) {
936                 strcpy(hold_rm, actual_rm);
937                 strcpy(actual_rm, force_room);
938                 }
939
940         /* This call to usergoto() changes rooms if necessary.  It also
941          * causes the latest message list to be read into memory.
942          */
943         usergoto(actual_rm, 0);
944
945         /* read in the quickroom record, obtaining a lock... */
946         lgetroom(&CC->quickroom, actual_rm);
947
948         /* Fix an obscure bug */
949         if (!strcasecmp(CC->quickroom.QRname, AIDEROOM)) {
950                 CC->quickroom.QRflags = CC->quickroom.QRflags & ~QR_MAILBOX;
951                 }
952
953         /* Add the message pointer to the room */
954         CC->quickroom.QRhighest = AddMessageToRoom(&CC->quickroom, newmsgid);
955
956         /* update quickroom */
957         lputroom(&CC->quickroom);
958
959         /* Network mail - send a copy to the network program. */
960         if ( (strlen(recipient)>0) && (mailtype != MES_LOCAL) ) {
961                 sprintf(aaa,"./network/spoolin/netmail.%04lx.%04x.%04x",
962                         (long)getpid(), CC->cs_pid, ++seqnum);
963                 copy_file(mtmp,aaa);
964                 system("exec nohup ./netproc -i >/dev/null 2>&1 &");
965                 }
966
967         /* Bump this user's messages posted counter. */
968         lgetuser(&CC->usersupp, CC->curr_user);
969         CC->usersupp.posted = CC->usersupp.posted + 1;
970         lputuser(&CC->usersupp, CC->curr_user);
971
972         /* If we've posted in a room other than the current room, then we
973          * have to now go back to the current room...
974          */
975         if (strlen(hold_rm) > 0) {
976                 usergoto(hold_rm, 0);
977                 }
978         unlink(mtmp);           /* delete the temporary file */
979         }
980
981
982 /*
983  * Generate an administrative message and post it in the Aide> room.
984  */
985 void aide_message(char *text)
986 {
987         FILE *fp;
988
989         fp=fopen(CC->temp,"wb");
990         fprintf(fp,"%c%c%c",255,MES_NORMAL,0);
991         fprintf(fp,"Psysop%c",0);
992         fprintf(fp,"T%ld%c", (long)time(NULL), 0);
993         fprintf(fp,"ACitadel%c",0);
994         fprintf(fp,"OAide%c",0);
995         fprintf(fp,"N%s%c",NODENAME,0);
996         fprintf(fp,"M%s\n%c",text,0);
997         fclose(fp);
998         save_message(CC->temp,"",AIDEROOM,MES_LOCAL,1);
999         syslog(LOG_NOTICE,text);
1000         }
1001
1002
1003
1004 /*
1005  * Build a binary message to be saved on disk.
1006  */
1007 void make_message(
1008         char *filename,                 /* temporary file name */
1009         struct usersupp *author,        /* author's usersupp structure */
1010         char *recipient,                /* NULL if it's not mail */
1011         char *room,                     /* room where it's going */
1012         int type,                       /* see MES_ types in header file */
1013         int net_type,                   /* see MES_ types in header file */
1014         int format_type,                /* local or remote (see citadel.h) */
1015         char *fake_name) {              /* who we're masquerading as */
1016
1017         FILE *fp;
1018         int a;
1019         time_t now;
1020         char dest_node[32];
1021         char buf[256];
1022
1023         /* Don't confuse the poor folks if it's not routed mail. */
1024         strcpy(dest_node, "");
1025
1026
1027         /* If net_type is MES_BINARY, split out the destination node. */
1028         if (net_type == MES_BINARY) {
1029                 strcpy(dest_node,NODENAME);
1030                 for (a=0; a<strlen(recipient); ++a) {
1031                         if (recipient[a]=='@') {
1032                                 recipient[a]=0;
1033                                 strcpy(dest_node,&recipient[a+1]);
1034                                 }
1035                         }
1036                 }
1037
1038         /* if net_type is MES_INTERNET, set the dest node to 'internet' */
1039         if (net_type == MES_INTERNET) {
1040                 strcpy(dest_node,"internet");
1041                 }
1042
1043         while (isspace(recipient[strlen(recipient)-1]))
1044                 recipient[strlen(recipient)-1] = 0;
1045
1046         time(&now);
1047         fp=fopen(filename,"w");
1048         putc(255,fp);
1049         putc(type,fp);  /* Normal or anonymous, see MES_ flags */
1050         putc(format_type,fp);   /* Formatted or unformatted */
1051         fprintf(fp,"Pcit%ld%c",author->usernum,0);      /* path */
1052         fprintf(fp,"T%ld%c",(long)now,0);               /* date/time */
1053         if (fake_name[0])
1054            fprintf(fp,"A%s%c",fake_name,0);
1055         else
1056            fprintf(fp,"A%s%c",author->fullname,0);      /* author */
1057
1058         if (CC->quickroom.QRflags & QR_MAILBOX) {       /* room */
1059                 fprintf(fp,"O%s%c", &CC->quickroom.QRname[11], 0);
1060                 }
1061         else {
1062                 fprintf(fp,"O%s%c",CC->quickroom.QRname,0);
1063                 }
1064
1065         fprintf(fp,"N%s%c",NODENAME,0);                 /* nodename */
1066         fprintf(fp,"H%s%c",HUMANNODE,0);                /* human nodename */
1067
1068         if (recipient[0]!=0) fprintf(fp, "R%s%c", recipient, 0);
1069         if (dest_node[0]!=0) fprintf(fp, "D%s%c", dest_node, 0);
1070
1071         putc('M',fp);
1072
1073         while (client_gets(buf), strcmp(buf,"000"))
1074         {
1075            fprintf(fp,"%s\n",buf);
1076         }
1077         syslog(LOG_INFO, "Closing message");
1078         putc(0,fp);
1079         fclose(fp);
1080         }
1081
1082
1083
1084
1085
1086 /*
1087  * message entry  -  mode 0 (normal) <bc>
1088  */
1089 void cmd_ent0(char *entargs)
1090 {
1091         int post = 0;
1092         char recipient[256];
1093         int anon_flag = 0;
1094         int format_type = 0;
1095         char newusername[256];          /* <bc> */
1096
1097         int a,b;
1098         int e = 0;
1099         int mtsflag = 0;
1100         struct usersupp tempUS;
1101         char buf[256];
1102
1103         post = extract_int(entargs,0);
1104         extract(recipient,entargs,1);
1105         anon_flag = extract_int(entargs,2);
1106         format_type = extract_int(entargs,3);
1107
1108         /* first check to make sure the request is valid. */
1109
1110         if (!(CC->logged_in)) {
1111                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
1112                 return;
1113                 }
1114         if ((CC->usersupp.axlevel<2)&&((CC->quickroom.QRflags&QR_MAILBOX)==0)) {
1115                 cprintf("%d Need to be validated to enter ",
1116                         ERROR+HIGHER_ACCESS_REQUIRED);
1117                 cprintf("(except in %s> to sysop)\n", MAILROOM);
1118                 return;
1119                 }
1120         if ((CC->usersupp.axlevel<4)&&(CC->quickroom.QRflags&QR_NETWORK)) {
1121                 cprintf("%d Need net privileges to enter here.\n",
1122                         ERROR+HIGHER_ACCESS_REQUIRED);
1123                 return;
1124                 }
1125         if ((CC->usersupp.axlevel<6)&&(CC->quickroom.QRflags&QR_READONLY)) {
1126                 cprintf("%d Sorry, this is a read-only room.\n",
1127                         ERROR+HIGHER_ACCESS_REQUIRED);
1128                 return;
1129                 }
1130
1131         mtsflag=0;
1132         
1133                 
1134         if (post==2) {                  /* <bc> */
1135            if (CC->usersupp.axlevel<6)
1136            {
1137               cprintf("%d You don't have permission to do an aide post.\n",
1138                 ERROR+HIGHER_ACCESS_REQUIRED);
1139               return;
1140            }
1141            extract(newusername,entargs,4);
1142            memset(CC->fake_postname, 0, 32);
1143            strcpy(CC->fake_postname, newusername);
1144            cprintf("%d Ok\n",OK);
1145            return;
1146         }
1147         
1148         CC->cs_flags |= CS_POSTING;
1149         
1150         buf[0]=0;
1151         if (CC->quickroom.QRflags & QR_MAILBOX) {
1152                 if (CC->usersupp.axlevel>=2) {
1153                         strcpy(buf,recipient);
1154                         }
1155                 else strcpy(buf,"sysop");
1156                 lprintf(9, "calling alias()\n");
1157                 e=alias(buf);                   /* alias and mail type */
1158                 lprintf(9, "alias() returned %d\n", e);
1159                 if ((buf[0]==0) || (e==MES_ERROR)) {
1160                         cprintf("%d Unknown address - cannot send message.\n",
1161                                 ERROR+NO_SUCH_USER);
1162                         return;
1163                         }
1164                 if ((e!=MES_LOCAL)&&(CC->usersupp.axlevel<4)) {
1165                         cprintf("%d Net privileges required for network mail.\n",
1166                                 ERROR+HIGHER_ACCESS_REQUIRED);
1167                         return;
1168                         }
1169                 if ((RESTRICT_INTERNET==1)&&(e==MES_INTERNET)
1170                    &&((CC->usersupp.flags&US_INTERNET)==0)
1171                    &&(!CC->internal_pgm) ) {
1172                         cprintf("%d You don't have access to Internet mail.\n",
1173                                 ERROR+HIGHER_ACCESS_REQUIRED);
1174                         return;
1175                         }
1176                 if (!strcasecmp(buf,"sysop")) {
1177                         mtsflag=1;
1178                         goto SKFALL;
1179                         }
1180                 if (e!=MES_LOCAL) goto SKFALL;  /* don't search local file  */
1181                 if (!strcasecmp(buf,CC->usersupp.fullname)) {
1182                         cprintf("%d Can't send mail to yourself!\n",
1183                                 ERROR+NO_SUCH_USER);
1184                         return;
1185                         }
1186
1187                 /* Check to make sure the user exists; also get the correct
1188                 * upper/lower casing of the name. 
1189                 */
1190                 a = getuser(&tempUS, buf);
1191                 if (a != 0) {
1192                         cprintf("%d No such user.\n",ERROR+NO_SUCH_USER);
1193                         return;
1194                         }
1195                 strcpy(buf,tempUS.fullname);
1196                 }
1197         
1198 SKFALL: b=MES_NORMAL;
1199         if (CC->quickroom.QRflags&QR_ANONONLY) b=MES_ANON;
1200         if (CC->quickroom.QRflags&QR_ANONOPT) {
1201                 if (anon_flag==1) b=MES_AN2;
1202                 }
1203         if ((CC->quickroom.QRflags & QR_MAILBOX) == 0) buf[0]=0;
1204
1205         /* If we're only checking the validity of the request, return
1206          * success without creating the message.
1207          */
1208         if (post==0) {
1209                 cprintf("%d %s\n",OK,buf);
1210                 return;
1211                 }
1212         
1213         cprintf("%d send message\n",SEND_LISTING);
1214         if (CC->fake_postname[0])
1215            make_message(CC->temp,&CC->usersupp,buf,CC->quickroom.QRname,b,e,format_type, CC->fake_postname);
1216         else
1217            if (CC->fake_username[0])
1218               make_message(CC->temp,&CC->usersupp,buf,CC->quickroom.QRname,b,e,format_type, CC->fake_username);
1219            else
1220               make_message(CC->temp,&CC->usersupp,buf,CC->quickroom.QRname,b,e,format_type, "");
1221         save_message(CC->temp,buf, (mtsflag ? AIDEROOM : ""), e,1);
1222         CC->fake_postname[0]='\0';
1223         return;
1224         }
1225
1226
1227
1228 /* 
1229  * message entry - mode 3 (raw)
1230  */
1231 void cmd_ent3(char *entargs)
1232 {
1233         char recp[256];
1234         char buf[256];
1235         int a;
1236         int e = 0;
1237         struct usersupp tempUS;
1238         long msglen;
1239         long bloklen;
1240         FILE *fp;
1241
1242         if (CC->internal_pgm == 0) {
1243                 cprintf("%d This command is for internal programs only.\n",
1244                         ERROR);
1245                 return;
1246                 }
1247
1248         /* See if there's a recipient, but make sure it's a real one */
1249         extract(recp, entargs, 1);
1250         for (a=0; a<strlen(recp); ++a)
1251                 if (!isprint(recp[a]))
1252                         strcpy(&recp[a], &recp[a+1]);
1253         while (isspace(recp[0])) strcpy(recp, &recp[1]);
1254         while (isspace(recp[strlen(recp)-1])) recp[strlen(recp)-1] = 0;
1255
1256         /* If we're in Mail, check the recipient */
1257         if (strlen(recp) > 0) {
1258                 e=alias(recp);                  /* alias and mail type */
1259                 if ((recp[0]==0) || (e==MES_ERROR)) {
1260                         cprintf("%d Unknown address - cannot send message.\n",
1261                                 ERROR+NO_SUCH_USER);
1262                         return;
1263                         }
1264                 if (e == MES_LOCAL) {
1265                         a = getuser(&tempUS,recp);
1266                         if (a!=0) {
1267                                 cprintf("%d No such user.\n",
1268                                         ERROR+NO_SUCH_USER);
1269                                 return;
1270                                 }
1271                         }
1272                 }
1273
1274         /* At this point, message has been approved. */
1275         if (extract_int(entargs, 0) == 0) {
1276                 cprintf("%d OK to send\n", OK);
1277                 return;
1278                 }
1279
1280         /* open a temp file to hold the message */
1281         fp = fopen(CC->temp, "wb");
1282         if (fp == NULL) {
1283                 cprintf("%d Cannot open %s: %s\n", 
1284                         ERROR + INTERNAL_ERROR,
1285                         CC->temp, strerror(errno) );
1286                 return;
1287                 }
1288
1289         msglen = extract_long(entargs, 2);
1290         cprintf("%d %ld\n", SEND_BINARY, msglen);
1291         while(msglen > 0L) {
1292                 bloklen = ((msglen >= 255L) ? 255 : msglen);
1293                 client_read(buf, (int)bloklen );
1294                 fwrite(buf, (int)bloklen, 1, fp);
1295                 msglen = msglen - bloklen;
1296                 }
1297         fclose(fp);
1298
1299         save_message(CC->temp, recp, "", e, 0);
1300         }
1301
1302
1303 /*
1304  * Delete message from current room
1305  */
1306 void cmd_dele(char *delstr)
1307 {
1308         long delnum;
1309         int a,ok;
1310
1311         getuser(&CC->usersupp,CC->curr_user);
1312         if ((CC->usersupp.axlevel < 6)
1313            && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
1314            && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
1315                 cprintf("%d Higher access required.\n",
1316                         ERROR+HIGHER_ACCESS_REQUIRED);
1317                 return;
1318                 }
1319
1320         delnum = extract_long(delstr, 0);
1321         
1322         /* get room records, obtaining a lock... */
1323         lgetroom(&CC->quickroom,CC->quickroom.QRname);
1324         get_msglist(&CC->quickroom);
1325
1326         ok = 0;
1327         if (CC->num_msgs > 0) for (a=0; a<(CC->num_msgs); ++a) {
1328                 if (MessageFromList(a) == delnum) {
1329                         SetMessageInList(a, 0L);
1330                         ok = 1;
1331                         }
1332                 }
1333
1334         CC->num_msgs = sort_msglist(CC->msglist, CC->num_msgs);
1335         CC->quickroom.QRhighest = MessageFromList(CC->num_msgs - 1);
1336
1337         put_msglist(&CC->quickroom);
1338         lputroom(&CC->quickroom);
1339         if (ok==1) {
1340                 AdjRefCount(delnum, -1); 
1341                 cprintf("%d Message deleted.\n",OK);
1342                 }
1343         else cprintf("%d No message %ld.\n",ERROR,delnum);
1344         } 
1345
1346
1347 /*
1348  * move a message to another room
1349  */
1350 void cmd_move(char *args)
1351 {
1352         long num;
1353         char targ[32];
1354         int a;
1355         struct quickroom qtemp;
1356         int foundit;
1357
1358         num = extract_long(args,0);
1359         extract(targ,args,1);
1360         
1361         getuser(&CC->usersupp,CC->curr_user);
1362         if ((CC->usersupp.axlevel < 6)
1363            && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) {
1364                 cprintf("%d Higher access required.\n",
1365                         ERROR+HIGHER_ACCESS_REQUIRED);
1366                 return;
1367                 }
1368
1369         if (getroom(&qtemp, targ) != 0) {
1370                 cprintf("%d '%s' does not exist.\n",ERROR,targ);
1371                 return;
1372                 }
1373
1374         /* yank the message out of the current room... */
1375         lgetroom(&CC->quickroom, CC->quickroom.QRname);
1376         get_msglist(&CC->quickroom);
1377
1378         foundit = 0;
1379         for (a=0; a<(CC->num_msgs); ++a) {
1380                 if (MessageFromList(a) == num) {
1381                         foundit = 1;
1382                         SetMessageInList(a, 0L);
1383                         }
1384                 }
1385         if (foundit) {
1386                 CC->num_msgs = sort_msglist(CC->msglist, CC->num_msgs);
1387                 put_msglist(&CC->quickroom);
1388                 CC->quickroom.QRhighest = MessageFromList((CC->num_msgs)-1);
1389                 }
1390         lputroom(&CC->quickroom);
1391         if (!foundit) {
1392                 cprintf("%d msg %ld does not exist.\n",ERROR,num);
1393                 return;
1394                 }
1395
1396         /* put the message into the target room */
1397         lgetroom(&qtemp, targ);
1398         qtemp.QRhighest = AddMessageToRoom(&qtemp, num);
1399         lputroom(&qtemp);
1400
1401         cprintf("%d Message moved.\n", OK);
1402         }
1403
1404
1405
1406 /*
1407  * GetSuppMsgInfo()  -  Get the supplementary record for a message
1408  */
1409 void GetSuppMsgInfo(struct SuppMsgInfo *smibuf, long msgnum) {
1410
1411         struct cdbdata *cdbsmi;
1412         long TheIndex;
1413
1414         memset(smibuf, 0, sizeof(struct SuppMsgInfo));
1415         smibuf->smi_msgnum = msgnum;
1416         smibuf->smi_refcount = 1;       /* Default reference count is 1 */
1417
1418         /* Use the negative of the message number for its supp record index */
1419         TheIndex = (0L - msgnum);
1420
1421         cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long) );
1422         if (cdbsmi == NULL) {
1423                 return;      /* record not found; go with defaults */
1424         }
1425
1426         memcpy(smibuf, cdbsmi->ptr,
1427                 ( (cdbsmi->len > sizeof(struct SuppMsgInfo)) ?
1428                 sizeof(struct SuppMsgInfo) : cdbsmi->len) );
1429         cdb_free(cdbsmi);
1430         return;
1431 }
1432    
1433
1434 /*
1435  * PutSuppMsgInfo()  -  (re)write supplementary record for a message
1436  */
1437 void PutSuppMsgInfo(struct SuppMsgInfo *smibuf)
1438 {
1439         long TheIndex;
1440
1441         /* Use the negative of the message number for its supp record index */
1442         TheIndex = (0L - smibuf->smi_msgnum);
1443
1444         cdb_store(CDB_MSGMAIN,
1445                 &TheIndex, sizeof(long),
1446                 smibuf, sizeof(struct SuppMsgInfo));
1447
1448         }         
1449
1450
1451
1452 /*
1453  * AdjRefCount  -  change the reference count for a message;
1454  *                 delete the message if it reaches zero
1455  */
1456 void AdjRefCount(long msgnum, int incr) {
1457
1458         struct SuppMsgInfo smi;
1459         long delnum;
1460
1461         lprintf(9, "Adjust msg <%ld> ref count by <%d>\n",
1462                 msgnum, incr);
1463
1464         /* This is a *tight* critical section; please keep it that way, as
1465          * it may get called while nested in other critical sections.  
1466          * Complicating this any further will surely cause deadlock!
1467          */
1468         begin_critical_section(S_SUPPMSGMAIN);
1469         GetSuppMsgInfo(&smi, msgnum);
1470         smi.smi_refcount += incr;
1471         PutSuppMsgInfo(&smi);
1472         end_critical_section(S_SUPPMSGMAIN);
1473
1474         lprintf(9, "Ref count for message <%ld> after write is <%d>\n",
1475                 msgnum, smi.smi_refcount);
1476
1477         /* If the reference count is now zero, delete the message
1478          * (and its supplementary record as well).
1479          */
1480         if (smi.smi_refcount == 0) {
1481                 lprintf(9, "Deleting message <%ld>\n", msgnum);
1482                 delnum = msgnum;
1483                 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));   
1484                 delnum = (0L - msgnum);
1485                 cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));   
1486         }
1487 }