]> code.citadel.org Git - citadel.git/blobdiff - citadel/user_ops.c
getuserbynumber() now uses a proper indexed database
[citadel.git] / citadel / user_ops.c
index 3fd6b4a8536ab6ddd2511e2dcb7787aab7a0e78b..ac25b1a8926616e4b9014c78a7f4ed5d124ddfa1 100644 (file)
@@ -59,13 +59,13 @@ int chkpwd_read_pipe[2];
  * makeuserkey() - convert a username into the format used as a database key
  *              (it's just the username converted into lower case)
  */
-static INLINE void makeuserkey(char *key, char *username) {
+INLINE void makeuserkey(char *key, char *username) {
        int i, len;
 
        len = strlen(username);
        if (len >= USERNAME_SIZE)
        {
-               lprintf (CTDL_EMERG, "Username to long: %s", username);
+               CtdlLogPrintf (CTDL_EMERG, "Username to long: %s", username);
                cit_backtrace ();
                len = USERNAME_SIZE - 1; 
                username[USERNAME_SIZE - 1]='\0';
@@ -187,12 +187,20 @@ int rename_user(char *oldname, char *newname) {
                }
 
                else {          /* Sanity checks succeeded.  Now rename the user. */
-
-                       lprintf(CTDL_DEBUG, "Renaming <%s> to <%s>\n", oldname, newname);
-                       cdb_delete(CDB_USERS, oldnamekey, strlen(oldnamekey));
-                       safestrncpy(usbuf.fullname, newname, sizeof usbuf.fullname);
-                       putuser(&usbuf);
-                       retcode = RENAMEUSER_OK;
+                       if (usbuf.usernum == 0)
+                       {
+                               CtdlLogPrintf (CTDL_DEBUG, "Can not rename user \"Citadel\".\n");
+                               retcode = RENAMEUSER_NOT_FOUND;
+                       } else {
+                               CtdlLogPrintf(CTDL_DEBUG, "Renaming <%s> to <%s>\n", oldname, newname);
+                               cdb_delete(CDB_USERS, oldnamekey, strlen(oldnamekey));
+                               safestrncpy(usbuf.fullname, newname, sizeof usbuf.fullname);
+                               putuser(&usbuf);
+                               cdb_store(CDB_USERSBYNUMBER, &usbuf.usernum, sizeof(long),
+                                       usbuf.fullname, strlen(usbuf.fullname)+1 );
+
+                               retcode = RENAMEUSER_OK;
+                       }
                }
        
        }
@@ -350,33 +358,83 @@ int is_room_aide(void)
 }
 
 /*
- * getuserbynumber()  -  get user by number
- *                    returns 0 if user was found
+ * getuserbynumber() - get user by number
+ *                     returns 0 if user was found
  *
- * WARNING: don't use this function unless you absolutely have to.  It does
- *       a sequential search and therefore is computationally expensive.
+ * Note: fetching a user this way requires one additional database operation.
  */
-int getuserbynumber(struct ctdluser *usbuf, long int number)
+int getuserbynumber(struct ctdluser *usbuf, long number)
 {
-       struct cdbdata *cdbus;
+       struct cdbdata *cdbun;
+       int r;
 
-       cdb_rewind(CDB_USERS);
+       cdbun = cdb_fetch(CDB_USERSBYNUMBER, &number, sizeof(long));
+       if (cdbun == NULL) {
+               CtdlLogPrintf(CTDL_INFO, "User %ld not found\n", number);
+               return(-1);
+       }
 
-       while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
-               memset(usbuf, 0, sizeof(struct ctdluser));
-               memcpy(usbuf, cdbus->ptr,
-                      ((cdbus->len > sizeof(struct ctdluser)) ?
-                       sizeof(struct ctdluser) : cdbus->len));
-               cdb_free(cdbus);
-               if (usbuf->usernum == number) {
-                       cdb_close_cursor(CDB_USERS);
-                       return (0);
-               }
+       CtdlLogPrintf(CTDL_INFO, "User %ld maps to %s\n", number, cdbun->ptr);
+       r = getuser(usbuf, cdbun->ptr);
+       cdb_free(cdbun);
+       return(r);
+}
+
+
+
+/*
+ * Helper function for rebuild_usersbynumber()
+ */
+void rebuild_ubn_for_user(struct ctdluser *usbuf, void *data) {
+
+       struct ubnlist {
+               struct ubnlist *next;
+               char username[USERNAME_SIZE];
+               long usernum;
+       };
+
+       static struct ubnlist *u = NULL;
+       struct ubnlist *ptr = NULL;
+
+       /* Lazy programming here.  Call this function as a ForEachUser backend
+        * in order to queue up the room names, or call it with a null user
+        * to make it do the processing.
+        */
+       if (usbuf != NULL) {
+               ptr = (struct ubnlist *) malloc(sizeof (struct ubnlist));
+               if (ptr == NULL) return;
+
+               ptr->usernum = usbuf->usernum;
+               safestrncpy(ptr->username, usbuf->fullname, sizeof ptr->username);
+               ptr->next = u;
+               u = ptr;
+               return;
        }
-       return (-1);
+
+       while (u != NULL) {
+               CtdlLogPrintf(CTDL_DEBUG, "Rebuilding usersbynumber index %10ld : %s\n",
+                       u->usernum, u->username);
+               cdb_store(CDB_USERSBYNUMBER, &u->usernum, sizeof(long), u->username, strlen(u->username)+1);
+
+               ptr = u;
+               u = u->next;
+               free(ptr);
+       }
+}
+
+
+
+/*
+ * Rebuild the users-by-number index
+ */
+void rebuild_usersbynumber(void) {
+       cdb_trunc(CDB_USERSBYNUMBER);                   /* delete the old indices */
+       ForEachUser(rebuild_ubn_for_user, NULL);        /* enumerate the users */
+       rebuild_ubn_for_user(NULL, NULL);               /* and index them */
 }
 
 
+
 /*
  * getuserbyuid()  -     get user by system uid (for PAM mode authentication)
  *                    returns 0 if user was found
@@ -414,13 +472,19 @@ int CtdlLoginExistingUser(char *authname, char *trythisname)
        char username[SIZ];
        int found_user;
 
-       lprintf(9, "CtdlLoginExistingUser(%s, %s)\n", authname, trythisname);
+       CtdlLogPrintf(9, "CtdlLoginExistingUser(%s, %s)\n", authname, trythisname);
 
        if ((CC->logged_in)) {
                return login_already_logged_in;
        }
 
        if (trythisname == NULL) return login_not_found;
+       
+       if (!strncasecmp(trythisname, "SYS_", 4))
+       {
+               CtdlLogPrintf(CTDL_DEBUG, "System user \"%s\" is not allowed to log in.\n", trythisname);
+               return login_not_found;
+       }
 
        /* If a "master user" is defined, handle its authentication if specified */
        CC->is_master = 0;
@@ -446,21 +510,21 @@ int CtdlLoginExistingUser(char *authname, char *trythisname)
                struct passwd *tempPwdPtr;
                char pwdbuffer[256];
        
-               lprintf(CTDL_DEBUG, "asking host about <%s>\n", username);
+               CtdlLogPrintf(CTDL_DEBUG, "asking host about <%s>\n", username);
 #ifdef HAVE_GETPWNAM_R
 #ifdef SOLARIS_GETPWUID
-               lprintf(CTDL_DEBUG, "Calling getpwnam_r()\n");
+               CtdlLogPrintf(CTDL_DEBUG, "Calling getpwnam_r()\n");
                tempPwdPtr = getpwnam_r(username, &pd, pwdbuffer, sizeof pwdbuffer);
 #else // SOLARIS_GETPWUID
-               lprintf(CTDL_DEBUG, "Calling getpwnam_r()\n");
+               CtdlLogPrintf(CTDL_DEBUG, "Calling getpwnam_r()\n");
                getpwnam_r(username, &pd, pwdbuffer, sizeof pwdbuffer, &tempPwdPtr);
 #endif // SOLARIS_GETPWUID
 #else // HAVE_GETPWNAM_R
-               lprintf(CTDL_DEBUG, "SHOULD NEVER GET HERE!!!\n");
+               CtdlLogPrintf(CTDL_DEBUG, "SHOULD NEVER GET HERE!!!\n");
                tempPwdPtr = NULL;
 #endif // HAVE_GETPWNAM_R
                if (tempPwdPtr == NULL) {
-                       lprintf(CTDL_DEBUG, "no such user <%s>\n", username);
+                       CtdlLogPrintf(CTDL_DEBUG, "no such user <%s>\n", username);
                        return login_not_found;
                }
        
@@ -468,7 +532,7 @@ int CtdlLoginExistingUser(char *authname, char *trythisname)
                 * If not found, make one attempt to create it.
                 */
                found_user = getuserbyuid(&CC->user, pd.pw_uid);
-               lprintf(CTDL_DEBUG, "found it: uid=%ld, gecos=%s here: %d\n",
+               CtdlLogPrintf(CTDL_DEBUG, "found it: uid=%ld, gecos=%s here: %d\n",
                        (long)pd.pw_uid, pd.pw_gecos, found_user);
                if (found_user != 0) {
                        create_user(username, 0);
@@ -556,9 +620,7 @@ void cmd_user(char *cmdbuf)
  */
 void session_startup(void)
 {
-       int i = 0;
-
-       lprintf(CTDL_NOTICE, "<%s> logged in\n", CC->curr_user);
+       CtdlLogPrintf(CTDL_NOTICE, "<%s> logged in\n", CC->curr_user);
 
        lgetuser(&CC->user, CC->curr_user);
        ++(CC->user.timescalled);
@@ -590,11 +652,7 @@ void session_startup(void)
         */
        snprintf(CC->cs_inet_email, sizeof CC->cs_inet_email, "%s@%s",
                CC->user.fullname, config.c_fqdn);
-       for (i=0; !IsEmptyStr(&CC->cs_inet_email[i]); ++i) {
-               if (isspace(CC->cs_inet_email[i])) {
-                       CC->cs_inet_email[i] = '_';
-               }
-       }
+       convert_spaces_to_underscores(CC->cs_inet_email);
 
        /* Create any personal rooms required by the system.
         * (Technically, MAILROOM should be there already, but just in case...)
@@ -625,50 +683,51 @@ void logged_in_response(void)
 /* 
  * misc things to be taken care of when a user is logged out
  */
-void logout(struct CitContext *who)
+void logout(void)
 {
-       /*
-        * Clear out some session data.  Most likely, the CitContext for this
-        * session is about to get nuked when the session disconnects, but
-        * since it's possible to log in again without reconnecting, we cannot
-        * make that assumption.
-        */
-       strcpy(who->fake_username, "");
-       strcpy(who->fake_hostname, "");
-       strcpy(who->fake_roomname, "");
-       who->logged_in = 0;
-
+       struct CitContext *CCC = CC;    /* CachedCitContext - performance boost */
        /*
         * If there is a download in progress, abort it.
         */
-       if (who->download_fp != NULL) {
-               fclose(who->download_fp);
-               who->download_fp = NULL;
+       if (CCC->download_fp != NULL) {
+               fclose(CCC->download_fp);
+               CCC->download_fp = NULL;
        }
 
        /*
         * If there is an upload in progress, abort it.
         */
-       if (who->upload_fp != NULL) {
-               abort_upl(who);
+       if (CCC->upload_fp != NULL) {
+               abort_upl(CCC);
        }
 
        /*
         * If we were talking to a network node, we're not anymore...
         */
-       if (!IsEmptyStr(who->net_node)) {
-               network_talking_to(who->net_node, NTT_REMOVE);
+       if (!IsEmptyStr(CCC->net_node)) {
+               network_talking_to(CCC->net_node, NTT_REMOVE);
        }
 
-       /* Do modular stuff... */
+       /* Run any hooks registered by modules... */
        PerformSessionHooks(EVT_LOGOUT);
        
+       /*
+        * Clear out some session data.  Most likely, the CitContext for this
+        * session is about to get nuked when the session disconnects, but
+        * since it's possible to log in again without reconnecting, we cannot
+        * make that assumption.
+        */
+       strcpy(CCC->fake_username, "");
+       strcpy(CCC->fake_hostname, "");
+       strcpy(CCC->fake_roomname, "");
+       CCC->logged_in = 0;
+
        /* Check to see if the user was deleted whilst logged in and purge them if necessary */
-       if (who->user.axlevel == 0)
-               purge_user(who->user.fullname);
+       if ((CCC->user.axlevel == 0) && (CCC->user.usernum))
+               purge_user(CCC->user.fullname);
 
        /* Free any output buffers */
-       if (who->output_buffer != NULL) {
+       if (CCC->output_buffer != NULL) {
                unbuffer_output();
        }
 }
@@ -681,11 +740,11 @@ static int validpw(uid_t uid, const char *pass)
        char buf[256];
 
        if (IsEmptyStr(pass)) {
-               lprintf(CTDL_DEBUG, "refusing to check empty password for uid=%d using chkpwd...\n", uid);
+               CtdlLogPrintf(CTDL_DEBUG, "refusing to check empty password for uid=%d using chkpwd...\n", uid);
                return 0;
        }
 
-       lprintf(CTDL_DEBUG, "Validating password for uid=%d using chkpwd...\n", uid);
+       CtdlLogPrintf(CTDL_DEBUG, "Validating password for uid=%d using chkpwd...\n", uid);
 
        begin_critical_section(S_CHKPWD);
        write(chkpwd_write_pipe[1], &uid, sizeof(uid_t));
@@ -694,11 +753,11 @@ static int validpw(uid_t uid, const char *pass)
        end_critical_section(S_CHKPWD);
 
        if (!strncmp(buf, "PASS", 4)) {
-               lprintf(CTDL_DEBUG, "...pass\n");
+               CtdlLogPrintf(CTDL_DEBUG, "...pass\n");
                return(1);
        }
 
-       lprintf(CTDL_DEBUG, "...fail\n");
+       CtdlLogPrintf(CTDL_DEBUG, "...fail\n");
        return 0;
 }
 
@@ -710,7 +769,7 @@ void start_chkpwd_daemon(void) {
        struct stat filestats;
        int i;
 
-       lprintf(CTDL_DEBUG, "Starting chkpwd daemon for host authentication mode\n");
+       CtdlLogPrintf(CTDL_DEBUG, "Starting chkpwd daemon for host authentication mode\n");
 
        if ((stat(file_chkpwd, &filestats)==-1) ||
            (filestats.st_size==0)){
@@ -718,29 +777,29 @@ void start_chkpwd_daemon(void) {
                abort();
        }
        if (pipe(chkpwd_write_pipe) != 0) {
-               lprintf(CTDL_EMERG, "Unable to create pipe for chkpwd daemon: %s\n", strerror(errno));
+               CtdlLogPrintf(CTDL_EMERG, "Unable to create pipe for chkpwd daemon: %s\n", strerror(errno));
                abort();
        }
        if (pipe(chkpwd_read_pipe) != 0) {
-               lprintf(CTDL_EMERG, "Unable to create pipe for chkpwd daemon: %s\n", strerror(errno));
+               CtdlLogPrintf(CTDL_EMERG, "Unable to create pipe for chkpwd daemon: %s\n", strerror(errno));
                abort();
        }
 
        chkpwd_pid = fork();
        if (chkpwd_pid < 0) {
-               lprintf(CTDL_EMERG, "Unable to fork chkpwd daemon: %s\n", strerror(errno));
+               CtdlLogPrintf(CTDL_EMERG, "Unable to fork chkpwd daemon: %s\n", strerror(errno));
                abort();
        }
        if (chkpwd_pid == 0) {
-               lprintf(CTDL_DEBUG, "Now calling dup2() write\n");
+               CtdlLogPrintf(CTDL_DEBUG, "Now calling dup2() write\n");
                dup2(chkpwd_write_pipe[0], 0);
-               lprintf(CTDL_DEBUG, "Now calling dup2() write\n");
+               CtdlLogPrintf(CTDL_DEBUG, "Now calling dup2() write\n");
                dup2(chkpwd_read_pipe[1], 1);
-               lprintf(CTDL_DEBUG, "Now closing stuff\n");
+               CtdlLogPrintf(CTDL_DEBUG, "Now closing stuff\n");
                for (i=2; i<256; ++i) close(i);
-               lprintf(CTDL_DEBUG, "Now calling execl(%s)\n", file_chkpwd);
+               CtdlLogPrintf(CTDL_DEBUG, "Now calling execl(%s)\n", file_chkpwd);
                execl(file_chkpwd, file_chkpwd, NULL);
-               lprintf(CTDL_EMERG, "Unable to exec chkpwd daemon: %s\n", strerror(errno));
+               CtdlLogPrintf(CTDL_EMERG, "Unable to exec chkpwd daemon: %s\n", strerror(errno));
                abort();
                exit(errno);
        }
@@ -759,19 +818,19 @@ int CtdlTryPassword(char *password)
        int code;
 
        if ((CC->logged_in)) {
-               lprintf(CTDL_WARNING, "CtdlTryPassword: already logged in\n");
+               CtdlLogPrintf(CTDL_WARNING, "CtdlTryPassword: already logged in\n");
                return pass_already_logged_in;
        }
        if (!strcmp(CC->curr_user, NLI)) {
-               lprintf(CTDL_WARNING, "CtdlTryPassword: no user selected\n");
+               CtdlLogPrintf(CTDL_WARNING, "CtdlTryPassword: no user selected\n");
                return pass_no_user;
        }
        if (getuser(&CC->user, CC->curr_user)) {
-               lprintf(CTDL_ERR, "CtdlTryPassword: internal error\n");
+               CtdlLogPrintf(CTDL_ERR, "CtdlTryPassword: internal error\n");
                return pass_internal_error;
        }
        if (password == NULL) {
-               lprintf(CTDL_INFO, "CtdlTryPassword: NULL password string supplied\n");
+               CtdlLogPrintf(CTDL_INFO, "CtdlTryPassword: NULL password string supplied\n");
                return pass_wrong_password;
        }
        code = (-1);
@@ -825,7 +884,7 @@ int CtdlTryPassword(char *password)
                do_login();
                return pass_ok;
        } else {
-               lprintf(CTDL_WARNING, "Bad password specified for <%s>\n", CC->curr_user);
+               CtdlLogPrintf(CTDL_WARNING, "Bad password specified for <%s>\n", CC->curr_user);
                return pass_wrong_password;
        }
 }
@@ -876,7 +935,7 @@ int purge_user(char pname[])
                return (ERROR + NO_SUCH_USER);
 
        if (getuser(&usbuf, pname) != 0) {
-               lprintf(CTDL_ERR, "Cannot purge user <%s> - not found\n", pname);
+               CtdlLogPrintf(CTDL_ERR, "Cannot purge user <%s> - not found\n", pname);
                return (ERROR + NO_SUCH_USER);
        }
        /* Don't delete a user who is currently logged in.  Instead, just
@@ -892,12 +951,12 @@ int purge_user(char pname[])
        }
        end_critical_section(S_SESSION_TABLE);
        if (user_is_logged_in == 1) {
-               lprintf(CTDL_WARNING, "User <%s> is logged in; not deleting.\n", pname);
+               CtdlLogPrintf(CTDL_WARNING, "User <%s> is logged in; not deleting.\n", pname);
                usbuf.axlevel = 0;
                putuser(&usbuf);
                return (1);
        }
-       lprintf(CTDL_NOTICE, "Deleting user <%s>\n", pname);
+       CtdlLogPrintf(CTDL_NOTICE, "Deleting user <%s>\n", pname);
 
        /* Perform any purge functions registered by server extensions */
        PerformUserHooks(&usbuf, EVT_PURGEUSER);
@@ -905,6 +964,9 @@ int purge_user(char pname[])
        /* delete any existing user/room relationships */
        cdb_delete(CDB_VISIT, &usbuf.usernum, sizeof(long));
 
+       /* delete the users-by-number index record */
+       cdb_delete(CDB_USERSBYNUMBER, &usbuf.usernum, sizeof(long));
+
        /* delete the userlog entry */
        cdb_delete(CDB_USERS, usernamekey, strlen(usernamekey));
 
@@ -969,7 +1031,7 @@ int create_user(char *newusername, int become_user)
                        uid = pd.pw_uid;
                        if (IsEmptyStr (username))
                        {
-                               lprintf (CTDL_EMERG, 
+                               CtdlLogPrintf (CTDL_EMERG, 
                                         "Can't find Realname for user %s [%d] in the Host Auth Database; giving up.\n", 
                                         newusername, pd.pw_uid);
                                snprintf(buf, SIZ, 
@@ -1012,8 +1074,9 @@ int create_user(char *newusername, int become_user)
                usbuf.axlevel = 6;
        }
 
-       /* add user to userlog */
+       /* add user to the database */
        putuser(&usbuf);
+       cdb_store(CDB_USERSBYNUMBER, &usbuf.usernum, sizeof(long), usbuf.fullname, strlen(usbuf.fullname)+1);
 
        /*
         * Give the user a private mailbox and a configuration room.
@@ -1055,7 +1118,7 @@ int create_user(char *newusername, int become_user)
                CC->cs_addr
        );
        aide_message(buf, "User Creation Notice");
-       lprintf(CTDL_NOTICE, "New user <%s> created\n", username);
+       CtdlLogPrintf(CTDL_NOTICE, "New user <%s> created\n", username);
        return (0);
 }
 
@@ -1153,7 +1216,7 @@ void cmd_setp(char *new_pw)
        safestrncpy(CC->user.password, new_pw, sizeof(CC->user.password));
        lputuser(&CC->user);
        cprintf("%d Password changed.\n", CIT_OK);
-       lprintf(CTDL_INFO, "Password changed for user <%s>\n", CC->curr_user);
+       CtdlLogPrintf(CTDL_INFO, "Password changed for user <%s>\n", CC->curr_user);
        PerformSessionHooks(EVT_SETPASS);
 }