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