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