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