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