client-move: edits to finish this.
[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 #ifndef HAVE_STRERROR
437 /*
438  * replacement strerror() for systems that don't have it
439  */
440 char *strerror(int e)
441 {
442         static char buf[128];
443
444         snprintf(buf, sizeof buf, "errno = %d",e);
445         return(buf);
446 }
447 #endif
448
449
450 void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax)
451 {
452         static char dots[] =
453                 "**************************************************";
454         char dots_printed[51];
455         char fmt[42];
456         unsigned long a;
457
458         if (curr >= cmax) {
459                 scr_printf("\r%79s\r","");
460         } else {
461                 /* a will be range 0-50 rather than 0-100 */
462                 a=(curr * 50) / cmax;
463                 sprintf(fmt, "[%%s%%%lds] %%3ld%%%% %%10ld/%%10ld\r", 50 - a);
464                 strncpy(dots_printed, dots, a);
465                 dots_printed[a] = 0;
466                 scr_printf(fmt, dots_printed, "",
467                                 curr * 100 / cmax, curr, cmax);
468                 scr_flush();
469         }
470 }
471
472
473 /*
474  * NOT the same locate_host() in locate_host.c.  This one just does a
475  * 'who am i' to try to discover where the user is...
476  */
477 void locate_host(CtdlIPC* ipc, char *hbuf)
478 {
479 #ifndef HAVE_UTMP_H
480         char buf[SIZ];
481         FILE *who;
482         int a,b;
483
484         who = (FILE *)popen("who am i","r");
485         if (who==NULL) {
486                 strcpy(hbuf, ipc->ServInfo.fqdn);
487                 return; 
488         }
489         fgets(buf,sizeof buf,who);
490         pclose(who);
491
492         b = 0;
493         for (a=0; !IsEmptyStr(&buf[a]); ++a) {
494                 if ((buf[a]=='(')||(buf[a]==')')) ++b;
495         }
496         if (b<2) {
497                 strcpy(hbuf, ipc->ServInfo.fqdn);
498                 return;
499         }
500
501         for (a=0; a<strlen(buf); ++a) {
502                 if (buf[a]=='(') {
503                         strcpy(buf,&buf[a+1]);
504                 }
505         }
506         for (a=0; a<strlen(buf); ++a) {
507                 if (buf[a]==')') buf[a] = 0;
508         }
509
510         if (IsEmptyStr(buf)) strcpy(hbuf, ipc->ServInfo.fqdn);
511         else strncpy(hbuf,buf,24);
512 #else
513         char *tty = ttyname(0);
514 #ifdef HAVE_GETUTXLINE
515         struct utmpx ut, *put;
516 #else
517         struct utmp ut, *put;
518 #endif
519
520         if (tty == NULL) {
521             fail:
522                 safestrncpy(hbuf, ipc->ServInfo.fqdn, 24);
523                 return;
524         }
525
526         if (strncmp(tty, "/dev/", 5))
527                 goto fail;
528
529         safestrncpy(ut.ut_line, &tty[5], sizeof ut.ut_line);
530
531 #ifdef HAVE_GETUTXLINE /* Solaris uses this */
532         if ((put = getutxline(&ut)) == NULL)
533 #else
534         if ((put = getutline(&ut)) == NULL)
535 #endif
536                 goto fail;
537
538 #if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
539         if (put->ut_type == USER_PROCESS) {
540 #endif
541 #if defined(HAVE_UT_HOST) || defined(HAVE_GETUTXLINE)
542                 if (*put->ut_host)
543                         safestrncpy(hbuf, put->ut_host, 24);
544                 else
545 #endif
546                         safestrncpy(hbuf, put->ut_line, 24);
547 #if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
548         }
549         else goto fail;
550 #endif
551 #endif /* HAVE_UTMP_H */
552 }
553
554 /*
555  * miscellaneous server commands (testing, etc.)
556  */
557 void misc_server_cmd(CtdlIPC *ipc, char *cmd) {
558         char buf[SIZ];
559
560         CtdlIPC_chat_send(ipc, cmd);
561         CtdlIPC_chat_recv(ipc, buf);
562         scr_printf("%s\n",buf);
563         if (buf[0]=='1') {
564                 set_keepalives(KA_HALF);
565                 while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf,"000")) {
566                         scr_printf("%s\n",buf);
567                 }
568                 set_keepalives(KA_YES);
569                 return;
570         }
571         if (buf[0]=='4') {
572                 do {
573                         newprompt("> ",buf,255);
574                         CtdlIPC_chat_send(ipc, buf);
575                 } while(strcmp(buf,"000"));
576                 return;
577         }
578 }
579
580
581 /*
582  * compute the checksum of a file
583  */
584 int file_checksum(char *filename)
585 {
586         int cksum = 0;
587         int ch;
588         FILE *fp;
589
590         fp = fopen(filename,"r");
591         if (fp == NULL) return(0);
592
593         /* yes, this algorithm may allow cksum to overflow, but that's ok
594          * as long as it overflows consistently, which it will.
595          */
596         while (ch=getc(fp), ch>=0) {
597                 cksum = (cksum + ch);
598         }
599
600         fclose(fp);
601         return(cksum);
602 }
603
604 /*
605  * nuke a directory and its contents
606  */
607 int nukedir(char *dirname)
608 {
609         DIR *dp;
610         struct dirent *d;
611         char filename[SIZ];
612
613         dp = opendir(dirname);
614         if (dp == NULL) {
615                 return(errno);
616         }
617
618         while (d = readdir(dp), d != NULL) {
619                 snprintf(filename, sizeof filename, "%s/%s",
620                         dirname, d->d_name);
621                 unlink(filename);
622         }
623
624         closedir(dp);
625         return(rmdir(dirname));
626 }