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