]> code.citadel.org Git - citadel.git/blob - citadel/user_ops.c
* configure.in, Makefile.in: only build the server if we find pthreads
[citadel.git] / citadel / user_ops.c
1 /* needed to properly enable crypt() stuff on some systems */
2 #define _XOPEN_SOURCE
3 /* needed for str[n]casecmp() on some systems if the above is defined */
4 #define _XOPEN_SOURCE_EXTENDED
5
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <stdio.h>
9 #include <fcntl.h>
10 #include <signal.h>
11 #include <pwd.h>
12 #include <sys/types.h>
13 #include <sys/time.h>
14 #include <string.h>
15 #include <syslog.h>
16 #include <pthread.h>
17 #include "citadel.h"
18 #include "server.h"
19 #include "database.h"
20 #include "user_ops.h"
21 #include "sysdep_decls.h"
22 #include "support.h"
23 #include "room_ops.h"
24 #include "logging.h"
25 #include "file_ops.h"
26 #include "control.h"
27 #include "msgbase.h"
28 #include "config.h"
29 #include "dynloader.h"
30 #include "sysdep.h"
31
32
33 /*
34  * getuser()  -  retrieve named user into supplied buffer.
35  *               returns 0 on success
36  */
37 int getuser(struct usersupp *usbuf, char name[]) {
38
39         char lowercase_name[32];
40         int a;
41         struct cdbdata *cdbus;
42
43         bzero(usbuf, sizeof(struct usersupp));
44         for (a=0; a<=strlen(name); ++a) {
45                 lowercase_name[a] = tolower(name[a]);
46                 }
47
48         cdbus = cdb_fetch(CDB_USERSUPP, lowercase_name, strlen(lowercase_name));
49         if (cdbus == NULL) {
50                 return(1);      /* user not found */
51                 }
52
53         memcpy(usbuf, cdbus->ptr,
54                 ( (cdbus->len > sizeof(struct usersupp)) ?
55                 sizeof(struct usersupp) : cdbus->len) );
56         cdb_free(cdbus);
57         return(0);
58         }
59
60
61 /*
62  * lgetuser()  -  same as getuser() but locks the record
63  */
64 int lgetuser(struct usersupp *usbuf, char *name)
65 {
66         int retcode;
67
68         retcode = getuser(usbuf,name);
69         if (retcode == 0) {
70                 begin_critical_section(S_USERSUPP);
71                 }
72         return(retcode);
73         }
74
75
76 /*
77  * putuser()  -  write user buffer into the correct place on disk
78  */
79 void putuser(struct usersupp *usbuf, char *name)
80 {
81         char lowercase_name[32];
82         int a;
83
84         for (a=0; a<=strlen(name); ++a) {
85                 lowercase_name[a] = tolower(name[a]);
86                 }
87
88         cdb_store(CDB_USERSUPP,
89                 lowercase_name, strlen(lowercase_name),
90                 usbuf, sizeof(struct usersupp));
91
92         }
93
94
95 /*
96  * lputuser()  -  same as putuser() but locks the record
97  */
98 void lputuser(struct usersupp *usbuf, char *name) {
99         putuser(usbuf,name);
100         end_critical_section(S_USERSUPP);
101         }
102
103
104 /*
105  * Define a relationship between a user and a room
106  */
107 void CtdlSetRelationship(struct visit *newvisit,
108                         struct usersupp *rel_user,
109                         struct quickroom *rel_room) {
110
111         struct cdbdata *cdbvisit;
112         struct visit *visits;
113         int num_visits;
114         int a;
115         int replaced = 0;
116
117         cdbvisit = cdb_fetch(CDB_VISIT, &rel_user->usernum, sizeof(long));
118         if (cdbvisit != NULL) {
119                 num_visits = cdbvisit->len / sizeof(struct visit);
120                 visits = (struct visit *)
121                         malloc(num_visits * sizeof(struct visit));
122                 memcpy(visits, cdbvisit->ptr,
123                         (num_visits * sizeof(struct visit)));
124                 cdb_free(cdbvisit);
125                 }
126         else {
127                 num_visits = 0;
128                 visits = NULL;
129                 }
130
131         /* Replace an existing relationship if possible */
132         if (num_visits > 0) for (a=0; a<num_visits; ++a) {
133                 if ( (!strcasecmp(visits[a].v_roomname, rel_room->QRname))
134                    && (visits[a].v_generation == rel_room->QRgen) ) {
135                         memcpy(&visits[a], newvisit, sizeof(struct visit));
136                         replaced = 1;
137                         }
138                 }
139
140         /* Otherwise, define a new one */
141         if (replaced == 0) {
142                 ++num_visits;
143                 visits = realloc(visits, 
144                         (num_visits * sizeof(struct visit)));
145                 memcpy(&visits[num_visits-1], newvisit, sizeof(struct visit));
146                 }
147
148         /* Now write the relationship back to disk */
149         cdb_store(CDB_VISIT,
150                 &rel_user->usernum, sizeof(long),
151                 visits,
152                 (num_visits * sizeof(struct visit)));
153         free(visits);
154         }
155
156 /*
157  * Locate a relationship between a user and a room
158  */
159 void CtdlGetRelationship(struct visit *vbuf,
160                         struct usersupp *rel_user,
161                         struct quickroom *rel_room) {
162
163         struct cdbdata *cdbvisit;
164         struct visit *visits;
165         int num_visits;
166         int a;
167
168         bzero(vbuf, sizeof(struct visit));
169         strcpy(vbuf->v_roomname, rel_room->QRname);
170         vbuf->v_generation = rel_room->QRgen;
171
172         cdbvisit = cdb_fetch(CDB_VISIT, &rel_user->usernum, sizeof(long));
173         if (cdbvisit != NULL) {
174                 if ((num_visits = cdbvisit->len / sizeof(struct visit)) == 0) {
175                         cdb_free(cdbvisit);
176                         return;
177                 }
178                 visits = (struct visit *)
179                         malloc(num_visits * sizeof(struct visit));
180                 memcpy(visits, cdbvisit->ptr,
181                         (num_visits * sizeof(struct visit)));
182                 cdb_free(cdbvisit);
183                 }
184         else return;
185
186         for (a=0; a<num_visits; ++a) {
187         
188                 if ( (!strcasecmp(visits[a].v_roomname, rel_room->QRname))
189                    && (visits[a].v_generation == rel_room->QRgen) ) {
190                         memcpy(vbuf, &visits[a], sizeof(struct visit));
191                         }
192                 }
193         
194         free(visits);
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                 bzero(usbuf, 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         int a;
298         struct quickroom qr;
299
300         syslog(LOG_NOTICE,"user <%s> logged in",CC->curr_user);
301
302         lgetuser(&CC->usersupp,CC->curr_user);
303         ++(CC->usersupp.timescalled);
304         CC->fake_username[0] = '\0';
305         CC->fake_postname[0] = '\0';
306         CC->fake_hostname[0] = '\0';
307         CC->fake_roomname[0] = '\0';
308         CC->last_pager[0] = '\0';
309         time(&CC->usersupp.lastcall);
310
311         /* If this user's name is the name of the system administrator
312          * (as specified in setup), automatically assign access level 6.
313          */
314         if (!strcasecmp(CC->usersupp.fullname, config.c_sysadm)) {
315                 CC->usersupp.axlevel = 6;
316                 }
317
318  /******************************************************************************/
319  /* FIX  It is safe to remove this segment during the cutover                  */
320         for (a=0; a<MAXROOMS; ++a) {
321                 getroom(&qr,a);
322                 if (CC->usersupp.generation[a] != qr.QRgen)
323                                         CC->usersupp.generation[a]=(-1);
324                 if (CC->usersupp.forget[a] != qr.QRgen)
325                                         CC->usersupp.forget[a]=(-1);
326                 }
327  /******************************************************************************/
328
329         lputuser(&CC->usersupp,CC->curr_user);
330
331         /* Run any cleanup routines registered by loadable modules */
332         PerformSessionHooks(EVT_LOGIN);
333
334         cprintf("%d %s|%d|%d|%d|%u|%ld\n",OK,CC->usersupp.fullname,CC->usersupp.axlevel,
335                 CC->usersupp.timescalled,CC->usersupp.posted,CC->usersupp.flags,
336                 CC->usersupp.usernum);
337         usergoto(0,0);          /* Enter the lobby */   
338         rec_log(CL_LOGIN,CC->curr_user);
339         }
340
341
342 /* 
343  * misc things to be taken care of when a user is logged out
344  */
345 void logout(struct CitContext *who)
346 {
347         who->logged_in = 0;
348         if (who->download_fp != NULL) {
349                 fclose(who->download_fp);
350                 who->download_fp = NULL;
351                 }
352         if (who->upload_fp != NULL) {
353                 abort_upl(who);
354                 }
355
356         /* Do modular stuff... */
357         PerformSessionHooks(EVT_LOGOUT);
358         }
359
360
361 void cmd_pass(char *buf)
362 {
363         char password[256];
364         int code;
365         struct passwd *p;
366
367         extract(password,buf,0);
368
369         if ((CC->logged_in)) {
370                 cprintf("%d Already logged in.\n",ERROR);
371                 return;
372                 }
373         if (!strcmp(CC->curr_user,"")) {
374                 cprintf("%d You must send a name with USER first.\n",ERROR);
375                 return;
376                 }
377         if (getuser(&CC->usersupp,CC->curr_user)) {
378                 cprintf("%d Can't find user record!\n",ERROR+INTERNAL_ERROR);
379                 return;
380                 }
381
382         code = (-1);
383         if (CC->usersupp.USuid == BBSUID) {
384                 strproc(password);
385                 strproc(CC->usersupp.password);
386                 code = strcasecmp(CC->usersupp.password,password);
387                 }
388         else {
389                 p = (struct passwd *)getpwuid(CC->usersupp.USuid);
390 #ifdef ENABLE_AUTOLOGIN
391                 if (p!=NULL) {
392                         if (!strcmp(p->pw_passwd,
393                            (char *)crypt(password,p->pw_passwd))) {
394                                 code = 0;
395                                 lgetuser(&CC->usersupp, CC->curr_user);
396                                 strcpy(CC->usersupp.password, password);
397                                 lputuser(&CC->usersupp, CC->curr_user);
398                                 }
399                         }
400 #endif
401                 }
402
403         if (!code) {
404                 (CC->logged_in) = 1;
405                 session_startup();
406                 }
407         else {
408                 cprintf("%d Wrong password.\n",ERROR);
409                 rec_log(CL_BADPW,CC->curr_user);
410                 }
411         }
412
413
414 /*
415  * Delete a user record *and* all of its related resources.
416  */
417 int purge_user(char *pname) {
418         char filename[64];
419         struct usersupp usbuf;
420         int a;
421         struct cdbdata *cdbmb;
422         long *mailbox;
423         int num_mails;
424
425         if (getuser(&usbuf, pname) != 0) {
426                 lprintf(5, "Cannot purge user <%s> - not found\n", pname);
427                 return(ERROR+NO_SUCH_USER);
428                 }
429
430         /* FIX   Don't delete a user who is currently logged in. */
431
432         /* Perform any purge functions registered by server extensions */
433         PerformUserHooks(usbuf.fullname, usbuf.usernum, EVT_PURGEUSER);
434
435         /* delete any messages in the user's mailbox */
436         cdbmb = cdb_fetch(CDB_MAILBOXES, &usbuf.usernum, sizeof(long));
437         if (cdbmb != NULL) {
438                 num_mails = cdbmb->len / sizeof(long);
439                 mailbox = (long *) cdbmb->ptr;
440                 if (num_mails > 0) for (a=0; a<num_mails; ++a) {
441                         cdb_delete(CDB_MSGMAIN, &mailbox[a], sizeof(long));
442                         }
443                 cdb_free(cdbmb);
444                 /* now delete the mailbox itself */
445                 cdb_delete(CDB_MAILBOXES, &usbuf.usernum, sizeof(long));
446                 }
447
448         /* delete any existing user/room relationships */
449         cdb_delete(CDB_VISIT, &usbuf.usernum, sizeof(long));
450
451         /* delete the userlog entry */
452         cdb_delete(CDB_USERSUPP, pname, strlen(pname));
453
454         /* remove the user's bio file */        
455         sprintf(filename, "./bio/%ld", usbuf.usernum);
456         unlink(filename);
457
458         /* remove the user's picture */
459         sprintf(filename, "./userpics/%ld.gif", usbuf.usernum);
460         unlink(filename);
461
462         return(0);
463         }
464
465
466 /*
467  * create_user()  -  back end processing to create a new user
468  */
469 int create_user(char *newusername)
470 {
471         struct usersupp usbuf;
472         int a;
473         struct passwd *p = NULL;
474         char username[64];
475
476         strcpy(username, newusername);
477         strproc(username);
478
479 #ifdef ENABLE_AUTOLOGIN
480         p = (struct passwd *)getpwnam(username);
481 #endif
482         if (p != NULL) {
483                 strcpy(username, p->pw_gecos);
484                 for (a=0; a<strlen(username); ++a) {
485                         if (username[a] == ',') username[a] = 0;
486                         }
487                 CC->usersupp.USuid = p->pw_uid;
488                 }
489         else {
490                 CC->usersupp.USuid = BBSUID;
491                 }
492
493         if (!getuser(&usbuf,username)) {
494                 return(ERROR+ALREADY_EXISTS);
495                 }
496
497         strcpy(CC->curr_user,username);
498         strcpy(CC->usersupp.fullname,username);
499         strcpy(CC->usersupp.password,"");
500         (CC->logged_in) = 1;
501
502         /********************************************************/
503         /* FIX this can safely be removed during the cutover... */
504         for (a=0; a<MAXROOMS; ++a) {
505                 CC->usersupp.lastseen[a]=0L;
506                 CC->usersupp.generation[a]=(-1);
507                 CC->usersupp.forget[a]=(-1);
508                 }
509         /********************************************************/
510
511         /* These are the default flags on new accounts */
512         CC->usersupp.flags =
513                 US_NEEDVALID|US_LASTOLD|US_DISAPPEAR|US_PAGINATOR|US_FLOORS;
514
515         CC->usersupp.timescalled = 0;
516         CC->usersupp.posted = 0;
517         CC->usersupp.axlevel = config.c_initax;
518         CC->usersupp.USscreenwidth = 80;
519         CC->usersupp.USscreenheight = 24;
520         time(&CC->usersupp.lastcall);
521         strcpy(CC->usersupp.USname, "");
522         strcpy(CC->usersupp.USaddr, "");
523         strcpy(CC->usersupp.UScity, "");
524         strcpy(CC->usersupp.USstate, "");
525         strcpy(CC->usersupp.USzip, "");
526         strcpy(CC->usersupp.USphone, "");
527
528         /* fetch a new user number */
529         CC->usersupp.usernum = get_new_user_number();
530
531         if (CC->usersupp.usernum == 1L) {
532                 CC->usersupp.axlevel = 6;
533                 }
534
535         /* add user to userlog */
536         putuser(&CC->usersupp,CC->curr_user);
537         if (getuser(&CC->usersupp,CC->curr_user)) {
538                 return(ERROR+INTERNAL_ERROR);
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 (CC->curr_rm < 0) {
685                 cprintf("%d No current room.\n",ERROR);
686                 return;
687                 }
688
689         if (!strncasecmp(new_ptr,"highest",7)) {
690                 newlr = CC->quickroom.QRhighest;
691 /* FIX ... if the current room is 1 (Mail), newlr needs to be set to the
692  * number of the highest mail message
693  */
694                 }
695         else {
696                 newlr = atol(new_ptr);
697                 }
698
699         lgetuser(&CC->usersupp, CC->curr_user);
700
701         /* old method - remove */
702         CC->usersupp.lastseen[CC->curr_rm] = newlr;
703
704         /* new method */
705         CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
706         vbuf.v_lastseen = newlr;
707         CtdlSetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
708
709         lputuser(&CC->usersupp, CC->curr_user);
710         cprintf("%d %ld\n",OK,newlr);
711         }
712
713
714 /*
715  * INVT and KICK commands
716  */
717 void cmd_invt_kick(char *iuser, int op)
718                         /* user name */
719         {               /* 1 = invite, 0 = kick out */
720         struct usersupp USscratch;
721         char bbb[256];
722         struct visit vbuf;
723
724         if (!(CC->logged_in)) {
725                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
726                 return;
727                 }
728
729         if (CC->curr_rm < 0) {
730                 cprintf("%d No current room.\n",ERROR);
731                 return;
732                 }
733
734         if (is_room_aide()==0) {
735                 cprintf("%d Higher access required.\n",
736                         ERROR+HIGHER_ACCESS_REQUIRED);
737                 return;
738                 }
739
740         /* FIX - with the new relationships scheme we can lock users out,
741            so it'll make sense to remove this routine */
742         if ( (op==1) && ((CC->quickroom.QRflags&QR_PRIVATE)==0) ) {
743                 cprintf("%d Not a private room.\n",ERROR+NOT_HERE);
744                 return;
745                 }
746
747         if (lgetuser(&USscratch,iuser)!=0) {
748                 cprintf("%d No such user.\n",ERROR);
749                 return;
750                 }
751
752         CtdlGetRelationship(&vbuf, &USscratch, &CC->quickroom);
753
754         if (op==1) {
755                 /* old method -- FIX remove this when we're ready */
756                 USscratch.generation[CC->curr_rm]=CC->quickroom.QRgen;
757                 USscratch.forget[CC->curr_rm]=(-1);
758
759                 /* new method */
760                 vbuf.v_flags = vbuf.v_flags & ~V_FORGET & ~V_LOCKOUT;
761                 vbuf.v_flags = vbuf.v_flags | V_ACCESS;
762                 }
763
764         if (op==0) {
765                 /* old method -- FIX remove this when we're ready */
766                 USscratch.generation[CC->curr_rm]=(-1);
767                 USscratch.forget[CC->curr_rm]=CC->quickroom.QRgen;
768
769                 /* new method */
770                 vbuf.v_flags = vbuf.v_flags & ~V_ACCESS;
771                 vbuf.v_flags = vbuf.v_flags | V_FORGET | V_LOCKOUT;
772                 }
773
774         CtdlSetRelationship(&vbuf, &USscratch, &CC->quickroom);
775
776         lputuser(&USscratch,iuser);
777
778         /* post a message in Aide> saying what we just did */
779         sprintf(bbb,"%s %s %s> by %s",
780                 iuser,
781                 ((op == 1) ? "invited to" : "kicked out of"),
782                 CC->quickroom.QRname,
783                 CC->usersupp.fullname);
784         aide_message(bbb);
785
786         if ((op==0)&&((CC->quickroom.QRflags&QR_PRIVATE)==0)) {
787                 cprintf("%d Ok. (Not a private room, <Z>ap effect only)\n",OK);
788                 }
789         else {
790                 cprintf("%d Ok.\n",OK);
791                 }
792         return;
793         }
794
795
796 /*
797  * forget (Zap) the current room
798  */
799 void cmd_forg(void) {
800         struct visit vbuf;
801
802         if (!(CC->logged_in)) {
803                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
804                 return;
805                 }
806
807         if (CC->curr_rm < 0) {
808                 cprintf("%d No current room.\n",ERROR);
809                 return;
810                 }
811
812         if (CC->curr_rm < 3) {
813                 cprintf("%d You cannot forget this room.\n",ERROR+NOT_HERE);
814                 return;
815                 }
816
817         if (is_aide()) {
818                 cprintf("%d Aides cannot forget rooms.\n",ERROR);
819                 return;
820                 }
821
822         lgetuser(&CC->usersupp,CC->curr_user);
823         CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
824
825         /* old method -- FIX remove this when we're ready */
826         CC->usersupp.forget[CC->curr_rm] = CC->quickroom.QRgen;
827         CC->usersupp.generation[CC->curr_rm] = (-1);
828
829         /* new method */
830         vbuf.v_flags = vbuf.v_flags | V_FORGET;
831
832         CtdlSetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
833         lputuser(&CC->usersupp,CC->curr_user);
834         cprintf("%d Ok\n",OK);
835         CC->curr_rm = (-1);
836         }
837
838 /*
839  * Get Next Unregistered User
840  */
841 void cmd_gnur(void) {
842         struct cdbdata *cdbus;
843         struct usersupp usbuf;
844
845         if (!(CC->logged_in)) {
846                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
847                 return;
848                 }
849
850         if (CC->usersupp.axlevel < 6) {
851                 cprintf("%d Higher access required.\n",
852                         ERROR+HIGHER_ACCESS_REQUIRED);
853                 return;
854                 }
855
856         if ((CitControl.MMflags&MM_VALID)==0) {
857                 cprintf("%d There are no unvalidated users.\n",OK);
858                 return;
859                 }
860
861         /* There are unvalidated users.  Traverse the usersupp database,
862          * and return the first user we find that needs validation.
863          */
864         cdb_rewind(CDB_USERSUPP);
865         while (cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
866                 bzero(&usbuf, sizeof(struct usersupp));
867                 memcpy(&usbuf, cdbus->ptr,
868                         ( (cdbus->len > sizeof(struct usersupp)) ?
869                         sizeof(struct usersupp) : cdbus->len) );
870                 cdb_free(cdbus);
871                 if ((usbuf.flags & US_NEEDVALID)
872                    &&(usbuf.axlevel > 0)) {
873                         cprintf("%d %s\n",MORE_DATA,usbuf.fullname);
874                         return;
875                         }
876                 } 
877
878         /* If we get to this point, there are no more unvalidated users.
879          * Therefore we clear the "users need validation" flag.
880          */
881
882         begin_critical_section(S_CONTROL);
883         get_control();
884         CitControl.MMflags = CitControl.MMflags&(~MM_VALID);
885         put_control();
886         end_critical_section(S_CONTROL);
887         cprintf("%d *** End of registration.\n",OK);
888
889
890         }
891
892
893 /*
894  * get registration info for a user
895  */
896 void cmd_greg(char *who)
897 {
898         struct usersupp usbuf;
899         int a,b;
900         char pbuf[32];
901
902         if (!(CC->logged_in)) {
903                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
904                 return;
905                 }
906
907         if (!strcasecmp(who,"_SELF_")) strcpy(who,CC->curr_user);
908
909         if ((CC->usersupp.axlevel < 6) && (strcasecmp(who,CC->curr_user))) {
910                 cprintf("%d Higher access required.\n",
911                         ERROR+HIGHER_ACCESS_REQUIRED);
912                 return;
913                 }
914
915         if (getuser(&usbuf,who) != 0) {
916                 cprintf("%d '%s' not found.\n",ERROR+NO_SUCH_USER,who);
917                 return;
918                 }
919
920         cprintf("%d %s\n",LISTING_FOLLOWS,usbuf.fullname);
921         cprintf("%ld\n",usbuf.usernum);
922         cprintf("%s\n",usbuf.password);
923         cprintf("%s\n",usbuf.USname);
924         cprintf("%s\n",usbuf.USaddr);
925         cprintf("%s\n%s\n%s\n",
926                 usbuf.UScity,usbuf.USstate,usbuf.USzip);
927         strcpy(pbuf,usbuf.USphone);
928         usbuf.USphone[0]=0;
929         for (a=0; a<strlen(pbuf); ++a) {
930                 if ((pbuf[a]>='0')&&(pbuf[a]<='9')) {
931                         b=strlen(usbuf.USphone);
932                         usbuf.USphone[b]=pbuf[a];
933                         usbuf.USphone[b+1]=0;
934                         }
935                 }
936         while(strlen(usbuf.USphone)<10) {
937                 strcpy(pbuf,usbuf.USphone);
938                 strcpy(usbuf.USphone," ");
939                 strcat(usbuf.USphone,pbuf);
940                 }
941
942         cprintf("(%c%c%c) %c%c%c-%c%c%c%c\n",
943                 usbuf.USphone[0],usbuf.USphone[1],
944                 usbuf.USphone[2],usbuf.USphone[3],
945                 usbuf.USphone[4],usbuf.USphone[5],
946                 usbuf.USphone[6],usbuf.USphone[7],
947                 usbuf.USphone[8],usbuf.USphone[9]);
948
949         cprintf("%d\n",usbuf.axlevel);
950         cprintf("%s\n",usbuf.USemail);
951         cprintf("000\n");
952         }
953
954 /*
955  * validate a user
956  */
957 void cmd_vali(char *v_args)
958 {
959         char user[256];
960         int newax;
961         struct usersupp userbuf;
962
963         extract(user,v_args,0);
964         newax = extract_int(v_args,1);
965
966         if (!(CC->logged_in)) {
967                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
968                 return;
969                 }
970
971         if (CC->usersupp.axlevel < 6) {
972                 cprintf("%d Higher access required.\n",
973                         ERROR+HIGHER_ACCESS_REQUIRED);
974                 return;
975                 }
976
977         if (lgetuser(&userbuf,user)!=0) {
978                 cprintf("%d '%s' not found.\n",ERROR+NO_SUCH_USER,user);
979                 return;
980                 }
981
982         userbuf.axlevel = newax;
983         userbuf.flags = (userbuf.flags & ~US_NEEDVALID);
984
985         lputuser(&userbuf,user);
986
987         /* If the access level was set to zero, delete the user */
988         if (newax == 0) {
989                 if (purge_user(user)==0) {
990                         cprintf("%d %s Deleted.\n", OK, userbuf.fullname);
991                         return;
992                         }
993                 }
994
995         cprintf("%d ok\n",OK);
996         }
997
998
999
1000 /* 
1001  *  Traverse the user file...
1002  */
1003 void ForEachUser(void (*CallBack)(struct usersupp *EachUser)) {
1004         struct usersupp usbuf;
1005         struct cdbdata *cdbus;
1006
1007         cdb_rewind(CDB_USERSUPP);
1008
1009         while(cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
1010                 bzero(&usbuf, sizeof(struct usersupp));
1011                 memcpy(&usbuf, cdbus->ptr,
1012                         ( (cdbus->len > sizeof(struct usersupp)) ?
1013                         sizeof(struct usersupp) : cdbus->len) );
1014                 cdb_free(cdbus);
1015                 (*CallBack)(&usbuf);
1016                 }
1017         }
1018
1019
1020 /*
1021  * List one user (this works with cmd_list)
1022  */
1023 void ListThisUser(struct usersupp *usbuf) {
1024         if (usbuf->axlevel > 0) {
1025                 if ((CC->usersupp.axlevel>=6)
1026                    ||((usbuf->flags&US_UNLISTED)==0)
1027                    ||((CC->internal_pgm))) {
1028                         cprintf("%s|%d|%ld|%ld|%d|%d|",
1029                                 usbuf->fullname,
1030                                 usbuf->axlevel,
1031                                 usbuf->usernum,
1032                                 usbuf->lastcall,
1033                                 usbuf->timescalled,
1034                                 usbuf->posted);
1035                         if (CC->usersupp.axlevel >= 6)
1036                                 cprintf("%s",usbuf->password);
1037                         cprintf("\n");
1038                         }
1039                 }
1040         }
1041
1042 /* 
1043  *  List users
1044  */
1045 void cmd_list(void) {
1046         cprintf("%d \n",LISTING_FOLLOWS);
1047         ForEachUser(ListThisUser);
1048         cprintf("000\n");
1049         }
1050
1051
1052 /*
1053  * enter registration info
1054  */
1055 void cmd_regi(void) {
1056         int a,b,c;
1057         char buf[256];
1058
1059         char tmpname[256];
1060         char tmpaddr[256];
1061         char tmpcity[256];
1062         char tmpstate[256];
1063         char tmpzip[256];
1064         char tmpphone[256];
1065         char tmpemail[256];
1066
1067         if (!(CC->logged_in)) {
1068                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
1069                 return;
1070                 }
1071
1072         strcpy(tmpname,"");
1073         strcpy(tmpaddr,"");
1074         strcpy(tmpcity,"");
1075         strcpy(tmpstate,"");
1076         strcpy(tmpzip,"");
1077         strcpy(tmpphone,"");
1078         strcpy(tmpemail,"");
1079
1080         cprintf("%d Send registration...\n",SEND_LISTING);
1081         a=0;
1082         while (client_gets(buf), strcmp(buf,"000")) {
1083                 if (a==0) strcpy(tmpname,buf);
1084                 if (a==1) strcpy(tmpaddr,buf);
1085                 if (a==2) strcpy(tmpcity,buf);
1086                 if (a==3) strcpy(tmpstate,buf);
1087                 if (a==4) {
1088                         for (c=0; c<strlen(buf); ++c) {
1089                                 if ((buf[c]>='0')&&(buf[c]<='9')) {
1090                                         b=strlen(tmpzip);
1091                                         tmpzip[b]=buf[c];
1092                                         tmpzip[b+1]=0;
1093                                         }
1094                                 }
1095                         }
1096                 if (a==5) {
1097                         for (c=0; c<strlen(buf); ++c) {
1098                                 if ((buf[c]>='0')&&(buf[c]<='9')) {
1099                                         b=strlen(tmpphone);
1100                                         tmpphone[b]=buf[c];
1101                                         tmpphone[b+1]=0;
1102                                         }
1103                                 }
1104                         }
1105                 if (a==6) strncpy(tmpemail,buf,31);
1106                 ++a;
1107                 }
1108
1109         tmpname[29]=0;
1110         tmpaddr[24]=0;
1111         tmpcity[14]=0;
1112         tmpstate[2]=0;
1113         tmpzip[9]=0;
1114         tmpphone[10]=0;
1115         tmpemail[31]=0;
1116
1117         lgetuser(&CC->usersupp,CC->curr_user);
1118         strcpy(CC->usersupp.USname,tmpname);
1119         strcpy(CC->usersupp.USaddr,tmpaddr);
1120         strcpy(CC->usersupp.UScity,tmpcity);
1121         strcpy(CC->usersupp.USstate,tmpstate);
1122         strcpy(CC->usersupp.USzip,tmpzip);
1123         strcpy(CC->usersupp.USphone,tmpphone);
1124         strcpy(CC->usersupp.USemail,tmpemail);
1125         CC->usersupp.flags=(CC->usersupp.flags|US_REGIS|US_NEEDVALID);
1126         lputuser(&CC->usersupp,CC->curr_user);
1127
1128         /* set global flag calling for validation */
1129         begin_critical_section(S_CONTROL);
1130         get_control();
1131         CitControl.MMflags = CitControl.MMflags | MM_VALID ;
1132         put_control();
1133         end_critical_section(S_CONTROL);
1134         cprintf("%d *** End of registration.\n",OK);
1135         }
1136
1137
1138 /*
1139  * assorted info we need to check at login
1140  */
1141 void cmd_chek(void) {
1142         int mail = 0;
1143         int regis = 0;
1144         int vali = 0;
1145         
1146         if (!(CC->logged_in)) {
1147                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
1148                 return;
1149                 }
1150
1151         getuser(&CC->usersupp,CC->curr_user); /* no lock is needed here */
1152         if ((REGISCALL!=0)&&((CC->usersupp.flags&US_REGIS)==0)) regis = 1;
1153
1154         if (CC->usersupp.axlevel >= 6) {
1155                 get_control();
1156                 if (CitControl.MMflags&MM_VALID) vali = 1;
1157                 }
1158
1159
1160         /* check for mail */
1161         mail = NewMailCount();
1162
1163         cprintf("%d %d|%d|%d\n",OK,mail,regis,vali);
1164         }
1165
1166
1167 /*
1168  * check to see if a user exists
1169  */
1170 void cmd_qusr(char *who)
1171 {
1172         struct usersupp usbuf;
1173
1174         if (getuser(&usbuf,who) == 0) {
1175                 cprintf("%d %s\n",OK,usbuf.fullname);
1176                 }
1177         else {
1178                 cprintf("%d No such user.\n",ERROR+NO_SUCH_USER);
1179                 }
1180         }
1181
1182
1183 /*
1184  * enter user bio
1185  */
1186 void cmd_ebio(void) {
1187         char buf[256];
1188         FILE *fp;
1189
1190         if (!(CC->logged_in)) {
1191                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
1192                 return;
1193                 }
1194
1195         sprintf(buf,"./bio/%ld",CC->usersupp.usernum);
1196         fp = fopen(buf,"w");
1197         if (fp == NULL) {
1198                 cprintf("%d Cannot create file\n",ERROR);
1199                 return;
1200                 }
1201         cprintf("%d  \n",SEND_LISTING);
1202         while(client_gets(buf), strcmp(buf,"000")) {
1203                 fprintf(fp,"%s\n",buf);
1204                 }
1205         fclose(fp);
1206         }
1207
1208 /*
1209  * read user bio
1210  */
1211 void cmd_rbio(char *cmdbuf)
1212 {
1213         struct usersupp ruser;
1214         char buf[256];
1215         FILE *fp;
1216
1217         extract(buf,cmdbuf,0);
1218         if (getuser(&ruser,buf)!=0) {
1219                 cprintf("%d No such user.\n",ERROR+NO_SUCH_USER);
1220                 return;
1221                 }
1222         sprintf(buf,"./bio/%ld",ruser.usernum);
1223         
1224         fp = fopen(buf,"r");
1225         if (fp == NULL) {
1226                 cprintf("%d %s has no bio on file.\n",
1227                         ERROR+FILE_NOT_FOUND,ruser.fullname);
1228                 return;
1229                 }
1230         cprintf("%d  \n",LISTING_FOLLOWS);
1231         while (fgets(buf,256,fp)!=NULL) cprintf("%s",buf);
1232         fclose(fp);
1233         cprintf("000\n");
1234         }
1235
1236 /*
1237  * list of users who have entered bios
1238  */
1239 void cmd_lbio(void) {
1240         char buf[256];
1241         FILE *ls;
1242         struct usersupp usbuf;
1243
1244         ls=popen("cd ./bio; ls","r");
1245         if (ls==NULL) {
1246                 cprintf("%d Cannot open listing.\n",ERROR+FILE_NOT_FOUND);
1247                 return;
1248                 }
1249
1250         cprintf("%d\n",LISTING_FOLLOWS);
1251         while (fgets(buf,255,ls)!=NULL)
1252                 if (getuserbynumber(&usbuf,atol(buf))==0)
1253                         cprintf("%s\n",usbuf.fullname);
1254         pclose(ls);
1255         cprintf("000\n");
1256         }
1257
1258
1259 /*
1260  * Administrative Get User Parameters
1261  */
1262 void cmd_agup(char *cmdbuf) {
1263         struct usersupp usbuf;
1264         char requested_user[256];
1265
1266         if ( (CC->internal_pgm==0)
1267            && ( (CC->logged_in == 0) || (is_aide()==0) ) ) {
1268                 cprintf("%d Higher access required.\n", 
1269                         ERROR + HIGHER_ACCESS_REQUIRED);
1270                 return;
1271                 }
1272
1273         extract(requested_user, cmdbuf, 0);
1274         if (getuser(&usbuf, requested_user) != 0) {
1275                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1276                 return;
1277                 }
1278
1279         cprintf("%d %s|%s|%u|%d|%d|%d|%ld\n", 
1280                 OK,
1281                 usbuf.fullname,
1282                 usbuf.password,
1283                 usbuf.flags,
1284                 usbuf.timescalled,
1285                 usbuf.posted,
1286                 (int)usbuf.axlevel,
1287                 usbuf.usernum);
1288
1289         }
1290
1291
1292
1293 /*
1294  * Administrative Set User Parameters
1295  */
1296 void cmd_asup(char *cmdbuf) {
1297         struct usersupp usbuf;
1298         char requested_user[256];
1299         int np;
1300         int newax;
1301         
1302         if ( (CC->internal_pgm==0)
1303            && ( (CC->logged_in == 0) || (is_aide()==0) ) ) {
1304                 cprintf("%d Higher access required.\n", 
1305                         ERROR + HIGHER_ACCESS_REQUIRED);
1306                 return;
1307                 }
1308
1309         extract(requested_user, cmdbuf, 0);
1310         if (lgetuser(&usbuf, requested_user) != 0) {
1311                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
1312                 return;
1313                 }
1314
1315         np = num_parms(cmdbuf);
1316         if (np > 1) extract(usbuf.password, cmdbuf, 1);
1317         if (np > 2) usbuf.flags = extract_int(cmdbuf, 2);
1318         if (np > 3) usbuf.timescalled = extract_int(cmdbuf, 3);
1319         if (np > 4) usbuf.posted = extract_int(cmdbuf, 4);
1320         if (np > 5) {
1321                 newax = extract_int(cmdbuf, 5);
1322                 if ((newax >=0) && (newax <= 6)) {
1323                         usbuf.axlevel = extract_int(cmdbuf, 5);
1324                         }
1325                 }
1326
1327         lputuser(&usbuf, requested_user);
1328         if (usbuf.axlevel == 0) {
1329                 if (purge_user(requested_user)==0) {
1330                         cprintf("%d %s deleted.\n", OK, requested_user);
1331                         }
1332                 }
1333         cprintf("%d Ok\n", OK);
1334         }
1335
1336
1337 /*
1338  * Count the number of new mail messages the user has
1339  */
1340 int NewMailCount() {
1341         int num_newmsgs = 0;
1342         struct cdbdata *cdbmb;
1343         int num_mails;
1344         long *mailbox;
1345         int a;
1346
1347         cdbmb = cdb_fetch(CDB_MAILBOXES, &CC->usersupp.usernum, sizeof(long));
1348         if (cdbmb != NULL) {
1349                 num_mails = cdbmb->len / sizeof(long);
1350                 mailbox = (long *) cdbmb->ptr;
1351                 if (num_mails > 0) for (a=0; a<num_mails; ++a) {
1352                         if (mailbox[a] > (CC->usersupp.lastseen[1]))
1353                                 ++num_newmsgs;
1354                         }
1355                 cdb_free(cdbmb);
1356                 }
1357         return(num_newmsgs);
1358         }