2013-09-08 14:30:26 +02:00
|
|
|
|
#!/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.
|
|
|
|
|
"""
|
|
|
|
|
|
2013-10-13 17:17:56 +02:00
|
|
|
|
import ctypes
|
2013-09-08 14:30:26 +02:00
|
|
|
|
import os
|
2013-10-13 17:17:56 +02:00
|
|
|
|
import socket
|
2013-09-08 14:30:26 +02:00
|
|
|
|
import sys
|
2013-10-12 21:30:34 +02:00
|
|
|
|
import tempfile
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UNIX_VERSION = 'Unix Version'
|
|
|
|
|
WINDOWS_VERSION = 'Windows Version'
|
|
|
|
|
WINDOWS_PORTABLE_VERSION = 'Windows Portable Version'
|
|
|
|
|
|
2013-10-13 17:17:56 +02:00
|
|
|
|
|
2013-10-12 21:30:34 +02:00
|
|
|
|
class PortableDirNotWritable(Exception):
|
|
|
|
|
pass
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
2013-10-13 17:17:56 +02:00
|
|
|
|
|
2013-09-08 14:30:26 +02:00
|
|
|
|
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():
|
|
|
|
|
"""
|
2013-10-13 17:17:56 +02:00
|
|
|
|
Returns the type of this OpenSlides version.
|
2013-09-08 14:30:26 +02:00
|
|
|
|
"""
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2013-10-13 17:17:56 +02:00
|
|
|
|
def get_default_user_data_path(openslides_type):
|
2013-09-08 14:30:26 +02:00
|
|
|
|
"""
|
2013-10-13 17:17:56 +02:00
|
|
|
|
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.
|
2013-09-08 14:30:26 +02:00
|
|
|
|
"""
|
2013-10-13 17:17:56 +02:00
|
|
|
|
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
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
2013-10-13 17:17:56 +02:00
|
|
|
|
raise Exception("Could not determine Windows' APPDATA path")
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
|
|
|
|
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:
|
2013-10-12 21:30:34 +02:00
|
|
|
|
raise PortableDirNotWritable(
|
|
|
|
|
'Portable directory is not writeable. '
|
2013-10-13 17:17:56 +02:00
|
|
|
|
'Please choose another directory for settings and data files.')
|
2013-10-12 21:30:34 +02:00
|
|
|
|
else:
|
2013-09-08 14:30:26 +02:00
|
|
|
|
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
|
2013-10-13 17:17:56 +02:00
|
|
|
|
SQLite3 database, the media directory and the search index. The argument
|
|
|
|
|
'name' can be 'database', 'media' or 'whoosh_index'.
|
2013-09-08 14:30:26 +02:00
|
|
|
|
"""
|
|
|
|
|
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', '')
|
2013-10-13 17:17:56 +02:00
|
|
|
|
elif name == 'whoosh_index':
|
|
|
|
|
path = os.path.join(get_win32_portable_path(), 'openslides', 'whoosh_index', '')
|
2013-09-08 14:30:26 +02:00
|
|
|
|
else:
|
2013-10-13 17:17:56 +02:00
|
|
|
|
raise TypeError('Unknown type %s' % name)
|
2013-09-08 14:30:26 +02:00
|
|
|
|
return path
|
2013-10-13 17:17:56 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|