14 sys
.path
.insert(0, os
.path
.dirname(os
.path
.dirname((os
.path
.abspath(__file__
))))) 
  15 from youtube_dl
.compat 
import ( 
  22 # These are not used outside of buildserver.py thus not in compat.py 
  25     import winreg 
as compat_winreg
 
  26 except ImportError:  # Python 2 
  27     import _winreg 
as compat_winreg
 
  30     import socketserver 
as compat_socketserver
 
  31 except ImportError:  # Python 2 
  32     import SocketServer 
as compat_socketserver
 
  35 class BuildHTTPServer(compat_socketserver
.ThreadingMixIn
, compat_http_server
.HTTPServer
): 
  36     allow_reuse_address 
= True 
  39 advapi32 
= ctypes
.windll
.advapi32
 
  41 SC_MANAGER_ALL_ACCESS 
= 0xf003f 
  42 SC_MANAGER_CREATE_SERVICE 
= 0x02 
  43 SERVICE_WIN32_OWN_PROCESS 
= 0x10 
  44 SERVICE_AUTO_START 
= 0x2 
  45 SERVICE_ERROR_NORMAL 
= 0x1 
  47 SERVICE_STATUS_START_PENDING 
= 0x00000002 
  48 SERVICE_STATUS_RUNNING 
= 0x00000004 
  49 SERVICE_ACCEPT_STOP 
= 0x1 
  51 SVCNAME 
= 'youtubedl_builder' 
  53 LPTSTR 
= ctypes
.c_wchar_p
 
  54 START_CALLBACK 
= ctypes
.WINFUNCTYPE(None, ctypes
.c_int
, ctypes
.POINTER(LPTSTR
)) 
  57 class SERVICE_TABLE_ENTRY(ctypes
.Structure
): 
  59         ('lpServiceName', LPTSTR
), 
  60         ('lpServiceProc', START_CALLBACK
) 
  64 HandlerEx 
= ctypes
.WINFUNCTYPE( 
  65     ctypes
.c_int
,     # return 
  66     ctypes
.c_int
,     # dwControl 
  67     ctypes
.c_int
,     # dwEventType 
  68     ctypes
.c_void_p
,  # lpEventData, 
  69     ctypes
.c_void_p
,  # lpContext, 
  73 def _ctypes_array(c_type
, py_array
): 
  74     ar 
= (c_type 
* len(py_array
))() 
  79 def win_OpenSCManager(): 
  80     res 
= advapi32
.OpenSCManagerW(None, None, SC_MANAGER_ALL_ACCESS
) 
  82         raise Exception('Opening service manager failed - ' 
  83                         'are you running this as administrator?') 
  87 def win_install_service(service_name
, cmdline
): 
  88     manager 
= win_OpenSCManager() 
  90         h 
= advapi32
.CreateServiceW( 
  91             manager
, service_name
, None, 
  92             SC_MANAGER_CREATE_SERVICE
, SERVICE_WIN32_OWN_PROCESS
, 
  93             SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
, 
  94             cmdline
, None, None, None, None, None) 
  96             raise OSError('Service creation failed: %s' % ctypes
.FormatError()) 
  98         advapi32
.CloseServiceHandle(h
) 
 100         advapi32
.CloseServiceHandle(manager
) 
 103 def win_uninstall_service(service_name
): 
 104     manager 
= win_OpenSCManager() 
 106         h 
= advapi32
.OpenServiceW(manager
, service_name
, DELETE
) 
 108             raise OSError('Could not find service %s: %s' % ( 
 109                 service_name
, ctypes
.FormatError())) 
 112             if not advapi32
.DeleteService(h
): 
 113                 raise OSError('Deletion failed: %s' % ctypes
.FormatError()) 
 115             advapi32
.CloseServiceHandle(h
) 
 117         advapi32
.CloseServiceHandle(manager
) 
 120 def win_service_report_event(service_name
, msg
, is_error
=True): 
 121     with open('C:/sshkeys/log', 'a', encoding
='utf-8') as f
: 
 124     event_log 
= advapi32
.RegisterEventSourceW(None, service_name
) 
 126         raise OSError('Could not report event: %s' % ctypes
.FormatError()) 
 129         type_id 
= 0x0001 if is_error 
else 0x0004 
 130         event_id 
= 0xc0000000 if is_error 
else 0x40000000 
 131         lines 
= _ctypes_array(LPTSTR
, [msg
]) 
 133         if not advapi32
.ReportEventW( 
 134                 event_log
, type_id
, 0, event_id
, None, len(lines
), 0, 
 136             raise OSError('Event reporting failed: %s' % ctypes
.FormatError()) 
 138         advapi32
.DeregisterEventSource(event_log
) 
 141 def win_service_handler(stop_event
, *args
): 
 143         raise ValueError('Handler called with args ' + repr(args
)) 
 145     except Exception as e
: 
 146         tb 
= traceback
.format_exc() 
 147         msg 
= str(e
) + '\n' + tb
 
 148         win_service_report_event(service_name
, msg
, is_error
=True) 
 152 def win_service_set_status(handle
, status_code
): 
 153     svcStatus 
= SERVICE_STATUS() 
 154     svcStatus
.dwServiceType 
= SERVICE_WIN32_OWN_PROCESS
 
 155     svcStatus
.dwCurrentState 
= status_code
 
 156     svcStatus
.dwControlsAccepted 
= SERVICE_ACCEPT_STOP
 
 158     svcStatus
.dwServiceSpecificExitCode 
= 0 
 160     if not advapi32
.SetServiceStatus(handle
, ctypes
.byref(svcStatus
)): 
 161         raise OSError('SetServiceStatus failed: %r' % ctypes
.FormatError()) 
 164 def win_service_main(service_name
, real_main
, argc
, argv_raw
): 
 166         # args = [argv_raw[i].value for i in range(argc)] 
 167         stop_event 
= threading
.Event() 
 168         handler 
= HandlerEx(functools
.partial(stop_event
, win_service_handler
)) 
 169         h 
= advapi32
.RegisterServiceCtrlHandlerExW(service_name
, handler
, None) 
 171             raise OSError('Handler registration failed: %s' % 
 172                           ctypes
.FormatError()) 
 175     except Exception as e
: 
 176         tb 
= traceback
.format_exc() 
 177         msg 
= str(e
) + '\n' + tb
 
 178         win_service_report_event(service_name
, msg
, is_error
=True) 
 182 def win_service_start(service_name
, real_main
): 
 185             functools
.partial(win_service_main
, service_name
, real_main
)) 
 186         dispatch_table 
= _ctypes_array(SERVICE_TABLE_ENTRY
, [ 
 191             SERVICE_TABLE_ENTRY(None, ctypes
.cast(None, START_CALLBACK
)) 
 194         if not advapi32
.StartServiceCtrlDispatcherW(dispatch_table
): 
 195             raise OSError('ctypes start failed: %s' % ctypes
.FormatError()) 
 196     except Exception as e
: 
 197         tb 
= traceback
.format_exc() 
 198         msg 
= str(e
) + '\n' + tb
 
 199         win_service_report_event(service_name
, msg
, is_error
=True) 
 204     parser 
= argparse
.ArgumentParser() 
 205     parser
.add_argument('-i', '--install', 
 206                         action
='store_const', dest
='action', const
='install', 
 207                         help='Launch at Windows startup') 
 208     parser
.add_argument('-u', '--uninstall', 
 209                         action
='store_const', dest
='action', const
='uninstall', 
 210                         help='Remove Windows service') 
 211     parser
.add_argument('-s', '--service', 
 212                         action
='store_const', dest
='action', const
='service', 
 213                         help='Run as a Windows service') 
 214     parser
.add_argument('-b', '--bind', metavar
='<host:port>', 
 215                         action
='store', default
='0.0.0.0:8142', 
 216                         help='Bind to host:port (default %default)') 
 217     options 
= parser
.parse_args(args
=args
) 
 219     if options
.action 
== 'install': 
 220         fn 
= os
.path
.abspath(__file__
).replace('v:', '\\\\vboxsrv\\vbox') 
 221         cmdline 
= '%s %s -s -b %s' % (sys
.executable
, fn
, options
.bind
) 
 222         win_install_service(SVCNAME
, cmdline
) 
 225     if options
.action 
== 'uninstall': 
 226         win_uninstall_service(SVCNAME
) 
 229     if options
.action 
== 'service': 
 230         win_service_start(SVCNAME
, main
) 
 233     host
, port_str 
= options
.bind
.split(':') 
 236     print('Listening on %s:%d' % (host
, port
)) 
 237     srv 
= BuildHTTPServer((host
, port
), BuildHTTPRequestHandler
) 
 238     thr 
= threading
.Thread(target
=srv
.serve_forever
) 
 240     compat_input('Press ENTER to shut down') 
 246     for name 
in os
.listdir(path
): 
 247         fname 
= os
.path
.join(path
, name
) 
 248         if os
.path
.isdir(fname
): 
 251             os
.chmod(fname
, 0o666) 
 256 class BuildError(Exception): 
 257     def __init__(self
, output
, code
=500): 
 265 class HTTPError(BuildError
): 
 269 class PythonBuilder(object): 
 270     def __init__(self
, **kwargs
): 
 271         python_version 
= kwargs
.pop('python', '3.4') 
 273         for node 
in ('Wow6432Node\\', ''): 
 275                 key 
= compat_winreg
.OpenKey( 
 276                     compat_winreg
.HKEY_LOCAL_MACHINE
, 
 277                     r
'SOFTWARE\%sPython\PythonCore\%s\InstallPath' % (node
, python_version
)) 
 279                     python_path
, _ 
= compat_winreg
.QueryValueEx(key
, '') 
 281                     compat_winreg
.CloseKey(key
) 
 287             raise BuildError('No such Python version: %s' % python_version
) 
 289         self
.pythonPath 
= python_path
 
 291         super(PythonBuilder
, self
).__init
__(**kwargs
) 
 294 class GITInfoBuilder(object): 
 295     def __init__(self
, **kwargs
): 
 297             self
.user
, self
.repoName 
= kwargs
['path'][:2] 
 298             self
.rev 
= kwargs
.pop('rev') 
 300             raise BuildError('Invalid path') 
 301         except KeyError as e
: 
 302             raise BuildError('Missing mandatory parameter "%s"' % e
.args
[0]) 
 304         path 
= os
.path
.join(os
.environ
['APPDATA'], 'Build archive', self
.repoName
, self
.user
) 
 305         if not os
.path
.exists(path
): 
 307         self
.basePath 
= tempfile
.mkdtemp(dir=path
) 
 308         self
.buildPath 
= os
.path
.join(self
.basePath
, 'build') 
 310         super(GITInfoBuilder
, self
).__init
__(**kwargs
) 
 313 class GITBuilder(GITInfoBuilder
): 
 316             subprocess
.check_output(['git', 'clone', 'git://github.com/%s/%s.git' % (self
.user
, self
.repoName
), self
.buildPath
]) 
 317             subprocess
.check_output(['git', 'checkout', self
.rev
], cwd
=self
.buildPath
) 
 318         except subprocess
.CalledProcessError 
as e
: 
 319             raise BuildError(e
.output
) 
 321         super(GITBuilder
, self
).build() 
 324 class YoutubeDLBuilder(object): 
 325     authorizedUsers 
= ['fraca7', 'phihag', 'rg3', 'FiloSottile'] 
 327     def __init__(self
, **kwargs
): 
 328         if self
.repoName 
!= 'youtube-dl': 
 329             raise BuildError('Invalid repository "%s"' % self
.repoName
) 
 330         if self
.user 
not in self
.authorizedUsers
: 
 331             raise HTTPError('Unauthorized user "%s"' % self
.user
, 401) 
 333         super(YoutubeDLBuilder
, self
).__init
__(**kwargs
) 
 337             proc 
= subprocess
.Popen([os
.path
.join(self
.pythonPath
, 'python.exe'), 'setup.py', 'py2exe'], stdin
=subprocess
.PIPE
, cwd
=self
.buildPath
) 
 339             #subprocess.check_output([os.path.join(self.pythonPath, 'python.exe'), 'setup.py', 'py2exe'], 
 340             #                        cwd=self.buildPath) 
 341         except subprocess
.CalledProcessError 
as e
: 
 342             raise BuildError(e
.output
) 
 344         super(YoutubeDLBuilder
, self
).build() 
 347 class DownloadBuilder(object): 
 348     def __init__(self
, **kwargs
): 
 349         self
.handler 
= kwargs
.pop('handler') 
 350         self
.srcPath 
= os
.path
.join(self
.buildPath
, *tuple(kwargs
['path'][2:])) 
 351         self
.srcPath 
= os
.path
.abspath(os
.path
.normpath(self
.srcPath
)) 
 352         if not self
.srcPath
.startswith(self
.buildPath
): 
 353             raise HTTPError(self
.srcPath
, 401) 
 355         super(DownloadBuilder
, self
).__init
__(**kwargs
) 
 358         if not os
.path
.exists(self
.srcPath
): 
 359             raise HTTPError('No such file', 404) 
 360         if os
.path
.isdir(self
.srcPath
): 
 361             raise HTTPError('Is a directory: %s' % self
.srcPath
, 401) 
 363         self
.handler
.send_response(200) 
 364         self
.handler
.send_header('Content-Type', 'application/octet-stream') 
 365         self
.handler
.send_header('Content-Disposition', 'attachment; filename=%s' % os
.path
.split(self
.srcPath
)[-1]) 
 366         self
.handler
.send_header('Content-Length', str(os
.stat(self
.srcPath
).st_size
)) 
 367         self
.handler
.end_headers() 
 369         with open(self
.srcPath
, 'rb') as src
: 
 370             shutil
.copyfileobj(src
, self
.handler
.wfile
) 
 372         super(DownloadBuilder
, self
).build() 
 375 class CleanupTempDir(object): 
 378             rmtree(self
.basePath
) 
 379         except Exception as e
: 
 380             print('WARNING deleting "%s": %s' % (self
.basePath
, e
)) 
 382         super(CleanupTempDir
, self
).build() 
 386     def __init__(self
, **kwargs
): 
 399 class Builder(PythonBuilder
, GITBuilder
, YoutubeDLBuilder
, DownloadBuilder
, CleanupTempDir
, Null
): 
 403 class BuildHTTPRequestHandler(compat_http_server
.BaseHTTPRequestHandler
): 
 404     actionDict 
= {'build': Builder
, 'download': Builder
}  # They're the same, no more caching. 
 407         path 
= compat_urlparse
.urlparse(self
.path
) 
 408         paramDict 
= dict([(key
, value
[0]) for key
, value 
in compat_urlparse
.parse_qs(path
.query
).items()]) 
 409         action
, _
, path 
= path
.path
.strip('/').partition('/') 
 411             path 
= path
.split('/') 
 412             if action 
in self
.actionDict
: 
 414                     builder 
= self
.actionDict
[action
](path
=path
, handler
=self
, **paramDict
) 
 420                 except BuildError 
as e
: 
 421                     self
.send_response(e
.code
) 
 422                     msg 
= compat_str(e
).encode('UTF-8') 
 423                     self
.send_header('Content-Type', 'text/plain; charset=UTF-8') 
 424                     self
.send_header('Content-Length', len(msg
)) 
 426                     self
.wfile
.write(msg
) 
 428                 self
.send_response(500, 'Unknown build method "%s"' % action
) 
 430             self
.send_response(500, 'Malformed URL') 
 432 if __name__ 
== '__main__':