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