"""
-Module for querying information about users. This mostly asks plugins for
-the extra information, and falls back to using a default that should work
-on most systems (but by no means all systems.)
+Module for querying information about users. This mostly asks plugins
+for the extra information, and falls back to using a default that should
+work on most systems (but by no means all systems.)
"""
import pkg_resources
import os
+import socket
+import logging
+import pwd
+
+from wizard import plugin
def quota(dir=None):
"""
- Returns a tuple (quota usage, quota limit). Returns ``(0, None)`` if
+ Returns a tuple (quota usage, quota limit). Returns ``None`` if
the quota usage is unknown. If ``dir`` is omitted, the current
working directory is assumed. Value returned is in bytes.
+
+ This function implements a plugin interface named
+ :ref:`wizard.user.quota`.
"""
if dir is None:
dir = os.getcwd()
- unknown = (0, None)
- for entry in pkg_resources.iter_entry_points("wizard.user.quota"):
- func = entry.load()
- r = func(dir)
- if r != unknown:
- return r
- return unknown
+ return plugin.hook("wizard.user.quota", [dir])
+
+def email(name=None):
+ """
+ Converts a username into an email address to that user. If you have
+ a UID, you will have to convert it into a username first. If no
+ canonical source of information is found, an heuristic approach
+ will be used. If ``name`` is ``None``, the current user will be
+ used unless it is root, in which case :func:`operator` is tried
+ first to determine the real current user.
+
+ This function implements a plugin interface named
+ :ref:`wizard.user.email`.
+ """
+ if name is None:
+ logging.info("wizard.user.email: Determining email for current user")
+ env_email = os.getenv("EMAIL")
+ if env_email is not None:
+ logging.info("wizard.user.email: Used environment email %s", env_email)
+ return env_email
+ name = operator()
+ # run plugins
+ r = plugin.hook("wizard.user.email", [name])
+ if r is not None:
+ return r
+ # guess an email
+ try:
+ mailname = open("/etc/mailname").read()
+ except:
+ mailname = socket.getfqdn()
+ return name + "@" + mailname
+
+def operator():
+ """
+ Determines the username of the true person who is running this
+ program. If the process's real uid is nonzero, just do a passwd
+ lookup; otherwise attempt to figure out the user behind the root
+ prompt some other way.
+
+ This function implements a plugin interface named
+ :ref:`wizard.user.operator`.
+ """
+ uid = os.getuid()
+ if uid:
+ pwdentry = pwd.getpwuid(uid)
+ return pwdentry.pw_name
+ # run plugins
+ r = plugin.hook("wizard.user.operator", [])
+ if r is not None:
+ return r
+ # use SUDO_USER
+ sudo_user = os.getenv("SUDO_USER")
+ if not sudo_user:
+ return None
+ pwdentry = pwd.getpwnam(sudo_user)
+ return pwdentry.pw_name
+
+def passwd(path=None, uid=None):
+ """
+ Returns a passwd-like entry (a :class:`Info` object) corresponding
+ to the owner of ``path``. If ``uid`` is specified, ``path`` is used
+ solely to determine the filesystem ``uid`` was determined from. It
+ will fall back to the local passwd database, and return ``None``
+ if no information is available. If ``path`` is omitted, it will
+ fall back to the current working directory.
+
+ This function implements a plugin interface named
+ :ref:`wizard.user.passwd`.
+ """
+ if path is None:
+ path = os.getcwd()
+ path = os.path.realpath(path)
+ if not uid:
+ uid = os.stat(path).st_uid
+ r = plugin.hook("wizard.user.passwd", [path, uid])
+ if r is not None:
+ return r
+ try:
+ return Info.pwentry(pwd.getpwuid(uid))
+ except KeyError:
+ return None
+
+def pwnam(name):
+ """
+ This user converts a username into a :class:`Info` object using
+ *only* the local password database.
+ """
+ return Info.pwentry(pwd.getpwnam(name))
+
+class Info(object):
+ """
+ Object containing information describing a user. It is analogous to
+ passwd, but has dropped the password field and dedicated the
+ ``gecos`` field for real name information.
+
+ .. note::
+
+ If a platform does not support retrieving information about a
+ field, it may have the value ``None``.
+ """
+ #: Login name
+ name = None
+ #: User ID
+ uid = None
+ #: Group ID
+ gid = None
+ #: Real name
+ realname = None
+ #: Home directory
+ homedir = None
+ #: Default command interpreter
+ shell = None
+ @staticmethod
+ def pwentry(pwentry):
+ return Info(pwentry.pw_name, pwentry.pw_uid, pwentry.pw_gid,
+ pwentry.pw_gecos.split(",")[0], pwentry.pw_dir, pwentry.pw_shell)
+ def __init__(self, name, uid, gid, realname, homedir, shell):
+ self.name = name
+ self.uid = uid
+ self.gid = gid
+ self.realname = realname
+ self.homedir = homedir
+ self.shell = shell
+ self._email = None
+ @property
+ def email(self):
+ """The email of this user, calculated on the fly."""
+ if self._email is None:
+ self._email = email(self.name)
+ return self._email