]> code.citadel.org Git - citadel.git/blob - citadel/user_ops.c
Misc small fixes to the new host auth mode.
[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 <ctype.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19
20 #if TIME_WITH_SYS_TIME
21 # include <sys/time.h>
22 # include <time.h>
23 #else
24 # if HAVE_SYS_TIME_H
25 #  include <sys/time.h>
26 # else
27 #  include <time.h>
28 # endif
29 #endif
30
31 #include <string.h>
32 #include <limits.h>
33 #ifndef ENABLE_CHKPWD
34 #include "auth.h"
35 #endif
36 #include "citadel.h"
37 #include "server.h"
38 #include "database.h"
39 #include "user_ops.h"
40 #include "serv_extensions.h"
41 #include "sysdep_decls.h"
42 #include "support.h"
43 #include "room_ops.h"
44 #include "file_ops.h"
45 #include "control.h"
46 #include "msgbase.h"
47 #include "config.h"
48 #include "tools.h"
49 #include "citserver.h"
50
51
52 /*
53  * makeuserkey() - convert a username into the format used as a database key
54  *                 (it's just the username converted into lower case)
55  */
56 static INLINE void makeuserkey(char *key, char *username) {
57         int i, len;
58
59         len = strlen(username);
60         for (i=0; i<=len; ++i) {
61                 key[i] = tolower(username[i]);
62         }
63 }
64
65
66 /*
67  * getuser()  -  retrieve named user into supplied buffer.
68  *               returns 0 on success
69  */
70 int getuser(struct ctdluser *usbuf, char name[])
71 {
72
73         char usernamekey[USERNAME_SIZE];
74         struct cdbdata *cdbus;
75
76         if (usbuf != NULL) {
77                 memset(usbuf, 0, sizeof(struct ctdluser));
78         }
79
80         makeuserkey(usernamekey, name);
81         cdbus = cdb_fetch(CDB_USERS, usernamekey, strlen(usernamekey));
82
83         if (cdbus == NULL) {    /* user not found */
84                 return(1);
85         }
86         if (usbuf != NULL) {
87                 memcpy(usbuf, cdbus->ptr,
88                         ((cdbus->len > sizeof(struct ctdluser)) ?
89                          sizeof(struct ctdluser) : cdbus->len));
90         }
91         cdb_free(cdbus);
92
93         return (0);
94 }
95
96
97 /*
98  * lgetuser()  -  same as getuser() but locks the record
99  */
100 int lgetuser(struct ctdluser *usbuf, char *name)
101 {
102         int retcode;
103
104         retcode = getuser(usbuf, name);
105         if (retcode == 0) {
106                 begin_critical_section(S_USERS);
107         }
108         return (retcode);
109 }
110
111
112 /*
113  * putuser()  -  write user buffer into the correct place on disk
114  */
115 void putuser(struct ctdluser *usbuf)
116 {
117         char usernamekey[USERNAME_SIZE];
118
119         makeuserkey(usernamekey, usbuf->fullname);
120
121         usbuf->version = REV_LEVEL;
122         cdb_store(CDB_USERS,
123                   usernamekey, strlen(usernamekey),
124                   usbuf, sizeof(struct ctdluser));
125
126 }
127
128
129 /*
130  * lputuser()  -  same as putuser() but locks the record
131  */
132 void lputuser(struct ctdluser *usbuf)
133 {
134         putuser(usbuf);
135         end_critical_section(S_USERS);
136 }
137
138 /*
139  * Index-generating function used by Ctdl[Get|Set]Relationship
140  */
141 int GenerateRelationshipIndex(char *IndexBuf,
142                               long RoomID,
143                               long RoomGen,
144                               long UserID)
145 {
146
147         struct {
148                 long iRoomID;
149                 long iRoomGen;
150                 long iUserID;
151         } TheIndex;
152
153         TheIndex.iRoomID = RoomID;
154         TheIndex.iRoomGen = RoomGen;
155         TheIndex.iUserID = UserID;
156
157         memcpy(IndexBuf, &TheIndex, sizeof(TheIndex));
158         return (sizeof(TheIndex));
159 }
160
161
162
163 /*
164  * Back end for CtdlSetRelationship()
165  */
166 void put_visit(struct visit *newvisit)
167 {
168         char IndexBuf[32];
169         int IndexLen;
170
171         /* Generate an index */
172         IndexLen = GenerateRelationshipIndex(IndexBuf,
173                                              newvisit->v_roomnum,
174                                              newvisit->v_roomgen,
175                                              newvisit->v_usernum);
176
177         /* Store the record */
178         cdb_store(CDB_VISIT, IndexBuf, IndexLen,
179                   newvisit, sizeof(struct visit)
180         );
181 }
182
183
184
185
186 /*
187  * Define a relationship between a user and a room
188  */
189 void CtdlSetRelationship(struct visit *newvisit,
190                          struct ctdluser *rel_user,
191                          struct ctdlroom *rel_room)
192 {
193
194
195         /* We don't use these in Citadel because they're implicit by the
196          * index, but they must be present if the database is exported.
197          */
198         newvisit->v_roomnum = rel_room->QRnumber;
199         newvisit->v_roomgen = rel_room->QRgen;
200         newvisit->v_usernum = rel_user->usernum;
201
202         put_visit(newvisit);
203 }
204
205 /*
206  * Locate a relationship between a user and a room
207  */
208 void CtdlGetRelationship(struct visit *vbuf,
209                          struct ctdluser *rel_user,
210                          struct ctdlroom *rel_room)
211 {
212
213         char IndexBuf[32];
214         int IndexLen;
215         struct cdbdata *cdbvisit;
216
217         /* Generate an index */
218         IndexLen = GenerateRelationshipIndex(IndexBuf,
219                                              rel_room->QRnumber,
220                                              rel_room->QRgen,
221                                              rel_user->usernum);
222
223         /* Clear out the buffer */
224         memset(vbuf, 0, sizeof(struct visit));
225
226         cdbvisit = cdb_fetch(CDB_VISIT, IndexBuf, IndexLen);
227         if (cdbvisit != NULL) {
228                 memcpy(vbuf, cdbvisit->ptr,
229                        ((cdbvisit->len > sizeof(struct visit)) ?
230                         sizeof(struct visit) : cdbvisit->len));
231                 cdb_free(cdbvisit);
232         }
233         else {
234                 /* If this is the first time the user has seen this room,
235                  * set the view to be the default for the room.
236                  */
237                 vbuf->v_view = rel_room->QRdefaultview;
238         }
239
240         /* Set v_seen if necessary */
241         if (vbuf->v_seen[0] == 0) {
242                 snprintf(vbuf->v_seen, sizeof vbuf->v_seen, "*:%ld", vbuf->v_lastseen);
243         }
244 }
245
246
247 void MailboxName(char *buf, size_t n, const struct ctdluser *who, const char *prefix)
248 {
249         snprintf(buf, n, "%010ld.%s", who->usernum, prefix);
250 }
251
252
253 /*
254  * Is the user currently logged in an Aide?
255  */
256 int is_aide(void)
257 {
258         if (CC->user.axlevel >= 6)
259                 return (1);
260         else
261                 return (0);
262 }
263
264
265 /*
266  * Is the user currently logged in an Aide *or* the room aide for this room?
267  */
268 int is_room_aide(void)
269 {
270
271         if (!CC->logged_in) {
272                 return (0);
273         }
274
275         if ((CC->user.axlevel >= 6)
276             || (CC->room.QRroomaide == CC->user.usernum)) {
277                 return (1);
278         } else {
279                 return (0);
280         }
281 }
282
283 /*
284  * getuserbynumber()  -  get user by number
285  *                       returns 0 if user was found
286  *
287  * WARNING: don't use this function unless you absolutely have to.  It does
288  *          a sequential search and therefore is computationally expensive.
289  */
290 int getuserbynumber(struct ctdluser *usbuf, long int number)
291 {
292         struct cdbdata *cdbus;
293
294         cdb_rewind(CDB_USERS);
295
296         while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
297                 memset(usbuf, 0, sizeof(struct ctdluser));
298                 memcpy(usbuf, cdbus->ptr,
299                        ((cdbus->len > sizeof(struct ctdluser)) ?
300                         sizeof(struct ctdluser) : cdbus->len));
301                 cdb_free(cdbus);
302                 if (usbuf->usernum == number) {
303                         cdb_close_cursor(CDB_USERS);
304                         return (0);
305                 }
306         }
307         return (-1);
308 }
309
310
311 #ifdef ENABLE_AUTOLOGIN
312 /*
313  * getuserbyuid()  -     get user by system uid (for PAM mode authentication)
314  *                       returns 0 if user was found
315  *
316  * WARNING: don't use this function unless you absolutely have to.  It does
317  *          a sequential search and therefore is computationally expensive.
318  */
319 int getuserbyuid(struct ctdluser *usbuf, uid_t number)
320 {
321         struct cdbdata *cdbus;
322
323         cdb_rewind(CDB_USERS);
324
325         while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
326                 memset(usbuf, 0, sizeof(struct ctdluser));
327                 memcpy(usbuf, cdbus->ptr,
328                        ((cdbus->len > sizeof(struct ctdluser)) ?
329                         sizeof(struct ctdluser) : cdbus->len));
330                 cdb_free(cdbus);
331                 if (usbuf->uid == number) {
332                         cdb_close_cursor(CDB_USERS);
333                         return (0);
334                 }
335         }
336         return (-1);
337 }
338 #endif /* ENABLE_AUTOLOGIN */
339
340
341
342 /*
343  * Back end for cmd_user() and its ilk
344  */
345 int CtdlLoginExistingUser(char *trythisname)
346 {
347         char username[SIZ];
348         int found_user;
349
350         if ((CC->logged_in)) {
351                 return login_already_logged_in;
352         }
353
354         if (trythisname == NULL) return login_not_found;
355         safestrncpy(username, trythisname, USERNAME_SIZE);
356         striplt(username);
357
358 #ifdef ENABLE_AUTOLOGIN
359
360         /* If this is an autologin build, the only valid auth source is the
361          * host operating system.
362          */
363         struct passwd pd;
364         struct passwd *tempPwdPtr;
365         char pwdbuffer[256];
366
367         lprintf(CTDL_DEBUG, "asking host about <%s>\n", username);
368         getpwnam_r(username, &pd, pwdbuffer, sizeof pwdbuffer, &tempPwdPtr);
369         if (tempPwdPtr == NULL) {
370                 return login_not_found;
371         }
372         lprintf(CTDL_DEBUG, "found it! uid=%d, gecos=%s\n", pd.pw_uid, pd.pw_gecos);
373
374         /* Locate the associated Citadel account.
375          * If not found, make one attempt to create it.
376          */
377         found_user = getuserbyuid(&CC->user, pd.pw_uid);
378         if (found_user != 0) {
379                 create_user(username, 0);
380                 found_user = getuserbyuid(&CC->user, pd.pw_uid);
381         }
382
383 #else /* ENABLE_AUTOLOGIN */
384         struct recptypes *valid = NULL;
385
386         /* First, try to log in as if the supplied name is a display name */
387         found_user = getuser(&CC->user, username);
388
389         /* If that didn't work, try to log in as if the supplied name
390          * is an e-mail address
391          */
392         if (found_user != 0) {
393                 valid = validate_recipients(username);
394                 if (valid != NULL) {
395                         if (valid->num_local == 1) {
396                                 found_user = getuser(&CC->user,
397                                                 valid->recp_local);
398                         }
399                         free(valid);
400                 }
401         }
402
403 #endif /* ENABLE_AUTOLOGIN */
404
405         /* Did we find something? */
406         if (found_user == 0) {
407                 if (((CC->nologin)) && (CC->user.axlevel < 6)) {
408                         return login_too_many_users;
409                 } else {
410                         safestrncpy(CC->curr_user, CC->user.fullname,
411                                         sizeof CC->curr_user);
412                         return login_ok;
413                 }
414         }
415         return login_not_found;
416 }
417
418
419
420 /*
421  * USER cmd
422  */
423 void cmd_user(char *cmdbuf)
424 {
425         char username[256];
426         int a;
427
428         extract_token(username, cmdbuf, 0, '|', sizeof username);
429         striplt(username);
430
431         a = CtdlLoginExistingUser(username);
432         switch (a) {
433         case login_already_logged_in:
434                 cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN);
435                 return;
436         case login_too_many_users:
437                 cprintf("%d %s: "
438                         "Too many users are already online "
439                         "(maximum is %d)\n",
440                         ERROR + MAX_SESSIONS_EXCEEDED,
441                         config.c_nodename, config.c_maxsessions);
442                 return;
443         case login_ok:
444                 cprintf("%d Password required for %s\n",
445                         MORE_DATA, CC->curr_user);
446                 return;
447         case login_not_found:
448                 cprintf("%d %s not found.\n", ERROR + NO_SUCH_USER, username);
449                 return;
450         default:
451                 cprintf("%d Internal error\n", ERROR + INTERNAL_ERROR);
452         }
453 }
454
455
456
457 /*
458  * session startup code which is common to both cmd_pass() and cmd_newu()
459  */
460 void session_startup(void)
461 {
462         int i;
463
464         lprintf(CTDL_NOTICE, "<%s> logged in\n", CC->curr_user);
465
466         lgetuser(&CC->user, CC->curr_user);
467         ++(CC->user.timescalled);
468         CC->previous_login = CC->user.lastcall;
469         time(&CC->user.lastcall);
470
471         /* If this user's name is the name of the system administrator
472          * (as specified in setup), automatically assign access level 6.
473          */
474         if (!strcasecmp(CC->user.fullname, config.c_sysadm)) {
475                 CC->user.axlevel = 6;
476         }
477
478 #ifdef ENABLE_AUTOLOGIN
479         /* If we're authenticating off the host system, automatically give
480          * root the highest level of access.
481          */
482         if (CC->user.uid == 0) {
483                 CC->user.axlevel = 6;
484         }
485 #endif
486
487         lputuser(&CC->user);
488
489         /*
490          * Populate CC->cs_inet_email with a default address.  This will be
491          * overwritten with the user's directory address, if one exists, when
492          * the vCard module's login hook runs.
493          */
494         snprintf(CC->cs_inet_email, sizeof CC->cs_inet_email, "%s@%s",
495                 CC->user.fullname, config.c_fqdn);
496         for (i=0; i<strlen(CC->cs_inet_email); ++i) {
497                 if (isspace(CC->cs_inet_email[i])) {
498                         CC->cs_inet_email[i] = '_';
499                 }
500         }
501
502         /* Create any personal rooms required by the system.
503          * (Technically, MAILROOM should be there already, but just in case...)
504          */
505         create_room(MAILROOM, 4, "", 0, 1, 0, VIEW_MAILBOX);
506         create_room(SENTITEMS, 4, "", 0, 1, 0, VIEW_MAILBOX);
507         create_room(USERTRASHROOM, 4, "", 0, 1, 0, VIEW_MAILBOX);
508
509         /* Run any startup routines registered by loadable modules */
510         PerformSessionHooks(EVT_LOGIN);
511
512         /* Enter the lobby */
513         usergoto(config.c_baseroom, 0, 0, NULL, NULL);
514 }
515
516
517 void logged_in_response(void)
518 {
519         cprintf("%d %s|%d|%ld|%ld|%u|%ld|%ld\n",
520                 CIT_OK, CC->user.fullname, CC->user.axlevel,
521                 CC->user.timescalled, CC->user.posted,
522                 CC->user.flags, CC->user.usernum,
523                 CC->previous_login);
524 }
525
526
527
528 /* 
529  * misc things to be taken care of when a user is logged out
530  */
531 void logout(struct CitContext *who)
532 {
533         /*
534          * Clear out some session data.  Most likely, the CitContext for this
535          * session is about to get nuked when the session disconnects, but
536          * since it's possible to log in again without reconnecting, we cannot
537          * make that assumption.
538          */
539         strcpy(who->fake_username, "");
540         strcpy(who->fake_postname, "");
541         strcpy(who->fake_hostname, "");
542         strcpy(who->fake_roomname, "");
543         who->logged_in = 0;
544
545         /*
546          * If there is a download in progress, abort it.
547          */
548         if (who->download_fp != NULL) {
549                 fclose(who->download_fp);
550                 who->download_fp = NULL;
551         }
552
553         /*
554          * If there is an upload in progress, abort it.
555          */
556         if (who->upload_fp != NULL) {
557                 abort_upl(who);
558         }
559
560         /*
561          * If we were talking to a network node, we're not anymore...
562          */
563         if (strlen(who->net_node) > 0) {
564                 network_talking_to(who->net_node, NTT_REMOVE);
565         }
566
567         /* Do modular stuff... */
568         PerformSessionHooks(EVT_LOGOUT);
569
570         /* Free any output buffers */
571         if (who->output_buffer != NULL) {
572                 unbuffer_output();
573         }
574 }
575
576 #ifdef ENABLE_CHKPWD
577 /*
578  * an alternate version of validpw() which executes `chkpwd' instead of
579  * verifying the password directly
580  */
581 static int validpw(uid_t uid, const char *pass)
582 {
583         pid_t pid;
584         int status, pipev[2];
585         char buf[24];
586
587         if (pipe(pipev)) {
588                 lprintf(CTDL_ERR, "pipe failed (%s): denying autologin access for "
589                         "uid %ld\n", strerror(errno), (long)uid);
590                 return 0;
591         }
592         switch (pid = fork()) {
593         case -1:
594                 lprintf(CTDL_ERR, "fork failed (%s): denying autologin access for "
595                         "uid %ld\n", strerror(errno), (long)uid);
596                 close(pipev[0]);
597                 close(pipev[1]);
598                 return 0;
599
600         case 0:
601                 close(pipev[1]);
602                 if (dup2(pipev[0], 0) == -1) {
603                         perror("dup2");
604                         exit(1);
605                 }
606                 close(pipev[0]);
607
608                 execl(CTDLDIR "/chkpwd", CTDLDIR "/chkpwd", NULL);
609                 perror(CTDLDIR "/chkpwd");
610                 exit(1);
611         }
612
613         close(pipev[0]);
614         write(pipev[1], buf,
615               snprintf(buf, sizeof buf, "%lu\n", (unsigned long) uid));
616         write(pipev[1], pass, strlen(pass));
617         write(pipev[1], "\n", 1);
618         close(pipev[1]);
619
620         while (waitpid(pid, &status, 0) == -1)
621                 if (errno != EINTR) {
622                         lprintf(CTDL_ERR, "waitpid failed (%s): denying autologin "
623                                 "access for uid %ld\n",
624                                 strerror(errno), (long)uid);
625                         return 0;
626                 }
627         if (WIFEXITED(status) && !WEXITSTATUS(status))
628                 return 1;
629
630         return 0;
631 }
632 #endif
633
634 void do_login()
635 {
636         (CC->logged_in) = 1;
637         session_startup();
638 }
639
640
641 int CtdlTryPassword(char *password)
642 {
643         int code;
644
645         if ((CC->logged_in)) {
646                 lprintf(CTDL_WARNING, "CtdlTryPassword: already logged in\n");
647                 return pass_already_logged_in;
648         }
649         if (!strcmp(CC->curr_user, NLI)) {
650                 lprintf(CTDL_WARNING, "CtdlTryPassword: no user selected\n");
651                 return pass_no_user;
652         }
653         if (getuser(&CC->user, CC->curr_user)) {
654                 lprintf(CTDL_ERR, "CtdlTryPassword: internal error\n");
655                 return pass_internal_error;
656         }
657         if (password == NULL) {
658                 lprintf(CTDL_INFO, "CtdlTryPassword: NULL password string supplied\n");
659                 return pass_wrong_password;
660         }
661         code = (-1);
662
663
664 #ifdef ENABLE_AUTOLOGIN
665
666         if (validpw(CC->user.uid, password)) {
667                 code = 0;
668                 /* we could get rid of this */
669                 lgetuser(&CC->user, CC->curr_user);
670                 safestrncpy(CC->user.password, password, sizeof CC->user.password);
671                 lputuser(&CC->user);
672                 /*                          */
673         }
674         else {
675                 code = (-1);
676         }
677
678 #else /* ENABLE_AUTOLOGIN */
679
680         strproc(password);
681         strproc(CC->user.password);
682         code = strcasecmp(CC->user.password, password);
683         strproc(password);
684         strproc(CC->user.password);
685         code = strcasecmp(CC->user.password, password);
686
687 #endif /* ENABLE_AUTOLOGIN */
688
689         if (!code) {
690                 do_login();
691                 return pass_ok;
692         } else {
693                 lprintf(CTDL_WARNING, "Bad password specified for <%s>\n", CC->curr_user);
694                 return pass_wrong_password;
695         }
696 }
697
698
699 void cmd_pass(char *buf)
700 {
701         char password[256];
702         int a;
703
704         extract_token(password, buf, 0, '|', sizeof password);
705         a = CtdlTryPassword(password);
706
707         switch (a) {
708         case pass_already_logged_in:
709                 cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN);
710                 return;
711         case pass_no_user:
712                 cprintf("%d You must send a name with USER first.\n",
713                         ERROR + USERNAME_REQUIRED);
714                 return;
715         case pass_wrong_password:
716                 cprintf("%d Wrong password.\n", ERROR + PASSWORD_REQUIRED);
717                 return;
718         case pass_ok:
719                 logged_in_response();
720                 return;
721         }
722 }
723
724
725
726 /*
727  * Delete a user record *and* all of its related resources.
728  */
729 int purge_user(char pname[])
730 {
731         char filename[64];
732         struct ctdluser usbuf;
733         char usernamekey[USERNAME_SIZE];
734         struct CitContext *ccptr;
735         int user_is_logged_in = 0;
736
737         makeuserkey(usernamekey, pname);
738
739         if (getuser(&usbuf, pname) != 0) {
740                 lprintf(CTDL_ERR, "Cannot purge user <%s> - not found\n", pname);
741                 return (ERROR + NO_SUCH_USER);
742         }
743         /* Don't delete a user who is currently logged in.  Instead, just
744          * set the access level to 0, and let the account get swept up
745          * during the next purge.
746          */
747         user_is_logged_in = 0;
748         begin_critical_section(S_SESSION_TABLE);
749         for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) {
750                 if (ccptr->user.usernum == usbuf.usernum) {
751                         user_is_logged_in = 1;
752                 }
753         }
754         end_critical_section(S_SESSION_TABLE);
755         if (user_is_logged_in == 1) {
756                 lprintf(CTDL_WARNING, "User <%s> is logged in; not deleting.\n", pname);
757                 usbuf.axlevel = 0;
758                 putuser(&usbuf);
759                 return (1);
760         }
761         lprintf(CTDL_NOTICE, "Deleting user <%s>\n", pname);
762
763         /* Perform any purge functions registered by server extensions */
764         PerformUserHooks(&usbuf, EVT_PURGEUSER);
765
766         /* delete any existing user/room relationships */
767         cdb_delete(CDB_VISIT, &usbuf.usernum, sizeof(long));
768
769         /* delete the userlog entry */
770         cdb_delete(CDB_USERS, usernamekey, strlen(usernamekey));
771
772         /* remove the user's bio file */
773         snprintf(filename, 
774                          sizeof filename, 
775                          "%s/%ld",
776                          ctdl_bio_dir,
777                          usbuf.usernum);
778         unlink(filename);
779
780         /* remove the user's picture */
781         snprintf(filename, 
782                          sizeof filename, 
783                          "%s/%ld.gif",
784                          ctdl_image_dir,
785                          usbuf.usernum);
786         unlink(filename);
787
788         return (0);
789 }
790
791
792 /*
793  * create_user()  -  back end processing to create a new user
794  *
795  * Set 'newusername' to the desired account name.
796  * Set 'become_user' to nonzero if this is self-service account creation and we want
797  * to actually log in as the user we just created, otherwise set it to 0.
798  */
799 int create_user(char *newusername, int become_user)
800 {
801         struct ctdluser usbuf;
802         struct ctdlroom qrbuf;
803         char username[256];
804         char mailboxname[ROOMNAMELEN];
805         uid_t uid = (-1);
806
807         safestrncpy(username, newusername, sizeof username);
808         strproc(username);
809
810 #ifdef ENABLE_AUTOLOGIN
811         struct passwd pd;
812         struct passwd *tempPwdPtr;
813         char pwdbuffer[256];
814
815         getpwnam_r(username, &pd, pwdbuffer, sizeof pwdbuffer, &tempPwdPtr);
816         if (tempPwdPtr != NULL) {
817                 extract_token(username, pd.pw_gecos, 0, ',', sizeof username);
818                 uid = pd.pw_uid;
819         }
820         else {
821                 return (ERROR + NO_SUCH_USER);
822         }
823 #endif
824
825         if (!getuser(&usbuf, username)) {
826                 return (ERROR + ALREADY_EXISTS);
827         }
828
829         /* Go ahead and initialize a new user record */
830         memset(&usbuf, 0, sizeof(struct ctdluser));
831         safestrncpy(usbuf.fullname, username, sizeof usbuf.fullname);
832         strcpy(usbuf.password, "");
833         usbuf.uid = uid;
834
835         /* These are the default flags on new accounts */
836         usbuf.flags = US_LASTOLD | US_DISAPPEAR | US_PAGINATOR | US_FLOORS;
837
838         usbuf.timescalled = 0;
839         usbuf.posted = 0;
840         usbuf.axlevel = config.c_initax;
841         usbuf.USscreenwidth = 80;
842         usbuf.USscreenheight = 24;
843         usbuf.lastcall = time(NULL);
844
845         /* fetch a new user number */
846         usbuf.usernum = get_new_user_number();
847
848         /* The very first user created on the system will always be an Aide */
849         if (usbuf.usernum == 1L) {
850                 usbuf.axlevel = 6;
851         }
852
853         /* add user to userlog */
854         putuser(&usbuf);
855
856         /*
857          * Give the user a private mailbox and a configuration room.
858          * Make the latter an invisible system room.
859          */
860         MailboxName(mailboxname, sizeof mailboxname, &usbuf, MAILROOM);
861         create_room(mailboxname, 5, "", 0, 1, 1, VIEW_MAILBOX);
862
863         MailboxName(mailboxname, sizeof mailboxname, &usbuf, USERCONFIGROOM);
864         create_room(mailboxname, 5, "", 0, 1, 1, VIEW_BBS);
865         if (lgetroom(&qrbuf, mailboxname) == 0) {
866                 qrbuf.QRflags2 |= QR2_SYSTEM;
867                 lputroom(&qrbuf);
868         }
869
870         /* Perform any create functions registered by server extensions */
871         PerformUserHooks(&usbuf, EVT_NEWUSER);
872
873         /* Everything below this line can be bypassed if administratively
874          * creating a user, instead of doing self-service account creation
875          */
876
877         if (become_user) {
878                 /* Now become the user we just created */
879                 memcpy(&CC->user, &usbuf, sizeof(struct ctdluser));
880                 safestrncpy(CC->curr_user, username, sizeof CC->curr_user);
881                 CC->logged_in = 1;
882         
883                 /* Check to make sure we're still who we think we are */
884                 if (getuser(&CC->user, CC->curr_user)) {
885                         return (ERROR + INTERNAL_ERROR);
886                 }
887         }
888
889         lprintf(CTDL_NOTICE, "New user <%s> created\n", username);
890         return (0);
891 }
892
893
894
895
896 /*
897  * cmd_newu()  -  create a new user account and log in as that user
898  */
899 void cmd_newu(char *cmdbuf)
900 {
901         int a;
902         char username[26];
903
904 #ifdef ENABLE_AUTOLOGIN
905         cprintf("%d This system does not use native mode authentication.\n",
906                 ERROR + NOT_HERE);
907         return;
908 #endif /* ENABLE_AUTOLOGIN */
909
910         if (config.c_disable_newu) {
911                 cprintf("%d Self-service user account creation "
912                         "is disabled on this system.\n", ERROR + NOT_HERE);
913                 return;
914         }
915
916         if (CC->logged_in) {
917                 cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN);
918                 return;
919         }
920         if (CC->nologin) {
921                 cprintf("%d %s: Too many users are already online (maximum is %d)\n",
922                         ERROR + MAX_SESSIONS_EXCEEDED,
923                         config.c_nodename, config.c_maxsessions);
924         }
925         extract_token(username, cmdbuf, 0, '|', sizeof username);
926         username[25] = 0;
927         strproc(username);
928
929         if (strlen(username) == 0) {
930                 cprintf("%d You must supply a user name.\n", ERROR + USERNAME_REQUIRED);
931                 return;
932         }
933
934         if ((!strcasecmp(username, "bbs")) ||
935             (!strcasecmp(username, "new")) ||
936             (!strcasecmp(username, "."))) {
937                 cprintf("%d '%s' is an invalid login name.\n", ERROR + ILLEGAL_VALUE, username);
938                 return;
939         }
940
941         a = create_user(username, 1);
942
943         if (a == 0) {
944                 session_startup();
945                 logged_in_response();
946         } else if (a == ERROR + ALREADY_EXISTS) {
947                 cprintf("%d '%s' already exists.\n",
948                         ERROR + ALREADY_EXISTS, username);
949                 return;
950         } else if (a == ERROR + INTERNAL_ERROR) {
951                 cprintf("%d Internal error - user record disappeared?\n",
952                         ERROR + INTERNAL_ERROR);
953                 return;
954         } else {
955                 cprintf("%d unknown error\n", ERROR + INTERNAL_ERROR);
956         }
957 }
958
959
960
961 /*
962  * set password
963  */
964 void cmd_setp(char *new_pw)
965 {
966         if (CtdlAccessCheck(ac_logged_in)) {
967                 return;
968         }
969         if ( (CC->user.uid != CTDLUID) && (CC->user.uid != (-1)) ) {
970                 cprintf("%d Not allowed.  Use the 'passwd' command.\n", ERROR + NOT_HERE);
971                 return;
972         }
973         strproc(new_pw);
974         if (strlen(new_pw) == 0) {
975                 cprintf("%d Password unchanged.\n", CIT_OK);
976                 return;
977         }
978         lgetuser(&CC->user, CC->curr_user);
979         safestrncpy(CC->user.password, new_pw, sizeof(CC->user.password));
980         lputuser(&CC->user);
981         cprintf("%d Password changed.\n", CIT_OK);
982         lprintf(CTDL_INFO, "Password changed for user <%s>\n", CC->curr_user);
983         PerformSessionHooks(EVT_SETPASS);
984 }
985
986
987 /*
988  * cmd_creu() - administratively create a new user account (do not log in to it)
989  */
990 void cmd_creu(char *cmdbuf)
991 {
992         int a;
993         char username[26];
994         char password[32];
995         struct ctdluser tmp;
996
997         if (CtdlAccessCheck(ac_aide)) {
998                 return;
999         }
1000
1001         extract_token(username, cmdbuf, 0, '|', sizeof username);
1002         extract_token(password, cmdbuf, 1, '|', sizeof password);
1003         username[25] = 0;
1004         password[31] = 0;
1005         strproc(username);
1006         strproc(password);
1007
1008         if (strlen(username) == 0) {
1009                 cprintf("%d You must supply a user name.\n", ERROR + USERNAME_REQUIRED);
1010                 return;
1011         }
1012
1013         a = create_user(username, 0);
1014
1015         if (a == 0) {
1016                 if (strlen(password) > 0) {
1017                         lgetuser(&tmp, username);
1018                         safestrncpy(tmp.password, password, sizeof(tmp.password));
1019                         lputuser(&tmp);
1020                 }
1021                 cprintf("%d User '%s' created %s.\n", CIT_OK, username,
1022                                 (strlen(password) > 0) ? "and password set" :
1023                                 "with no password");
1024                 return;
1025         } else if (a == ERROR + ALREADY_EXISTS) {
1026                 cprintf("%d '%s' already exists.\n",
1027                         ERROR + ALREADY_EXISTS, username);
1028                 return;
1029         } else {
1030                 cprintf("%d An error occured creating the user account.\n", ERROR + INTERNAL_ERROR);
1031         }
1032 }
1033
1034
1035
1036 /*
1037  * get user parameters
1038  */
1039 void cmd_getu(void)
1040 {
1041
1042         if (CtdlAccessCheck(ac_logged_in))
1043                 return;
1044
1045         getuser(&CC->user, CC->curr_user);
1046         cprintf("%d %d|%d|%d|\n",
1047                 CIT_OK,
1048                 CC->user.USscreenwidth,
1049                 CC->user.USscreenheight,
1050                 (CC->user.flags & US_USER_SET)
1051             );
1052 }
1053
1054 /*
1055  * set user parameters
1056  */
1057 void cmd_setu(char *new_parms)
1058 {
1059         if (CtdlAccessCheck(ac_logged_in))
1060                 return;
1061
1062         if (num_parms(new_parms) < 3) {
1063                 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
1064                 return;
1065         }
1066         lgetuser(&CC->user, CC->curr_user);
1067         CC->user.USscreenwidth = extract_int(new_parms, 0);
1068         CC->user.USscreenheight = extract_int(new_parms, 1);
1069         CC->user.flags = CC->user.flags & (~US_USER_SET);
1070         CC->user.flags = CC->user.flags |
1071             (extract_int(new_parms, 2) & US_USER_SET);
1072
1073         lputuser(&CC->user);
1074         cprintf("%d Ok\n", CIT_OK);
1075 }
1076
1077 /*
1078  * set last read pointer
1079  */
1080 void cmd_slrp(char *new_ptr)
1081 {
1082         long newlr;
1083         struct visit vbuf;
1084         struct visit original_vbuf;
1085
1086         if (CtdlAccessCheck(ac_logged_in)) {
1087                 return;
1088         }
1089
1090         if (!strncasecmp(new_ptr, "highest", 7)) {
1091                 newlr = CC->room.QRhighest;
1092         } else {
1093                 newlr = atol(new_ptr);
1094         }
1095
1096         lgetuser(&CC->user, CC->curr_user);
1097
1098         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
1099         memcpy(&original_vbuf, &vbuf, sizeof(struct visit));
1100         vbuf.v_lastseen = newlr;
1101         snprintf(vbuf.v_seen, sizeof vbuf.v_seen, "*:%ld", newlr);
1102
1103         /* Only rewrite the record if it changed */
1104         if ( (vbuf.v_lastseen != original_vbuf.v_lastseen)
1105            || (strcmp(vbuf.v_seen, original_vbuf.v_seen)) ) {
1106                 CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
1107         }
1108
1109         lputuser(&CC->user);
1110         cprintf("%d %ld\n", CIT_OK, newlr);
1111 }
1112
1113
1114 void cmd_seen(char *argbuf) {
1115         long target_msgnum = 0L;
1116         int target_setting = 0;
1117
1118         if (CtdlAccessCheck(ac_logged_in)) {
1119                 return;
1120         }
1121
1122         if (num_parms(argbuf) != 2) {
1123                 cprintf("%d Invalid parameters\n", ERROR + ILLEGAL_VALUE);
1124                 return;
1125         }
1126
1127         target_msgnum = extract_long(argbuf, 0);
1128         target_setting = extract_int(argbuf, 1);
1129
1130         CtdlSetSeen(&target_msgnum, 1, target_setting,
1131                         ctdlsetseen_seen, NULL, NULL);
1132         cprintf("%d OK\n", CIT_OK);
1133 }
1134
1135
1136 void cmd_gtsn(char *argbuf) {
1137         char buf[SIZ];
1138
1139         if (CtdlAccessCheck(ac_logged_in)) {
1140                 return;
1141         }
1142
1143         CtdlGetSeen(buf, ctdlsetseen_seen);
1144         cprintf("%d %s\n", CIT_OK, buf);
1145 }
1146
1147
1148 /*
1149  * API function for cmd_invt_kick() and anything else that needs to
1150  * invite or kick out a user to/from a room.
1151  * 
1152  * Set iuser to the name of the user, and op to 1=invite or 0=kick
1153  */
1154 int CtdlInvtKick(char *iuser, int op) {
1155         struct ctdluser USscratch;
1156         struct visit vbuf;
1157         char bbb[SIZ];
1158
1159         if (getuser(&USscratch, iuser) != 0) {
1160                 return(1);
1161         }
1162
1163         CtdlGetRelationship(&vbuf, &USscratch, &CC->room);
1164         if (op == 1) {
1165                 vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
1166                 vbuf.v_flags = vbuf.v_flags | V_ACCESS;
1167         }
1168         if (op == 0) {
1169                 vbuf.v_flags = vbuf.v_flags & ~V_ACCESS;
1170                 vbuf.v_flags = vbuf.v_flags | V_FORGET | V_LOCKOUT;
1171         }
1172         CtdlSetRelationship(&vbuf, &USscratch, &CC->room);
1173
1174         /* post a message in Aide> saying what we just did */
1175         snprintf(bbb, sizeof bbb, "%s has been %s \"%s\" by %s.\n",
1176                 iuser,
1177                 ((op == 1) ? "invited to" : "kicked out of"),
1178                 CC->room.QRname,
1179                 CC->user.fullname);
1180         aide_message(bbb);
1181
1182         return(0);
1183 }
1184
1185
1186 /*
1187  * INVT and KICK commands
1188  */
1189 void cmd_invt_kick(char *iuser, int op) {
1190
1191         /*
1192          * These commands are only allowed by aides, room aides,
1193          * and room namespace owners
1194          */
1195         if (is_room_aide()
1196            || (atol(CC->room.QRname) == CC->user.usernum) ) {
1197                 /* access granted */
1198         } else {
1199                 /* access denied */
1200                 cprintf("%d Higher access or room ownership required.\n",
1201                         ERROR + HIGHER_ACCESS_REQUIRED);
1202                 return;
1203         }
1204
1205         if (!strncasecmp(CC->room.QRname, config.c_baseroom,
1206                          ROOMNAMELEN)) {
1207                 cprintf("%d Can't add/remove users from this room.\n",
1208                         ERROR + NOT_HERE);
1209                 return;
1210         }
1211
1212         if (CtdlInvtKick(iuser, op) != 0) {
1213                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1214                 return;
1215         }
1216
1217         cprintf("%d %s %s %s.\n",
1218                 CIT_OK, iuser,
1219                 ((op == 1) ? "invited to" : "kicked out of"),
1220                 CC->room.QRname);
1221         return;
1222 }
1223
1224
1225 /*
1226  * Forget (Zap) the current room (API call)
1227  * Returns 0 on success
1228  */
1229 int CtdlForgetThisRoom(void) {
1230         struct visit vbuf;
1231
1232         /* On some systems, Aides are not allowed to forget rooms */
1233         if (is_aide() && (config.c_aide_zap == 0)
1234            && ((CC->room.QRflags & QR_MAILBOX) == 0)  ) {
1235                 return(1);
1236         }
1237
1238         lgetuser(&CC->user, CC->curr_user);
1239         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
1240
1241         vbuf.v_flags = vbuf.v_flags | V_FORGET;
1242         vbuf.v_flags = vbuf.v_flags & ~V_ACCESS;
1243
1244         CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
1245         lputuser(&CC->user);
1246
1247         /* Return to the Lobby, so we don't end up in an undefined room */
1248         usergoto(config.c_baseroom, 0, 0, NULL, NULL);
1249         return(0);
1250
1251 }
1252
1253
1254 /*
1255  * forget (Zap) the current room
1256  */
1257 void cmd_forg(void)
1258 {
1259
1260         if (CtdlAccessCheck(ac_logged_in)) {
1261                 return;
1262         }
1263
1264         if (CtdlForgetThisRoom() == 0) {
1265                 cprintf("%d Ok\n", CIT_OK);
1266         }
1267         else {
1268                 cprintf("%d You may not forget this room.\n", ERROR + NOT_HERE);
1269         }
1270 }
1271
1272 /*
1273  * Get Next Unregistered User
1274  */
1275 void cmd_gnur(void)
1276 {
1277         struct cdbdata *cdbus;
1278         struct ctdluser usbuf;
1279
1280         if (CtdlAccessCheck(ac_aide)) {
1281                 return;
1282         }
1283
1284         if ((CitControl.MMflags & MM_VALID) == 0) {
1285                 cprintf("%d There are no unvalidated users.\n", CIT_OK);
1286                 return;
1287         }
1288
1289         /* There are unvalidated users.  Traverse the user database,
1290          * and return the first user we find that needs validation.
1291          */
1292         cdb_rewind(CDB_USERS);
1293         while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
1294                 memset(&usbuf, 0, sizeof(struct ctdluser));
1295                 memcpy(&usbuf, cdbus->ptr,
1296                        ((cdbus->len > sizeof(struct ctdluser)) ?
1297                         sizeof(struct ctdluser) : cdbus->len));
1298                 cdb_free(cdbus);
1299                 if ((usbuf.flags & US_NEEDVALID)
1300                     && (usbuf.axlevel > 0)) {
1301                         cprintf("%d %s\n", MORE_DATA, usbuf.fullname);
1302                         cdb_close_cursor(CDB_USERS);
1303                         return;
1304                 }
1305         }
1306
1307         /* If we get to this point, there are no more unvalidated users.
1308          * Therefore we clear the "users need validation" flag.
1309          */
1310
1311         begin_critical_section(S_CONTROL);
1312         get_control();
1313         CitControl.MMflags = CitControl.MMflags & (~MM_VALID);
1314         put_control();
1315         end_critical_section(S_CONTROL);
1316         cprintf("%d *** End of registration.\n", CIT_OK);
1317
1318
1319 }
1320
1321
1322 /*
1323  * validate a user
1324  */
1325 void cmd_vali(char *v_args)
1326 {
1327         char user[128];
1328         int newax;
1329         struct ctdluser userbuf;
1330
1331         extract_token(user, v_args, 0, '|', sizeof user);
1332         newax = extract_int(v_args, 1);
1333
1334         if (CtdlAccessCheck(ac_aide)) {
1335                 return;
1336         }
1337
1338         if (lgetuser(&userbuf, user) != 0) {
1339                 cprintf("%d '%s' not found.\n", ERROR + NO_SUCH_USER, user);
1340                 return;
1341         }
1342
1343         userbuf.axlevel = newax;
1344         userbuf.flags = (userbuf.flags & ~US_NEEDVALID);
1345
1346         lputuser(&userbuf);
1347
1348         /* If the access level was set to zero, delete the user */
1349         if (newax == 0) {
1350                 if (purge_user(user) == 0) {
1351                         cprintf("%d %s Deleted.\n", CIT_OK, userbuf.fullname);
1352                         return;
1353                 }
1354         }
1355         cprintf("%d User '%s' validated.\n", CIT_OK, userbuf.fullname);
1356 }
1357
1358
1359
1360 /* 
1361  *  Traverse the user file...
1362  */
1363 void ForEachUser(void (*CallBack) (struct ctdluser * EachUser, void *out_data),
1364                  void *in_data)
1365 {
1366         struct ctdluser usbuf;
1367         struct cdbdata *cdbus;
1368
1369         cdb_rewind(CDB_USERS);
1370
1371         while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
1372                 memset(&usbuf, 0, sizeof(struct ctdluser));
1373                 memcpy(&usbuf, cdbus->ptr,
1374                        ((cdbus->len > sizeof(struct ctdluser)) ?
1375                         sizeof(struct ctdluser) : cdbus->len));
1376                 cdb_free(cdbus);
1377                 (*CallBack) (&usbuf, in_data);
1378         }
1379 }
1380
1381
1382 /*
1383  * List one user (this works with cmd_list)
1384  */
1385 void ListThisUser(struct ctdluser *usbuf, void *data)
1386 {
1387         char *searchstring;
1388
1389         searchstring = (char *)data;
1390         if (bmstrcasestr(usbuf->fullname, searchstring) == NULL) {
1391                 return;
1392         }
1393
1394         if (usbuf->axlevel > 0) {
1395                 if ((CC->user.axlevel >= 6)
1396                     || ((usbuf->flags & US_UNLISTED) == 0)
1397                     || ((CC->internal_pgm))) {
1398                         cprintf("%s|%d|%ld|%ld|%ld|%ld|",
1399                                 usbuf->fullname,
1400                                 usbuf->axlevel,
1401                                 usbuf->usernum,
1402                                 (long)usbuf->lastcall,
1403                                 usbuf->timescalled,
1404                                 usbuf->posted);
1405                         if (CC->user.axlevel >= 6)
1406                                 cprintf("%s", usbuf->password);
1407                         cprintf("\n");
1408                 }
1409         }
1410 }
1411
1412 /* 
1413  *  List users (searchstring may be empty to list all users)
1414  */
1415 void cmd_list(char *cmdbuf)
1416 {
1417         char searchstring[256];
1418         extract_token(searchstring, cmdbuf, 0, '|', sizeof searchstring);
1419         striplt(searchstring);
1420         cprintf("%d \n", LISTING_FOLLOWS);
1421         ForEachUser(ListThisUser, (void *)searchstring );
1422         cprintf("000\n");
1423 }
1424
1425
1426
1427
1428 /*
1429  * assorted info we need to check at login
1430  */
1431 void cmd_chek(void)
1432 {
1433         int mail = 0;
1434         int regis = 0;
1435         int vali = 0;
1436
1437         if (CtdlAccessCheck(ac_logged_in)) {
1438                 return;
1439         }
1440
1441         getuser(&CC->user, CC->curr_user);      /* no lock is needed here */
1442         if ((REGISCALL != 0) && ((CC->user.flags & US_REGIS) == 0))
1443                 regis = 1;
1444
1445         if (CC->user.axlevel >= 6) {
1446                 get_control();
1447                 if (CitControl.MMflags & MM_VALID)
1448                         vali = 1;
1449         }
1450
1451         /* check for mail */
1452         mail = InitialMailCheck();
1453
1454         cprintf("%d %d|%d|%d|%s|\n", CIT_OK, mail, regis, vali, CC->cs_inet_email);
1455 }
1456
1457
1458 /*
1459  * check to see if a user exists
1460  */
1461 void cmd_qusr(char *who)
1462 {
1463         struct ctdluser usbuf;
1464
1465         if (getuser(&usbuf, who) == 0) {
1466                 cprintf("%d %s\n", CIT_OK, usbuf.fullname);
1467         } else {
1468                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1469         }
1470 }
1471
1472
1473 /*
1474  * Administrative Get User Parameters
1475  */
1476 void cmd_agup(char *cmdbuf)
1477 {
1478         struct ctdluser usbuf;
1479         char requested_user[128];
1480
1481         if (CtdlAccessCheck(ac_aide)) {
1482                 return;
1483         }
1484
1485         extract_token(requested_user, cmdbuf, 0, '|', sizeof requested_user);
1486         if (getuser(&usbuf, requested_user) != 0) {
1487                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1488                 return;
1489         }
1490         cprintf("%d %s|%s|%u|%ld|%ld|%d|%ld|%ld|%d\n",
1491                 CIT_OK,
1492                 usbuf.fullname,
1493                 usbuf.password,
1494                 usbuf.flags,
1495                 usbuf.timescalled,
1496                 usbuf.posted,
1497                 (int) usbuf.axlevel,
1498                 usbuf.usernum,
1499                 (long)usbuf.lastcall,
1500                 usbuf.USuserpurge);
1501 }
1502
1503
1504
1505 /*
1506  * Administrative Set User Parameters
1507  */
1508 void cmd_asup(char *cmdbuf)
1509 {
1510         struct ctdluser usbuf;
1511         char requested_user[128];
1512         char notify[SIZ];
1513         int np;
1514         int newax;
1515         int deleted = 0;
1516
1517         if (CtdlAccessCheck(ac_aide))
1518                 return;
1519
1520         extract_token(requested_user, cmdbuf, 0, '|', sizeof requested_user);
1521         if (lgetuser(&usbuf, requested_user) != 0) {
1522                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1523                 return;
1524         }
1525         np = num_parms(cmdbuf);
1526         if (np > 1)
1527                 extract_token(usbuf.password, cmdbuf, 1, '|', sizeof usbuf.password);
1528         if (np > 2)
1529                 usbuf.flags = extract_int(cmdbuf, 2);
1530         if (np > 3)
1531                 usbuf.timescalled = extract_int(cmdbuf, 3);
1532         if (np > 4)
1533                 usbuf.posted = extract_int(cmdbuf, 4);
1534         if (np > 5) {
1535                 newax = extract_int(cmdbuf, 5);
1536                 if ((newax >= 0) && (newax <= 6)) {
1537                         usbuf.axlevel = extract_int(cmdbuf, 5);
1538                 }
1539         }
1540         if (np > 7) {
1541                 usbuf.lastcall = extract_long(cmdbuf, 7);
1542         }
1543         if (np > 8) {
1544                 usbuf.USuserpurge = extract_int(cmdbuf, 8);
1545         }
1546         lputuser(&usbuf);
1547         if (usbuf.axlevel == 0) {
1548                 if (purge_user(requested_user) == 0) {
1549                         deleted = 1;
1550                 }
1551         }
1552
1553         if (deleted) {
1554                 sprintf(notify, "User \"%s\" has been deleted by %s.\n",
1555                         usbuf.fullname, CC->user.fullname);
1556                 aide_message(notify);
1557         }
1558
1559         cprintf("%d Ok", CIT_OK);
1560         if (deleted)
1561                 cprintf(" (%s deleted)", requested_user);
1562         cprintf("\n");
1563 }
1564
1565
1566
1567 /*
1568  * Check to see if the user who we just sent mail to is logged in.  If yes,
1569  * bump the 'new mail' counter for their session.  That enables them to
1570  * receive a new mail notification without having to hit the database.
1571  */
1572 void BumpNewMailCounter(long which_user) {
1573         struct CitContext *ptr;
1574
1575         begin_critical_section(S_SESSION_TABLE);
1576
1577         for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
1578                 if (ptr->user.usernum == which_user) {
1579                         ptr->newmail += 1;
1580                 }
1581         }
1582
1583         end_critical_section(S_SESSION_TABLE);
1584 }
1585
1586
1587 /*
1588  * Count the number of new mail messages the user has
1589  */
1590 int NewMailCount()
1591 {
1592         int num_newmsgs = 0;
1593
1594         num_newmsgs = CC->newmail;
1595         CC->newmail = 0;
1596
1597         return (num_newmsgs);
1598 }
1599
1600
1601 /*
1602  * Count the number of new mail messages the user has
1603  */
1604 int InitialMailCheck()
1605 {
1606         int num_newmsgs = 0;
1607         int a;
1608         char mailboxname[ROOMNAMELEN];
1609         struct ctdlroom mailbox;
1610         struct visit vbuf;
1611         struct cdbdata *cdbfr;
1612         long *msglist = NULL;
1613         int num_msgs = 0;
1614
1615         MailboxName(mailboxname, sizeof mailboxname, &CC->user, MAILROOM);
1616         if (getroom(&mailbox, mailboxname) != 0)
1617                 return (0);
1618         CtdlGetRelationship(&vbuf, &CC->user, &mailbox);
1619
1620         cdbfr = cdb_fetch(CDB_MSGLISTS, &mailbox.QRnumber, sizeof(long));
1621
1622         if (cdbfr != NULL) {
1623                 msglist = malloc(cdbfr->len);
1624                 memcpy(msglist, cdbfr->ptr, cdbfr->len);
1625                 num_msgs = cdbfr->len / sizeof(long);
1626                 cdb_free(cdbfr);
1627         }
1628         if (num_msgs > 0)
1629                 for (a = 0; a < num_msgs; ++a) {
1630                         if (msglist[a] > 0L) {
1631                                 if (msglist[a] > vbuf.v_lastseen) {
1632                                         ++num_newmsgs;
1633                                 }
1634                         }
1635                 }
1636         if (msglist != NULL)
1637                 free(msglist);
1638
1639         return (num_newmsgs);
1640 }
1641
1642
1643
1644 /*
1645  * Set the preferred view for the current user/room combination
1646  */
1647 void cmd_view(char *cmdbuf) {
1648         int requested_view;
1649         struct visit vbuf;
1650
1651         if (CtdlAccessCheck(ac_logged_in)) {
1652                 return;
1653         }
1654
1655         requested_view = extract_int(cmdbuf, 0);
1656
1657         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
1658         vbuf.v_view = requested_view;
1659         CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
1660         
1661         cprintf("%d ok\n", CIT_OK);
1662 }