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