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