* Various changes to allow "new messages" to work correctly with Mail
[citadel.git] / citadel / user_ops.c
1 /* needed to properly enable crypt() stuff on some systems */
2 #define _XOPEN_SOURCE
3 /* needed for str[n]casecmp() on some systems if the above is defined */
4 #define _XOPEN_SOURCE_EXTENDED
5
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <stdio.h>
9 #include <fcntl.h>
10 #include <signal.h>
11 #include <pwd.h>
12 #include <sys/types.h>
13 #include <sys/time.h>
14 #include <string.h>
15 #include <syslog.h>
16 #include <pthread.h>
17 #include "citadel.h"
18 #include "server.h"
19 #include "database.h"
20 #include "user_ops.h"
21 #include "sysdep_decls.h"
22 #include "support.h"
23 #include "room_ops.h"
24 #include "logging.h"
25 #include "file_ops.h"
26 #include "control.h"
27 #include "msgbase.h"
28 #include "config.h"
29
30
31 /*
32  * getuser()  -  retrieve named user into supplied buffer.
33  *               returns 0 on success
34  */
35 int getuser(struct usersupp *usbuf, char name[]) {
36
37         char lowercase_name[32];
38         int a;
39         struct cdbdata *cdbus;
40
41         bzero(usbuf, sizeof(struct usersupp));
42         for (a=0; a<=strlen(name); ++a) {
43                 lowercase_name[a] = tolower(name[a]);
44                 }
45
46         cdbus = cdb_fetch(CDB_USERSUPP, lowercase_name, strlen(lowercase_name));
47         if (cdbus == NULL) {
48                 return(1);      /* user not found */
49                 }
50
51         memcpy(usbuf, cdbus->ptr,
52                 ( (cdbus->len > sizeof(struct usersupp)) ?
53                 sizeof(struct usersupp) : cdbus->len) );
54         cdb_free(cdbus);
55         return(0);
56         }
57
58
59 /*
60  * lgetuser()  -  same as getuser() but locks the record
61  */
62 int lgetuser(struct usersupp *usbuf, char *name)
63 {
64         int retcode;
65
66         retcode = getuser(usbuf,name);
67         if (retcode == 0) {
68                 begin_critical_section(S_USERSUPP);
69                 }
70         return(retcode);
71         }
72
73
74 /*
75  * putuser()  -  write user buffer into the correct place on disk
76  */
77 void putuser(struct usersupp *usbuf, char *name)
78 {
79         char lowercase_name[32];
80         int a;
81
82         for (a=0; a<=strlen(name); ++a) {
83                 lowercase_name[a] = tolower(name[a]);
84                 }
85
86         cdb_store(CDB_USERSUPP,
87                 lowercase_name, strlen(lowercase_name),
88                 usbuf, sizeof(struct usersupp));
89
90         }
91
92
93 /*
94  * lputuser()  -  same as putuser() but locks the record
95  */
96 void lputuser(struct usersupp *usbuf, char *name) {
97         putuser(usbuf,name);
98         end_critical_section(S_USERSUPP);
99         }
100
101
102 /*
103  * Is the user currently logged in an Aide?
104  */
105 int is_aide(void) {
106         if (CC->usersupp.axlevel >= 6) return(1);
107         else return(0);
108         }
109
110
111 /*
112  * Is the user currently logged in an Aide *or* the room aide for this room?
113  */
114 int is_room_aide(void) {
115         if ( (CC->usersupp.axlevel >= 6)
116            || (CC->quickroom.QRroomaide == CC->usersupp.usernum) ) {
117                 return(1);
118                 }
119         else {
120                 return(0);
121                 }
122         }
123
124 /*
125  * getuserbynumber()  -  get user by number
126  *                       returns 0 if user was found
127  */
128 int getuserbynumber(struct usersupp *usbuf, long int number)
129 {
130         struct cdbdata *cdbus;
131
132         cdb_rewind(CDB_USERSUPP);
133
134         while(cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
135                 bzero(usbuf, sizeof(struct usersupp));
136                 memcpy(usbuf, cdbus->ptr,
137                         ( (cdbus->len > sizeof(struct usersupp)) ?
138                         sizeof(struct usersupp) : cdbus->len) );
139                 cdb_free(cdbus);
140                 if (usbuf->usernum == number) {
141                         return(0);
142                         }
143                 }
144         return(-1);
145         }
146
147
148 /*
149  * USER cmd
150  */
151 void cmd_user(char *cmdbuf)
152 {
153         char username[256];
154         char autoname[256];
155         int found_user = 0;
156         struct passwd *p;
157         int a;
158
159         extract(username,cmdbuf,0);
160         username[25] = 0;
161         strproc(username);
162
163         if ((CC->logged_in)) {
164                 cprintf("%d Already logged in.\n",ERROR);
165                 return;
166                 }
167
168         found_user = getuser(&CC->usersupp,username);
169         if (found_user != 0) {
170                 p = (struct passwd *)getpwnam(username);
171                 if (p!=NULL) {
172                         strcpy(autoname,p->pw_gecos);
173                         for (a=0; a<strlen(autoname); ++a)
174                                 if (autoname[a]==',') autoname[a]=0;
175                         found_user = getuser(&CC->usersupp,autoname);
176                         }
177                 }
178         if (found_user == 0) {
179                 if (((CC->nologin)) && (CC->usersupp.axlevel < 6)) {
180                         cprintf("%d %s: Too many users are already online (maximum is %d)\n",
181                         ERROR+MAX_SESSIONS_EXCEEDED,
182                         config.c_nodename,config.c_maxsessions);
183                         }
184                 else {
185                         strcpy(CC->curr_user,CC->usersupp.fullname);
186                         cprintf("%d Password required for %s\n",
187                                 MORE_DATA,CC->curr_user);
188                         }
189                 }
190         else {
191                 cprintf("%d %s not found.\n",ERROR,username);
192                 }
193         }
194
195
196
197 /*
198  * session startup code which is common to both cmd_pass() and cmd_newu()
199  */
200 void session_startup(void) {
201         int a;
202         struct quickroom qr;
203
204         syslog(LOG_NOTICE,"user <%s> logged in",CC->curr_user);
205
206         lgetuser(&CC->usersupp,CC->curr_user);
207         ++(CC->usersupp.timescalled);
208         CC->fake_username[0] = '\0';
209         CC->fake_postname[0] = '\0';
210         CC->fake_hostname[0] = '\0';
211         CC->fake_roomname[0] = '\0';
212         CC->last_pager[0] = '\0';
213         time(&CC->usersupp.lastcall);
214
215         /* If this user's name is the name of the system administrator
216          * (as specified in setup), automatically assign access level 6.
217          */
218         if (!strcasecmp(CC->usersupp.fullname, config.c_sysadm)) {
219                 CC->usersupp.axlevel = 6;
220                 }
221
222 /* A room's generation number changes each time it is recycled. Users are kept
223  * out of private rooms or forget rooms by matching the generation numbers. To
224  * avoid an accidental matchup, unmatched numbers are set to -1 here.
225  */
226         for (a=0; a<MAXROOMS; ++a) {
227                 getroom(&qr,a);
228                 if (CC->usersupp.generation[a] != qr.QRgen)
229                                         CC->usersupp.generation[a]=(-1);
230                 if (CC->usersupp.forget[a] != qr.QRgen)
231                                         CC->usersupp.forget[a]=(-1);
232                 }
233
234         lputuser(&CC->usersupp,CC->curr_user);
235
236         /* Run any cleanup routines registered by loadable modules */
237         PerformSessionHooks(EVT_LOGIN);
238
239         cprintf("%d %s|%d|%d|%d|%u|%ld\n",OK,CC->usersupp.fullname,CC->usersupp.axlevel,
240                 CC->usersupp.timescalled,CC->usersupp.posted,CC->usersupp.flags,
241                 CC->usersupp.usernum);
242         usergoto(0,0);          /* Enter the lobby */   
243         rec_log(CL_LOGIN,CC->curr_user);
244         }
245
246
247 /* 
248  * misc things to be taken care of when a user is logged out
249  */
250 void logout(struct CitContext *who)
251 {
252         who->logged_in = 0;
253         if (who->download_fp != NULL) {
254                 fclose(who->download_fp);
255                 who->download_fp = NULL;
256                 }
257         if (who->upload_fp != NULL) {
258                 abort_upl(who);
259                 }
260
261         /* Do modular stuff... */
262         PerformSessionHooks(EVT_LOGOUT);
263         }
264
265
266 void cmd_pass(char *buf)
267 {
268         char password[256];
269         int code;
270         struct passwd *p;
271
272         extract(password,buf,0);
273
274         if ((CC->logged_in)) {
275                 cprintf("%d Already logged in.\n",ERROR);
276                 return;
277                 }
278         if (!strcmp(CC->curr_user,"")) {
279                 cprintf("%d You must send a name with USER first.\n",ERROR);
280                 return;
281                 }
282         if (getuser(&CC->usersupp,CC->curr_user)) {
283                 cprintf("%d Can't find user record!\n",ERROR+INTERNAL_ERROR);
284                 return;
285                 }
286
287         code = (-1);
288         if (CC->usersupp.USuid == BBSUID) {
289                 strproc(password);
290                 strproc(CC->usersupp.password);
291                 code = strcasecmp(CC->usersupp.password,password);
292                 }
293         else {
294                 p = (struct passwd *)getpwuid(CC->usersupp.USuid);
295 #ifdef ENABLE_AUTOLOGIN
296                 if (p!=NULL) {
297                         if (!strcmp(p->pw_passwd,
298                            (char *)crypt(password,p->pw_passwd))) {
299                                 code = 0;
300                                 lgetuser(&CC->usersupp, CC->curr_user);
301                                 strcpy(CC->usersupp.password, password);
302                                 lputuser(&CC->usersupp, CC->curr_user);
303                                 }
304                         }
305 #endif
306                 }
307
308         if (!code) {
309                 (CC->logged_in) = 1;
310                 session_startup();
311                 }
312         else {
313                 cprintf("%d Wrong password.\n",ERROR);
314                 rec_log(CL_BADPW,CC->curr_user);
315                 }
316         }
317
318
319 /*
320  * Delete a user record *and* all of its related resources.
321  */
322 int purge_user(char *pname) {
323         char filename[64];
324         struct usersupp usbuf;
325         int a;
326         struct cdbdata *cdbmb;
327         long *mailbox;
328         int num_mails;
329
330         if (getuser(&usbuf, pname) != 0) {
331                 lprintf(5, "Cannot purge user <%s> - not found\n", pname);
332                 return(ERROR+NO_SUCH_USER);
333                 }
334
335         /* FIX   Don't delete a user who is currently logged in. */
336
337         /* delete any messages in the user's mailbox */
338         cdbmb = cdb_fetch(CDB_MAILBOXES, &usbuf.usernum, sizeof(long));
339         if (cdbmb != NULL) {
340                 num_mails = cdbmb->len / sizeof(long);
341                 mailbox = (long *) cdbmb->ptr;
342                 if (num_mails > 0) for (a=0; a<num_mails; ++a) {
343                         cdb_delete(CDB_MSGMAIN, &mailbox[a], sizeof(long));
344                         }
345                 cdb_free(cdbmb);
346                 /* now delete the mailbox itself */
347                 cdb_delete(CDB_MAILBOXES, &usbuf.usernum, sizeof(long));
348                 }
349
350
351         /* delete the userlog entry */
352         cdb_delete(CDB_USERSUPP, pname, strlen(pname));
353
354         /* remove the user's bio file */        
355         sprintf(filename, "./bio/%ld", usbuf.usernum);
356         unlink(filename);
357
358         /* remove the user's picture */
359         sprintf(filename, "./userpics/%ld.gif", usbuf.usernum);
360         unlink(filename);
361
362         return(0);
363         }
364
365
366 /*
367  * create_user()  -  back end processing to create a new user
368  */
369 int create_user(char *newusername)
370 {
371         struct usersupp usbuf;
372         int a;
373         struct passwd *p = NULL;
374         char username[64];
375
376         strcpy(username, newusername);
377         strproc(username);
378
379 #ifdef ENABLE_AUTOLOGIN
380         p = (struct passwd *)getpwnam(username);
381 #endif
382         if (p != NULL) {
383                 strcpy(username, p->pw_gecos);
384                 for (a=0; a<strlen(username); ++a) {
385                         if (username[a] == ',') username[a] = 0;
386                         }
387                 CC->usersupp.USuid = p->pw_uid;
388                 }
389         else {
390                 CC->usersupp.USuid = BBSUID;
391                 }
392
393         if (!getuser(&usbuf,username)) {
394                 return(ERROR+ALREADY_EXISTS);
395                 }
396
397         strcpy(CC->curr_user,username);
398         strcpy(CC->usersupp.fullname,username);
399         (CC->logged_in) = 1;
400
401         for (a=0; a<MAXROOMS; ++a) {
402                 CC->usersupp.lastseen[a]=0L;
403                 CC->usersupp.generation[a]=(-1);
404                 CC->usersupp.forget[a]=(-1);
405                 }
406         strcpy(CC->usersupp.password,"");
407
408         /* These are the default flags on new accounts */
409         CC->usersupp.flags =
410                 US_NEEDVALID|US_LASTOLD|US_DISAPPEAR|US_PAGINATOR|US_FLOORS;
411
412         CC->usersupp.timescalled = 0;
413         CC->usersupp.posted = 0;
414         CC->usersupp.axlevel = INITAX;
415         CC->usersupp.USscreenwidth = 80;
416         CC->usersupp.USscreenheight = 24;
417         time(&CC->usersupp.lastcall);
418         strcpy(CC->usersupp.USname, "");
419         strcpy(CC->usersupp.USaddr, "");
420         strcpy(CC->usersupp.UScity, "");
421         strcpy(CC->usersupp.USstate, "");
422         strcpy(CC->usersupp.USzip, "");
423         strcpy(CC->usersupp.USphone, "");
424
425         /* fetch a new user number */
426         CC->usersupp.usernum = get_new_user_number();
427
428         if (CC->usersupp.usernum == 1L) {
429                 CC->usersupp.axlevel = 6;
430                 }
431
432         /* add user to userlog */
433         putuser(&CC->usersupp,CC->curr_user);
434         if (getuser(&CC->usersupp,CC->curr_user)) {
435                 return(ERROR+INTERNAL_ERROR);
436                 }
437         rec_log(CL_NEWUSER,CC->curr_user);
438         return(0);
439         }
440
441
442
443
444 /*
445  * cmd_newu()  -  create a new user account
446  */
447 void cmd_newu(char *cmdbuf)
448 {
449         int a;
450         char username[256];
451
452         if ((CC->logged_in)) {
453                 cprintf("%d Already logged in.\n",ERROR);
454                 return;
455                 }
456
457         if ((CC->nologin)) {
458                 cprintf("%d %s: Too many users are already online (maximum is %d)\n",
459                 ERROR+MAX_SESSIONS_EXCEEDED,
460                 config.c_nodename,config.c_maxsessions);
461                 }
462
463         extract(username,cmdbuf,0);
464         username[25] = 0;
465         strproc(username);
466
467         if (strlen(username)==0) {
468                 cprintf("%d You must supply a user name.\n",ERROR);
469                 return;
470                 }
471
472         a = create_user(username);
473         if ((!strcasecmp(username, "bbs")) ||
474             (!strcasecmp(username, "new")) ||
475             (!strcasecmp(username, ".")))
476         {
477            cprintf("%d '%s' is an invalid login name.\n", ERROR);
478            return;
479         }
480         if (a==ERROR+ALREADY_EXISTS) {
481                 cprintf("%d '%s' already exists.\n",
482                         ERROR+ALREADY_EXISTS,username);
483                 return;
484                 }
485         else if (a==ERROR+INTERNAL_ERROR) {
486                 cprintf("%d Internal error - user record disappeared?\n",
487                         ERROR+INTERNAL_ERROR);
488                 return;
489                 }
490         else if (a==0) {
491                 session_startup();
492                 }
493         else {
494                 cprintf("%d unknown error\n",ERROR);
495                 }
496         rec_log(CL_NEWUSER,CC->curr_user);
497         }
498
499
500
501 /*
502  * set password
503  */
504 void cmd_setp(char *new_pw)
505 {
506         if (!(CC->logged_in)) {
507                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
508                 return;
509                 }
510         if (CC->usersupp.USuid != BBSUID) {
511                 cprintf("%d Not allowed.  Use the 'passwd' command.\n",ERROR);
512                 return;
513                 }
514         strproc(new_pw);
515         if (strlen(new_pw)==0) {
516                 cprintf("%d Password unchanged.\n",OK);
517                 return;
518                 }
519         lgetuser(&CC->usersupp,CC->curr_user);
520         strcpy(CC->usersupp.password,new_pw);
521         lputuser(&CC->usersupp,CC->curr_user);
522         cprintf("%d Password changed.\n",OK);
523         rec_log(CL_PWCHANGE,CC->curr_user);
524         PerformSessionHooks(EVT_SETPASS);
525         }
526
527 /*
528  * get user parameters
529  */
530 void cmd_getu(void) {
531         if (!(CC->logged_in)) {
532                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
533                 return;
534                 }
535         getuser(&CC->usersupp,CC->curr_user);
536         cprintf("%d %d|%d|%d\n",
537                 OK,
538                 CC->usersupp.USscreenwidth,
539                 CC->usersupp.USscreenheight,
540                 (CC->usersupp.flags & US_USER_SET)
541                 );
542         }
543
544 /*
545  * set user parameters
546  */
547 void cmd_setu(char *new_parms)
548 {
549
550         if (num_parms(new_parms)!=3) {
551                 cprintf("%d Usage error.\n",ERROR);
552                 return;
553                 }       
554         if (!(CC->logged_in)) {
555                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
556                 return;
557                 }
558         lgetuser(&CC->usersupp,CC->curr_user);
559         CC->usersupp.USscreenwidth = extract_int(new_parms,0);
560         CC->usersupp.USscreenheight = extract_int(new_parms,1);
561         CC->usersupp.flags = CC->usersupp.flags & (~US_USER_SET);
562         CC->usersupp.flags = CC->usersupp.flags | 
563                 (extract_int(new_parms,2) & US_USER_SET);
564         lputuser(&CC->usersupp,CC->curr_user);
565         cprintf("%d Ok\n",OK);
566         }
567
568 /*
569  * set last read pointer
570  */
571 void cmd_slrp(char *new_ptr)
572 {
573         long newlr;
574
575         if (!(CC->logged_in)) {
576                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
577                 return;
578                 }
579
580         if (CC->curr_rm < 0) {
581                 cprintf("%d No current room.\n",ERROR);
582                 return;
583                 }
584
585         if (!strncasecmp(new_ptr,"highest",7)) {
586                 newlr = CC->quickroom.QRhighest;
587 /* FIX ... if the current room is 1 (Mail), newlr needs to be set to the
588  * number of the highest mail message
589  */
590                 }
591         else {
592                 newlr = atol(new_ptr);
593                 }
594
595         lgetuser(&CC->usersupp, CC->curr_user);
596         CC->usersupp.lastseen[CC->curr_rm] = newlr;
597         lputuser(&CC->usersupp, CC->curr_user);
598         cprintf("%d %ld\n",OK,newlr);
599         }
600
601
602 /*
603  * INVT and KICK commands
604  */
605 void cmd_invt_kick(char *iuser, int op)
606                         /* user name */
607         {               /* 1 = invite, 0 = kick out */
608         struct usersupp USscratch;
609         char bbb[256];
610
611         if (!(CC->logged_in)) {
612                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
613                 return;
614                 }
615
616         if (CC->curr_rm < 0) {
617                 cprintf("%d No current room.\n",ERROR);
618                 return;
619                 }
620
621         if (is_room_aide()==0) {
622                 cprintf("%d Higher access required.\n",
623                         ERROR+HIGHER_ACCESS_REQUIRED);
624                 return;
625                 }
626
627         if ( (op==1) && ((CC->quickroom.QRflags&QR_PRIVATE)==0) ) {
628                 cprintf("%d Not a private room.\n",ERROR+NOT_HERE);
629                 return;
630                 }
631
632         if (lgetuser(&USscratch,iuser)!=0) {
633                 cprintf("%d No such user.\n",ERROR);
634                 return;
635                 }
636
637         if (op==1) {
638                 USscratch.generation[CC->curr_rm]=CC->quickroom.QRgen;
639                 USscratch.forget[CC->curr_rm]=(-1);
640                 }
641
642         if (op==0) {
643                 USscratch.generation[CC->curr_rm]=(-1);
644                 USscratch.forget[CC->curr_rm]=CC->quickroom.QRgen;
645                 }
646
647         lputuser(&USscratch,iuser);
648
649         /* post a message in Aide> saying what we just did */
650         sprintf(bbb,"%s %s %s> by %s",
651                 iuser,
652                 ((op == 1) ? "invited to" : "kicked out of"),
653                 CC->quickroom.QRname,
654                 CC->usersupp.fullname);
655         aide_message(bbb);
656
657         if ((op==0)&&((CC->quickroom.QRflags&QR_PRIVATE)==0)) {
658                 cprintf("%d Ok. (Not a private room, <Z>ap effect only)\n",OK);
659                 }
660         else {
661                 cprintf("%d Ok.\n",OK);
662                 }
663         return;
664         }
665
666
667 /*
668  * forget (Zap) the current room
669  */
670 void cmd_forg(void) {
671         if (!(CC->logged_in)) {
672                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
673                 return;
674                 }
675
676         if (CC->curr_rm < 0) {
677                 cprintf("%d No current room.\n",ERROR);
678                 return;
679                 }
680
681         if (CC->curr_rm < 3) {
682                 cprintf("%d You cannot forget this room.\n",ERROR+NOT_HERE);
683                 return;
684                 }
685
686         if (is_aide()) {
687                 cprintf("%d Aides cannot forget rooms.\n",ERROR);
688                 return;
689                 }
690
691         lgetuser(&CC->usersupp,CC->curr_user);
692         CC->usersupp.forget[CC->curr_rm] = CC->quickroom.QRgen;
693         CC->usersupp.generation[CC->curr_rm] = (-1);
694         lputuser(&CC->usersupp,CC->curr_user);
695         cprintf("%d Ok\n",OK);
696         CC->curr_rm = (-1);
697         }
698
699 /*
700  * Get Next Unregistered User
701  */
702 void cmd_gnur(void) {
703         struct cdbdata *cdbus;
704         struct usersupp usbuf;
705
706         if (!(CC->logged_in)) {
707                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
708                 return;
709                 }
710
711         if (CC->usersupp.axlevel < 6) {
712                 cprintf("%d Higher access required.\n",
713                         ERROR+HIGHER_ACCESS_REQUIRED);
714                 return;
715                 }
716
717         if ((CitControl.MMflags&MM_VALID)==0) {
718                 cprintf("%d There are no unvalidated users.\n",OK);
719                 return;
720                 }
721
722         /* There are unvalidated users.  Traverse the usersupp database,
723          * and return the first user we find that needs validation.
724          */
725         cdb_rewind(CDB_USERSUPP);
726         while (cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
727                 bzero(&usbuf, sizeof(struct usersupp));
728                 memcpy(&usbuf, cdbus->ptr,
729                         ( (cdbus->len > sizeof(struct usersupp)) ?
730                         sizeof(struct usersupp) : cdbus->len) );
731                 cdb_free(cdbus);
732                 if ((usbuf.flags & US_NEEDVALID)
733                    &&(usbuf.axlevel > 0)) {
734                         cprintf("%d %s\n",MORE_DATA,usbuf.fullname);
735                         return;
736                         }
737                 } 
738
739         /* If we get to this point, there are no more unvalidated users.
740          * Therefore we clear the "users need validation" flag.
741          */
742
743         begin_critical_section(S_CONTROL);
744         get_control();
745         CitControl.MMflags = CitControl.MMflags&(~MM_VALID);
746         put_control();
747         end_critical_section(S_CONTROL);
748         cprintf("%d *** End of registration.\n",OK);
749
750
751         }
752
753
754 /*
755  * get registration info for a user
756  */
757 void cmd_greg(char *who)
758 {
759         struct usersupp usbuf;
760         int a,b;
761         char pbuf[32];
762
763         if (!(CC->logged_in)) {
764                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
765                 return;
766                 }
767
768         if (!strcasecmp(who,"_SELF_")) strcpy(who,CC->curr_user);
769
770         if ((CC->usersupp.axlevel < 6) && (strcasecmp(who,CC->curr_user))) {
771                 cprintf("%d Higher access required.\n",
772                         ERROR+HIGHER_ACCESS_REQUIRED);
773                 return;
774                 }
775
776         if (getuser(&usbuf,who) != 0) {
777                 cprintf("%d '%s' not found.\n",ERROR+NO_SUCH_USER,who);
778                 return;
779                 }
780
781         cprintf("%d %s\n",LISTING_FOLLOWS,usbuf.fullname);
782         cprintf("%ld\n",usbuf.usernum);
783         cprintf("%s\n",usbuf.password);
784         cprintf("%s\n",usbuf.USname);
785         cprintf("%s\n",usbuf.USaddr);
786         cprintf("%s\n%s\n%s\n",
787                 usbuf.UScity,usbuf.USstate,usbuf.USzip);
788         strcpy(pbuf,usbuf.USphone);
789         usbuf.USphone[0]=0;
790         for (a=0; a<strlen(pbuf); ++a) {
791                 if ((pbuf[a]>='0')&&(pbuf[a]<='9')) {
792                         b=strlen(usbuf.USphone);
793                         usbuf.USphone[b]=pbuf[a];
794                         usbuf.USphone[b+1]=0;
795                         }
796                 }
797         while(strlen(usbuf.USphone)<10) {
798                 strcpy(pbuf,usbuf.USphone);
799                 strcpy(usbuf.USphone," ");
800                 strcat(usbuf.USphone,pbuf);
801                 }
802
803         cprintf("(%c%c%c) %c%c%c-%c%c%c%c\n",
804                 usbuf.USphone[0],usbuf.USphone[1],
805                 usbuf.USphone[2],usbuf.USphone[3],
806                 usbuf.USphone[4],usbuf.USphone[5],
807                 usbuf.USphone[6],usbuf.USphone[7],
808                 usbuf.USphone[8],usbuf.USphone[9]);
809
810         cprintf("%d\n",usbuf.axlevel);
811         cprintf("%s\n",usbuf.USemail);
812         cprintf("000\n");
813         }
814
815 /*
816  * validate a user
817  */
818 void cmd_vali(char *v_args)
819 {
820         char user[256];
821         int newax;
822         struct usersupp userbuf;
823
824         extract(user,v_args,0);
825         newax = extract_int(v_args,1);
826
827         if (!(CC->logged_in)) {
828                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
829                 return;
830                 }
831
832         if (CC->usersupp.axlevel < 6) {
833                 cprintf("%d Higher access required.\n",
834                         ERROR+HIGHER_ACCESS_REQUIRED);
835                 return;
836                 }
837
838         if (lgetuser(&userbuf,user)!=0) {
839                 cprintf("%d '%s' not found.\n",ERROR+NO_SUCH_USER,user);
840                 return;
841                 }
842
843         userbuf.axlevel = newax;
844         userbuf.flags = (userbuf.flags & ~US_NEEDVALID);
845
846         lputuser(&userbuf,user);
847
848         /* If the access level was set to zero, delete the user */
849         if (newax == 0) {
850                 if (purge_user(user)==0) {
851                         cprintf("%d %s Deleted.\n", OK, userbuf.fullname);
852                         return;
853                         }
854                 }
855
856         cprintf("%d ok\n",OK);
857         }
858
859
860
861 /* 
862  *  Traverse the user file...
863  */
864 void ForEachUser(void (*CallBack)(struct usersupp *EachUser)) {
865         struct usersupp usbuf;
866         struct cdbdata *cdbus;
867
868         cdb_rewind(CDB_USERSUPP);
869
870         while(cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
871                 bzero(&usbuf, sizeof(struct usersupp));
872                 memcpy(&usbuf, cdbus->ptr,
873                         ( (cdbus->len > sizeof(struct usersupp)) ?
874                         sizeof(struct usersupp) : cdbus->len) );
875                 cdb_free(cdbus);
876                 (*CallBack)(&usbuf);
877                 }
878         }
879
880
881 /*
882  * List one user (this works with cmd_list)
883  */
884 void ListThisUser(struct usersupp *usbuf) {
885         if (usbuf->axlevel > 0) {
886                 if ((CC->usersupp.axlevel>=6)
887                    ||((usbuf->flags&US_UNLISTED)==0)
888                    ||((CC->internal_pgm))) {
889                         cprintf("%s|%d|%ld|%ld|%d|%d|",
890                                 usbuf->fullname,
891                                 usbuf->axlevel,
892                                 usbuf->usernum,
893                                 usbuf->lastcall,
894                                 usbuf->timescalled,
895                                 usbuf->posted);
896                         if (CC->usersupp.axlevel >= 6)
897                                 cprintf("%s",usbuf->password);
898                         cprintf("\n");
899                         }
900                 }
901         }
902
903 /* 
904  *  List users
905  */
906 void cmd_list(void) {
907         cprintf("%d \n",LISTING_FOLLOWS);
908         ForEachUser(ListThisUser);
909         cprintf("000\n");
910         }
911
912
913 /*
914  * enter registration info
915  */
916 void cmd_regi(void) {
917         int a,b,c;
918         char buf[256];
919
920         char tmpname[256];
921         char tmpaddr[256];
922         char tmpcity[256];
923         char tmpstate[256];
924         char tmpzip[256];
925         char tmpphone[256];
926         char tmpemail[256];
927
928         if (!(CC->logged_in)) {
929                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
930                 return;
931                 }
932
933         strcpy(tmpname,"");
934         strcpy(tmpaddr,"");
935         strcpy(tmpcity,"");
936         strcpy(tmpstate,"");
937         strcpy(tmpzip,"");
938         strcpy(tmpphone,"");
939         strcpy(tmpemail,"");
940
941         cprintf("%d Send registration...\n",SEND_LISTING);
942         a=0;
943         while (client_gets(buf), strcmp(buf,"000")) {
944                 if (a==0) strcpy(tmpname,buf);
945                 if (a==1) strcpy(tmpaddr,buf);
946                 if (a==2) strcpy(tmpcity,buf);
947                 if (a==3) strcpy(tmpstate,buf);
948                 if (a==4) {
949                         for (c=0; c<strlen(buf); ++c) {
950                                 if ((buf[c]>='0')&&(buf[c]<='9')) {
951                                         b=strlen(tmpzip);
952                                         tmpzip[b]=buf[c];
953                                         tmpzip[b+1]=0;
954                                         }
955                                 }
956                         }
957                 if (a==5) {
958                         for (c=0; c<strlen(buf); ++c) {
959                                 if ((buf[c]>='0')&&(buf[c]<='9')) {
960                                         b=strlen(tmpphone);
961                                         tmpphone[b]=buf[c];
962                                         tmpphone[b+1]=0;
963                                         }
964                                 }
965                         }
966                 if (a==6) strncpy(tmpemail,buf,31);
967                 ++a;
968                 }
969
970         tmpname[29]=0;
971         tmpaddr[24]=0;
972         tmpcity[14]=0;
973         tmpstate[2]=0;
974         tmpzip[9]=0;
975         tmpphone[10]=0;
976         tmpemail[31]=0;
977
978         lgetuser(&CC->usersupp,CC->curr_user);
979         strcpy(CC->usersupp.USname,tmpname);
980         strcpy(CC->usersupp.USaddr,tmpaddr);
981         strcpy(CC->usersupp.UScity,tmpcity);
982         strcpy(CC->usersupp.USstate,tmpstate);
983         strcpy(CC->usersupp.USzip,tmpzip);
984         strcpy(CC->usersupp.USphone,tmpphone);
985         strcpy(CC->usersupp.USemail,tmpemail);
986         CC->usersupp.flags=(CC->usersupp.flags|US_REGIS|US_NEEDVALID);
987         lputuser(&CC->usersupp,CC->curr_user);
988
989         /* set global flag calling for validation */
990         begin_critical_section(S_CONTROL);
991         get_control();
992         CitControl.MMflags = CitControl.MMflags | MM_VALID ;
993         put_control();
994         end_critical_section(S_CONTROL);
995         cprintf("%d *** End of registration.\n",OK);
996         }
997
998
999 /*
1000  * assorted info we need to check at login
1001  */
1002 void cmd_chek(void) {
1003         int mail = 0;
1004         int regis = 0;
1005         int vali = 0;
1006         
1007         if (!(CC->logged_in)) {
1008                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
1009                 return;
1010                 }
1011
1012         getuser(&CC->usersupp,CC->curr_user); /* no lock is needed here */
1013         if ((REGISCALL!=0)&&((CC->usersupp.flags&US_REGIS)==0)) regis = 1;
1014
1015         if (CC->usersupp.axlevel >= 6) {
1016                 get_control();
1017                 if (CitControl.MMflags&MM_VALID) vali = 1;
1018                 }
1019
1020
1021         /* check for mail */
1022         mail = NewMailCount();
1023
1024         cprintf("%d %d|%d|%d\n",OK,mail,regis,vali);
1025         }
1026
1027
1028 /*
1029  * check to see if a user exists
1030  */
1031 void cmd_qusr(char *who)
1032 {
1033         struct usersupp usbuf;
1034
1035         if (getuser(&usbuf,who) == 0) {
1036                 cprintf("%d %s\n",OK,usbuf.fullname);
1037                 }
1038         else {
1039                 cprintf("%d No such user.\n",ERROR+NO_SUCH_USER);
1040                 }
1041         }
1042
1043
1044 /*
1045  * enter user bio
1046  */
1047 void cmd_ebio(void) {
1048         char buf[256];
1049         FILE *fp;
1050
1051         if (!(CC->logged_in)) {
1052                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
1053                 return;
1054                 }
1055
1056         sprintf(buf,"./bio/%ld",CC->usersupp.usernum);
1057         fp = fopen(buf,"w");
1058         if (fp == NULL) {
1059                 cprintf("%d Cannot create file\n",ERROR);
1060                 return;
1061                 }
1062         cprintf("%d  \n",SEND_LISTING);
1063         while(client_gets(buf), strcmp(buf,"000")) {
1064                 fprintf(fp,"%s\n",buf);
1065                 }
1066         fclose(fp);
1067         }
1068
1069 /*
1070  * read user bio
1071  */
1072 void cmd_rbio(char *cmdbuf)
1073 {
1074         struct usersupp ruser;
1075         char buf[256];
1076         FILE *fp;
1077
1078         extract(buf,cmdbuf,0);
1079         if (getuser(&ruser,buf)!=0) {
1080                 cprintf("%d No such user.\n",ERROR+NO_SUCH_USER);
1081                 return;
1082                 }
1083         sprintf(buf,"./bio/%ld",ruser.usernum);
1084         
1085         fp = fopen(buf,"r");
1086         if (fp == NULL) {
1087                 cprintf("%d %s has no bio on file.\n",
1088                         ERROR+FILE_NOT_FOUND,ruser.fullname);
1089                 return;
1090                 }
1091         cprintf("%d  \n",LISTING_FOLLOWS);
1092         while (fgets(buf,256,fp)!=NULL) cprintf("%s",buf);
1093         fclose(fp);
1094         cprintf("000\n");
1095         }
1096
1097 /*
1098  * list of users who have entered bios
1099  */
1100 void cmd_lbio(void) {
1101         char buf[256];
1102         FILE *ls;
1103         struct usersupp usbuf;
1104
1105         ls=popen("cd ./bio; ls","r");
1106         if (ls==NULL) {
1107                 cprintf("%d Cannot open listing.\n",ERROR+FILE_NOT_FOUND);
1108                 return;
1109                 }
1110
1111         cprintf("%d\n",LISTING_FOLLOWS);
1112         while (fgets(buf,255,ls)!=NULL)
1113                 if (getuserbynumber(&usbuf,atol(buf))==0)
1114                         cprintf("%s\n",usbuf.fullname);
1115         pclose(ls);
1116         cprintf("000\n");
1117         }
1118
1119
1120 /*
1121  * Administrative Get User Parameters
1122  */
1123 void cmd_agup(char *cmdbuf) {
1124         struct usersupp usbuf;
1125         char requested_user[256];
1126
1127         if ( (CC->internal_pgm==0)
1128            && ( (CC->logged_in == 0) || (is_aide()==0) ) ) {
1129                 cprintf("%d Higher access required.\n", 
1130                         ERROR + HIGHER_ACCESS_REQUIRED);
1131                 return;
1132                 }
1133
1134         extract(requested_user, cmdbuf, 0);
1135         if (getuser(&usbuf, requested_user) != 0) {
1136                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1137                 return;
1138                 }
1139
1140         cprintf("%d %s|%s|%u|%d|%d|%d|%ld\n", 
1141                 OK,
1142                 usbuf.fullname,
1143                 usbuf.password,
1144                 usbuf.flags,
1145                 usbuf.timescalled,
1146                 usbuf.posted,
1147                 (int)usbuf.axlevel,
1148                 usbuf.usernum);
1149
1150         }
1151
1152
1153
1154 /*
1155  * Administrative Set User Parameters
1156  */
1157 void cmd_asup(char *cmdbuf) {
1158         struct usersupp usbuf;
1159         char requested_user[256];
1160         int np;
1161         int newax;
1162         
1163         if ( (CC->internal_pgm==0)
1164            && ( (CC->logged_in == 0) || (is_aide()==0) ) ) {
1165                 cprintf("%d Higher access required.\n", 
1166                         ERROR + HIGHER_ACCESS_REQUIRED);
1167                 return;
1168                 }
1169
1170         extract(requested_user, cmdbuf, 0);
1171         if (lgetuser(&usbuf, requested_user) != 0) {
1172                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1173                 return;
1174                 }
1175
1176         np = num_parms(cmdbuf);
1177         if (np > 1) extract(usbuf.password, cmdbuf, 1);
1178         if (np > 2) usbuf.flags = extract_int(cmdbuf, 2);
1179         if (np > 3) usbuf.timescalled = extract_int(cmdbuf, 3);
1180         if (np > 4) usbuf.posted = extract_int(cmdbuf, 4);
1181         if (np > 5) {
1182                 newax = extract_int(cmdbuf, 5);
1183                 if ((newax >=0) && (newax <= 6)) {
1184                         usbuf.axlevel = extract_int(cmdbuf, 5);
1185                         }
1186                 }
1187
1188         lputuser(&usbuf, requested_user);
1189         if (usbuf.axlevel == 0) {
1190                 if (purge_user(requested_user)==0) {
1191                         cprintf("%d %s deleted.\n", OK, requested_user);
1192                         }
1193                 }
1194         cprintf("%d Ok\n", OK);
1195         }
1196
1197
1198 /*
1199  * Count the number of new mail messages the user has
1200  */
1201 int NewMailCount() {
1202         int num_newmsgs = 0;
1203         struct cdbdata *cdbmb;
1204         int num_mails;
1205         long *mailbox;
1206         int a;
1207
1208         cdbmb = cdb_fetch(CDB_MAILBOXES, &CC->usersupp.usernum, sizeof(long));
1209         if (cdbmb != NULL) {
1210                 num_mails = cdbmb->len / sizeof(long);
1211                 mailbox = (long *) cdbmb->ptr;
1212                 if (num_mails > 0) for (a=0; a<num_mails; ++a) {
1213                         if (mailbox[a] > (CC->usersupp.lastseen[1]))
1214                                 ++num_newmsgs;
1215                         }
1216                 cdb_free(cdbmb);
1217                 }
1218         return(num_newmsgs);
1219         }