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