hi,

windows-xp/7 python-2.6.2 pywin32-217

ages ago i wrote some code to get a user's home directory on windows
(even if they'd gone into the registry and moved it). i remember it
working when i wrote it but at some point it stopped working. when i
looked into it, it turned out that the registry subkeys no longer
looked like what the code was expecting.

the code was something like:

  import win32api, win32net, win32netcon, win32security, _winreg, re, os
  def user():
    try:
      dc = win32net.NetServerEnum(None, 100, win32netcon.SV_TYPE_DOMAIN_CTRL)
      dcname = r'\\' + dc[0][0]['name'] if dc[0] and isinstance(dc[0][0], dict) 
else None
    except Exception:
      dcname = None
    return win32net.NetUserGetInfo(dcname, win32api.GetUserName(), 1)['name']
  def home(username=None):
    if username is None:
      username = user()
    sid = 
win32security.ConvertSidToStringSid(win32security.LookupAccountName(None, 
username)[0])
    subkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\' + 
sid
    key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey)
    val, typ = _winreg.QueryValueEx(key, 'ProfileImagePath')
    if typ == _winreg.REG_EXPAND_SZ: # Which it is
      while True:
        match = re.compile('%\w+%').search(val)
        if match is None:
          break
        varname = val[match.start()+1:match.end()-1]
        val = val[0:match.start()] + os.getenv(varname, '') + val[match.end():]
    return val

given the account sid:

  S-1-5-5-21-725345543-1957994488-859522115

it expected to find this registry key:

  HKLM\SOFTWARE\Microsoft\Windows 
NT\CurrentVersion\ProfileList\S-1-5-5-21-725345543-1957994488-859522115

inside which it expected to find:

  ProfileImagePath = %SystemDrive%\Documents and Settings\patricia (WindowsXP)
  ProfileImagePath = C:\Users\patricia                             (Windows7)

however, instead of the above registry key, there are:

  HKLM\SOFTWARE\Microsoft\Windows 
NT\CurrentVersion\ProfileList\S-1-5-5-21-725345543-1957994488-859522115-1003
  HKLM\SOFTWARE\Microsoft\Windows 
NT\CurrentVersion\ProfileList\S-1-5-5-21-725345543-1957994488-859522115-1004
  HKLM\SOFTWARE\Microsoft\Windows 
NT\CurrentVersion\ProfileList\S-1-5-5-21-725345543-1957994488-859522115-1005
  HKLM\SOFTWARE\Microsoft\Windows 
NT\CurrentVersion\ProfileList\S-1-5-5-21-725345543-1957994488-859522115-1006
  HKLM\SOFTWARE\Microsoft\Windows 
NT\CurrentVersion\ProfileList\S-1-5-5-21-725345543-1957994488-859522115-1008
  HKLM\SOFTWARE\Microsoft\Windows 
NT\CurrentVersion\ProfileList\S-1-5-5-21-725345543-1957994488-859522115-1009

each referring to a different user (patricia happens to be the one ending in 
1009).

sadly, i've resorted to poking around likely locations
in the file system but that's not very satisfying.
and i'd like to avoid enumerating through all of these
keys and just assuming that the home directory contains
the user's login name (even though that's very likely).

so my questions are:
did the sid for the account name ever uniquely identify the user?
how do i obtain the "1009" that needs to be appended to the sid
to complete it?

i've attached the real code in case it's of any help.

p.s. i know (as of a few minutes ago) that there is a win32profile module
that looks like it should do all of this for me but according to its
documentation, i can only use win32profile.GetUserProfileDirectory()
if i have a token returned by win32security.LogonUser() but i don't
want or need to log the user in (even if i knew their passwords!)
so i have no such token and i can't use that function.

cheers,
raf

#!/usr/bin/env python

'''This module provides functions for obtaining the current user's name
and any user's home directory. They don't rely on environment variables
and they work properly on both POSIX and Windows systems.

Actually, finding the home directory doesn't work properly on Windows.
It gets the ProfileImagePath out of the registry but this is not the
same as the home directory in local users and groups. So, if the user
has specified a non-default home directory, this won't find it. It
will find the default one instead.

Also, the registry structure seems to have changed so that no longer
works anymore and instead we poke around the file system looking for
a likely candidate. I wish Windows would stop moving my chair.'''

import os, re

_debug = 0

_has_domain_controller = 0
def set_has_dc(has_domain_controller=1):
	'''Tell this module to look for a windows domain controller. Don't do
	this unless there is a domain controller because sometimes it causes
	inordinately long and unnecessary delays.'''
	global _has_domain_controller
	_has_domain_controller = has_domain_controller

def user():
	'''Return the user name of the current user.'''
	try:
		return _user_mswin()
	except ImportError:
		try: return _user_posix()
		except Exception: return ''

def _user_posix():
	'''Return the name of the current user on POSIX systems.
	Called by user().'''
	import pwd
	return pwd.getpwuid(os.getuid())[0]

def _user_mswin():
	'''Return the name of the current user on Windows systems.
	Called by user().'''
	import win32api, win32net, win32netcon
	if _has_domain_controller:
		try:
			dc = win32net.NetServerEnum(None, 100, win32netcon.SV_TYPE_DOMAIN_CTRL)
			dcname = r'\\' + dc[0][0]['name'] if dc[0] and isinstance(dc[0][0], dict) else None
		except Exception, e:
			if _debug:
				import traceback
				print('debug: Error: %s\n%s' % (e, traceback.format_exc()))
			dcname = None
	else:
		dcname = None
	user = win32api.GetUserName()
	return win32net.NetUserGetInfo(dcname, user, 1)['name']

def home(username=None):
	'''Return the home directory of the current user or the given user.'''
	try:
		return _home_mswin(username)
	except ImportError, e:
		try: return _home_posix(username)
		except Exception: return os.curdir

def _home_posix(username=None):
	'''Return the home directory of the current user or the given user on POSIX systems.
	Called by home().'''
	if not username:
		username = _user_posix()
	import pwd
	return pwd.getpwnam(username)[5]

def _home_mswin(username=None):
	'''Return the home directory of the current user or the given user on Windows systems.
	Called by home().'''
	if username is None:
		username = _user_mswin()
	import _winreg, win32security
	acct = win32security.LookupAccountName(None, username)
	if _debug:
		print('debug: acct = %r' % (acct,))
	sid = acct[0]
	if _debug:
		print('debug: sid = %r' % sid)
	#sid = win32security.ConvertSidToStringSid(win32security.LookupAccountName(None, username)[0])
	sid = win32security.ConvertSidToStringSid(sid)
	subkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\' + sid
	if _debug:
		print('debug: sid = %r' % sid)
		print('debug: subkey = %r' % subkey)
	try:
		key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey)
	except WindowsError, e:
		if _debug:
			import traceback
			print('debug: Error: %s\n%s' % (e, traceback.format_exc()))
		# XXX The above no longer works. There are registry entries that look
		# XXX like subkey-#### but none that look like subkey. It looks like
		# XXX the sid is not complete.
		val = 'C:/Users/' + username
		if os.path.exists(val):
			return val
		val = 'C:/Documents and Settings/' + username
		if os.path.exists(val):
			return val
		return os.curdir()
	val, typ = _winreg.QueryValueEx(key, 'ProfileImagePath')
	if _debug:
		print('debug: val = %r' % val)
		print('debug: typ = %r' % typ)
	if typ == _winreg.REG_EXPAND_SZ: # Which it is
		while True:
			_ = re.compile('%\w+%').search(val)
			if not _:
				break
			varname = val[_.start()+1:_.end()-1]
			val = val[0:_.start()] + os.getenv(varname, '') + val[_.end():]
			if _debug:
				print('debug: val = %r' % val)
	else:
		if _debug:
			print('debug: typ unexpected (!= %s)' % _winreg.REG_EXPAND_SZ)
	return val

if __name__ == '__main__':
	import test as unittest
	class Test(unittest.TestCase):
		def test_set_has_dc(self):
			set_has_dc()
			self.eq(_has_domain_controller, 1)
			set_has_dc(0)
			self.eq(_has_domain_controller, 0)
			set_has_dc(1)
			self.eq(_has_domain_controller, 1)
			set_has_dc(0)
			self.eq(_has_domain_controller, 0)
		def test_user_1(self):
			u = user()
			print('user=%r' % u)
			self.eq(len(u) != 0, True)
		def test_user_2(self):
			set_has_dc()
			u = user()
			set_has_dc(0)
			self.eq(len(u) != 0, True)
		def test_home_1(self):
			h = home()
			print('home=%r' % h)
			self.eq(len(h) != 0, True)
			self.eq(os.path.isdir(h), True)
		def test_home_2(self):
			h = home(user())
			self.eq(len(h) != 0, True)
			self.eq(os.path.isdir(h), True)

	unittest.main()

# vi:set ts=4 sw=4:
_______________________________________________
python-win32 mailing list
python-win32@python.org
http://mail.python.org/mailman/listinfo/python-win32

Reply via email to