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