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