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