5a7162977c717e91f5a2decbffa3d77d420c4670
[citadel.git] / citadel / modules / ctdlproto / serv_user.c
1 /* 
2  * Server functions which perform operations on user objects.
3  *
4  * Copyright (c) 1987-2011 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License, version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include "support.h"
16 #include "control.h"
17 #include "ctdl_module.h"
18
19 #include "citserver.h"
20
21 #include "user_ops.h"
22 #include "internet_addressing.h"
23
24
25
26 /*
27  * USER cmd
28  */
29 void cmd_user(char *cmdbuf)
30 {
31         char username[256];
32         int a;
33
34         CON_syslog(LOG_DEBUG, "cmd_user(%s)\n", cmdbuf);
35         extract_token(username, cmdbuf, 0, '|', sizeof username);
36         CON_syslog(LOG_DEBUG, "username: %s\n", username);
37         striplt(username);
38         CON_syslog(LOG_DEBUG, "username: %s\n", username);
39
40         a = CtdlLoginExistingUser(NULL, username);
41         switch (a) {
42         case login_already_logged_in:
43                 cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN);
44                 return;
45         case login_too_many_users:
46                 cprintf("%d %s: "
47                         "Too many users are already online "
48                         "(maximum is %d)\n",
49                         ERROR + MAX_SESSIONS_EXCEEDED,
50                         config.c_nodename, config.c_maxsessions);
51                 return;
52         case login_ok:
53                 cprintf("%d Password required for %s\n",
54                         MORE_DATA, CC->curr_user);
55                 return;
56         case login_not_found:
57                 cprintf("%d %s not found.\n", ERROR + NO_SUCH_USER, username);
58                 return;
59         default:
60                 cprintf("%d Internal error\n", ERROR + INTERNAL_ERROR);
61         }
62 }
63
64
65 void cmd_pass(char *buf)
66 {
67         char password[SIZ];
68         int a;
69         long len;
70
71         memset(password, 0, sizeof(password));
72         len = extract_token(password, buf, 0, '|', sizeof password);
73         a = CtdlTryPassword(password, len);
74
75         switch (a) {
76         case pass_already_logged_in:
77                 cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN);
78                 return;
79         case pass_no_user:
80                 cprintf("%d You must send a name with USER first.\n",
81                         ERROR + USERNAME_REQUIRED);
82                 return;
83         case pass_wrong_password:
84                 cprintf("%d Wrong password.\n", ERROR + PASSWORD_REQUIRED);
85                 return;
86         case pass_ok:
87                 logged_in_response();
88                 return;
89         }
90 }
91
92
93 /*
94  * cmd_newu()  -  create a new user account and log in as that user
95  */
96 void cmd_newu(char *cmdbuf)
97 {
98         int a;
99         long len;
100         char username[SIZ];
101
102         if (config.c_auth_mode != AUTHMODE_NATIVE) {
103                 cprintf("%d This system does not use native mode authentication.\n",
104                         ERROR + NOT_HERE);
105                 return;
106         }
107
108         if (config.c_disable_newu) {
109                 cprintf("%d Self-service user account creation "
110                         "is disabled on this system.\n", ERROR + NOT_HERE);
111                 return;
112         }
113
114         if (CC->logged_in) {
115                 cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN);
116                 return;
117         }
118         if (CC->nologin) {
119                 cprintf("%d %s: Too many users are already online (maximum is %d)\n",
120                         ERROR + MAX_SESSIONS_EXCEEDED,
121                         config.c_nodename, config.c_maxsessions);
122         }
123         extract_token(username, cmdbuf, 0, '|', sizeof username);
124         strproc(username);
125         len = cutuserkey(username);
126
127         if (IsEmptyStr(username)) {
128                 cprintf("%d You must supply a user name.\n", ERROR + USERNAME_REQUIRED);
129                 return;
130         }
131
132         if ((!strcasecmp(username, "bbs")) ||
133             (!strcasecmp(username, "new")) ||
134             (!strcasecmp(username, "."))) {
135                 cprintf("%d '%s' is an invalid login name.\n", ERROR + ILLEGAL_VALUE, username);
136                 return;
137         }
138
139         a = create_user(username, len, 1);
140
141         if (a == 0) {
142                 logged_in_response();
143         } else if (a == ERROR + ALREADY_EXISTS) {
144                 cprintf("%d '%s' already exists.\n",
145                         ERROR + ALREADY_EXISTS, username);
146                 return;
147         } else if (a == ERROR + INTERNAL_ERROR) {
148                 cprintf("%d Internal error - user record disappeared?\n",
149                         ERROR + INTERNAL_ERROR);
150                 return;
151         } else {
152                 cprintf("%d unknown error\n", ERROR + INTERNAL_ERROR);
153         }
154 }
155
156 /*
157  * set password - citadel protocol implementation
158  */
159 void cmd_setp(char *new_pw)
160 {
161         if (CtdlAccessCheck(ac_logged_in)) {
162                 return;
163         }
164         if ( (CC->user.uid != CTDLUID) && (CC->user.uid != (-1)) ) {
165                 cprintf("%d Not allowed.  Use the 'passwd' command.\n", ERROR + NOT_HERE);
166                 return;
167         }
168         if (CC->is_master) {
169                 cprintf("%d The master prefix password cannot be changed with this command.\n",
170                         ERROR + NOT_HERE);
171                 return;
172         }
173
174         if (!strcasecmp(new_pw, "GENERATE_RANDOM_PASSWORD")) {
175                 char random_password[17];
176                 snprintf(random_password, sizeof random_password, "%08lx%08lx", random(), random());
177                 CtdlSetPassword(random_password);
178                 cprintf("%d %s\n", CIT_OK, random_password);
179         }
180         else {
181                 strproc(new_pw);
182                 if (IsEmptyStr(new_pw)) {
183                         cprintf("%d Password unchanged.\n", CIT_OK);
184                         return;
185                 }
186                 CtdlSetPassword(new_pw);
187                 cprintf("%d Password changed.\n", CIT_OK);
188         }
189 }
190
191
192 /*
193  * cmd_creu() - administratively create a new user account (do not log in to it)
194  */
195 void cmd_creu(char *cmdbuf)
196 {
197         int a;
198         long len;
199         char username[SIZ];
200         char password[SIZ];
201         struct ctdluser tmp;
202
203         if (CtdlAccessCheck(ac_aide)) {
204                 return;
205         }
206
207         extract_token(username, cmdbuf, 0, '|', sizeof username);
208         strproc(username);
209         strproc(password);
210         if (IsEmptyStr(username)) {
211                 cprintf("%d You must supply a user name.\n", ERROR + USERNAME_REQUIRED);
212                 return;
213         }
214         len = cutuserkey(username);
215
216
217         extract_token(password, cmdbuf, 1, '|', sizeof password);
218
219         a = create_user(username, len, 0);
220
221         if (a == 0) {
222                 if (!IsEmptyStr(password)) {
223                         CtdlGetUserLock(&tmp, username);
224                         safestrncpy(tmp.password, password, sizeof(tmp.password));
225                         CtdlPutUserLock(&tmp);
226                 }
227                 cprintf("%d User '%s' created %s.\n", CIT_OK, username,
228                                 (!IsEmptyStr(password)) ? "and password set" :
229                                 "with no password");
230                 return;
231         } else if (a == ERROR + ALREADY_EXISTS) {
232                 cprintf("%d '%s' already exists.\n", ERROR + ALREADY_EXISTS, username);
233                 return;
234         } else if ( (config.c_auth_mode != AUTHMODE_NATIVE) && (a == ERROR + NO_SUCH_USER) ) {
235                 cprintf("%d User accounts are not created within Citadel in host authentication mode.\n",
236                         ERROR + NO_SUCH_USER);
237                 return;
238         } else {
239                 cprintf("%d An error occurred creating the user account.\n", ERROR + INTERNAL_ERROR);
240         }
241 }
242
243
244
245 /*
246  * get user parameters
247  */
248 void cmd_getu(char *cmdbuf)
249 {
250
251         if (CtdlAccessCheck(ac_logged_in))
252                 return;
253
254         CtdlGetUser(&CC->user, CC->curr_user);
255         cprintf("%d 80|24|%d|\n",
256                 CIT_OK,
257                 (CC->user.flags & US_USER_SET)
258         );
259 }
260
261 /*
262  * set user parameters
263  */
264 void cmd_setu(char *new_parms)
265 {
266         if (CtdlAccessCheck(ac_logged_in))
267                 return;
268
269         if (num_parms(new_parms) < 3) {
270                 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
271                 return;
272         }
273         CtdlLockGetCurrentUser();
274         CC->user.flags = CC->user.flags & (~US_USER_SET);
275         CC->user.flags = CC->user.flags | (extract_int(new_parms, 2) & US_USER_SET);
276         CtdlPutCurrentUserLock();
277         cprintf("%d Ok\n", CIT_OK);
278 }
279
280 /*
281  * set last read pointer
282  */
283 void cmd_slrp(char *new_ptr)
284 {
285         long newlr;
286         visit vbuf;
287         visit original_vbuf;
288
289         if (CtdlAccessCheck(ac_logged_in)) {
290                 return;
291         }
292
293         if (!strncasecmp(new_ptr, "highest", 7)) {
294                 newlr = CC->room.QRhighest;
295         } else {
296                 newlr = atol(new_ptr);
297         }
298
299         CtdlLockGetCurrentUser();
300
301         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
302         memcpy(&original_vbuf, &vbuf, sizeof(visit));
303         vbuf.v_lastseen = newlr;
304         snprintf(vbuf.v_seen, sizeof vbuf.v_seen, "*:%ld", newlr);
305
306         /* Only rewrite the record if it changed */
307         if ( (vbuf.v_lastseen != original_vbuf.v_lastseen)
308            || (strcmp(vbuf.v_seen, original_vbuf.v_seen)) ) {
309                 CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
310         }
311
312         CtdlPutCurrentUserLock();
313         cprintf("%d %ld\n", CIT_OK, newlr);
314 }
315
316
317 void cmd_seen(char *argbuf) {
318         long target_msgnum = 0L;
319         int target_setting = 0;
320
321         if (CtdlAccessCheck(ac_logged_in)) {
322                 return;
323         }
324
325         if (num_parms(argbuf) != 2) {
326                 cprintf("%d Invalid parameters\n", ERROR + ILLEGAL_VALUE);
327                 return;
328         }
329
330         target_msgnum = extract_long(argbuf, 0);
331         target_setting = extract_int(argbuf, 1);
332
333         CtdlSetSeen(&target_msgnum, 1, target_setting,
334                         ctdlsetseen_seen, NULL, NULL);
335         cprintf("%d OK\n", CIT_OK);
336 }
337
338
339 void cmd_gtsn(char *argbuf) {
340         visit vbuf;
341
342         if (CtdlAccessCheck(ac_logged_in)) {
343                 return;
344         }
345
346         /* Learn about the user and room in question */
347         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
348
349         cprintf("%d ", CIT_OK);
350         client_write(vbuf.v_seen, strlen(vbuf.v_seen));
351         client_write(HKEY("\n"));
352 }
353
354 /*
355  * INVT and KICK commands
356  */
357 void cmd_invt_kick(char *iuser, int op) {
358
359         /*
360          * These commands are only allowed by admins, room admins,
361          * and room namespace owners
362          */
363         if (is_room_aide()) {
364                 /* access granted */
365         } else if ( ((atol(CC->room.QRname) == CC->user.usernum) ) && (CC->user.usernum != 0) ) {
366                 /* access granted */
367         } else {
368                 /* access denied */
369                 cprintf("%d Higher access or room ownership required.\n",
370                         ERROR + HIGHER_ACCESS_REQUIRED);
371                 return;
372         }
373
374         if (!strncasecmp(CC->room.QRname, config.c_baseroom,
375                          ROOMNAMELEN)) {
376                 cprintf("%d Can't add/remove users from this room.\n",
377                         ERROR + NOT_HERE);
378                 return;
379         }
380
381         if (CtdlInvtKick(iuser, op) != 0) {
382                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
383                 return;
384         }
385
386         cprintf("%d %s %s %s.\n",
387                 CIT_OK, iuser,
388                 ((op == 1) ? "invited to" : "kicked out of"),
389                 CC->room.QRname);
390         return;
391 }
392
393 void cmd_invt(char *iuser) {cmd_invt_kick(iuser, 1);}
394 void cmd_kick(char *iuser) {cmd_invt_kick(iuser, 0);}
395
396
397 /*
398  * forget (Zap) the current room
399  */
400 void cmd_forg(char *argbuf)
401 {
402
403         if (CtdlAccessCheck(ac_logged_in)) {
404                 return;
405         }
406
407         if (CtdlForgetThisRoom() == 0) {
408                 cprintf("%d Ok\n", CIT_OK);
409         }
410         else {
411                 cprintf("%d You may not forget this room.\n", ERROR + NOT_HERE);
412         }
413 }
414
415 /*
416  * Get Next Unregistered User
417  */
418 void cmd_gnur(char *argbuf)
419 {
420         struct cdbdata *cdbus;
421         struct ctdluser usbuf;
422
423         if (CtdlAccessCheck(ac_aide)) {
424                 return;
425         }
426
427         if ((CitControl.MMflags & MM_VALID) == 0) {
428                 cprintf("%d There are no unvalidated users.\n", CIT_OK);
429                 return;
430         }
431
432         /* There are unvalidated users.  Traverse the user database,
433          * and return the first user we find that needs validation.
434          */
435         cdb_rewind(CDB_USERS);
436         while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
437                 memset(&usbuf, 0, sizeof(struct ctdluser));
438                 memcpy(&usbuf, cdbus->ptr,
439                        ((cdbus->len > sizeof(struct ctdluser)) ?
440                         sizeof(struct ctdluser) : cdbus->len));
441                 cdb_free(cdbus);
442                 if ((usbuf.flags & US_NEEDVALID)
443                     && (usbuf.axlevel > AxDeleted)) {
444                         cprintf("%d %s\n", MORE_DATA, usbuf.fullname);
445                         cdb_close_cursor(CDB_USERS);
446                         return;
447                 }
448         }
449
450         /* If we get to this point, there are no more unvalidated users.
451          * Therefore we clear the "users need validation" flag.
452          */
453
454         begin_critical_section(S_CONTROL);
455         get_control();
456         CitControl.MMflags = CitControl.MMflags & (~MM_VALID);
457         put_control();
458         end_critical_section(S_CONTROL);
459         cprintf("%d *** End of registration.\n", CIT_OK);
460
461
462 }
463
464
465 /*
466  * validate a user
467  */
468 void cmd_vali(char *v_args)
469 {
470         char user[128];
471         int newax;
472         struct ctdluser userbuf;
473
474         extract_token(user, v_args, 0, '|', sizeof user);
475         newax = extract_int(v_args, 1);
476
477         if (CtdlAccessCheck(ac_aide) || 
478             (newax > AxAideU) ||
479             (newax < AxDeleted)) {
480                 return;
481         }
482
483         if (CtdlGetUserLock(&userbuf, user) != 0) {
484                 cprintf("%d '%s' not found.\n", ERROR + NO_SUCH_USER, user);
485                 return;
486         }
487
488         userbuf.axlevel = newax;
489         userbuf.flags = (userbuf.flags & ~US_NEEDVALID);
490
491         CtdlPutUserLock(&userbuf);
492
493         /* If the access level was set to zero, delete the user */
494         if (newax == 0) {
495                 if (purge_user(user) == 0) {
496                         cprintf("%d %s Deleted.\n", CIT_OK, userbuf.fullname);
497                         return;
498                 }
499         }
500         cprintf("%d User '%s' validated.\n", CIT_OK, userbuf.fullname);
501 }
502
503 /* 
504  *  List users (searchstring may be empty to list all users)
505  */
506 void cmd_list(char *cmdbuf)
507 {
508         char searchstring[256];
509         extract_token(searchstring, cmdbuf, 0, '|', sizeof searchstring);
510         striplt(searchstring);
511         cprintf("%d \n", LISTING_FOLLOWS);
512         ForEachUser(ListThisUser, (void *)searchstring );
513         cprintf("000\n");
514 }
515
516
517
518
519 /*
520  * assorted info we need to check at login
521  */
522 void cmd_chek(char *argbuf)
523 {
524         int mail = 0;
525         int regis = 0;
526         int vali = 0;
527
528         if (CtdlAccessCheck(ac_logged_in)) {
529                 return;
530         }
531
532         CtdlGetUser(&CC->user, CC->curr_user);  /* no lock is needed here */
533         if ((REGISCALL != 0) && ((CC->user.flags & US_REGIS) == 0))
534                 regis = 1;
535
536         if (CC->user.axlevel >= AxAideU) {
537                 get_control();
538                 if (CitControl.MMflags & MM_VALID)
539                         vali = 1;
540         }
541
542         /* check for mail */
543         mail = InitialMailCheck();
544
545         cprintf("%d %d|%d|%d|%s|\n", CIT_OK, mail, regis, vali, CC->cs_inet_email);
546 }
547
548
549 /*
550  * check to see if a user exists
551  */
552 void cmd_qusr(char *who)
553 {
554         struct ctdluser usbuf;
555
556         if (CtdlGetUser(&usbuf, who) == 0) {
557                 cprintf("%d %s\n", CIT_OK, usbuf.fullname);
558         } else {
559                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
560         }
561 }
562
563
564 /*
565  * Administrative Get User Parameters
566  */
567 void cmd_agup(char *cmdbuf)
568 {
569         struct ctdluser usbuf;
570         char requested_user[128];
571
572         if (CtdlAccessCheck(ac_aide)) {
573                 return;
574         }
575
576         extract_token(requested_user, cmdbuf, 0, '|', sizeof requested_user);
577         if (CtdlGetUser(&usbuf, requested_user) != 0) {
578                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
579                 return;
580         }
581         cprintf("%d %s|%s|%u|%ld|%ld|%d|%ld|%ld|%d\n",
582                 CIT_OK,
583                 usbuf.fullname,
584                 usbuf.password,
585                 usbuf.flags,
586                 usbuf.timescalled,
587                 usbuf.posted,
588                 (int) usbuf.axlevel,
589                 usbuf.usernum,
590                 (long)usbuf.lastcall,
591                 usbuf.USuserpurge);
592 }
593
594
595
596 /*
597  * Administrative Set User Parameters
598  */
599 void cmd_asup(char *cmdbuf)
600 {
601         struct ctdluser usbuf;
602         char requested_user[128];
603         char notify[SIZ];
604         int np;
605         int newax;
606         int deleted = 0;
607
608         if (CtdlAccessCheck(ac_aide))
609                 return;
610
611         extract_token(requested_user, cmdbuf, 0, '|', sizeof requested_user);
612         if (CtdlGetUserLock(&usbuf, requested_user) != 0) {
613                 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
614                 return;
615         }
616         np = num_parms(cmdbuf);
617         if (np > 1)
618                 extract_token(usbuf.password, cmdbuf, 1, '|', sizeof usbuf.password);
619         if (np > 2)
620                 usbuf.flags = extract_int(cmdbuf, 2);
621         if (np > 3)
622                 usbuf.timescalled = extract_int(cmdbuf, 3);
623         if (np > 4)
624                 usbuf.posted = extract_int(cmdbuf, 4);
625         if (np > 5) {
626                 newax = extract_int(cmdbuf, 5);
627                 if ((newax >= AxDeleted) && (newax <= AxAideU)) {
628                         usbuf.axlevel = newax;
629                 }
630         }
631         if (np > 7) {
632                 usbuf.lastcall = extract_long(cmdbuf, 7);
633         }
634         if (np > 8) {
635                 usbuf.USuserpurge = extract_int(cmdbuf, 8);
636         }
637         CtdlPutUserLock(&usbuf);
638         if (usbuf.axlevel == AxDeleted) {
639                 if (purge_user(requested_user) == 0) {
640                         deleted = 1;
641                 }
642         }
643
644         if (deleted) {
645                 snprintf(notify, SIZ, 
646                          "User \"%s\" has been deleted by %s.\n",
647                          usbuf.fullname,
648                         (CC->logged_in ? CC->user.fullname : "an administrator")
649                 );
650                 CtdlAideMessage(notify, "User Deletion Message");
651         }
652
653         cprintf("%d Ok", CIT_OK);
654         if (deleted)
655                 cprintf(" (%s deleted)", requested_user);
656         cprintf("\n");
657 }
658
659
660 /*
661  * Citadel protocol command to do the same
662  */
663 void cmd_isme(char *argbuf) {
664         char addr[256];
665
666         if (CtdlAccessCheck(ac_logged_in)) return;
667         extract_token(addr, argbuf, 0, '|', sizeof addr);
668
669         if (CtdlIsMe(addr, sizeof addr)) {
670                 cprintf("%d %s\n", CIT_OK, addr);
671         }
672         else {
673                 cprintf("%d Not you.\n", ERROR + ILLEGAL_VALUE);
674         }
675
676 }
677
678
679 /*
680  * Set the preferred view for the current user/room combination
681  */
682 void cmd_view(char *cmdbuf) {
683         int requested_view;
684         visit vbuf;
685
686         if (CtdlAccessCheck(ac_logged_in)) {
687                 return;
688         }
689
690         requested_view = extract_int(cmdbuf, 0);
691
692         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
693         vbuf.v_view = requested_view;
694         CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
695         
696         cprintf("%d ok\n", CIT_OK);
697 }
698
699
700 /*
701  * Rename a user
702  */
703 void cmd_renu(char *cmdbuf)
704 {
705         int retcode;
706         char oldname[USERNAME_SIZE];
707         char newname[USERNAME_SIZE];
708
709         if (CtdlAccessCheck(ac_aide)) {
710                 return;
711         }
712
713         extract_token(oldname, cmdbuf, 0, '|', sizeof oldname);
714         extract_token(newname, cmdbuf, 1, '|', sizeof newname);
715
716         retcode = rename_user(oldname, newname);
717         switch(retcode) {
718                 case RENAMEUSER_OK:
719                         cprintf("%d '%s' has been renamed to '%s'.\n", CIT_OK, oldname, newname);
720                         return;
721                 case RENAMEUSER_LOGGED_IN:
722                         cprintf("%d '%s' is currently logged in and cannot be renamed.\n",
723                                 ERROR + ALREADY_LOGGED_IN , oldname);
724                         return;
725                 case RENAMEUSER_NOT_FOUND:
726                         cprintf("%d '%s' does not exist.\n", ERROR + NO_SUCH_USER, oldname);
727                         return;
728                 case RENAMEUSER_ALREADY_EXISTS:
729                         cprintf("%d A user named '%s' already exists.\n", ERROR + ALREADY_EXISTS, newname);
730                         return;
731         }
732
733         cprintf("%d An unknown error occurred.\n", ERROR);
734 }
735
736
737
738 void cmd_quit(char *argbuf)
739 {
740         cprintf("%d Goodbye.\n", CIT_OK);
741         CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
742 }
743
744
745 void cmd_lout(char *argbuf)
746 {
747         if (CC->logged_in) 
748                 CtdlUserLogout();
749         cprintf("%d logged out.\n", CIT_OK);
750 }
751
752
753 /*****************************************************************************/
754 /*                      MODULE INITIALIZATION STUFF                          */
755 /*****************************************************************************/
756
757
758 CTDL_MODULE_INIT(serv_user)
759 {
760         if (!threading) {
761                 CtdlRegisterProtoHook(cmd_user, "USER", "Submit username for login");
762                 CtdlRegisterProtoHook(cmd_pass, "PASS", "Complete login by submitting a password");
763                 CtdlRegisterProtoHook(cmd_quit, "QUIT", "log out and disconnect from server");
764                 CtdlRegisterProtoHook(cmd_lout, "LOUT", "log out but do not disconnect from server");
765                 CtdlRegisterProtoHook(cmd_creu, "CREU", "Create User");
766                 CtdlRegisterProtoHook(cmd_setp, "SETP", "Set the password for an account");
767                 CtdlRegisterProtoHook(cmd_getu, "GETU", "Get User parameters");
768                 CtdlRegisterProtoHook(cmd_setu, "SETU", "Set User parameters");
769                 CtdlRegisterProtoHook(cmd_slrp, "SLRP", "Set Last Read Pointer");
770                 CtdlRegisterProtoHook(cmd_invt, "INVT", "Invite a user to a room");
771                 CtdlRegisterProtoHook(cmd_kick, "KICK", "Kick a user out of a room");
772                 CtdlRegisterProtoHook(cmd_forg, "FORG", "Forget a room");
773                 CtdlRegisterProtoHook(cmd_gnur, "GNUR", "Get Next Unregistered User");
774                 CtdlRegisterProtoHook(cmd_vali, "VALI", "Validate new users");
775                 CtdlRegisterProtoHook(cmd_list, "LIST", "List users");
776                 CtdlRegisterProtoHook(cmd_chek, "CHEK", "assorted info we need to check at login");
777                 CtdlRegisterProtoHook(cmd_qusr, "QUSR", "check to see if a user exists");
778                 CtdlRegisterProtoHook(cmd_agup, "AGUP", "Administratively Get User Parameters");
779                 CtdlRegisterProtoHook(cmd_asup, "ASUP", "Administratively Set User Parameters");
780                 CtdlRegisterProtoHook(cmd_seen, "SEEN", "Manipulate seen/unread message flags");
781                 CtdlRegisterProtoHook(cmd_gtsn, "GTSN", "Fetch seen/unread message flags");
782                 CtdlRegisterProtoHook(cmd_view, "VIEW", "Set preferred view for user/room combination");
783                 CtdlRegisterProtoHook(cmd_renu, "RENU", "Rename a user");
784                 CtdlRegisterProtoHook(cmd_newu, "NEWU", "Log in as a new user");
785                 CtdlRegisterProtoHook(cmd_isme, "ISME", "Determine whether an email address belongs to a user");
786         }
787         /* return our Subversion id for the Log */
788         return "user";
789 }