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