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