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