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