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