Initial revision
[citadel.git] / citadel / citadel.c
1 /*
2  * Citadel/UX  
3  *
4  * citadel.c - Main source file.
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 <time.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <sys/stat.h>
19 #include <sys/ioctl.h>
20 #include <signal.h>
21 #include <pwd.h>
22 #include <setjmp.h>
23
24 #include "citadel.h"
25 #include "axdefs.h"
26
27 struct march {
28         struct march *next;
29         char march_name[32];
30         char march_floor;
31         };
32
33 #define IFEXPERT if (userflags&US_EXPERT)
34 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
35 #define IFAIDE if (axlevel>=6)
36 #define IFNAIDE if (axlevel<6)
37
38 struct march *march = NULL;
39 long finduser();
40 void extract();
41 long extract_long();
42 int extract_int();
43 void load_command_set();
44 void updatelsa();
45 void movefile();
46 void deletefile();
47 void netsendfile();
48 void listzrooms();
49 int ka_system();
50 void interr();
51 int struncmp();
52 int yesno();
53 void sttybbs();
54 void newprompt();
55 void strprompt();
56 int fmout();
57 int checkpagin();
58 int pattern();
59 int pattern2();
60 void readinfo();
61 int num_parms();
62 void attach_to_server();
63 void strproc();
64 void enter_config();
65 void entregis();
66 int entmsg();
67 void updatels();
68 void forget();
69 void readmsgs();
70 int getcmd();
71 void subshell();
72 void entroom();
73 void killroom();
74 void invite();
75 void kickout();
76 void editthisroom();
77 void roomdir();
78 void download();
79 void upload();
80 void cli_upload();
81 void ungoto();
82 void whoknows();
83 void validate();
84 void enterinfo();
85 void display_help();
86 void edituser();
87 void knrooms();
88 void locate_host();
89 void chatmode();
90 void load_floorlist();
91 void create_floor();
92 void edit_floor();
93 void kill_floor();
94 void enter_bio();
95 void read_bio();
96 void misc_server_cmd();
97 int nukedir();
98 void color();
99 void cls();
100 void edit_system_message();
101 void send_ansi_detect();
102 void look_for_ansi();
103 void cli_image_upload();
104
105
106 /* globals associated with the client program */
107 char temp[16];                          /* Name of general temp file */
108 char temp2[16];                         /* Name of general temp file */
109 char tempdir[16];                       /* Name of general temp dir */
110 char editor_path[256];                  /* path to external editor */
111 char printcmd[256];                     /* print command */
112 int editor_pid = (-1);
113 char fullname[32];
114 jmp_buf nextbuf;
115 struct serv_info serv_info;             /* Info on the server connected */
116 int screenwidth;
117 int screenheight;
118 unsigned room_flags;
119 char room_name[32];
120 char ugname[20];
121 long uglsn;                             /* holds <u>ngoto info */
122 char is_mail = 0;                       /* nonzero when we're in a mail room */
123 char axlevel = 0;                       /* access level */
124 char is_room_aide = 0;                  /* boolean flag, 1 if room aide */
125 int timescalled;
126 int posted;
127 unsigned userflags;
128 long usernum = 0L;                      /* user number */
129 char newnow;
130 long highest_msg_read;                  /* used for <A>bandon room cmd */
131 long maxmsgnum;                         /* used for <G>oto */
132 char sigcaught = 0;
133 char have_xterm = 0;                    /* are we running on an xterm? */
134 char rc_username[32];
135 char rc_password[32];
136 char rc_floor_mode;
137 char floor_mode;
138 char curr_floor = 0;                    /* number of current floor */
139 char floorlist[128][256];               /* names of floors */
140 char express_msgs = 0;                  /* express messages waiting! */
141 char last_paged[32]="";
142
143 extern char server_is_local;            /* defined in ipc module */
144
145 /*
146  * here is our 'clean up gracefully and exit' routine
147  */
148 void logoff(code)
149 int code; {
150         if (editor_pid>0) {             /* kill the editor if it's running */
151                 kill(editor_pid,SIGHUP);
152                 }
153
154 /* shut down the server... but not if the logoff code is 3, because
155  * that means we're exiting because we already lost the server
156  */
157         if (code!=3) serv_puts("QUIT");
158
159 /*
160  * now clean up various things
161  */
162
163         unlink(temp);
164         unlink(temp2);
165         nukedir(tempdir);
166
167         /* Violently kill off any child processes if Citadel is
168          * the login shell. 
169          */
170         if (getppid()==1) {
171                 kill(0-getpgrp(),SIGTERM);
172                 sleep(1);
173                 kill(0-getpgrp(),SIGKILL);
174                 }
175
176         sttybbs(SB_RESTORE);            /* return the old terminal settings */
177         exit(code);                     /* exit with the proper exit code */
178         }
179
180
181
182 /*
183  * We handle "next" and "stop" much differently than in earlier versions.
184  * The signal catching routine simply sets a flag and returns.
185  */
186 void sighandler(which_sig)
187 int which_sig; {
188         signal(SIGINT,SIG_IGN);
189         signal(SIGQUIT,SIG_IGN);
190         sigcaught = which_sig;
191         return;
192         }
193
194
195 /*
196  * signal catching function for hangups...
197  */
198 void dropcarr() {
199         logoff(SIGHUP);
200         }
201
202
203
204 /* general purpose routines */
205
206 void formout(name) /* display a file */
207 char name[];
208         {
209         char cmd[256];
210         sprintf(cmd,"MESG %s",name);
211         serv_puts(cmd);
212         serv_gets(cmd);
213         if (cmd[0]!='1') {
214                 printf("%s\n",&cmd[4]);
215                 return;
216                 }
217         fmout(screenwidth,NULL,
218                 ((userflags & US_PAGINATOR) ? 1 : 0),
219                 screenheight,1,1);
220         }
221
222
223 void userlist() { 
224         char buf[256];
225         char fl[256];
226         struct tm *tmbuf;
227         long lc;
228         int linecount = 2;
229
230         serv_puts("LIST");
231         serv_gets(buf);
232         if (buf[0]!='1') {
233                 printf("%s\n",&buf[4]);
234                 return;
235                 }
236         sigcaught = 0;
237         sttybbs(SB_YES_INTR);
238         printf("       User Name           Num  L  LastCall  Calls Posts\n");
239         printf("------------------------- ----- - ---------- ----- -----\n");
240         while (serv_gets(buf), strcmp(buf,"000")) {
241                 if (sigcaught == 0) {
242                         extract(fl,buf,0);
243                         printf("%-25s ",fl);
244                         printf("%5ld %d ",extract_long(buf,2),
245                                 extract_int(buf,1));
246                         lc = extract_long(buf,3);
247                         tmbuf = (struct tm *)localtime(&lc);
248                         printf("%02d/%02d/%04d ",
249                                 (tmbuf->tm_mon+1),
250                                 tmbuf->tm_mday,
251                                 (tmbuf->tm_year + 1900));
252                         printf("%5ld %5ld\n",extract_long(buf,4),extract_long(buf,5));
253
254                         ++linecount;
255                         linecount = checkpagin(linecount,
256                                 ((userflags & US_PAGINATOR) ? 1 : 0),
257                                 screenheight);
258
259                         }
260                 }
261         sttybbs(SB_NO_INTR);
262         printf("\n");
263         }
264
265
266 /*
267  * get info about the server we've connected to
268  */
269 void get_serv_info() {
270         char buf[256];
271         int a;
272
273         /* fetch info */        
274         serv_puts("INFO");
275         serv_gets(buf);
276         if (buf[0]!='1') return;
277
278         a = 0;
279         while(serv_gets(buf), strcmp(buf,"000")) {
280             switch(a) {
281                 case 0:         serv_info.serv_pid = atoi(buf);
282                                 break;
283                 case 1:         strcpy(serv_info.serv_nodename,buf);
284                                 break;
285                 case 2:         strcpy(serv_info.serv_humannode,buf);
286                                 break;
287                 case 3:         strcpy(serv_info.serv_fqdn,buf);
288                                 break;
289                 case 4:         strcpy(serv_info.serv_software,buf);
290                                 break;
291                 case 5:         serv_info.serv_rev_level = atoi(buf);
292                                 break;
293                 case 6:         strcpy(serv_info.serv_bbs_city,buf);
294                                 break;
295                 case 7:         strcpy(serv_info.serv_sysadm,buf);
296                                 break;
297                 case 9:         strcpy(serv_info.serv_moreprompt,buf);
298                                 break;
299                 case 10:        serv_info.serv_ok_floors = atoi(buf);
300                                 break;
301                 }
302             ++a;
303             }
304
305         /* be nice and identify ourself to the server */
306         sprintf(buf,"IDEN %d|%d|%d|%s%s|",
307                 SERVER_TYPE,0,REV_LEVEL,CITADEL,
308                 (server_is_local ? "(local)" : ""));
309         locate_host(&buf[strlen(buf)]); /* append to the end */
310         serv_puts(buf);
311         serv_gets(buf); /* we don't care about the result code */
312
313         }
314
315 /*
316  * grab assorted info about the user...
317  */
318 void load_user_info(params)
319 char *params; {
320         extract(fullname,params,0);
321         axlevel = extract_int(params,1);
322         timescalled = extract_int(params,2);
323         posted = extract_int(params,3);
324         userflags = extract_int(params,4);
325         usernum = extract_long(params,5);
326         }
327
328
329 /*
330  * Remove a room from the march list.  'floornum' is ignored unless
331  * 'roomname' is set to _FLOOR_, in which case all rooms on the requested
332  * floor will be removed from the march list.
333  */
334 void remove_march(roomname,floornum)
335 char *roomname;
336 int floornum; {
337         struct march *mptr,*mptr2;
338
339         if (march==NULL) return;
340
341         if ( (!strucmp(march->march_name,roomname))
342          || ((!strucmp(roomname,"_FLOOR_"))&&(march->march_floor==floornum))) {
343                 mptr = march->next;
344                 free(march);
345                 march = mptr;
346                 return;
347                 }
348
349         mptr2 = march;
350         for (mptr=march; mptr!=NULL; mptr=mptr->next) {
351
352                 if ( (!strucmp(mptr->march_name,roomname))
353                    || ((!strucmp(roomname,"_FLOOR_"))
354                         &&(mptr->march_floor==floornum))) {
355
356                         mptr2->next = mptr->next;
357                         free(mptr);
358                         mptr=mptr2;
359                         }
360                 else {
361                         mptr2=mptr;
362                         }
363                 }
364         }
365
366 /*
367  * sort the march list by floor
368  */
369 void sort_march_list() {
370         struct march *mlist[129];
371         struct march *mptr = NULL;
372         int a;
373
374         if (march == NULL) return;
375
376         for (a=0; a<129; ++a) mlist[a] = NULL;
377
378         /* first, create 128 separate lists for each floor. */
379         while (march != NULL) {
380
381                 a = (int)(march->march_floor);
382
383                 /* assign an illegal floor number of 128 to _BASEROOM_
384                  * in order to force it to show up last */      
385                 if (!strucmp(march->march_name,"_BASEROOM_")) a = 128;
386
387                 mptr = march;
388                 march = march->next;
389                 mptr->next = mlist[a];
390                 mlist[a] = mptr;
391                 }
392
393         /* now merge the lists, in order, into one big list, 
394          * except the current floor
395          */
396         for (a=128; a>=0; --a) if (a != curr_floor) {
397                 while (mlist[a] != NULL) {
398                         mptr = mlist[a];
399                         mlist[a] = mlist[a]->next;
400                         mptr->next = march;
401                         march = mptr;
402                         }
403                 }
404
405         /* now merge in rooms from the current floor */
406         while (mlist[(int)curr_floor] != NULL) {
407                 mptr = mlist[(int)curr_floor];
408                 mlist[(int)curr_floor] = mlist[(int)curr_floor]->next;
409                 mptr->next = march;
410                 march = mptr;
411                 }
412
413         }
414
415
416
417 /*
418  * jump directly to a room
419  */
420 void dotgoto(towhere,display_name)
421 char *towhere;
422 int display_name; {
423         char aaa[256],bbb[256],psearch[256];
424         static long ls = 0L;
425         int newmailcount;
426         static int oldmailcount = (-1);
427         int partial_match,best_match;
428         char from_floor;
429
430         /* store ungoto information */
431         strcpy(ugname,room_name);
432         uglsn = ls;
433
434         /* first try an exact match */
435         sprintf(aaa,"GOTO %s",towhere);
436         serv_puts(aaa);
437         serv_gets(aaa);
438         if (aaa[3]=='*') express_msgs = 1;
439         if (!strncmp(aaa,"54",2)) {
440                 newprompt("Enter room password: ",bbb,9);
441                 sprintf(aaa,"GOTO %s|%s",towhere,bbb);
442                 serv_puts(aaa);
443                 serv_gets(aaa);
444                 if (aaa[3]=='*') express_msgs = 1;
445                 }
446         if (!strncmp(aaa,"54",2)) {
447                 printf("Wrong password.\n");
448                 return;
449                 }
450
451
452         /*
453          * If a match is not found, try a partial match.
454          * Partial matches anywhere in the string carry a weight of 1,
455          * left-aligned matches carry a weight of 2.  Pick the room that
456          * has the highest-weighted match.
457          */
458         if (aaa[0]!='2') {
459                 best_match = 0;
460                 strcpy(bbb,"");
461                 serv_puts("LKRA");
462                 serv_gets(aaa);
463                 if (aaa[0]=='1') while (serv_gets(aaa), strcmp(aaa,"000")) {
464                         extract(psearch,aaa,0);
465                         partial_match = 0;
466                         if (pattern(psearch,towhere)>=0) {
467                                 partial_match = 1;
468                                 }
469                         if (!struncmp(towhere,psearch,strlen(towhere))) {
470                                 partial_match = 2;
471                                 }
472                         if (partial_match > best_match) {
473                                 strcpy(bbb,psearch);
474                                 best_match = partial_match;
475                                 }
476                         }
477                 if (strlen(bbb)==0) {
478                         printf("No room '%s'.\n",towhere);
479                         return;
480                         }
481                 sprintf(aaa,"GOTO %s",bbb);
482                 serv_puts(aaa);
483                 serv_gets(aaa);
484                 if (aaa[3]=='*') express_msgs = 1;
485                 }
486
487         if (aaa[0]!='2') {
488                 printf("%s\n",aaa);
489                 return;
490                 }
491
492         extract(room_name,&aaa[4],0);
493         room_flags = extract_int(&aaa[4],4);
494         from_floor = curr_floor;
495         curr_floor = extract_int(&aaa[4],10);
496
497         remove_march(room_name,0);
498         if (!strucmp(towhere,"_BASEROOM_")) remove_march(towhere,0);
499         if ((from_floor!=curr_floor) && (display_name>0) && (floor_mode==1)) {
500                 if (floorlist[(int)curr_floor][0]==0) load_floorlist();
501                 printf("(Entering floor: %s)\n",&floorlist[(int)curr_floor][0]);
502                 }
503         if (display_name == 1) printf("%s - ",room_name);
504         if (display_name != 2) printf("%d new of %d messages.\n",
505                 extract_int(&aaa[4],1),
506                 extract_int(&aaa[4],2));
507         highest_msg_read = extract_int(&aaa[4],6);
508         maxmsgnum = extract_int(&aaa[4],5);
509         is_mail = (char) extract_int(&aaa[4],7);
510         is_room_aide = (char) extract_int(&aaa[4],8);
511         ls = extract_long(&aaa[4],6);
512
513         /* read info file if necessary */
514         if (extract_int(&aaa[4],3) > 0) readinfo();
515
516         /* check for newly arrived mail if we can */
517         if (num_parms(&aaa[4])>=10) {
518                 newmailcount = extract_int(&aaa[4],9);
519                 if ( (oldmailcount >= 0) && (newmailcount > oldmailcount) )
520                         printf("*** You have new mail\n");
521                 oldmailcount = newmailcount;
522                 }
523         }
524
525 /* Goto next room having unread messages.
526  * We want to skip over rooms that the user has already been to, and take the
527  * user back to the lobby when done.  The room we end up in is placed in
528  * newroom - which is set to 0 (the lobby) initially.
529  * We start the search in the current room rather than the beginning to prevent
530  * two or more concurrent users from dragging each other back to the same room.
531  */
532 void gotonext() {
533         char buf[256];
534         struct march *mptr,*mptr2;
535         char next_room[32];
536
537         /* First check to see if the march-mode list is already allocated.
538          * If it is, pop the first room off the list and go there.
539          */
540
541         if (march==NULL) {
542                 serv_puts("LKRN");
543                 serv_gets(buf);
544                 if (buf[0]=='1')
545                     while (serv_gets(buf), strcmp(buf,"000")) {
546                         mptr = (struct march *) malloc(sizeof(struct march));
547                         mptr->next = NULL;
548                         extract(mptr->march_name,buf,0);
549                         mptr->march_floor = (char) (extract_int(buf,2) & 0x7F);
550                         if (march==NULL) {
551                                 march = mptr;
552                                 }
553                         else {
554                                 mptr2 = march;
555                                 while (mptr2->next != NULL)
556                                         mptr2 = mptr2->next;
557                                 mptr2->next = mptr;
558                                 }
559                         }
560
561 /* add _BASEROOM_ to the end of the march list, so the user will end up
562  * in the system base room (usually the Lobby>) at the end of the loop
563  */
564                 mptr = (struct march *) malloc(sizeof(struct march));
565                 mptr->next = NULL;
566                 strcpy(mptr->march_name,"_BASEROOM_");
567                 if (march==NULL) {
568                         march = mptr;
569                         }
570                 else {
571                         mptr2 = march;
572                         while (mptr2->next != NULL)
573                                 mptr2 = mptr2->next;
574                         mptr2->next = mptr;
575                         }
576 /*
577  * ...and remove the room we're currently in, so a <G>oto doesn't make us
578  * walk around in circles
579  */
580                 remove_march(room_name,0);
581                 }
582
583         sort_march_list();
584
585         if (march!=NULL) {
586                 strcpy(next_room,march->march_name);
587                 }
588         else {
589                 strcpy(next_room,"_BASEROOM_");
590                 }
591         remove_march(next_room,0);
592         dotgoto(next_room,1);
593    }
594
595 /*
596  * forget all rooms on a given floor
597  */
598 void forget_all_rooms_on(ffloor)
599 int ffloor; {
600         char buf[256];
601         struct march *flist,*fptr;
602
603         printf("Forgetting all rooms on %s...\r",&floorlist[ffloor][0]);
604         fflush(stdout);
605         sprintf(buf,"LKRA %d",ffloor);
606         serv_puts(buf);
607         serv_gets(buf);
608         if (buf[0]!='1') {
609                 printf("%-72s\n",&buf[4]);
610                 return;
611                 }
612         flist = NULL;
613         while (serv_gets(buf), strcmp(buf,"000")) {
614                 fptr = (struct march *) malloc(sizeof(struct march));
615                 fptr->next = flist;
616                 flist = fptr;
617                 extract(fptr->march_name,buf,0);
618                 }
619         while (flist != NULL) {
620                 sprintf(buf,"GOTO %s",flist->march_name);
621                 serv_puts(buf);
622                 serv_gets(buf);
623                 if (buf[0]=='2') {
624                         serv_puts("FORG");
625                         serv_gets(buf);
626                         }
627                 fptr = flist;
628                 flist = flist->next;
629                 free(fptr);
630                 }
631         printf("%-72s\r","");
632         }
633
634
635 /*
636  * routine called by gotofloor() to move to a new room on a new floor
637  */
638 void gf_toroom(towhere,mode)
639 char *towhere;
640 int mode; {
641         int floor_being_left;
642
643         floor_being_left = curr_floor;
644
645         if (mode == GF_GOTO) {          /* <;G>oto mode */
646                 updatels();
647                 dotgoto(towhere,1);
648                 }
649
650         if (mode == GF_SKIP) {          /* <;S>kip mode */
651                 dotgoto(towhere,1);
652                 remove_march("_FLOOR_",floor_being_left);
653                 }
654
655         if (mode == GF_ZAP) {           /* <;Z>ap mode */
656                 dotgoto(towhere,1);
657                 remove_march("_FLOOR_",floor_being_left);
658                 forget_all_rooms_on(floor_being_left);
659                 }
660         }
661
662
663 /*
664  * go to a new floor
665  */
666 void gotofloor(towhere,mode)
667 char *towhere;
668 int mode; {
669         int a,tofloor;
670         struct march *mptr;
671         char buf[256],targ[256];
672
673         if (floorlist[0][0]==0) load_floorlist();
674         tofloor = (-1);
675         for (a=0; a<128; ++a) if (!strucmp(&floorlist[a][0],towhere))
676                 tofloor = a;
677
678         if (tofloor<0) {
679                 for (a=0; a<128; ++a) {
680                     if (!struncmp(&floorlist[a][0],towhere,strlen(towhere))) {
681                         tofloor = a;
682                         }
683                     }
684                 }
685         
686         if (tofloor<0) {
687                 for (a=0; a<128; ++a)
688                     if (pattern(towhere,&floorlist[a][0])>0)
689                         tofloor = a;
690                 }
691         
692         if (tofloor<0) {
693                 printf("No floor '%s'.\n",towhere);
694                 return;
695                 }
696
697         for (mptr = march; mptr != NULL; mptr = mptr->next) {
698                 if ((mptr->march_floor) == tofloor) 
699                         gf_toroom(mptr->march_name,mode);
700                         return;
701                 }
702
703         strcpy(targ,"");
704         sprintf(buf,"LKRA %d",tofloor);
705         serv_puts(buf);
706         serv_gets(buf);
707         if (buf[0]=='1') while (serv_gets(buf), strcmp(buf,"000")) {
708                 if ((extract_int(buf,2)==tofloor)&&(strlen(targ)==0))
709                          extract(targ,buf,0);
710                 }
711         if (strlen(targ)>0) {
712                 gf_toroom(targ,mode);
713                 }
714         else {
715                 printf("There are no rooms on '%s'.\n",&floorlist[tofloor][0]);
716                 }
717         }
718
719
720 /*
721  * forget all rooms on current floor
722  */
723 void forget_this_floor() {
724         
725         if (curr_floor == 0) {
726                 printf("Can't forget this floor.\n");
727                 return;
728                 }
729
730         if (floorlist[0][0]==0) load_floorlist();
731         printf("Are you sure you want to forget all rooms on %s? ",
732                 &floorlist[(int)curr_floor][0]);
733         if (yesno()==0) return;
734
735         gf_toroom("_BASEROOM_",GF_ZAP); 
736         }
737
738
739 /* 
740  * Figure out the physical screen dimensions, if we can
741  */
742 void check_screen_dims() {
743 #ifdef TIOCGWINSZ
744         struct {
745                 unsigned short height;          /* rows */
746                 unsigned short width;           /* columns */
747                 unsigned short xpixels;
748                 unsigned short ypixels;         /* pixels */
749         } xwinsz;
750
751         if (have_xterm) {       /* dynamically size screen if on an xterm */
752                 if ( ioctl(0, TIOCGWINSZ, &xwinsz) == 0 ) {
753                         if (xwinsz.height) screenheight = (int) xwinsz.height;
754                         if (xwinsz.width) screenwidth = (int) xwinsz.width;
755                         }
756                 }
757 #endif
758         }
759
760
761 /*
762  * set floor mode depending on client, server, and user settings
763  */
764 void set_floor_mode() {
765         if (serv_info.serv_ok_floors == 0) {
766                 floor_mode = 0;         /* Don't use floors if the server */
767                 }                       /* doesn't support them!          */
768         else {
769                 if (rc_floor_mode == RC_NO) {   /* never use floors */
770                         floor_mode = 0;
771                         }
772                 if (rc_floor_mode == RC_YES) {  /* always use floors */
773                         floor_mode = 1;
774                         }
775                 if (rc_floor_mode == RC_DEFAULT) {      /* user choice */
776                         floor_mode = ((userflags & US_FLOORS) ? 1 : 0);
777                         }
778                 }
779         }
780
781 /*
782  * Set or change the user's password
783  */
784 int set_password() {
785         char pass1[20];
786         char pass2[20];
787         char buf[256];
788
789         if (strlen(rc_password) > 0) {
790                 strcpy(pass1,rc_password);
791                 strcpy(pass2,rc_password);
792                 }
793         else {
794                 IFNEXPERT formout("changepw");
795                 newprompt("Enter a new password: ", pass1, -19);
796                 newprompt("Enter it again to confirm: ", pass2, -19);
797                 }
798         if (!strucmp(pass1,pass2)) {
799                 sprintf(buf,"SETP %s",pass1);
800                 serv_puts(buf);
801                 serv_gets(buf);
802                 printf("%s\n",&buf[4]);
803                 return(0);
804                 }
805         else {
806                 printf("*** They don't match... try again.\n");
807                 return(1);
808                 }
809         }
810
811
812 /*
813  * Display list of users currently logged on to the server
814  */
815 void who_is_online(int longlist) 
816 {
817         char buf[128], username[128], roomname[128], fromhost[128], flags[128];
818         char tbuf[128], timestr[128], idlebuf[128];
819         long timenow=0;
820         long idletime;
821         
822         printf("FLG ###        User Name                 Room                 From host\n");
823         printf("--- --- ------------------------- -------------------- ------------------------\n");
824         if (longlist)
825         {
826            serv_puts("TIME");
827            serv_gets(tbuf);
828            extract(timestr, tbuf, 1);
829            timenow = atol(timestr);
830         }
831         serv_puts("RWHO");
832         serv_gets(buf);
833         if (buf[0]=='1') 
834         {
835                 while(serv_gets(buf), strcmp(buf,"000")) 
836                 {
837                         extract(username,buf,1);
838                         extract(roomname,buf,2);
839                         extract(fromhost,buf,3);
840                         extract(idlebuf, buf,5);
841                         extract(flags,buf,7);
842                         printf("%-3s %-3d %-25s %-20s %-24s\n",
843                                 flags, extract_int(buf,0), username,
844                                 roomname, fromhost);
845                         if (longlist)
846                         {
847                            idletime = atol(idlebuf);
848                            printf("Idle time: %ld seconds\n", timenow-idletime);
849                         }
850                 }
851         }
852 }
853
854 void enternew(char *desc, char *buf, int maxlen)
855 {
856    char bbb[128];
857    sprintf(bbb, "Enter in your new %s: ", desc);
858    newprompt(bbb, buf, maxlen);
859 }
860
861 /*
862  * main
863  */
864 void main(argc,argv)
865 int argc;
866 char *argv[]; {
867 int a,b,mcmd;
868 int termn8 = 0;
869 char aaa[100],bbb[100],ccc[100],eee[100];       /* general purpose variables */
870 char argbuf[32];                                /* command line buf */
871
872
873 load_command_set();             /* parse the citadel.rc file */
874 sttybbs(SB_SAVE);               /* Store the old terminal parameters */
875 sttybbs(SB_NO_INTR);            /* Install the new ones */
876 signal(SIGINT,SIG_IGN);
877 signal(SIGQUIT,SIG_IGN);
878 signal(SIGHUP,dropcarr);        /* Cleanup gracefully if carrier is dropped */
879 signal(SIGTERM,dropcarr);       /* Cleanup gracefully if terminated */
880
881 send_ansi_detect();
882 printf("Attaching to server...\r");
883 fflush(stdout);
884 attach_to_server(argc,argv);
885
886 cls();
887 color(7);
888 serv_gets(aaa);
889 if (aaa[0]!='2') {
890         printf("%s\n",&aaa[4]);
891         logoff(atoi(aaa));
892         }
893 get_serv_info();
894
895 printf("%-22s\n%s\n%s\n",serv_info.serv_software,serv_info.serv_humannode,
896         serv_info.serv_bbs_city);
897 screenwidth = 80;               /* default screen dimensions */
898 screenheight = 24;
899
900 printf(" pause    next    stop\n");
901 printf(" ctrl-s  ctrl-o  ctrl-c\n\n");
902 formout("hello");               /* print the opening greeting */
903 printf("\n");
904
905         /* if we're not the login shell, try auto-login */
906 if (getppid()!=1) {
907         serv_puts("AUTO");
908         serv_gets(aaa);
909         if (aaa[0]=='2') {
910                 load_user_info(&aaa[4]);
911                 goto PWOK;
912                 }
913         }
914
915 look_for_ansi();
916
917 GSTA:   termn8=0; newnow=0;
918         do {
919                 if (strlen(rc_username) > 0) {
920                         strcpy(fullname,rc_username);
921                         }
922                 else {
923                         newprompt("Enter your name: ",fullname,29);
924                         }
925                 strproc(fullname); 
926                 if (!strucmp(fullname,"new")) {         /* just in case */
927                    printf("Please enter the name you wish to log in with.\n");
928                    }
929                 } while( 
930                         (!strucmp(fullname,"bbs"))
931                         || (!strucmp(fullname,"new"))
932                         || (strlen(fullname)==0) );
933
934         if (!strucmp(fullname,"off")) {
935                 mcmd=29;
936                 goto TERMN8;
937                 }
938
939         /* sign on to the server */
940         sprintf(aaa,"USER %s",fullname);
941         serv_puts(aaa);
942         serv_gets(aaa);
943         if (aaa[0]!='3') goto NEWUSR;
944
945         /* password authentication */
946         if (strlen(rc_password)>0) {
947                 strcpy(eee,rc_password);
948                 }
949         else {
950                 newprompt("\rPlease enter your password: ",eee,-19);
951                 }
952         strproc(eee);
953         sprintf(aaa,"PASS %s",eee);
954         serv_puts(aaa);
955         serv_gets(aaa);
956         if (aaa[0]=='2') {
957                 load_user_info(&aaa[4]);
958                 goto PWOK;
959                 }
960         
961         printf("<< wrong password >>\n");
962         if (strlen(rc_password)>0) logoff(0);
963         goto GSTA;
964
965 NEWUSR: if (strlen(rc_password)==0) {
966                 printf("No record. Enter as new user? ");
967                 if (yesno()==0) goto GSTA;
968                 }
969
970         sprintf(aaa,"NEWU %s",fullname);
971         serv_puts(aaa);
972         serv_gets(aaa);
973         if (aaa[0]!='2') {
974                 printf("%s\n",aaa);
975                 goto GSTA;
976                 }
977         load_user_info(&aaa[4]);
978
979         while (set_password() != 0) ;;
980         newnow=1;
981
982         enter_config(1);
983         
984
985 PWOK:   printf("%s\nAccess level: %d (%s)\nUser #%ld / Call #%d\n",
986                 fullname,axlevel,axdefs[(int)axlevel],
987                 usernum,timescalled);
988
989         serv_puts("CHEK");
990         serv_gets(aaa);
991         if (aaa[0]=='2') {
992                 b = extract_int(&aaa[4],0);
993                 if (b==1) printf("*** You have a new private message in Mail>\n");
994                 if (b>1)  printf("*** You have %d new private messages in Mail>\n",b);
995
996                 if ((axlevel>=6) && (extract_int(&aaa[4],2)>0)) {
997                         printf("*** Users need validation\n");
998                         }
999
1000                 if (extract_int(&aaa[4],1)>0) {
1001                         printf("*** Please register.\n");
1002                         formout("register");
1003                         entregis();
1004                         }
1005                 }
1006
1007         /* Make up some temporary filenames for use in various parts of the
1008          * program.  Don't mess with these once they've been set, because we
1009          * will be unlinking them later on in the program and we don't
1010          * want to delete something that we didn't create. */
1011         sprintf(temp,"/tmp/citA%d",getpid());
1012         sprintf(temp2,"/tmp/citB%d",getpid());
1013         sprintf(tempdir,"/tmp/citC%d",getpid());
1014
1015         /* Get screen dimensions.  First we go to a default of 80x24.  Then
1016          * we try to get the user's actual screen dimensions off the server.
1017          * However, if we're running on an xterm, all this stuff is
1018          * irrelevant because we're going to dynamically size the screen
1019          * during the session.
1020          */
1021         screenwidth = 80;
1022         screenheight = 24;
1023         serv_puts("GETU");
1024         serv_gets(aaa);
1025         if (aaa[0]=='2') {
1026                 screenwidth = extract_int(&aaa[4],0);
1027                 screenheight = extract_int(&aaa[4],1);
1028                 }
1029         if (getenv("TERM")!=NULL) if (!strcmp(getenv("TERM"),"xterm")) {
1030                 have_xterm = 1;
1031                 }
1032 #ifdef TIOCGWINSZ
1033         check_screen_dims();
1034 #endif
1035
1036         set_floor_mode();
1037
1038
1039         /* Enter the lobby */
1040         dotgoto("_BASEROOM_",1);
1041
1042 /* Main loop for the system... user is logged in. */
1043         strcpy(ugname,"");
1044         uglsn = 0L;
1045
1046         if (newnow==1)  readmsgs(3,1,5);
1047                 else    readmsgs(1,1,0);
1048
1049 do {    /* MAIN LOOP OF PROGRAM */
1050         signal(SIGINT,SIG_IGN);
1051         signal(SIGQUIT,SIG_IGN);
1052         mcmd=getcmd(argbuf);
1053
1054 #ifdef TIOCGWINSZ
1055         check_screen_dims();
1056 #endif
1057
1058         if (termn8==0) switch(mcmd) {
1059            case 1:      formout("help");
1060                         break;
1061            case 4:      entmsg(0,0);
1062                         break;
1063            case 36:     entmsg(0,1);
1064                         break;
1065            case 46:     entmsg(0,2);
1066                         break;
1067            case 78:     newprompt("What do you want your username to be? ", aaa, 32);
1068                         sprintf(bbb, "ENT0 2|0|0|0|%s", aaa);
1069                         serv_puts(bbb);
1070                         serv_gets(aaa);
1071                         if (strncmp("200", aaa, 3))
1072                            printf("\n%s\n", aaa);
1073                         else
1074                            entmsg(0, 0);
1075                         break;
1076            case 5:      updatels();
1077                         gotonext();
1078                         break;
1079            case 47:     updatelsa();
1080                         gotonext();
1081                         break;
1082            case 58:     updatelsa();
1083                         dotgoto("_MAIL_",1);
1084                         break;
1085            case 20:     updatels();
1086            case 52:     dotgoto(argbuf,0);
1087                         break;
1088            case 10:     readmsgs(0,1,0);
1089                         break;
1090            case 9:      readmsgs(3,1,5);
1091                         break;
1092            case 13:     readmsgs(1,1,0);
1093                         break;
1094            case 11:     readmsgs(0,(-1),0);
1095                         break;
1096            case 12:     readmsgs(2,(-1),0);
1097                         break;
1098            case 71:     readmsgs(3, 1, atoi(argbuf));
1099                         break;
1100            case 7:      forget();       break;
1101            case 18:     subshell();     break;
1102            case 38:     updatels();
1103                         entroom();      break;
1104            case 22:     killroom();     break;
1105            case 32:     userlist();     break;
1106            case 27:     invite();       break;
1107            case 28:     kickout();      break;
1108            case 23:     editthisroom(); break;
1109            case 14:     roomdir();      break;
1110            case 33:     download(0);    break;
1111            case 34:     download(1);    break;
1112            case 31:     download(2);    break;
1113            case 43:     download(3);    break;
1114            case 45:     download(4);    break;
1115            case 55:     download(5);    break;
1116            case 39:     upload(0);      break;
1117            case 40:     upload(1);      break;
1118            case 42:     upload(2);      break;
1119            case 44:     upload(3);      break;
1120            case 57:     cli_upload();   break;
1121            case 16:     ungoto();       break;
1122            case 24:     whoknows();     break;
1123            case 26:     validate();     break;
1124            case 29:     updatels();
1125                         termn8=1;
1126                         break;
1127            case 30:     updatels();
1128                         termn8=1;
1129                         break;
1130            case 48:     enterinfo();
1131                         break;
1132            case 49:     readinfo();
1133                         break;
1134            case 72:     cli_image_upload("_userpic_");
1135                         break;
1136            case 73:     cli_image_upload("_roompic_");
1137                         break;
1138
1139 case 74:
1140         sprintf(aaa, "_floorpic_|%d", curr_floor);
1141         cli_image_upload(aaa);
1142         break;
1143         
1144 case 75:
1145         enternew("roomname", aaa, 20);
1146         sprintf(bbb, "RCHG %s", aaa);
1147         serv_puts(bbb);
1148         serv_gets(aaa);
1149         if (strncmp("200",aaa, 3))
1150            printf("\n%s\n", aaa);
1151         break;
1152 case 76:
1153         enternew("hostname", aaa, 25);
1154         sprintf(bbb, "HCHG %s", aaa);
1155         serv_puts(bbb);
1156         serv_gets(aaa);
1157         if (strncmp("200",aaa, 3))
1158            printf("\n%s\n", aaa);
1159         break;
1160 case 77:
1161         enternew("username", aaa, 32);
1162         sprintf(bbb, "UCHG %s", aaa);
1163         serv_puts(bbb);
1164         serv_gets(aaa);
1165         if (strncmp("200",aaa, 3))
1166            printf("\n%s\n", aaa);
1167         break;
1168
1169 case 35:
1170         set_password();
1171         break;
1172
1173 case 21:
1174         if (argbuf[0]==0) strcpy(aaa,"?");
1175         display_help(argbuf);
1176         break;
1177
1178 case 41:
1179         formout("register");
1180         entregis();
1181         break;
1182
1183 case 15:
1184         printf("Are you sure (y/n)? ");
1185         if (yesno()==1) {
1186                 updatels();
1187                 a=0;
1188                 termn8=1;
1189                 }
1190         break;
1191
1192 case 6:
1193         gotonext();
1194         break;
1195
1196 case 3: chatmode();
1197         break;
1198
1199 case 2: if (server_is_local) {
1200                 sttybbs(SB_RESTORE);
1201 sprintf(aaa,"USERNAME=\042%s\042; export USERNAME; exec ./subsystem %ld %d %d",
1202                         fullname,
1203                         usernum,screenwidth,axlevel);
1204                 ka_system(aaa);
1205                 sttybbs(SB_NO_INTR);
1206                 }
1207         else {
1208                 printf("*** Can't run doors when server is not local.\n");
1209                 }
1210         break;
1211
1212 case 17:
1213         who_is_online(0);
1214         break;
1215
1216 case 79:
1217         who_is_online(1);
1218         break;
1219
1220 case 50:
1221         enter_config(2);
1222         break;
1223
1224 case 37:
1225         enter_config(0);
1226         set_floor_mode();
1227         break;
1228
1229 case 59:
1230         enter_config(3);
1231         set_floor_mode();
1232         break;
1233
1234 case 60:
1235         gotofloor(argbuf,GF_GOTO);
1236         break;
1237         
1238 case 61:
1239         gotofloor(argbuf,GF_SKIP);
1240         break;
1241         
1242 case 62:
1243         forget_this_floor();
1244         break;
1245
1246 case 63:
1247         create_floor();
1248         break;
1249
1250 case 64:
1251         edit_floor();
1252         break;
1253
1254 case 65:
1255         kill_floor();
1256         break;
1257
1258 case 66:
1259         enter_bio();
1260         break;
1261
1262 case 67:
1263         read_bio();
1264         break;
1265
1266 case 25:
1267         edituser();
1268         break;
1269
1270 case 8:
1271         knrooms(floor_mode);
1272         printf("\n");
1273         break;
1274
1275 case 68:
1276         knrooms(2);
1277         printf("\n");
1278         break;
1279
1280 case 69:
1281         misc_server_cmd(argbuf);
1282         break;
1283
1284 case 70:
1285         edit_system_message(argbuf);
1286         break;
1287
1288 case 19:
1289         listzrooms();
1290         printf("\n");
1291         break;
1292
1293 case 51:
1294         deletefile();
1295         break;
1296
1297 case 53:
1298         netsendfile();
1299         break;
1300
1301 case 54:
1302         movefile();
1303         break;
1304
1305 case 56:
1306         if (last_paged[0])
1307            sprintf(bbb, "Page who [%s]? ", last_paged);
1308         else
1309            sprintf(bbb, "Page who? ");
1310         newprompt(bbb,aaa,30);
1311         if (!aaa[0])
1312            strcpy(aaa, last_paged);
1313         strproc(aaa);
1314         newprompt("Message:  ",bbb,69);
1315         sprintf(ccc,"SEXP %s|%s",aaa,bbb);
1316         serv_puts(ccc);
1317         serv_gets(ccc);
1318         if (!strncmp("200", ccc, 3))
1319            strcpy(last_paged, aaa);
1320         printf("%s\n",&ccc[4]);
1321         break;
1322
1323         } /* end switch */
1324     } while(termn8==0);
1325
1326 TERMN8: printf("%s logged out.\n",fullname);
1327         while (march!=NULL) remove_march(march->march_name,0);
1328         if (mcmd==30)
1329                 printf("\n\nType 'off' to hang up, or next user...\n");
1330         sprintf(aaa,"LOUT");
1331         serv_puts(aaa);
1332         serv_gets(aaa);
1333         if ((mcmd==29)||(mcmd==15)) {
1334                 formout("goodbye");
1335                 logoff(0);
1336                 }
1337         goto GSTA;
1338
1339 } /* end main() */
1340