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