1# -*- coding: utf-8 -*-
2# Part of Odoo. See LICENSE file for full copyright and licensing details.
3
4"""
5Some functions related to the os and os.path module
6"""
7from contextlib import contextmanager
8import logging
9import os
10from os.path import join as opj
11import tempfile
12import zipfile
13
14_logger = logging.getLogger(__name__)
15
16
17def listdir(dir, recursive=False):
18    """Allow to recursively get the file listing following symlinks, returns
19    paths relative to the provided `dir` except completely broken if the symlink
20    it follows leaves `dir`...
21    """
22    if not recursive:
23        _logger.getChild('listdir').warning("Deprecated: just call os.listdir...")
24    dir = os.path.normpath(dir)
25    if not recursive:
26        return os.listdir(dir)
27
28    res = []
29    for root, _, files in os.walk(dir, followlinks=True):
30        r = os.path.relpath(root, dir)
31        # FIXME: what should happen if root is outside dir?
32        yield from (opj(r, f) for f in files)
33    return res
34
35def walksymlinks(top, topdown=True, onerror=None):
36    _logger.getChild('walksymlinks').warning("Deprecated: use os.walk(followlinks=True) instead")
37    return os.walk(top, topdown=topdown, onerror=onerror, followlinks=True)
38
39@contextmanager
40def tempdir():
41    _logger.getChild('tempdir').warning("Deprecated: use tempfile.TemporaryDirectory")
42    with tempfile.TemporaryDirectory() as d:
43        yield d
44
45def zip_dir(path, stream, include_dir=True, fnct_sort=None):      # TODO add ignore list
46    """
47    : param fnct_sort : Function to be passed to "key" parameter of built-in
48                        python sorted() to provide flexibility of sorting files
49                        inside ZIP archive according to specific requirements.
50    """
51    path = os.path.normpath(path)
52    len_prefix = len(os.path.dirname(path)) if include_dir else len(path)
53    if len_prefix:
54        len_prefix += 1
55
56    with zipfile.ZipFile(stream, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zipf:
57        for dirpath, dirnames, filenames in os.walk(path):
58            filenames = sorted(filenames, key=fnct_sort)
59            for fname in filenames:
60                bname, ext = os.path.splitext(fname)
61                ext = ext or bname
62                if ext not in ['.pyc', '.pyo', '.swp', '.DS_Store']:
63                    path = os.path.normpath(os.path.join(dirpath, fname))
64                    if os.path.isfile(path):
65                        zipf.write(path, path[len_prefix:])
66
67
68if os.name != 'nt':
69    getppid = os.getppid
70    is_running_as_nt_service = lambda: False
71else:
72    import ctypes
73    import win32service as ws
74    import win32serviceutil as wsu
75
76    # based on http://mail.python.org/pipermail/python-win32/2007-June/006174.html
77    _TH32CS_SNAPPROCESS = 0x00000002
78    class _PROCESSENTRY32(ctypes.Structure):
79        _fields_ = [("dwSize", ctypes.c_ulong),
80                    ("cntUsage", ctypes.c_ulong),
81                    ("th32ProcessID", ctypes.c_ulong),
82                    ("th32DefaultHeapID", ctypes.c_ulong),
83                    ("th32ModuleID", ctypes.c_ulong),
84                    ("cntThreads", ctypes.c_ulong),
85                    ("th32ParentProcessID", ctypes.c_ulong),
86                    ("pcPriClassBase", ctypes.c_ulong),
87                    ("dwFlags", ctypes.c_ulong),
88                    ("szExeFile", ctypes.c_char * 260)]
89
90    def getppid():
91        CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot
92        Process32First = ctypes.windll.kernel32.Process32First
93        Process32Next = ctypes.windll.kernel32.Process32Next
94        CloseHandle = ctypes.windll.kernel32.CloseHandle
95        hProcessSnap = CreateToolhelp32Snapshot(_TH32CS_SNAPPROCESS, 0)
96        current_pid = os.getpid()
97        try:
98            pe32 = _PROCESSENTRY32()
99            pe32.dwSize = ctypes.sizeof(_PROCESSENTRY32)
100            if not Process32First(hProcessSnap, ctypes.byref(pe32)):
101                raise OSError('Failed getting first process.')
102            while True:
103                if pe32.th32ProcessID == current_pid:
104                    return pe32.th32ParentProcessID
105                if not Process32Next(hProcessSnap, ctypes.byref(pe32)):
106                    return None
107        finally:
108            CloseHandle(hProcessSnap)
109
110    from contextlib import contextmanager
111    from odoo.release import nt_service_name
112
113    def is_running_as_nt_service():
114        @contextmanager
115        def close_srv(srv):
116            try:
117                yield srv
118            finally:
119                ws.CloseServiceHandle(srv)
120
121        try:
122            with close_srv(ws.OpenSCManager(None, None, ws.SC_MANAGER_ALL_ACCESS)) as hscm:
123                with close_srv(wsu.SmartOpenService(hscm, nt_service_name, ws.SERVICE_ALL_ACCESS)) as hs:
124                    info = ws.QueryServiceStatusEx(hs)
125                    return info['ProcessId'] == getppid()
126        except Exception:
127            return False
128
129if __name__ == '__main__':
130    from pprint import pprint as pp
131    pp(listdir('../report', True))
132