d7a2cf0f234af54b39682d8de835011adc8fa6c8
[citadel.git] / citadel / routines.c
1 /* Citadel/UX support routines */
2
3 #include "sysdep.h"
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <ctype.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #include <sys/ioctl.h>
12 #include <pwd.h>
13 #include <signal.h>
14 #include <dirent.h>
15 #include <errno.h>
16
17
18 #define ROUTINES_C
19
20 #include "citadel.h"
21
22 char inkey();
23 void sttybbs();
24 void newprompt();
25 void val_user();
26 int intprompt();
27 void formout();
28 void logoff();
29 void set_keepalives();
30 void strprompt();
31 void newprompt();
32 void color();
33
34 #define IFAIDE if(axlevel>=6)
35 #define IFNAIDE if (axlevel<6)
36
37 extern unsigned userflags;
38 extern char *axdefs[7];
39 extern char sigcaught;
40 extern struct CtdlServInfo serv_info;
41 extern char rc_floor_mode;
42
43 int struncmp(lstr,rstr,len)
44 char lstr[],rstr[];
45 int len; {
46         int pos = 0;
47         char lc,rc;
48         while (pos<len) {
49                 lc=tolower(lstr[pos]);
50                 rc=tolower(rstr[pos]);
51                 if ((lc==0)&&(rc==0)) return(0);
52                 if (lc<rc) return(-1);
53                 if (lc>rc) return(1);
54                 pos=pos+1;
55                 }
56         return(0);
57         }
58
59
60 /* 
61  * check for the presence of a character within a string (returns count)
62  */
63 int haschar(st,ch)
64 char st[];
65 int ch; {
66         int a,b;
67         b=0;
68         for (a=0; a<strlen(st); ++a) if (st[a]==ch) ++b;
69         return(b);
70         }
71
72
73 /*
74  * num_parms()  -  discover number of parameters...
75  */
76 int num_parms(source)
77 char source[]; {
78         int a;
79         int count = 1;
80
81         for (a=0; a<strlen(source); ++a) 
82                 if (source[a]=='|') ++count;
83         return(count);
84         }
85
86 /*
87  * extract()  -  extract a parameter from a series of "|" separated...
88  */
89 void extract(dest,source,parmnum)
90 char dest[];
91 char source[];
92 int parmnum; {
93         char buf[256];
94         int count = 0;
95         int n;
96
97         n = num_parms(source);
98
99         if (parmnum >= n) {
100                 strcpy(dest,"");
101                 return;
102                 }
103         strcpy(buf,source);
104         if ( (parmnum == 0) && (n == 1) ) {
105                 strcpy(dest,buf);
106                 return;
107                 }
108
109         while (count++ < parmnum) do {
110                 strcpy(buf,&buf[1]);
111                 } while( (strlen(buf)>0) && (buf[0]!='|') );
112         if (buf[0]=='|') strcpy(buf,&buf[1]);
113         for (count = 0; count<strlen(buf); ++count)
114                 if (buf[count] == '|') buf[count] = 0;
115         strcpy(dest,buf);
116         }
117
118 /*
119  * extract_int()  -  extract an int parm w/o supplying a buffer
120  */
121 int extract_int(source,parmnum)
122 char *source;
123 int parmnum; {
124         char buf[256];
125         
126         extract(buf,source,parmnum);
127         return(atoi(buf));
128         }
129
130 /*
131  * extract_long()  -  extract a long parm w/o supplying a buffer
132  */
133 long extract_long(source,parmnum)
134 char *source;
135 int parmnum; {
136         char buf[256];
137         
138         extract(buf,source,parmnum);
139         return(atol(buf));
140         }
141
142 void back(spaces) /* Destructive backspace */
143 int spaces; {
144 int a;
145         for (a=1; a<=spaces; ++a) {
146                 putc(8,stdout); putc(32,stdout); putc(8,stdout);
147                 }
148         }
149
150 int yesno() { /* Returns 1 for yes, 0 for no */
151 int a;
152         while (1) {
153                 a=inkey(); a=tolower(a);
154                 if (a=='y') { printf("Yes\n"); return(1); }
155                 if (a=='n') { printf("No\n");  return(0); }
156                 }
157         }
158
159 int yesno_d(d) /* Returns 1 for yes, 0 for no, arg is default value */
160 int d; {
161 int a;
162         while (1) {
163                 a=inkey(); a=tolower(a);
164                 if (a==13) a=(d ? 'y' : 'n');
165                 if (a=='y') { printf("Yes\n"); return(1); }
166                 if (a=='n') { printf("No\n");  return(0); }
167                 }
168         }
169
170
171 void hit_any_key() {            /* hit any key to continue */
172         int a,b;
173
174         printf("%s\r",serv_info.serv_moreprompt);
175         sttybbs(0);
176         b=inkey();
177         for (a=0; a<strlen(serv_info.serv_moreprompt); ++a)
178                 putc(' ',stdout);
179         putc(13,stdout);
180         sttybbs(1);
181         if (b==NEXT_KEY) sigcaught = SIGINT;
182         if (b==STOP_KEY) sigcaught = SIGQUIT;
183         }
184
185 /*
186  * change a user's access level
187  */
188 void edituser(userbuf)
189 struct usersupp *userbuf; {
190         char who[256];
191         char buf[256];
192
193         newprompt("User name: ",who,25);
194         sprintf(buf,"QUSR %s",who);
195         serv_puts(buf);
196         serv_gets(buf);
197         if (buf[0]!='2') {
198                 printf("%s\n",&buf[4]);
199                 return;
200                 }
201         
202         val_user(who);
203         }
204
205
206 int set_attr(sval,prompt,sbit)
207 int sval;
208 char *prompt;
209 unsigned sbit; {
210         int a;
211         int temp;
212
213         temp = sval;
214         color(3);
215         printf("%45s [", prompt);
216         color(1);
217         printf("%3s", ((temp&sbit) ? "Yes":"No"));
218         color(3);
219         printf("]? ");
220         color(2);
221         a=yesno_d(temp&sbit);
222         color(7);
223         temp=(temp|sbit);
224         if (!a) temp=(temp^sbit);
225         return(temp);
226         }
227
228 /*
229  * modes are:  0 - .EC command, 1 - .EC for new user,
230  *             2 - toggle Xpert mode  3 - toggle floor mode
231  */
232 void enter_config(mode)
233 int mode; {
234         int width,height,flags;
235         char buf[128];
236
237         sprintf(buf,"GETU");
238         serv_puts(buf);
239         serv_gets(buf);
240         if (buf[0]!='2') {
241                 printf("%s\n",&buf[4]);
242                 return;
243                 }
244
245         width = extract_int(&buf[4],0);
246         height = extract_int(&buf[4],1);
247         flags = extract_int(&buf[4],2);
248
249         if ((mode==0)||(mode==1)) {
250
251          width = intprompt("Enter your screen width",width,20,255);
252          height = intprompt("Enter your screen height",height,3,255);
253  
254          flags = set_attr(flags,
255                 "Are you an experienced Citadel user",US_EXPERT);
256          if ( ((flags&US_EXPERT)==0) && (mode==1))
257                 return;
258          flags = set_attr(flags,
259                 "Print last old message on New message request",US_LASTOLD);
260          if ((flags&US_EXPERT)==0) formout("unlisted");
261          flags = set_attr(flags,"Be unlisted in userlog",US_UNLISTED);
262          flags = set_attr(flags,"Suppress message prompts",US_NOPROMPT);
263          if ((flags & US_NOPROMPT)==0)
264             flags = set_attr(flags,"Use 'disappearing' prompts",US_DISAPPEAR);
265          flags = set_attr(flags,
266                 "Pause after each screenful of text",US_PAGINATOR);
267          if (rc_floor_mode == RC_DEFAULT) {
268           flags = set_attr(flags,
269                 "View rooms by floor",US_FLOORS);
270           }
271          }
272
273         if (mode==2) {
274          if (flags & US_EXPERT) {
275                 flags = (flags ^ US_EXPERT);
276                 printf("Expert mode now OFF\n");
277                 }
278          else {
279                 flags = (flags | US_EXPERT);
280                 printf("Expert mode now ON\n");
281                 }
282          }
283
284         if (mode==3) {
285          if (flags & US_FLOORS) {
286                 flags = (flags ^ US_FLOORS);
287                 printf("Floor mode now OFF\n");
288                 }
289          else {
290                 flags = (flags | US_FLOORS);
291                 printf("Floor mode now ON\n");
292                 }
293          }
294
295         sprintf(buf,"SETU %d|%d|%d",width,height,flags);
296         serv_puts(buf);
297         serv_gets(buf);
298         if (buf[0]!='2') printf("%s\n",&buf[4]);
299         userflags = flags;
300 }
301
302 /*
303  * getstring()  -  get a line of text from a file
304  *                 ignores lines beginning with "#"
305  */
306 int getstring(fp,string)
307 FILE *fp;               
308 char string[]; {
309         int a,c;
310         do {
311                 strcpy(string,"");
312                 a=0;
313                 do {
314                         c=getc(fp);
315                         if (c<0) {
316                                 string[a]=0;
317                                 return(-1);
318                                 }
319                         string[a++]=c;
320                         } while(c!=10);
321                         string[a-1]=0;
322                 } while(string[0]=='#');
323         return(strlen(string));
324         }
325
326 int pattern(search,patn)        /* Searches for patn in search string */
327 char search[];
328 char patn[];
329 {
330         int a,b;
331         for (a=0; a<strlen(search); ++a)
332         {       b=struncmp(&search[a],patn,strlen(patn));
333                 if (b==0) return(b);
334                 }
335         return(-1);
336 }
337
338 void interr(errnum)     /* display internal error as defined in errmsgs */
339 int errnum; {
340         printf("*** INTERNAL ERROR %d\n",errnum);
341         printf("(Press any key to continue)\n");
342         inkey();
343         logoff(errnum);
344 }
345
346
347
348 /*
349  * Check to see if we need to pause at the end of a screen.
350  * If we do, we have to disable server keepalives during the pause because
351  * we are probably in the middle of a server operation and the NOOP command
352  * would confuse everything.
353  */
354 int checkpagin(lp,pagin,height)
355 int lp;
356 int pagin; {
357         if (pagin!=1) return(0);
358         if (lp>=(height-1)) {
359                 set_keepalives(KA_NO);
360                 hit_any_key();
361                 set_keepalives(KA_YES);
362                 return(0);
363                 }
364         return(lp);
365         }
366
367
368 void strproc(string)
369 char string[];
370 {
371         int a;
372
373         if (strlen(string)==0) return;
374
375         /* Convert non-printable characters to blanks */
376         for (a=0; a<strlen(string); ++a) {
377                 if (string[a]<32) string[a]=32;
378                 if (string[a]>126) string[a]=32;
379                 }
380
381         /* Remove leading and trailing blanks */
382         while(string[0]<33) strcpy(string,&string[1]);
383         while(string[strlen(string)-1]<33) string[strlen(string)-1]=0;
384
385         /* Remove double blanks */
386         for (a=0; a<strlen(string); ++a) {
387                 if ((string[a]==32)&&(string[a+1]==32)) {
388                         strcpy(&string[a],&string[a+1]);
389                         a=0;
390                         }
391                 }
392
393         /* remove characters which would interfere with the network */
394         for (a=0; a<strlen(string); ++a) {
395                 if (string[a]=='!') strcpy(&string[a],&string[a+1]);
396                 if (string[a]=='@') strcpy(&string[a],&string[a+1]);
397                 if (string[a]=='_') strcpy(&string[a],&string[a+1]);
398                 if (string[a]==',') strcpy(&string[a],&string[a+1]);
399                 if (string[a]=='%') strcpy(&string[a],&string[a+1]);
400                 if (string[a]=='|') strcpy(&string[a],&string[a+1]);
401                 }
402
403         }
404
405
406 int hash(str)
407 char str[]; {
408         int h = 0;
409         int i;
410
411         for (i=0; i<strlen(str); ++i) h=h+((i+1)*tolower(str[i]));
412         return(h);
413         }
414
415 long finduser(file,name)
416 int file;
417 char *name; {
418         FILE *fp;
419         int uh,fh;
420         long pp=0L;
421         
422         uh=hash(name);
423         fp=fopen("hashtab","r");
424         while(fread((char *)&fh,sizeof(int),1,fp)>0) {
425                 if (uh==fh) {
426                         lseek(file,pp,0);
427                         return(pp);
428                         }
429                 pp = pp + (long)sizeof(struct usersupp);
430                 }
431         fclose(fp);
432         return(-1L);
433         }
434
435
436 int alias(name)         /* process alias and routing info for mail */
437 char name[]; {
438         FILE *fp;
439         int a,b;
440         char aaa[300],bbb[300];
441         
442         fp=fopen("network/mail.aliases","r");
443         if (fp==NULL) return(2);
444 GNA:    strcpy(aaa,""); strcpy(bbb,"");
445         do {
446                 a=getc(fp);
447                 if (a==',') a=0;
448                 if (a>0) { b=strlen(aaa); aaa[b]=a; aaa[b+1]=0; }
449                 } while(a>0);
450         do {
451                 a=getc(fp);
452                 if (a==10) a=0;
453                 if (a>0) { b=strlen(bbb); bbb[b]=a; bbb[b+1]=0; }
454                 } while(a>0);
455         if (a<0) {
456                 fclose(fp);
457                 goto DETYPE;
458                 }
459         if (strucmp(name,aaa)) goto GNA;
460         fclose(fp);
461         strcpy(name,bbb);
462         printf("*** Mail is being forwarded to %s\n",name);
463
464 DETYPE: /* determine local or remote type, see citadel.h */
465         for (a=0; a<strlen(name); ++a) if (name[a]=='!') return(M_INTERNET);
466         for (a=0; a<strlen(name); ++a)
467                 if (name[a]=='@')
468                         for (b=a; b<strlen(name); ++b)
469                                 if (name[b]=='.') return(M_INTERNET);
470         b=0; for (a=0; a<strlen(name); ++a) if (name[a]=='@') ++b;
471         if (b>1) {
472                 printf("Too many @'s in address\n");
473                 return(M_ERROR);
474                 }
475         if (b==1) {
476                 for (a=0; a<strlen(name); ++a)
477                         if (name[a]=='@') strcpy(bbb,&name[a+1]);
478                 while (bbb[0]==32) strcpy(bbb,&bbb[1]);
479                 fp = fopen("network/mail.sysinfo","r");
480                 if (fp==NULL) return(M_ERROR);
481 GETSN:          do {
482                         a=getstring(fp,aaa);
483                         } while ((a>=0)&&(strucmp(aaa,bbb)));
484                 a=getstring(fp,aaa);
485                 if (!strncmp(aaa,"use ",4)) {
486                         strcpy(bbb,&aaa[4]);
487                         fseek(fp,0L,0);
488                         goto GETSN;
489                         }
490                 fclose(fp);
491                 if (!strncmp(aaa,"uum",3)) {
492                         strcpy(bbb,name);
493                         for (a=0; a<strlen(bbb); ++a) {
494                                 if (bbb[a]=='@') bbb[a]=0;
495                                 if (bbb[a]==' ') bbb[a]='_';
496                                 }
497                         while(bbb[strlen(bbb)-1]=='_') bbb[strlen(bbb)-1]=0;
498                         sprintf(name,&aaa[4],bbb);
499                         return(M_INTERNET);
500                         }
501                 if (!strncmp(aaa,"bin",3)) {
502                         strcpy(aaa,name); strcpy(bbb,name);
503                         while (aaa[strlen(aaa)-1]!='@') aaa[strlen(aaa)-1]=0;
504                         aaa[strlen(aaa)-1]=0;
505                         while (aaa[strlen(aaa)-1]==' ') aaa[strlen(aaa)-1]=0;
506                         while (bbb[0]!='@') strcpy(bbb,&bbb[1]);
507                         strcpy(bbb,&bbb[1]);
508                         while (bbb[0]==' ') strcpy(bbb,&bbb[1]);
509                         sprintf(name,"%s @%s",aaa,bbb);
510                         return(M_BINARY);
511                         }
512                 return(M_ERROR);
513                 }
514         return(M_LOCAL);
515         }
516
517
518 #ifdef NO_STRERROR
519 /*
520  * replacement strerror() for systems that don't have it
521  */
522 char *strerror(e)
523 int e; {
524         static char buf[32];
525
526         sprintf(buf,"errno = %d",e);
527         return(buf);
528         }
529 #endif
530
531
532 void progress(curr,cmax)
533 long curr;
534 long cmax; {
535         static long dots_printed;
536         long a;
537
538         if (curr==0) {
539                 printf(".......................................");
540                 printf(".......................................\r");
541                 fflush(stdout);
542                 dots_printed = 0;
543                 }
544         else if (curr==cmax) {
545                 printf("\r%79s\n","");
546                 }
547         else {
548                 a=(curr * 100) / cmax;
549                 a=a*78; a=a/100;
550                 while (dots_printed < a) {
551                         printf("*");
552                         ++dots_printed;
553                         fflush(stdout);
554                         }
555                 }
556         }
557
558
559 /*
560  * NOT the same locate_host() in locate_host.c.  This one just does a
561  * 'who am i' to try to discover where the user is...
562  */
563 void locate_host(hbuf)
564 char hbuf[]; {
565         char buf[256];
566         FILE *who;
567         int a,b;
568
569         who = (FILE *)popen("who am i","r");
570         if (who==NULL) {
571                 strcpy(hbuf,serv_info.serv_fqdn);
572                 return; 
573                 }
574         fgets(buf,256,who);
575         pclose(who);
576
577         b = 0;
578         for (a=0; a<strlen(buf); ++a) {
579                 if ((buf[a]=='(')||(buf[a]==')')) ++b;
580                 }
581         if (b<2) {
582                 strcpy(hbuf,serv_info.serv_fqdn);
583                 return;
584                 }
585
586         for (a=0; a<strlen(buf); ++a) {
587                 if (buf[a]=='(') {
588                         strcpy(buf,&buf[a+1]);
589                         }
590                 }
591         for (a=0; a<strlen(buf); ++a) {
592                 if (buf[a]==')') buf[a] = 0;
593                 }
594
595         if (strlen(buf)==0) strcpy(hbuf,serv_info.serv_fqdn);
596         else strncpy(hbuf,buf,24);
597         }
598
599 /*
600  * miscellaneous server commands (testing, etc.)
601  */
602 void misc_server_cmd(char *cmd) {
603         char buf[256];
604
605         serv_puts(cmd);
606         serv_gets(buf);
607         printf("%s\n",buf);
608         if (buf[0]=='1') {
609                 while (serv_gets(buf), strcmp(buf,"000")) {
610                         printf("%s\n",buf);
611                         }
612                 return;
613                 }
614         if (buf[0]=='4') {
615                 do {
616                         newprompt("> ",buf,255);
617                         serv_puts(buf);
618                         } while(strcmp(buf,"000"));
619                 return;
620                 }
621         }
622
623
624 /*
625  * compute the checksum of a file
626  */
627 int file_checksum(filename)
628 char *filename; {
629         int cksum = 0;
630         int ch;
631         FILE *fp;
632
633         fp = fopen(filename,"r");
634         if (fp == NULL) return(0);
635
636         /* yes, this algorithm may allow cksum to overflow, but that's ok
637          * as long as it overflows consistently, which it will.
638          */
639         while (ch=getc(fp), ch>=0) {
640                 cksum = (cksum + ch);
641                 }
642
643         fclose(fp);
644         return(cksum);
645         }
646
647 /*
648  * nuke a directory and its contents
649  */
650 int nukedir(dirname)
651 char *dirname; {
652         DIR *dp;
653         struct dirent *d;
654         char filename[256];
655
656         dp = opendir(dirname);
657         if (dp == NULL) {
658                 return(errno);
659                 }
660
661         while (d = readdir(dp), d != NULL) {
662                 sprintf(filename, "%s/%s", dirname, d->d_name);
663                 unlink(filename);
664                 }
665
666         closedir(dp);
667         return(rmdir(dirname));
668         }