#!/usr/bin/python
# -*- coding: UTF-8 -*-
###########################################################################
# wineread.py - description                                             #
# ------------------------------                                          #
# begin     : Fri Mar 26 2004                                             #
# copyright : (C) 2006 by Yuriy Kozlov                                    #
# email     : yuriy.kozlov@gmail.com                                      #
#                                                                         #
###########################################################################
#                                                                         #
#   This program is free software; you can redistribute it and/or modify  #
#   it under the terms of the GNU General Public License as published by  #
#   the Free Software Foundation; either version 2 of the License, or     #
#   (at your option) any later version.                                   #
#                                                                         #
###########################################################################

import os

""" Provides a set of functions for accessing wine's settings """

# Assumes the fake windows is installed in ~/.wine
default_winepath = os.environ['HOME'] + "/.wine"
winepath = default_winepath
defaultwinfolderspath = "c:\\users\\" + os.environ['USER']

# Where the dll's are
default_winebuildpath = "/usr/lib/wine"
default_winebuildpath2 = "/usr/lib32/wine"
default_winebuildpath3 = "/usr/local/lib/wine"
    
winebuildpath = None

def GetWineBuildPath():
    """ Returns the wine build path, None if not present """
    path = None
    if winebuildpath:
        path = winebuildpath
    elif os.path.exists(default_winebuildpath):
        path = default_winebuildpath
    elif os.path.exists(default_winebuildpath2):
        path = default_winebuildpath2
    elif os.path.exists(default_winebuildpath3):
        path = default_winebuildpath3

    return path
    
def SetWineBuildPath(path):
    """ Sets the winebuildpath to path """
    global winebuildpath
    winebuildpath = path

# drive = [index, letter, unixpath/mapping, type, label, serial]
empty_drives = (
    [0,"A:","","","",""],
    [1,"B:","","","",""],
    [2,"C:","","","",""],
    [3,"D:","","","",""],
    [4,"E:","","","",""],
    [5,"F:","","","",""],
    [6,"G:","","","",""],
    [7,"H:","","","",""],
    [8,"I:","","","",""],
    [9,"J:","","","",""],
    [10,"K:","","","",""],
    [11,"L:","","","",""],
    [12,"M:","","","",""],
    [13,"N:","","","",""],
    [14,"O:","","","",""],
    [15,"P:","","","",""],
    [16,"Q:","","","",""],
    [17,"R:","","","",""],
    [18,"S:","","","",""],
    [19,"T:","","","",""],
    [20,"U:","","","",""],
    [21,"V:","","","",""],
    [22,"W:","","","",""],
    [23,"X:","","","",""],
    [24,"Y:","","","",""],
    [25,"Z:","","","",""])
     
def GetEmptyDrives():
    """ Returns a list of 26 empty drives """
    drives = []
    for drive in empty_drives:
        drives.append(drive[:])
    return drives
        
def LoadDrives():
    drives = GetEmptyDrives()
    
    driveletters = os.listdir(winepath + "/dosdevices")
    for folder in driveletters:
        if len(folder) > 2 or folder[1] != ':':
            del folder
    set(driveletters)
    
    drivetypes = GetDriveTypes()
    
    for drive in drives:
        letter = drive[1].lower()
        if letter in driveletters:
            drive[2] = os.readlink(winepath + "/dosdevices/" + letter)
            if drive[1] in drivetypes:
                drive[3] = drivetypes[drive[1]]
    return drives

empty_shelllinks = ([26,"Desktop","","","",""],
    [27,"My Documents","","","",""],
    [28,"My Pictures","","","",""],
    [29,"My Music","","","",""],
    [30,"My Video","","","",""])
    
folder_nonexistent = "This folder does not exist, please map it."
profilesdirectory = winepath + "/dosdevices/c:/users/" + os.environ['USER']
        
def GetEmptyShellLinks():
    """ Returns a list of important windows folders """
    shelllinks = []
    for link in empty_shelllinks:
        shelllinks.append(link[:])
    return shelllinks
        
def GetShellLinks():
    shelllinks = GetEmptyShellLinks()
        
    existingshelllinks = os.listdir(profilesdirectory)
    set(existingshelllinks)
    shellregistry = GetShellRegistry()
    usershellregistry = GetUserShellRegistry()
    
    for link in shelllinks:
        if link[1] in existingshelllinks:
            linkpath = profilesdirectory + "/" + link[1]
            if os.path.islink(linkpath):
                link[2] = os.readlink(linkpath)
            else:
                link[2] = linkpath
            link[3] = "shellfolder"
            winpath = defaultwinfolderspath + "\\" + link[1]
            link[4] = winpath
            link[5] = shellregistry.get(link[1], defaultwinfolderspath + "\\" + link[1])
            link[5] = link[5].replace("\\\\","\\")
        else:
            link[3] = "shellfolder"
            link[4] = folder_nonexistent
            link[5] = shellregistry.get(link[1], folder_nonexistent)
            link[5] = link[5].replace("\\\\","\\")
            
    return shelllinks

def GetValue(key, value):
    """ Returns a specific value, returns a blank string if the value is not there. """
    # Need 4 \'s to generate one because both python and the shell use it as an escape character
    key = key.replace("\\","\\\\")
    key = key.replace(" ","\\ ")
    error = os.system("wine regedit /E .registryvalue.reg " + key)
    if error != 0:
        return ""
    
    file=open('.registryvalue.reg', encoding='utf-16', mode='r')
    
    for line in file:
        if line and line[0] == '"' or line[0] == '@':
            line = line.strip('\r\n')
            line = line.split('=')
            line = (line[0].strip('"'),line[1].strip('"@'))
            if line[0] == value:
                file.close()
                os.remove(".registryvalue.reg")
                return line[1]
    else:
        file.close()
        os.remove(".registryvalue.reg")
        return ""

def GetKeyValues(key):
    """ Returns a dictionary of all the values in the key
    Returns an empty dictionary if the key does not exist
    Does not read subkeys within the key """
    # Need 4 \'s to generate one because both python and the shell use it as an escape character
    key = key.replace("\\","\\\\")
    key = key.replace(" ","\\ ")
    error = os.system("wine regedit /E .registrykey.reg " + key)
    if error != 0:
        return {}
    
    settings = {}
    
    file=open('.registrykey.reg', encoding='utf-16', mode='r')
    
    keycount = 0
    for line in file:
        if keycount > 1:
            break
        elif line and line[0] == '[':
            keycount += 1
        elif line and line[0] == '"':
            line = line.split('=')
            settings[line[0].strip('"')] = line[1].strip('"\r\n@')
    
    file.close()
    os.remove(".registrykey.reg")
        
    return settings

def GetUserShellRegistry():
    error = os.system("wine regedit /E .registryshelluser.reg HKEY_USERS\\\\.Default\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\User\\ Shell\\ Folders")
    if error != 0:
        return {}
    
    usershellfile=open('.registryshelluser.reg', encoding='utf-16', mode='r')
    usershellfilelines = usershellfile.readlines()
    usershellfile.close()
    os.remove(".registryshelluser.reg")
    
    settings = {}
    
    del(usershellfilelines[:3])
    
    for usershellline in usershellfilelines:
        usershellline = usershellline.split('=')
        if len(usershellline) == 2:
            settings[usershellline[0].strip('"')] = usershellline[1].strip('"\r\n')
        
    return settings
    
def GetShellRegistry():
    error = os.system("wine regedit /E .registryshell.reg HKEY_USERS\\\\.Default\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\Shell\\ Folders")
    if error != 0:
        return {}
    
    shellfile=open('.registryshell.reg', encoding='utf-16', mode='r')
    shellfilelines = shellfile.readlines()
    shellfile.close()
    os.remove(".registryshell.reg")
    
    settings = {}
    
    del(shellfilelines[:3])
    
    for shellline in shellfilelines:
        shellline = shellline.split('=')
        if len(shellline) == 2:
            settings[shellline[0].strip('"')] = shellline[1].strip('"\r\n')
        
    return settings
    
def GetDriveTypes():
    """ Returns a dictionary of the drive types """
    # Drive C: doesn't get stored in the registry
    defaulttypes = {"C:":"hd"}
    
    types = GetKeyValues("HKEY_LOCAL_MACHINE\\Software\\Wine\\Drives")
    types.update(defaulttypes)
    
    return types
    
def GetAudioDriver():
    """ Returns the audio driver currently set in the registry """
    return GetValue("HKEY_CURRENT_USER\\Software\\Wine\\Drivers","Audio")
    
def GetDSoundSettings(app = None):
    """ Returns a dictionary of the settings for the DirectSound section """
    if not app:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine\\DirectSound")
    else:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine\\AppDefaults\\" +\
            app + "\\DirectSound")
    
def GetWindowSettings(app = None):
    """ Returns a dictionary of the Window Settings """
    if not app:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine\\X11 Driver")
    else:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine\\AppDefaults\\" +\
            app + "\\X11 Driver")
    
def GetD3DSettings(app = None):
    """ Returns a dictionary of the Direct3D Settings """
    if not app:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine\\Direct3D")
    else:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine\\AppDefaults\\" +\
            app + "\\Direct3D")

# Copied from winecfg
winversions = (
        ( "win2003", "Windows 2003",   5,  2, 0xECE, "VER_PLATFORM_WIN32_NT", "Service Pack 1", 1, 0, "ServerNT"),
        ( "winxp",   "Windows XP",     5,  1, 0xA28, "VER_PLATFORM_WIN32_NT", "Service Pack 2", 2, 0, "WinNT"),
        ( "win2k",   "Windows 2000",   5,  0, 0x893, "VER_PLATFORM_WIN32_NT", "Service Pack 4", 4, 0, "WinNT"),
        ( "winme",   "Windows ME",     4, 90, 0xBB8, "VER_PLATFORM_WIN32_WINDOWS", " ", 0, 0, ""),
        ( "win98",   "Windows 98",     4, 10, 0x8AE, "VER_PLATFORM_WIN32_WINDOWS", " A ", 0, 0, ""),
        ( "win95",   "Windows 95",     4,  0, 0x3B6, "VER_PLATFORM_WIN32_WINDOWS", "", 0, 0, ""),
        ( "nt40",    "Windows NT 4.0", 4,  0, 0x565, "VER_PLATFORM_WIN32_NT", "Service Pack 6a", 6, 0, "WinNT"),
        ( "nt351",   "Windows NT 3.5", 3, 51, 0x421, "VER_PLATFORM_WIN32_NT", "Service Pack 2", 0, 0, "WinNT"),
        ( "win31",   "Windows 3.1",    2, 10,     0, "VER_PLATFORM_WIN32s", "Win32s 1.3", 0, 0, ""),
        ( "win30",   "Windows 3.0",    3,  0,     0, "VER_PLATFORM_WIN32s", "Win32s 1.3", 0, 0, ""),
        ( "win20",   "Windows 2.0",    2,  0,     0, "VER_PLATFORM_WIN32s", "Win32s 1.3", 0, 0, ""))
    
def GetGeneralWineSettings(app = None):
    """ Returns a dictionary of the general wine Settings, including the windows version """
    if not app:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine")
    else:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine\\AppDefaults\\" +\
            app)
    
def GetApps():
    """
    Returns a list of the applications which have keys for application
    specific settings.
    """
    error = os.system("wine regedit /E .registryapps.reg HKEY_CURRENT_USER\\\\Software\\\\Wine\\\\AppDefaults")
    if error != 0:
        return []
    
    settingsfile=open('.registryapps.reg', encoding='utf-16', mode='r')
    settingsfilelines = settingsfile.readlines()
    settingsfile.close()
    os.remove('.registryapps.reg')
    
    apps = set([])
    
    del(settingsfilelines[:3])
    
    for line in settingsfilelines:
        if line[0] == '[':
            line = line.split('\\')
            line[4] = line[4].strip(']\r\n')
            apps.add(line[4])
            
    apps = list(apps)
    apps.sort()
    
    return apps
    
builtin_only = set(("advapi32",
    "capi2032",
    "dbghelp",
    "ddraw",
    "gdi32",
    "glu32",
    "icmp",
    "iphlpapi",
    "joystick.drv",
    "kernel32",
    "mswsock",
    "ntdll",
    "opengl32",
    "stdole2.tlb",
    "stdole32.tlb",
    "twain_32",
    "unicows",
    "user32",
    "vdmdbg",
    "w32skrnl",
    "winealsa.drv",
    "winearts.drv",
    "wineaudioio.drv",
    "wined3d",
    "winedos",
    "wineesd.drv",
    "winejack.drv",
    "winemp3.acm",
    "winenas.drv",
    "wineoss.drv",
    "wineps",
    "wineps.drv",
    "winex11.drv",
    "winmm",
    "wintab32",
    "wnaspi32",
    "wow32",
    "ws2_32",
    "wsock32"))

def GetDllsList():
    """ Returns a list of dlls that can be overridden """
    origdlls = os.listdir(GetWineBuildPath())
    dlls = [""]
    
    for dll in origdlls:
        dll = dll.rstrip('.so')
        dots = dll.count('.')
        if dots != 1:
            continue
        dll, extension = dll.split('.')
        if not (extension != "dll" or dll in builtin_only):
            dlls.append(dll)
            
    dlls.sort()
    return dlls
        
def GetDllOverrides(app = None):
    """ Returns a dictionary of overridden dlls """
    if not app:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine\\DllOverrides")
    else:
        return GetKeyValues("HKEY_CURRENT_USER\\Software\\Wine\\AppDefaults\\" +\
            app + "\\DllOverrides")
            
            
# --- Getting and Setting the Default browser ---

# WineBrowser>Browsers
# List of browsers that Wine will attempt to launch when running winebrowser
# command or clicking on a link in a windows application. Default value is
default_browserlist = ["firefox","konqueror","mozilla","netscape","galeon","opera","dillo"]

# WineBrowser>Mailers
# List of mail clients that Wine will attempt to launch when running winebrowser
# Default value is 
default_mailerlist = ["mozilla-thunderbird","thunderbird","evolution","kmail"]
      
#with firefox installed
browser_formats = ["CHROME","FirefoxHTML","HTML","htmlfile","FTP","GOPHER","http","https"]

default_browser_formats = ["htmlfile","http","https"]   # just "winebrowser"
      
default_mailer_formats = ["mailto"]     # "winebrowser %1"

def GetBrowser():
    """ Returns the default browser """
    browser = GetValue("HKEY_LOCAL_MACHINE\\Software\\Classes\\http\\shell\\open\\command",'@')
    
    if browser == "winebrowser":
        return GetWineBrowser()
    else:
        return browser
    
def GetWineBrowser():
    """ Returns the first browser tried by winebrowser """
    browserlist = GetValue("HKEY_CURRENT_USER\\Software\\Wine\\WineBrowser","Browsers")
    if browserlist:
        browser = browserlist.split(',')[0].strip()
        return browser
    else:
        return default_browserlist[0]
    
#def GetWinBrowserList():
    
def GetNativeBrowserList():
    """ Returns the list of browsers tried by winebrowser """
    browserlist = GetValue("HKEY_CURRENT_USER\\Software\\Wine\\WineBrowser","Browsers")
    if browserlist:
        browserlist = list(set(browserlist.split(',')))
        for i,item in enumerate(browserlist):
            browserlist[i] = item.strip()
        return browserlist
    else:
        return default_browserlist
    
def GetMailer():
    """ Returns the default mail client """
    mailer = GetValue("HKEY_LOCAL_MACHINE\\Software\\Classes\\mailto\\shell\\open\\command",'@')
    
    if mailer == "winebrowser" or mailer == "winebrowser %1":
        return GetWineMailer()
    else:
        return mailer
    
def GetWineMailer():
    """ Returns the first mail client tried by winebrowser """
    mailerlist = GetValue("HKEY_CURRENT_USER\\Software\\Wine\\WineBrowser","Mailers")
    if mailerlist:
        mailer = mailerlist.split(',')[0].strip()
        return mailer
    else:
        # Default first mailer to try in wine is mozilla-thunderbird
        return default_mailerlist[0]
    
def GetNativeMailerList():
    """ Returns the list of mail clients tried by winebrowser """
    mailerlist = GetValue("HKEY_CURRENT_USER\\Software\\Wine\\WineBrowser","Mailers")
    if mailerlist:
        mailerlist = list(set(mailerlist.split(',')))
        for i,item in enumerate(mailerlist):
            mailerlist[i] = item.strip()
        return mailerlist
    else:
        return default_mailerlist


# ----- Theming -----

def GetThemesList():
    """ Returns a list of installed thiemes """
    if not os.path.exists(winepath + "/dosdevices/c:/windows/Resources/Themes"):
        return []
    origthemes = os.listdir(winepath + "/dosdevices/c:/windows/Resources/Themes")
    themes = []
    
    for i,theme in enumerate(origthemes):
        if os.path.exists(winepath +\
            "/dosdevices/c:/windows/Resources/Themes/" + theme +\
            "/" + theme + ".msstyles"):
            themes.append(theme)
            
    themes.sort()
    return themes
            
def GetCurrentTheme():
    """ Returns the current (theme,color,size), None if none is set """
    themeinfo = GetKeyValues("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\ThemeManager")
    
    if not themeinfo or themeinfo["ThemeActive"] == "0":
        return None
    else:
        # themename from themename.msstyles
        themename = themeinfo["DllName"].split('\\\\')[-1].split('.')[0]
        theme = (themename, themeinfo["ColorName"], themeinfo["SizeName"])
        return theme
    
def GetColorSettings():
    """ Returns a dictionary of the set colors """
    return GetKeyValues("HKEY_CURRENT_USER\\Control Panel\\Colors")

def GetWindowMetrics():
    """ Returns a dictionary of the WindowMetrics settings """
    return GetKeyValues("HKEY_CURRENT_USER\\Control Panel\\Desktop\\WindowMetrics")

def GetDesktopSettings():
    """ Returns a dictionary of the control panel \ Desktop settings """
    return GetKeyValues("HKEY_CURRENT_USER\\Control Panel\\Desktop")
    
def SetWinePath(path):
    """ Sets the winepath to path """
    global winepath
    winepath = path
    
def GetWinePath():
    return winepath
    
def VerifyWineDrive(path = None):
    """ Does a very basic check of if the given path is a valid fake windows installation
    Returns False if there is no C: drive """
    if not path:
        path = self.default_winepath
    
    return os.path.exists(path + "/dosdevices/c:/users/" + os.environ['USER']) and \
        os.path.exists(path + "/dosdevices/c:/windows/system32") and \
        os.path.exists(path + "/system.reg") and os.path.exists(path + "/userdef.reg") and \
        os.path.exists(path + "/user.reg")
