77f740b5c4c92940cf2e8a6233fabab8c17d399e
[citadel.git] / citadel / routines2.c
1 /* More Citadel/UX routines...
2  * unlike routines.c, some of these DO use global variables.
3  * $Id$
4  */
5
6 #include "sysdep.h"
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <time.h>
16 #include <signal.h>
17 #include <pwd.h>
18 #include <setjmp.h>
19 #include <errno.h>
20 #include <stdarg.h>
21 #include "citadel.h"
22 #include "routines2.h"
23 #include "routines.h"
24 #include "commands.h"
25 #include "tools.h"
26 #include "messages.h"
27 #ifndef HAVE_SNPRINTF
28 #include "snprintf.h"
29 #endif
30
31 void interr(int errnum);
32 void strprompt(char *prompt, char *str, int len);
33 void newprompt(char *prompt, char *str, int len);
34 void sttybbs(int cmd);
35 int inkey(void);
36 void serv_write(char *buf, int nbytes);
37 int haschar(char *st, int ch);
38 void progress(long int curr, long int cmax);
39 void citedit(FILE * fp, long int base_pos);
40 int yesno(void);
41
42 extern char temp[];
43 extern char tempdir[];
44 extern char *axdefs[7];
45 extern long highest_msg_read;
46 extern long maxmsgnum;
47 extern unsigned room_flags;
48 extern int screenwidth;
49
50
51 int eopen(char *name, int mode)
52 {
53         int ret;
54         ret = open(name, mode);
55         if (ret < 0) {
56                 fprintf(stderr, "Cannot open file '%s', mode=%d, errno=%d\n",
57                         name, mode, errno);
58                 interr(errno);
59         }
60         return (ret);
61 }
62
63
64 int room_prompt(int qrflags)
65 {                               /* return proper room prompt character */
66         int a;
67         a = '>';
68         if (qrflags & QR_DIRECTORY)
69                 a = ']';
70         if ((a == ']') && (qrflags & QR_NETWORK))
71                 a = '}';
72         if ((a == '>') && (qrflags & QR_NETWORK))
73                 a = ')';
74         return (a);
75 }
76
77 void entregis(void)
78 {                               /* register with name and address */
79
80         char buf[256];
81         char tmpname[256];
82         char tmpaddr[256];
83         char tmpcity[256];
84         char tmpstate[256];
85         char tmpzip[256];
86         char tmpphone[256];
87         char tmpemail[256];
88         int a;
89
90         strcpy(tmpname, "");
91         strcpy(tmpaddr, "");
92         strcpy(tmpcity, "");
93         strcpy(tmpstate, "");
94         strcpy(tmpzip, "");
95         strcpy(tmpphone, "");
96         strcpy(tmpemail, "");
97
98         serv_puts("GREG _SELF_");
99         serv_gets(buf);
100         if (buf[0] == '1') {
101                 a = 0;
102                 while (serv_gets(buf), strcmp(buf, "000")) {
103                         if (a == 2)
104                                 strcpy(tmpname, buf);
105                         if (a == 3)
106                                 strcpy(tmpaddr, buf);
107                         if (a == 4)
108                                 strcpy(tmpcity, buf);
109                         if (a == 5)
110                                 strcpy(tmpstate, buf);
111                         if (a == 6)
112                                 strcpy(tmpzip, buf);
113                         if (a == 7)
114                                 strcpy(tmpphone, buf);
115                         if (a == 9)
116                                 strcpy(tmpemail, buf);
117                         ++a;
118                 }
119         }
120         strprompt("REAL name", tmpname, 29);
121         strprompt("Address", tmpaddr, 24);
122         strprompt("City/town", tmpcity, 14);
123         strprompt("State", tmpstate, 2);
124         strprompt("ZIP Code", tmpzip, 10);
125         strprompt("Telephone number", tmpphone, 14);
126         strprompt("Email address", tmpemail, 31);
127
128         /* now send the registration info back to the server */
129         serv_puts("REGI");
130         serv_gets(buf);
131         if (buf[0] != '4') {
132                 printf("%s\n", &buf[4]);
133                 return;
134         }
135         serv_puts(tmpname);
136         serv_puts(tmpaddr);
137         serv_puts(tmpcity);
138         serv_puts(tmpstate);
139         serv_puts(tmpzip);
140         serv_puts(tmpphone);
141         serv_puts(tmpemail);
142         serv_puts("000");
143         printf("\n");
144 }
145
146 void updatels(void)
147 {                               /* make all messages old in current room */
148         char buf[256];
149         serv_puts("SLRP HIGHEST");
150         serv_gets(buf);
151         if (buf[0] != '2')
152                 printf("%s\n", &buf[4]);
153 }
154
155 /*
156  * only make messages old in this room that have been read
157  */
158 void updatelsa(void)
159 {
160         char buf[256];
161         sprintf(buf, "SLRP %ld", highest_msg_read);
162         serv_puts(buf);
163         serv_gets(buf);
164         if (buf[0] != '2')
165                 printf("%s\n", &buf[4]);
166 }
167
168
169 /*
170  * This routine completes a client upload
171  */
172 void do_upload(int fd)
173 {
174         char buf[256];
175         char tbuf[4096];
176         long transmitted_bytes, total_bytes;
177         int bytes_to_send;
178         int bytes_expected;
179
180         /* learn the size of the file */
181         total_bytes = lseek(fd, 0L, 2);
182         lseek(fd, 0L, 0);
183
184         transmitted_bytes = 0L;
185         progress(transmitted_bytes, total_bytes);
186         do {
187                 bytes_to_send = read(fd, tbuf, 4096);
188                 if (bytes_to_send > 0) {
189                         sprintf(buf, "WRIT %d", bytes_to_send);
190                         serv_puts(buf);
191                         serv_gets(buf);
192                         if (buf[0] == '7') {
193                                 bytes_expected = atoi(&buf[4]);
194                                 serv_write(tbuf, bytes_expected);
195                         } else {
196                                 printf("%s\n", &buf[4]);
197                         }
198                 }
199                 transmitted_bytes = transmitted_bytes + (long) bytes_to_send;
200                 progress(transmitted_bytes, total_bytes);
201         } while (bytes_to_send > 0);
202
203         /* close the upload file, locally and at the server */
204         close(fd);
205         serv_puts("UCLS 1");
206         serv_gets(buf);
207         printf("%s\n", &buf[4]);
208 }
209
210
211 /*
212  * client-based uploads (for users with their own clientware)
213  */
214 void cli_upload(void)
215 {
216         char flnm[256];
217         char desc[151];
218         char buf[256];
219         char tbuf[256];
220         int a;
221         int fd;
222
223         if ((room_flags & QR_UPLOAD) == 0) {
224                 printf("*** You cannot upload to this room.\n");
225                 return;
226         }
227         newprompt("File to be uploaded: ", flnm, 55);
228         fd = open(flnm, O_RDONLY);
229         if (fd < 0) {
230                 printf("Cannot open '%s': %s\n", flnm, strerror(errno));
231                 return;
232         }
233         printf("Enter a description of this file:\n");
234         newprompt(": ", desc, 75);
235
236         /* keep generating filenames in hope of finding a unique one */
237         a = 0;
238         do {
239                 if (a == 10)
240                         return; /* fail if tried 10 times */
241                 strcpy(buf, flnm);
242                 while ((strlen(buf) > 0) && (haschar(buf, '/')))
243                         strcpy(buf, &buf[1]);
244                 if (a > 0)
245                         sprintf(&buf[strlen(buf)], "%d", a);
246                 sprintf(tbuf, "UOPN %s|%s", buf, desc);
247                 serv_puts(tbuf);
248                 serv_gets(buf);
249                 if (buf[0] != '2')
250                         printf("%s\n", &buf[4]);
251                 ++a;
252         } while (buf[0] != '2');
253
254         /* at this point we have an open upload file at the server */
255         do_upload(fd);
256 }
257
258
259 /*
260  * Function used for various image upload commands
261  */
262 void cli_image_upload(char *keyname)
263 {
264         char flnm[256];
265         char buf[256];
266         int fd;
267
268         sprintf(buf, "UIMG 0|%s", keyname);
269         serv_puts(buf);
270         serv_gets(buf);
271         if (buf[0] != '2') {
272                 printf("%s\n", &buf[4]);
273                 return;
274         }
275         newprompt("Image file to be uploaded: ", flnm, 55);
276         fd = open(flnm, O_RDONLY);
277         if (fd < 0) {
278                 printf("Cannot open '%s': %s\n", flnm, strerror(errno));
279                 return;
280         }
281         sprintf(buf, "UIMG 1|%s", keyname);
282         serv_puts(buf);
283         serv_gets(buf);
284         if (buf[0] != '2') {
285                 printf("%s\n", &buf[4]);
286                 return;
287         }
288         do_upload(fd);
289 }
290
291
292 /*
293  * protocol-based uploads (Xmodem, Ymodem, Zmodem)
294  */
295 void upload(int c)
296 {                               /* c = upload mode */
297         char flnm[256];
298         char desc[151];
299         char buf[256];
300         char tbuf[4096];
301         int xfer_pid;
302         int a, b;
303         FILE *fp, *lsfp;
304         int fd;
305
306         if ((room_flags & QR_UPLOAD) == 0) {
307                 printf("*** You cannot upload to this room.\n");
308                 return;
309         }
310         /* we don't need a filename when receiving batch y/z modem */
311         if ((c == 2) || (c == 3))
312                 strcpy(flnm, "x");
313         else
314                 newprompt("Enter filename: ", flnm, 15);
315
316         for (a = 0; a < strlen(flnm); ++a)
317                 if ((flnm[a] == '/') || (flnm[a] == '\\') || (flnm[a] == '>')
318                     || (flnm[a] == '?') || (flnm[a] == '*')
319                     || (flnm[a] == ';') || (flnm[a] == '&'))
320                         flnm[a] = '_';
321
322         newprompt("Enter a short description of the file:\n: ", desc, 150);
323
324         /* create a temporary directory... */
325         if (mkdir(tempdir, 0700) != 0) {
326                 printf("*** Could not create temporary directory %s: %s\n",
327                        tempdir, strerror(errno));
328                 return;
329         }
330         /* now do the transfer ... in a separate process */
331         xfer_pid = fork();
332         if (xfer_pid == 0) {
333                 chdir(tempdir);
334                 switch (c) {
335                 case 0:
336                         sttybbs(0);
337                         printf("Receiving %s - press Ctrl-D to end.\n", flnm);
338                         fp = fopen(flnm, "w");
339                         do {
340                                 b = inkey();
341                                 if (b == 13) {
342                                         b = 10;
343                                         printf("\r");
344                                 }
345                                 if (b != 4) {
346                                         printf("%c", b);
347                                         putc(b, fp);
348                                 }
349                         } while (b != 4);
350                         fclose(fp);
351                         exit(0);
352                 case 1:
353                         sttybbs(3);
354                         execlp("rx", "rx", flnm, NULL);
355                         exit(1);
356                 case 2:
357                         sttybbs(3);
358                         execlp("rb", "rb", NULL);
359                         exit(1);
360                 case 3:
361                         sttybbs(3);
362                         execlp("rz", "rz", NULL);
363                         exit(1);
364                 }
365         } else
366                 do {
367                         b = ka_wait(&a);
368                 } while ((b != xfer_pid) && (b != (-1)));
369         sttybbs(0);
370
371         if (a != 0) {
372                 printf("\r*** Transfer unsuccessful.\n");
373                 nukedir(tempdir);
374                 return;
375         }
376         printf("\r*** Transfer successful.  Sending file(s) to server...\n");
377         sprintf(buf, "cd %s; ls", tempdir);
378         lsfp = popen(buf, "r");
379         if (lsfp != NULL) {
380                 while (fgets(flnm, 256, lsfp) != NULL) {
381                         flnm[strlen(flnm) - 1] = 0;
382                         sprintf(buf, "%s/%s", tempdir, flnm);
383                         fd = open(buf, O_RDONLY);
384                         if (fd >= 0) {
385                                 a = 0;
386                                 do {
387                                         sprintf(buf, "UOPN %s|%s", flnm, desc);
388                                         if (a > 0)
389                                                 sprintf(&buf[strlen(buf)],
390                                                         ".%d", a);
391                                         ++a;
392                                         serv_puts(buf);
393                                         serv_gets(buf);
394                                 } while ((buf[0] != '2') && (a < 100));
395                                 if (buf[0] == '2')
396                                         do {
397                                                 a = read(fd, tbuf, 4096);
398                                                 if (a > 0) {
399                                                         sprintf(buf, "WRIT %d", a);
400                                                         serv_puts(buf);
401                                                         serv_gets(buf);
402                                                         if (buf[0] == '7')
403                                                                 serv_write(tbuf, a);
404                                                 }
405                                         } while (a > 0);
406                                 close(fd);
407                                 serv_puts("UCLS 1");
408                                 serv_gets(buf);
409                                 printf("%s\n", &buf[4]);
410                         }
411                 }
412                 pclose(lsfp);
413         }
414         nukedir(tempdir);
415 }
416
417 /* 
418  * validate a user
419  */
420 void val_user(char *user, int do_validate)
421 {
422         int a;
423         char cmd[256];
424         char buf[256];
425         int ax = 0;
426
427         sprintf(cmd, "GREG %s", user);
428         serv_puts(cmd);
429         serv_gets(cmd);
430         if (cmd[0] == '1') {
431                 a = 0;
432                 do {
433                         serv_gets(buf);
434                         ++a;
435                         if (a == 1)
436                                 printf("User #%s - %s  ", buf, &cmd[4]);
437                         if (a == 2)
438                                 printf("PW: %s\n", buf);
439                         if (a == 3)
440                                 printf("%s\n", buf);
441                         if (a == 4)
442                                 printf("%s\n", buf);
443                         if (a == 5)
444                                 printf("%s, ", buf);
445                         if (a == 6)
446                                 printf("%s ", buf);
447                         if (a == 7)
448                                 printf("%s\n", buf);
449                         if (a == 8)
450                                 printf("%s\n", buf);
451                         if (a == 9)
452                                 ax = atoi(buf);
453                         if (a == 10)
454                                 printf("%s\n", buf);
455                 } while (strcmp(buf, "000"));
456                 printf("Current access level: %d (%s)\n", ax, axdefs[ax]);
457         } else {
458                 printf("%-30s\n%s\n", user, &cmd[4]);
459         }
460
461         if (do_validate) {
462                 /* now set the access level */
463                 ax = intprompt("Access level", ax, 0, 6);
464                 sprintf(cmd, "VALI %s|%d", user, ax);
465                 serv_puts(cmd);
466                 serv_gets(cmd);
467                 if (cmd[0] != '2')
468                         printf("%s\n", &cmd[4]);
469         }
470         printf("\n");
471 }
472
473
474 void validate(void)
475 {                               /* validate new users */
476         char cmd[256];
477         char buf[256];
478         int finished = 0;
479
480         do {
481                 serv_puts("GNUR");
482                 serv_gets(cmd);
483                 if (cmd[0] != '3')
484                         finished = 1;
485                 if (cmd[0] == '2')
486                         printf("%s\n", &cmd[4]);
487                 if (cmd[0] == '3') {
488                         extract(buf, cmd, 0);
489                         val_user(&buf[4], 1);
490                 }
491         } while (finished == 0);
492 }
493
494 void subshell(void)
495 {
496         int a, b;
497         a = fork();
498         if (a == 0) {
499                 sttybbs(SB_RESTORE);
500                 signal(SIGINT, SIG_DFL);
501                 signal(SIGQUIT, SIG_DFL);
502                 execlp(getenv("SHELL"), getenv("SHELL"), NULL);
503                 printf("Could not open a shell: %s\n", strerror(errno));
504                 exit(errno);
505         }
506         do {
507                 b = ka_wait(NULL);
508         } while ((a != b) && (a != (-1)));
509         sttybbs(0);
510 }
511
512 /*
513  * <.A>ide <F>ile <D>elete command
514  */
515 void deletefile(void)
516 {
517         char filename[32];
518         char cmd[256];
519
520         newprompt("Filename: ", filename, 31);
521         if (strlen(filename) == 0)
522                 return;
523         sprintf(cmd, "DELF %s", filename);
524         serv_puts(cmd);
525         serv_gets(cmd);
526         printf("%s\n", &cmd[4]);
527 }
528
529 /*
530  * <.A>ide <F>ile <S>end command
531  */
532 void netsendfile(void)
533 {
534         char filename[32], destsys[20], cmd[256];
535
536         newprompt("Filename: ", filename, 31);
537         if (strlen(filename) == 0)
538                 return;
539         newprompt("System to send to: ", destsys, 19);
540         sprintf(cmd, "NETF %s|%s", filename, destsys);
541         serv_puts(cmd);
542         serv_gets(cmd);
543         printf("%s\n", &cmd[4]);
544         return;
545 }
546
547 /*
548  * <.A>ide <F>ile <M>ove command
549  */
550 void movefile(void)
551 {
552         char filename[64];
553         char newroom[ROOMNAMELEN];
554         char cmd[256];
555
556         newprompt("Filename: ", filename, 63);
557         if (strlen(filename) == 0)
558                 return;
559         newprompt("Enter target room: ", newroom, ROOMNAMELEN - 1);
560
561         sprintf(cmd, "MOVF %s|%s", filename, newroom);
562         serv_puts(cmd);
563         serv_gets(cmd);
564         printf("%s\n", &cmd[4]);
565 }
566
567
568 /* 
569  * list of users who have filled out a bio
570  */
571 void list_bio(void)
572 {
573         char buf[256];
574         int pos = 1;
575
576         serv_puts("LBIO");
577         serv_gets(buf);
578         if (buf[0] != '1') {
579                 printf("%s\n", &buf[4]);
580                 return;
581         }
582         while (serv_gets(buf), strcmp(buf, "000")) {
583                 if ((pos + strlen(buf) + 5) > screenwidth) {
584                         printf("\n");
585                         pos = 1;
586                 }
587                 printf("%s, ", buf);
588                 pos = pos + strlen(buf) + 2;
589         }
590         printf("%c%c  \n\n", 8, 8);
591 }
592
593
594 /*
595  * read bio
596  */
597 void read_bio(void)
598 {
599         char who[256];
600         char buf[256];
601
602         do {
603                 newprompt("Read bio for who ('?' for list) : ", who, 25);
604                 printf("\n");
605                 if (!strcmp(who, "?"))
606                         list_bio();
607         } while (!strcmp(who, "?"));
608         sprintf(buf, "RBIO %s", who);
609         serv_puts(buf);
610         serv_gets(buf);
611         if (buf[0] != '1') {
612                 printf("%s\n", &buf[4]);
613                 return;
614         }
615         while (serv_gets(buf), strcmp(buf, "000")) {
616                 printf("%s\n", buf);
617         }
618 }
619
620
621 /* 
622  * General system configuration command
623  */
624 void do_system_configuration(void)
625 {
626         char buf[256];
627         char sc[21][256];
628         int expire_mode = 0;
629         int expire_value = 0;
630         int a;
631
632         /* Clear out the config buffers */
633         memset(&sc[0][0], 0, sizeof(sc));
634
635         /* Fetch the current config */
636         serv_puts("CONF get");
637         serv_gets(buf);
638         if (buf[0] == '1') {
639                 a = 0;
640                 while (serv_gets(buf), strcmp(buf, "000")) {
641                         if (a < 21)
642                                 strcpy(&sc[a][0], buf);
643                         ++a;
644                 }
645         }
646         /* Fetch the expire policy (this will silently fail on old servers,
647          * resulting in "default" policy)
648          */
649         serv_puts("GPEX site");
650         serv_gets(buf);
651         if (buf[0] == '2') {
652                 expire_mode = extract_int(&buf[4], 0);
653                 expire_value = extract_int(&buf[4], 1);
654         }
655         strprompt("Node name", &sc[0][0], 15);
656         strprompt("Fully qualified domain name", &sc[1][0], 63);
657         strprompt("Human readable node name", &sc[2][0], 20);
658         strprompt("Modem dialup number", &sc[3][0], 15);
659
660         sprintf(&sc[4][0], "%d", (boolprompt(
661                                                     "Automatically give room aide privs to a user who creates a private room",
662                                                     atoi(&sc[4][0]))));
663
664         strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
665         strprompt("Initial access level for new users", &sc[6][0], 1);
666         strprompt("Access level required to create rooms", &sc[19][0], 1);
667
668         sprintf(&sc[7][0], "%d", (boolprompt(
669                                     "Require registration for new users",
670                                                     atoi(&sc[7][0]))));
671
672         sprintf(&sc[8][0], "%d", (boolprompt(
673                  "Automatically move problem user messages to twit room",
674                                                     atoi(&sc[8][0]))));
675
676         strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
677         strprompt("Paginator prompt", &sc[10][0], 79);
678
679         sprintf(&sc[11][0], "%d", (boolprompt(
680               "Restrict Internet mail to only those with that privilege",
681                                                      atoi(&sc[11][0]))));
682
683         strprompt("Geographic location of this system", &sc[12][0], 31);
684         strprompt("Name of system administrator", &sc[13][0], 25);
685         strprompt("Maximum concurrent sessions", &sc[14][0], 4);
686         strprompt("Server-to-server networking password", &sc[15][0], 19);
687         strprompt("Default user purge time (days)", &sc[16][0], 5);
688         strprompt("Default room purge time (days)", &sc[17][0], 5);
689         strprompt("Name of room to log pages", &sc[18][0], ROOMNAMELEN);
690         strprompt("Maximum message length", &sc[20][0], 20);
691
692         /* Angels and demons dancing in my head... */
693         do {
694                 sprintf(buf, "%d", expire_mode);
695                 strprompt("System default message expire policy (? for list)",
696                           buf, 1);
697                 if (buf[0] == '?') {
698                         printf("\n");
699                         printf("1. Never automatically expire messages\n");
700                         printf("2. Expire by message count\n");
701                         printf("3. Expire by message age\n");
702                 }
703         } while ((buf[0] < 49) || (buf[0] > 51));
704         expire_mode = buf[0] - 48;
705
706         /* ...lunatics and monsters underneath my bed */
707         if (expire_mode == 2) {
708                 sprintf(buf, "%d", expire_value);
709                 strprompt("Keep how many messages online?", buf, 10);
710                 expire_value = atol(buf);
711         }
712         if (expire_mode == 3) {
713                 sprintf(buf, "%d", expire_value);
714                 strprompt("Keep messages for how many days?", buf, 10);
715                 expire_value = atol(buf);
716         }
717         /* Save it */
718         printf("Save this configuration? ");
719         if (yesno()) {
720                 serv_puts("CONF set");
721                 serv_gets(buf);
722                 if (buf[0] == '4') {
723                         for (a = 0; a < 21; ++a)
724                                 serv_puts(&sc[a][0]);
725                         serv_puts("000");
726                 }
727                 snprintf(buf, sizeof buf, "SPEX site|%d|%d",
728                          expire_mode, expire_value);
729                 serv_puts(buf);
730                 serv_gets(buf);
731         }
732 }