Removed the USscreenwidth and USscreenheight fields. Everywhere. Since these were...
[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                 user->flags = set_attr(ipc, user->flags,
230                                        "Are you an experienced Citadel user",
231                                        US_EXPERT, 0);
232                 if ((user->flags & US_EXPERT) == 0 && mode == 1) {
233                         free(user);
234                         return;
235                 }
236
237                 user->flags = set_attr(
238                         ipc,
239                         user->flags,
240                         "Print last old message on New message request",
241                         US_LASTOLD,
242                         0
243                 );
244
245                 user->flags = set_attr(
246                         ipc,
247                         user->flags,
248                         "Prompt after each message",
249                         US_NOPROMPT,
250                         1
251                 );
252
253                 if ((user->flags & US_NOPROMPT) == 0) {
254                         user->flags = set_attr(
255                                 ipc,
256                                 user->flags,
257                                 "Use 'disappearing' prompts",
258                                 US_DISAPPEAR,
259                                 0
260                         );
261                 }
262
263                 user->flags = set_attr(
264                         ipc,
265                         user->flags,
266                         "Pause after each screenful of text",
267                         US_PAGINATOR,
268                         0
269                 );
270
271                 if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR)) {
272                         user->flags = set_attr(
273                                 ipc,
274                                 user->flags,
275                                 "<N>ext and <S>top work at paginator prompt",
276                                 US_PROMPTCTL,
277                                 0
278                         );
279                 }
280
281                 if (rc_floor_mode == RC_DEFAULT) {
282                         user->flags = set_attr(
283                                 ipc,
284                                 user->flags,
285                                 "View rooms by floor",
286                                 US_FLOORS,
287                                 0
288                         );
289                 }
290
291                 if (rc_ansi_color == 3) {
292                         user->flags = set_attr(
293                                 ipc,
294                                 user->flags,
295                                 "Enable color support",
296                                 US_COLOR,
297                                 0
298                         );
299                 }
300
301                 if ((user->flags & US_EXPERT) == 0) {
302                         formout(ipc, "unlisted");
303                 }
304
305                 user->flags = set_attr(
306                         ipc,
307                         user->flags,
308                         "Be unlisted in userlog",
309                         US_UNLISTED,
310                         0
311                 );
312
313                 if (!IsEmptyStr(editor_paths[0])) {
314                         user->flags = set_attr(
315                                 ipc,
316                                 user->flags,
317                                 "Always enter messages with the full-screen editor",
318                                 US_EXTEDIT,
319                                 0
320                         );
321                 }
322
323         }
324
325         if (mode == 2) {
326                 if (user->flags & US_EXPERT) {
327                         user->flags ^= US_EXPERT;
328                         scr_printf("Expert mode now OFF\n");
329                 } else {
330                         user->flags |= US_EXPERT;
331                         scr_printf("Expert mode now ON\n");
332                 }
333         }
334
335         if (mode == 3) {
336                 if (user->flags & US_FLOORS) {
337                         user->flags ^= US_FLOORS;
338                         scr_printf("Floor mode now OFF\n");
339                 } else {
340                         user->flags |= US_FLOORS;
341                         scr_printf("Floor mode now ON\n");
342                 }
343         }
344
345         r = CtdlIPCSetConfig(ipc, user, buf);
346         if (r / 100 != 2) scr_printf("%s\n", buf);
347         userflags = user->flags;
348         free(user);
349 }
350
351 /*
352  * getstring()  -  get a line of text from a file
353  *                 ignores lines beginning with "#"
354  */
355 int getstring(FILE *fp, char *string)
356 {
357         int a,c;
358         do {
359                 strcpy(string,"");
360                 a=0;
361                 do {
362                         c=getc(fp);
363                         if (c<0) {
364                                 string[a]=0;
365                                 return(-1);
366                         }
367                         string[a++]=c;
368                 } while(c!=10);
369                         string[a-1]=0;
370         } while(string[0]=='#');
371         return(strlen(string));
372 }
373
374
375 /* Searches for patn in search string */
376 int pattern(char *search, char *patn) {
377         int a,b,len;
378         
379         len = strlen(patn);
380         for (a=0; !IsEmptyStr(&search[a]); ++a) {
381                 b=strncasecmp(&search[a],patn,len);
382                 if (b==0) return(b);
383         }
384         return(-1);
385 }
386
387
388 void strproc(char *string)
389 {
390         int a;
391
392         if (IsEmptyStr(string)) return;
393
394         /* Convert non-printable characters to blanks */
395         for (a=0; !IsEmptyStr(&string[a]); ++a) {
396                 if (string[a]<32) string[a]=32;
397                 if (string[a]>126) string[a]=32;
398         }
399
400         /* Remove leading and trailing blanks */
401         while(string[0]<33) strcpy(string,&string[1]);
402         while(string[strlen(string)-1]<33) string[strlen(string)-1]=0;
403
404         /* Remove double blanks */
405         for (a=0; a<strlen(string); ++a) {
406                 if ((string[a]==32)&&(string[a+1]==32)) {
407                         strcpy(&string[a],&string[a+1]);
408                         a=0;
409                 }
410         }
411
412         /* remove characters which would interfere with the network */
413         for (a=0; a<strlen(string); ++a) {
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                 if (string[a]==',') strcpy(&string[a],&string[a+1]);
418                 if (string[a]=='%') strcpy(&string[a],&string[a+1]);
419                 if (string[a]=='|') strcpy(&string[a],&string[a+1]);
420         }
421
422 }
423
424
425 #ifndef HAVE_STRERROR
426 /*
427  * replacement strerror() for systems that don't have it
428  */
429 char *strerror(int e)
430 {
431         static char buf[128];
432
433         snprintf(buf, sizeof buf, "errno = %d",e);
434         return(buf);
435 }
436 #endif
437
438
439 void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax)
440 {
441         static char dots[] =
442                 "**************************************************";
443         char dots_printed[51];
444         char fmt[42];
445         unsigned long a;
446
447         if (curr >= cmax) {
448                 scr_printf("\r%79s\r","");
449         } else {
450                 /* a will be range 0-50 rather than 0-100 */
451                 a=(curr * 50) / cmax;
452                 sprintf(fmt, "[%%s%%%lds] %%3ld%%%% %%10ld/%%10ld\r", 50 - a);
453                 strncpy(dots_printed, dots, a);
454                 dots_printed[a] = 0;
455                 scr_printf(fmt, dots_printed, "",
456                                 curr * 100 / cmax, curr, cmax);
457                 scr_flush();
458         }
459 }
460
461
462 /*
463  * NOT the same locate_host() in locate_host.c.  This one just does a
464  * 'who am i' to try to discover where the user is...
465  */
466 void locate_host(CtdlIPC* ipc, char *hbuf)
467 {
468 #ifndef HAVE_UTMP_H
469         char buf[SIZ];
470         FILE *who;
471         int a,b;
472
473         who = (FILE *)popen("who am i","r");
474         if (who==NULL) {
475                 strcpy(hbuf, ipc->ServInfo.fqdn);
476                 return; 
477         }
478         fgets(buf,sizeof buf,who);
479         pclose(who);
480
481         b = 0;
482         for (a=0; !IsEmptyStr(&buf[a]); ++a) {
483                 if ((buf[a]=='(')||(buf[a]==')')) ++b;
484         }
485         if (b<2) {
486                 strcpy(hbuf, ipc->ServInfo.fqdn);
487                 return;
488         }
489
490         for (a=0; a<strlen(buf); ++a) {
491                 if (buf[a]=='(') {
492                         strcpy(buf,&buf[a+1]);
493                 }
494         }
495         for (a=0; a<strlen(buf); ++a) {
496                 if (buf[a]==')') buf[a] = 0;
497         }
498
499         if (IsEmptyStr(buf)) strcpy(hbuf, ipc->ServInfo.fqdn);
500         else strncpy(hbuf,buf,24);
501 #else
502         char *tty = ttyname(0);
503 #ifdef HAVE_GETUTXLINE
504         struct utmpx ut, *put;
505 #else
506         struct utmp ut, *put;
507 #endif
508
509         if (tty == NULL) {
510             fail:
511                 safestrncpy(hbuf, ipc->ServInfo.fqdn, 24);
512                 return;
513         }
514
515         if (strncmp(tty, "/dev/", 5))
516                 goto fail;
517
518         safestrncpy(ut.ut_line, &tty[5], sizeof ut.ut_line);
519
520 #ifdef HAVE_GETUTXLINE /* Solaris uses this */
521         if ((put = getutxline(&ut)) == NULL)
522 #else
523         if ((put = getutline(&ut)) == NULL)
524 #endif
525                 goto fail;
526
527 #if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
528         if (put->ut_type == USER_PROCESS) {
529 #endif
530 #if defined(HAVE_UT_HOST) || defined(HAVE_GETUTXLINE)
531                 if (*put->ut_host)
532                         safestrncpy(hbuf, put->ut_host, 24);
533                 else
534 #endif
535                         safestrncpy(hbuf, put->ut_line, 24);
536 #if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
537         }
538         else goto fail;
539 #endif
540 #endif /* HAVE_UTMP_H */
541 }
542
543 /*
544  * miscellaneous server commands (testing, etc.)
545  */
546 void misc_server_cmd(CtdlIPC *ipc, char *cmd) {
547         char buf[SIZ];
548
549         CtdlIPC_chat_send(ipc, cmd);
550         CtdlIPC_chat_recv(ipc, buf);
551         scr_printf("%s\n",buf);
552         if (buf[0]=='1') {
553                 set_keepalives(KA_HALF);
554                 while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf,"000")) {
555                         scr_printf("%s\n",buf);
556                 }
557                 set_keepalives(KA_YES);
558                 return;
559         }
560         if (buf[0]=='4') {
561                 do {
562                         newprompt("> ",buf,255);
563                         CtdlIPC_chat_send(ipc, buf);
564                 } while(strcmp(buf,"000"));
565                 return;
566         }
567 }
568
569
570 /*
571  * compute the checksum of a file
572  */
573 int file_checksum(char *filename)
574 {
575         int cksum = 0;
576         int ch;
577         FILE *fp;
578
579         fp = fopen(filename,"r");
580         if (fp == NULL) return(0);
581
582         /* yes, this algorithm may allow cksum to overflow, but that's ok
583          * as long as it overflows consistently, which it will.
584          */
585         while (ch=getc(fp), ch>=0) {
586                 cksum = (cksum + ch);
587         }
588
589         fclose(fp);
590         return(cksum);
591 }
592
593 /*
594  * nuke a directory and its contents
595  */
596 int nukedir(char *dirname)
597 {
598         DIR *dp;
599         struct dirent *d;
600         char filename[SIZ];
601
602         dp = opendir(dirname);
603         if (dp == NULL) {
604                 return(errno);
605         }
606
607         while (d = readdir(dp), d != NULL) {
608                 snprintf(filename, sizeof filename, "%s/%s",
609                         dirname, d->d_name);
610                 unlink(filename);
611         }
612
613         closedir(dp);
614         return(rmdir(dirname));
615 }