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