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