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