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