903fe9ce162ae703e474df7a87e31f3c29333b65
[citadel.git] / citadel / routines.c
1 /* Citadel/UX support routines */
2 /* $Id$ */
3
4 #include "sysdep.h"
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <stdio.h>
9 #include <ctype.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/ioctl.h>
13 #include <pwd.h>
14 #include <signal.h>
15 #include <dirent.h>
16 #include <errno.h>
17 #include <time.h>
18 #include <limits.h>
19 #ifdef HAVE_UTMP_H
20 #include <utmp.h>
21 #endif
22
23 #ifndef HAVE_GETUTLINE
24 struct utmp *getutline(struct utmp *ut);
25 #endif
26
27 #define ROUTINES_C
28
29 #include "citadel.h"
30 #include "routines.h"
31 #include "commands.h"
32 #include "tools.h"
33
34 void sttybbs(int cmd);
35 void newprompt(char *prompt, char *str, int len);
36 void val_user(char *, int);
37 void formout(char *name);
38 void logoff(int code);
39 void set_keepalives(int s);
40 void strprompt(char *prompt, char *str, int len);
41 void newprompt(char *prompt, char *str, int len);
42 void color(int colornum);
43
44 #define IFAIDE if(axlevel>=6)
45 #define IFNAIDE if (axlevel<6)
46
47 extern unsigned userflags;
48 extern char *axdefs[7];
49 extern char sigcaught;
50 extern struct CtdlServInfo serv_info;
51 extern char rc_floor_mode;
52 extern int rc_ansi_color;
53
54 int struncmp(char *lstr, char *rstr, int len)
55 {
56         int pos = 0;
57         char lc,rc;
58         while (pos<len) {
59                 lc=tolower(lstr[pos]);
60                 rc=tolower(rstr[pos]);
61                 if ((lc==0)&&(rc==0)) return(0);
62                 if (lc<rc) return(-1);
63                 if (lc>rc) return(1);
64                 pos=pos+1;
65                 }
66         return(0);
67         }
68
69
70
71 void back(int spaces) /* Destructive backspace */
72             {
73 int a;
74         for (a=1; a<=spaces; ++a) {
75                 putc(8,stdout); putc(32,stdout); putc(8,stdout);
76                 }
77         }
78
79 void hit_any_key(void) {                /* hit any key to continue */
80         int a,b;
81
82         color(COLOR_PUSH);
83         color(DIM_RED);
84         printf("%s\r",serv_info.serv_moreprompt);
85         color(COLOR_POP);
86         sttybbs(0);
87         b=inkey();
88         for (a=0; a<strlen(serv_info.serv_moreprompt); ++a)
89                 putc(' ',stdout);
90         putc(13,stdout);
91         sttybbs(1);
92         if (b==NEXT_KEY) sigcaught = SIGINT;
93         if (b==STOP_KEY) sigcaught = SIGQUIT;
94         }
95
96 /*
97  * change a user's access level
98  */
99 void edituser(void)
100 {
101         char buf[256];
102         char who[256];
103         char pass[256];
104         int flags;
105         int timescalled;
106         int posted;
107         int axlevel;
108         long usernum;
109         time_t lastcall;
110         int userpurge;
111
112         newprompt("User name: ",who,25);
113         sprintf(buf,"AGUP %s",who);
114         serv_puts(buf);
115         serv_gets(buf);
116         if (buf[0]!='2') {
117                 printf("%s\n",&buf[4]);
118                 return;
119                 }
120         extract(who, &buf[4], 0);
121         extract(pass, &buf[4], 1);
122         flags = extract_int(&buf[4], 2);
123         timescalled = extract_int(&buf[4], 3);
124         posted = extract_int(&buf[4], 4);
125         axlevel = extract_int(&buf[4], 5);
126         usernum = extract_long(&buf[4], 6);
127         lastcall = extract_long(&buf[4], 7);
128         userpurge = extract_int(&buf[4], 8);
129
130         val_user(who, 0); /* Display registration */
131         strprompt("Password", pass, 19);
132         axlevel = intprompt("Access level", axlevel, 0, 6);
133         timescalled = intprompt("Times called", timescalled, 0, INT_MAX);
134         posted = intprompt("Messages posted", posted, 0, INT_MAX);
135         lastcall = (boolprompt("Set last call to now", 0)?time(NULL):lastcall);
136         userpurge = intprompt("Purge time (in days, 0 for system default",
137                                 userpurge, 0, INT_MAX);
138
139         sprintf(buf, "ASUP %s|%s|%d|%d|%d|%d|%ld|%ld|%d",
140                 who, pass, flags, timescalled, posted, axlevel, usernum,
141                 (long)lastcall, userpurge);
142         serv_puts(buf);
143         serv_gets(buf);
144         if (buf[0]!='2') {
145                 printf("%s\n",&buf[4]);
146                 }
147         }
148
149
150 int set_attr(int sval, char *prompt, unsigned int sbit)
151 {
152         int a;
153         int temp;
154
155         temp = sval;
156         color(DIM_WHITE);
157         printf("%45s ", prompt);
158         color(DIM_MAGENTA);
159         printf("[");
160         color(BRIGHT_MAGENTA);
161         printf("%3s", ((temp&sbit) ? "Yes":"No"));
162         color(DIM_MAGENTA);
163         printf("]? ");
164         color(BRIGHT_CYAN);
165         a=yesno_d(temp&sbit);
166         color(DIM_WHITE);
167         temp=(temp|sbit);
168         if (!a) temp=(temp^sbit);
169         return(temp);
170         }
171
172 /*
173  * modes are:  0 - .EC command, 1 - .EC for new user,
174  *             2 - toggle Xpert mode  3 - toggle floor mode
175  */
176 void enter_config(int mode)
177 {
178         int width,height,flags;
179         char buf[128];
180
181         sprintf(buf,"GETU");
182         serv_puts(buf);
183         serv_gets(buf);
184         if (buf[0]!='2') {
185                 printf("%s\n",&buf[4]);
186                 return;
187                 }
188
189         width = extract_int(&buf[4],0);
190         height = extract_int(&buf[4],1);
191         flags = extract_int(&buf[4],2);
192
193         if ((mode==0)||(mode==1)) {
194
195          width = intprompt("Enter your screen width",width,20,255);
196          height = intprompt("Enter your screen height",height,3,255);
197  
198          flags = set_attr(flags,
199                 "Are you an experienced Citadel user",US_EXPERT);
200          if ( ((flags&US_EXPERT)==0) && (mode==1))
201                 return;
202          flags = set_attr(flags,
203                 "Print last old message on New message request",US_LASTOLD);
204          if ((flags&US_EXPERT)==0) formout("unlisted");
205          flags = set_attr(flags,"Be unlisted in userlog",US_UNLISTED);
206          flags = set_attr(flags,"Suppress message prompts",US_NOPROMPT);
207          if ((flags & US_NOPROMPT)==0)
208             flags = set_attr(flags,"Use 'disappearing' prompts",US_DISAPPEAR);
209          flags = set_attr(flags,
210                 "Pause after each screenful of text",US_PAGINATOR);
211          if (rc_floor_mode == RC_DEFAULT) {
212           flags = set_attr(flags,
213                 "View rooms by floor",US_FLOORS);
214           }
215          if (rc_ansi_color == 3) {
216           flags = set_attr(flags,
217                 "Enable color support",US_COLOR);
218           }
219          }
220
221         if (mode==2) {
222          if (flags & US_EXPERT) {
223                 flags = (flags ^ US_EXPERT);
224                 printf("Expert mode now OFF\n");
225                 }
226          else {
227                 flags = (flags | US_EXPERT);
228                 printf("Expert mode now ON\n");
229                 }
230          }
231
232         if (mode==3) {
233          if (flags & US_FLOORS) {
234                 flags = (flags ^ US_FLOORS);
235                 printf("Floor mode now OFF\n");
236                 }
237          else {
238                 flags = (flags | US_FLOORS);
239                 printf("Floor mode now ON\n");
240                 }
241          }
242
243         sprintf(buf,"SETU %d|%d|%d",width,height,flags);
244         serv_puts(buf);
245         serv_gets(buf);
246         if (buf[0]!='2') printf("%s\n",&buf[4]);
247         userflags = flags;
248 }
249
250 /*
251  * getstring()  -  get a line of text from a file
252  *                 ignores lines beginning with "#"
253  */
254 int getstring(FILE *fp, char *string)
255 {
256         int a,c;
257         do {
258                 strcpy(string,"");
259                 a=0;
260                 do {
261                         c=getc(fp);
262                         if (c<0) {
263                                 string[a]=0;
264                                 return(-1);
265                                 }
266                         string[a++]=c;
267                         } while(c!=10);
268                         string[a-1]=0;
269                 } while(string[0]=='#');
270         return(strlen(string));
271         }
272
273 int pattern(char *search, char *patn)   /* Searches for patn in search string */
274               
275             
276 {
277         int a,b;
278         for (a=0; a<strlen(search); ++a)
279         {       b=struncmp(&search[a],patn,strlen(patn));
280                 if (b==0) return(b);
281                 }
282         return(-1);
283 }
284
285 void interr(int errnum) /* display internal error as defined in errmsgs */
286             {
287         printf("*** INTERNAL ERROR %d\n",errnum);
288         printf("(Press any key to continue)\n");
289         inkey();
290         logoff(errnum);
291 }
292
293
294
295 /*
296  * Check to see if we need to pause at the end of a screen.
297  * If we do, we have to disable server keepalives during the pause because
298  * we are probably in the middle of a server operation and the NOOP command
299  * would confuse everything.
300  */
301 int checkpagin(int lp, int pagin, int height)
302 {
303         if (pagin!=1) return(0);
304         if (lp>=(height-1)) {
305                 set_keepalives(KA_NO);
306                 hit_any_key();
307                 set_keepalives(KA_YES);
308                 return(0);
309                 }
310         return(lp);
311         }
312
313
314 void strproc(char *string)
315 {
316         int a;
317
318         if (strlen(string)==0) return;
319
320         /* Convert non-printable characters to blanks */
321         for (a=0; a<strlen(string); ++a) {
322                 if (string[a]<32) string[a]=32;
323                 if (string[a]>126) string[a]=32;
324                 }
325
326         /* Remove leading and trailing blanks */
327         while(string[0]<33) strcpy(string,&string[1]);
328         while(string[strlen(string)-1]<33) string[strlen(string)-1]=0;
329
330         /* Remove double blanks */
331         for (a=0; a<strlen(string); ++a) {
332                 if ((string[a]==32)&&(string[a+1]==32)) {
333                         strcpy(&string[a],&string[a+1]);
334                         a=0;
335                         }
336                 }
337
338         /* remove characters which would interfere with the network */
339         for (a=0; a<strlen(string); ++a) {
340                 if (string[a]=='!') strcpy(&string[a],&string[a+1]);
341                 if (string[a]=='@') strcpy(&string[a],&string[a+1]);
342                 if (string[a]=='_') strcpy(&string[a],&string[a+1]);
343                 if (string[a]==',') strcpy(&string[a],&string[a+1]);
344                 if (string[a]=='%') strcpy(&string[a],&string[a+1]);
345                 if (string[a]=='|') strcpy(&string[a],&string[a+1]);
346                 }
347
348         }
349
350
351 #ifndef HAVE_STRERROR
352 /*
353  * replacement strerror() for systems that don't have it
354  */
355 char *strerror(int e)
356 {
357         static char buf[32];
358
359         sprintf(buf,"errno = %d",e);
360         return(buf);
361         }
362 #endif
363
364
365 void progress(long int curr, long int cmax)
366 {
367         static long dots_printed;
368         long a;
369
370         if (curr==0) {
371                 printf(".......................................");
372                 printf(".......................................\r");
373                 fflush(stdout);
374                 dots_printed = 0;
375                 }
376         else if (curr==cmax) {
377                 printf("\r%79s\n","");
378                 }
379         else {
380                 a=(curr * 100) / cmax;
381                 a=a*78; a=a/100;
382                 while (dots_printed < a) {
383                         printf("*");
384                         ++dots_printed;
385                         fflush(stdout);
386                         }
387                 }
388         }
389
390
391 /*
392  * NOT the same locate_host() in locate_host.c.  This one just does a
393  * 'who am i' to try to discover where the user is...
394  */
395 void locate_host(char *hbuf)
396 {
397 #ifndef HAVE_UTMP_H
398         char buf[256];
399         FILE *who;
400         int a,b;
401
402         who = (FILE *)popen("who am i","r");
403         if (who==NULL) {
404                 strcpy(hbuf,serv_info.serv_fqdn);
405                 return; 
406                 }
407         fgets(buf,256,who);
408         pclose(who);
409
410         b = 0;
411         for (a=0; a<strlen(buf); ++a) {
412                 if ((buf[a]=='(')||(buf[a]==')')) ++b;
413                 }
414         if (b<2) {
415                 strcpy(hbuf,serv_info.serv_fqdn);
416                 return;
417                 }
418
419         for (a=0; a<strlen(buf); ++a) {
420                 if (buf[a]=='(') {
421                         strcpy(buf,&buf[a+1]);
422                         }
423                 }
424         for (a=0; a<strlen(buf); ++a) {
425                 if (buf[a]==')') buf[a] = 0;
426                 }
427
428         if (strlen(buf)==0) strcpy(hbuf,serv_info.serv_fqdn);
429         else strncpy(hbuf,buf,24);
430 #else
431         char *tty = ttyname(0);
432         struct utmp ut, *put;
433
434         if (tty == NULL) {
435             fail:
436                 safestrncpy(hbuf, serv_info.serv_fqdn, 24);
437                 return;
438                 }
439
440         if (strncmp(tty, "/dev/", 5))
441                 goto fail;
442
443         safestrncpy(ut.ut_line, &tty[5], sizeof ut.ut_line);
444
445         if ((put = getutline(&ut)) == NULL)
446                 goto fail;
447
448 #ifdef HAVE_UT_TYPE
449         if (put->ut_type == USER_PROCESS) {
450 #endif
451 #ifdef HAVE_UT_HOST
452                 if (*put->ut_host)
453                         safestrncpy(hbuf, put->ut_host, 24);
454                 else
455 #endif
456                         safestrncpy(hbuf, put->ut_line, 24);
457 #ifdef HAVE_UT_TYPE
458                 }
459         else goto fail;
460 #endif
461 #endif /* HAVE_UTMP_H */
462         }
463
464 /*
465  * miscellaneous server commands (testing, etc.)
466  */
467 void misc_server_cmd(char *cmd) {
468         char buf[256];
469
470         serv_puts(cmd);
471         serv_gets(buf);
472         printf("%s\n",buf);
473         if (buf[0]=='1') {
474                 set_keepalives(KA_NO);
475                 while (serv_gets(buf), strcmp(buf,"000")) {
476                         printf("%s\n",buf);
477                         }
478                 set_keepalives(KA_YES);
479                 return;
480                 }
481         if (buf[0]=='4') {
482                 do {
483                         newprompt("> ",buf,255);
484                         serv_puts(buf);
485                         } while(strcmp(buf,"000"));
486                 return;
487                 }
488         }
489
490
491 /*
492  * compute the checksum of a file
493  */
494 int file_checksum(char *filename)
495 {
496         int cksum = 0;
497         int ch;
498         FILE *fp;
499
500         fp = fopen(filename,"r");
501         if (fp == NULL) return(0);
502
503         /* yes, this algorithm may allow cksum to overflow, but that's ok
504          * as long as it overflows consistently, which it will.
505          */
506         while (ch=getc(fp), ch>=0) {
507                 cksum = (cksum + ch);
508                 }
509
510         fclose(fp);
511         return(cksum);
512         }
513
514 /*
515  * nuke a directory and its contents
516  */
517 int nukedir(char *dirname)
518 {
519         DIR *dp;
520         struct dirent *d;
521         char filename[256];
522
523         dp = opendir(dirname);
524         if (dp == NULL) {
525                 return(errno);
526                 }
527
528         while (d = readdir(dp), d != NULL) {
529                 sprintf(filename, "%s/%s", dirname, d->d_name);
530                 unlink(filename);
531                 }
532
533         closedir(dp);
534         return(rmdir(dirname));
535         }