#!/usr/bin/env python # -*- coding: utf-8 -*- """ openslides.utils.main ~~~~~~~~~~~~~~~~~~~~~ Some functions for OpenSlides. :copyright: 2011–2013 by OpenSlides team, see AUTHORS. :license: GNU GPL, see LICENSE for more details. """ import ctypes import os import socket import sys import tempfile UNIX_VERSION = 'Unix Version' WINDOWS_VERSION = 'Windows Version' WINDOWS_PORTABLE_VERSION = 'Windows Portable Version' class PortableDirNotWritable(Exception): pass def filesystem2unicode(path): """ Transforms a path string to unicode according to the filesystem's encoding. """ # TODO: Delete this function after switch to Python 3. if not isinstance(path, unicode): filesystem_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() path = path.decode(filesystem_encoding) return path def detect_openslides_type(): """ Returns the type of this OpenSlides version. """ if sys.platform == 'win32': if os.path.basename(sys.executable).lower() == 'openslides.exe': # Note: sys.executable is the path of the *interpreter* # the portable version embeds python so it *is* the interpreter. # The wrappers generated by pip and co. will spawn the usual # python(w).exe, so there is no danger of mistaking them # for the portable even though they may also be called # openslides.exe openslides_type = WINDOWS_PORTABLE_VERSION else: openslides_type = WINDOWS_VERSION else: openslides_type = UNIX_VERSION return openslides_type def get_default_user_data_path(openslides_type): """ Returns the default path for user specific data according to the OpenSlides type. The argument 'openslides_type' has to be one of the three types mentioned in openslides.utils.main. """ if openslides_type == UNIX_VERSION: default_user_data_path = filesystem2unicode(os.environ.get( 'XDG_DATA_HOME', os.path.join(os.path.expanduser('~'), '.local', 'share'))) elif openslides_type == WINDOWS_VERSION: default_user_data_path = get_win32_app_data_path() elif openslides_type == WINDOWS_PORTABLE_VERSION: default_user_data_path = get_win32_portable_path() else: raise TypeError('%s is not a valid OpenSlides type.' % openslides_type) return default_user_data_path def get_win32_app_data_path(): """ Returns the path to Windows' AppData directory. """ shell32 = ctypes.WinDLL("shell32.dll") SHGetFolderPath = shell32.SHGetFolderPathW SHGetFolderPath.argtypes = ( ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_uint32, ctypes.c_wchar_p) SHGetFolderPath.restype = ctypes.c_uint32 CSIDL_LOCAL_APPDATA = 0x001c MAX_PATH = 260 buf = ctypes.create_unicode_buffer(MAX_PATH) res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf) if res != 0: raise Exception("Could not determine Windows' APPDATA path") return buf.value def get_win32_portable_path(): """ Returns the path to the Windows portable version. """ # NOTE: sys.executable will be the path to openslides.exe # since it is essentially a small wrapper that embeds the # python interpreter portable_path = filesystem2unicode(os.path.dirname(os.path.abspath(sys.executable))) try: fd, test_file = tempfile.mkstemp(dir=portable_path) except OSError: raise PortableDirNotWritable( 'Portable directory is not writeable. ' 'Please choose another directory for settings and data files.') else: os.close(fd) os.unlink(test_file) return portable_path def get_portable_paths(name): """ Returns the paths for the Windows portable version on runtime for the SQLite3 database, the media directory and the search index. The argument 'name' can be 'database', 'media' or 'whoosh_index'. """ if name == 'database': path = os.path.join(get_win32_portable_path(), 'openslides', 'database.sqlite') elif name == 'media': path = os.path.join(get_win32_portable_path(), 'openslides', 'media', '') elif name == 'whoosh_index': path = os.path.join(get_win32_portable_path(), 'openslides', 'whoosh_index', '') else: raise TypeError('Unknown type %s' % name) return path def get_port(address, port): """ Returns the port for the server. If port 80 is given, checks if it is available. If not returns port 8000. The argument 'address' should be an IP address. The argument 'port' should be an integer. """ if port == 80: # test if we can use port 80 s = socket.socket() try: s.bind((address, port)) s.listen(-1) except socket.error: port = 8000 finally: s.close() return port