neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

users.c (5491B)


      1 // users.c -- operating system user information
      2 
      3 #include <stdbool.h>
      4 #include <stdio.h>
      5 #include <string.h>
      6 #include <uv.h>
      7 
      8 #include "auto/config.h"
      9 #include "nvim/ascii_defs.h"
     10 #include "nvim/cmdexpand_defs.h"
     11 #include "nvim/garray.h"
     12 #include "nvim/garray_defs.h"
     13 #include "nvim/memory.h"
     14 #include "nvim/os/os.h"
     15 #include "nvim/os/os_defs.h"
     16 #include "nvim/vim_defs.h"
     17 #ifdef HAVE_PWD_FUNCS
     18 # include <pwd.h>
     19 #endif
     20 #ifdef MSWIN
     21 # include <lm.h>
     22 
     23 # include "nvim/mbyte.h"
     24 # include "nvim/message.h"
     25 #endif
     26 
     27 #include "os/users.c.generated.h"
     28 
     29 // All user names (for ~user completion as done by shell).
     30 static garray_T ga_users = GA_EMPTY_INIT_VALUE;
     31 
     32 // Add a user name to the list of users in garray_T *users.
     33 // Do nothing if user name is NULL or empty.
     34 static void add_user(garray_T *users, char *user, bool need_copy)
     35 {
     36  char *user_copy = (user != NULL && need_copy)
     37                    ? xstrdup(user) : user;
     38 
     39  if (user_copy == NULL || *user_copy == NUL) {
     40    if (need_copy) {
     41      xfree(user_copy);
     42    }
     43    return;
     44  }
     45  GA_APPEND(char *, users, user_copy);
     46 }
     47 
     48 // Initialize users garray and fill it with os usernames.
     49 // Return Ok for success, FAIL for failure.
     50 int os_get_usernames(garray_T *users)
     51 {
     52  if (users == NULL) {
     53    return FAIL;
     54  }
     55  ga_init(users, sizeof(char *), 20);
     56 
     57 #ifdef HAVE_PWD_FUNCS
     58  {
     59    struct passwd *pw;
     60 
     61    setpwent();
     62    while ((pw = getpwent()) != NULL) {
     63      add_user(users, pw->pw_name, true);
     64    }
     65    endpwent();
     66  }
     67 #elif defined(MSWIN)
     68  {
     69    DWORD nusers = 0, ntotal = 0, i;
     70    PUSER_INFO_0 uinfo;
     71 
     72    if (NetUserEnum(NULL, 0, 0, (LPBYTE *)&uinfo, MAX_PREFERRED_LENGTH,
     73                    &nusers, &ntotal, NULL) == NERR_Success) {
     74      for (i = 0; i < nusers; i++) {
     75        char *user;
     76        int conversion_result = utf16_to_utf8(uinfo[i].usri0_name, -1, &user);
     77        if (conversion_result != 0) {
     78          semsg("utf16_to_utf8 failed: %d", conversion_result);
     79          break;
     80        }
     81        add_user(users, user, false);
     82      }
     83 
     84      NetApiBufferFree(uinfo);
     85    }
     86  }
     87 #endif
     88 #ifdef HAVE_PWD_FUNCS
     89  {
     90    char *user_env = os_getenv_noalloc("USER");
     91 
     92    // The $USER environment variable may be a valid remote user name (NIS,
     93    // LDAP) not already listed by getpwent(), as getpwent() only lists
     94    // local user names.  If $USER is not already listed, check whether it
     95    // is a valid remote user name using getpwnam() and if it is, add it to
     96    // the list of user names.
     97 
     98    if (user_env != NULL && *user_env != NUL) {
     99      int i;
    100 
    101      for (i = 0; i < users->ga_len; i++) {
    102        char *local_user = ((char **)users->ga_data)[i];
    103 
    104        if (strcmp(local_user, user_env) == 0) {
    105          break;
    106        }
    107      }
    108 
    109      if (i == users->ga_len) {
    110        struct passwd *pw = getpwnam(user_env);  // NOLINT
    111 
    112        if (pw != NULL) {
    113          add_user(users, pw->pw_name, true);
    114        }
    115      }
    116    }
    117  }
    118 #endif
    119 
    120  return OK;
    121 }
    122 
    123 /// Gets the username that owns the current Nvim process.
    124 ///
    125 /// @param s[out] Username.
    126 /// @param len Length of `s`.
    127 ///
    128 /// @return OK if a name found.
    129 int os_get_username(char *s, size_t len)
    130 {
    131 #ifdef UNIX
    132  return os_get_uname((uv_uid_t)getuid(), s, len);
    133 #else
    134  // TODO(equalsraf): Windows GetUserName()
    135  return os_get_uname((uv_uid_t)0, s, len);
    136 #endif
    137 }
    138 
    139 /// Gets the username associated with `uid`.
    140 ///
    141 /// @param uid User id.
    142 /// @param s[out] Username, or `uid` on failure.
    143 /// @param len Length of `s`.
    144 ///
    145 /// @return OK if a username was found, else FAIL.
    146 int os_get_uname(uv_uid_t uid, char *s, size_t len)
    147 {
    148 #ifdef HAVE_PWD_FUNCS
    149  struct passwd *pw;
    150 
    151  if ((pw = getpwuid(uid)) != NULL  // NOLINT(runtime/threadsafe_fn)
    152      && pw->pw_name != NULL && *(pw->pw_name) != NUL) {
    153    xstrlcpy(s, pw->pw_name, len);
    154    return OK;
    155  }
    156 #endif
    157  snprintf(s, len, "%d", (int)uid);
    158  return FAIL;  // a number is not a name
    159 }
    160 
    161 /// Gets the user directory for the given username, or NULL on failure.
    162 ///
    163 /// Caller must free() the returned string.
    164 char *os_get_userdir(const char *name)
    165 {
    166 #ifdef HAVE_PWD_FUNCS
    167  if (name == NULL || *name == NUL) {
    168    return NULL;
    169  }
    170  struct passwd *pw = getpwnam(name);  // NOLINT(runtime/threadsafe_fn)
    171  if (pw != NULL) {
    172    // save the string from the static passwd entry into malloced memory
    173    return xstrdup(pw->pw_dir);
    174  }
    175 #endif
    176  return NULL;
    177 }
    178 
    179 #if defined(EXITFREE)
    180 
    181 void free_users(void)
    182 {
    183  ga_clear_strings(&ga_users);
    184 }
    185 
    186 #endif
    187 
    188 /// Find all user names for user completion.
    189 ///
    190 /// Done only once and then cached.
    191 static void init_users(void)
    192 {
    193  static bool lazy_init_done = false;
    194 
    195  if (lazy_init_done) {
    196    return;
    197  }
    198 
    199  lazy_init_done = true;
    200 
    201  os_get_usernames(&ga_users);
    202 }
    203 
    204 /// Given to ExpandGeneric() to obtain user names.
    205 char *get_users(expand_T *xp, int idx)
    206 {
    207  init_users();
    208  if (idx < ga_users.ga_len) {
    209    return ((char **)ga_users.ga_data)[idx];
    210  }
    211  return NULL;
    212 }
    213 
    214 /// Check whether name matches a user name.
    215 ///
    216 /// @return 0 if name does not match any user name.
    217 ///         1 if name partially matches the beginning of a user name.
    218 ///         2 is name fully matches a user name.
    219 int match_user(char *name)
    220 {
    221  int n = (int)strlen(name);
    222  int result = 0;
    223 
    224  init_users();
    225  for (int i = 0; i < ga_users.ga_len; i++) {
    226    if (strcmp(((char **)ga_users.ga_data)[i], name) == 0) {
    227      return 2;       // full match
    228    }
    229    if (strncmp(((char **)ga_users.ga_data)[i], name, (size_t)n) == 0) {
    230      result = 1;       // partial match
    231    }
    232  }
    233  return result;
    234 }