3 from http
.server 
import HTTPServer
, BaseHTTPRequestHandler
 
   4 from socketserver 
import ThreadingMixIn
 
  14 class BuildHTTPServer(ThreadingMixIn
, HTTPServer
): 
  15     allow_reuse_address 
= True 
  18 advapi32 
= ctypes
.windll
.advapi32
 
  20 SC_MANAGER_ALL_ACCESS 
= 0xf003f 
  21 SC_MANAGER_CREATE_SERVICE 
= 0x02 
  22 SERVICE_WIN32_OWN_PROCESS 
= 0x10 
  23 SERVICE_AUTO_START 
= 0x2 
  24 SERVICE_ERROR_NORMAL 
= 0x1 
  26 SERVICE_STATUS_START_PENDING 
= 0x00000002 
  27 SERVICE_STATUS_RUNNING 
= 0x00000004 
  28 SERVICE_ACCEPT_STOP 
= 0x1 
  30 SVCNAME 
= 'youtubedl_builder' 
  32 LPTSTR 
= ctypes
.c_wchar_p
 
  33 START_CALLBACK 
= ctypes
.WINFUNCTYPE(None, ctypes
.c_int
, ctypes
.POINTER(LPTSTR
)) 
  36 class SERVICE_TABLE_ENTRY(ctypes
.Structure
): 
  38         ('lpServiceName', LPTSTR
), 
  39         ('lpServiceProc', START_CALLBACK
) 
  43 HandlerEx 
= ctypes
.WINFUNCTYPE( 
  44     ctypes
.c_int
,     # return 
  45     ctypes
.c_int
,     # dwControl 
  46     ctypes
.c_int
,     # dwEventType 
  47     ctypes
.c_void_p
,  # lpEventData, 
  48     ctypes
.c_void_p
,  # lpContext, 
  52 def _ctypes_array(c_type
, py_array
): 
  53     ar 
= (c_type 
* len(py_array
))() 
  58 def win_OpenSCManager(): 
  59     res 
= advapi32
.OpenSCManagerW(None, None, SC_MANAGER_ALL_ACCESS
) 
  61         raise Exception('Opening service manager failed - ' 
  62                         'are you running this as administrator?') 
  66 def win_install_service(service_name
, cmdline
): 
  67     manager 
= win_OpenSCManager() 
  69         h 
= advapi32
.CreateServiceW( 
  70             manager
, service_name
, None, 
  71             SC_MANAGER_CREATE_SERVICE
, SERVICE_WIN32_OWN_PROCESS
, 
  72             SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
, 
  73             cmdline
, None, None, None, None, None) 
  75             raise OSError('Service creation failed: %s' % ctypes
.FormatError()) 
  77         advapi32
.CloseServiceHandle(h
) 
  79         advapi32
.CloseServiceHandle(manager
) 
  82 def win_uninstall_service(service_name
): 
  83     manager 
= win_OpenSCManager() 
  85         h 
= advapi32
.OpenServiceW(manager
, service_name
, DELETE
) 
  87             raise OSError('Could not find service %s: %s' % ( 
  88                 service_name
, ctypes
.FormatError())) 
  91             if not advapi32
.DeleteService(h
): 
  92                 raise OSError('Deletion failed: %s' % ctypes
.FormatError()) 
  94             advapi32
.CloseServiceHandle(h
) 
  96         advapi32
.CloseServiceHandle(manager
) 
  99 def win_service_report_event(service_name
, msg
, is_error
=True): 
 100     with open('C:/sshkeys/log', 'a', encoding
='utf-8') as f
: 
 103     event_log 
= advapi32
.RegisterEventSourceW(None, service_name
) 
 105         raise OSError('Could not report event: %s' % ctypes
.FormatError()) 
 108         type_id 
= 0x0001 if is_error 
else 0x0004 
 109         event_id 
= 0xc0000000 if is_error 
else 0x40000000 
 110         lines 
= _ctypes_array(LPTSTR
, [msg
]) 
 112         if not advapi32
.ReportEventW( 
 113                 event_log
, type_id
, 0, event_id
, None, len(lines
), 0, 
 115             raise OSError('Event reporting failed: %s' % ctypes
.FormatError()) 
 117         advapi32
.DeregisterEventSource(event_log
) 
 120 def win_service_handler(stop_event
, *args
): 
 122         raise ValueError('Handler called with args ' + repr(args
)) 
 124     except Exception as e
: 
 125         tb 
= traceback
.format_exc() 
 126         msg 
= str(e
) + '\n' + tb
 
 127         win_service_report_event(service_name
, msg
, is_error
=True) 
 131 def win_service_set_status(handle
, status_code
): 
 132     svcStatus 
= SERVICE_STATUS() 
 133     svcStatus
.dwServiceType 
= SERVICE_WIN32_OWN_PROCESS
 
 134     svcStatus
.dwCurrentState 
= status_code
 
 135     svcStatus
.dwControlsAccepted 
= SERVICE_ACCEPT_STOP
 
 137     svcStatus
.dwServiceSpecificExitCode 
= 0 
 139     if not advapi32
.SetServiceStatus(handle
, ctypes
.byref(svcStatus
)): 
 140         raise OSError('SetServiceStatus failed: %r' % ctypes
.FormatError()) 
 143 def win_service_main(service_name
, real_main
, argc
, argv_raw
): 
 145         # args = [argv_raw[i].value for i in range(argc)] 
 146         stop_event 
= threading
.Event() 
 147         handler 
= HandlerEx(functools
.partial(stop_event
, win_service_handler
)) 
 148         h 
= advapi32
.RegisterServiceCtrlHandlerExW(service_name
, handler
, None) 
 150             raise OSError('Handler registration failed: %s' % 
 151                           ctypes
.FormatError()) 
 154     except Exception as e
: 
 155         tb 
= traceback
.format_exc() 
 156         msg 
= str(e
) + '\n' + tb
 
 157         win_service_report_event(service_name
, msg
, is_error
=True) 
 161 def win_service_start(service_name
, real_main
): 
 164             functools
.partial(win_service_main
, service_name
, real_main
)) 
 165         dispatch_table 
= _ctypes_array(SERVICE_TABLE_ENTRY
, [ 
 170             SERVICE_TABLE_ENTRY(None, ctypes
.cast(None, START_CALLBACK
)) 
 173         if not advapi32
.StartServiceCtrlDispatcherW(dispatch_table
): 
 174             raise OSError('ctypes start failed: %s' % 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) 
 183     parser 
= argparse
.ArgumentParser() 
 184     parser
.add_argument('-i', '--install', 
 185                         action
='store_const', dest
='action', const
='install', 
 186                         help='Launch at Windows startup') 
 187     parser
.add_argument('-u', '--uninstall', 
 188                         action
='store_const', dest
='action', const
='uninstall', 
 189                         help='Remove Windows service') 
 190     parser
.add_argument('-s', '--service', 
 191                         action
='store_const', dest
='action', const
='service', 
 192                         help='Run as a Windows service') 
 193     parser
.add_argument('-b', '--bind', metavar
='<host:port>', 
 194                         action
='store', default
='localhost:8142', 
 195                         help='Bind to host:port (default %default)') 
 196     options 
= parser
.parse_args(args
=args
) 
 198     if options
.action 
== 'install': 
 199         fn 
= os
.path
.abspath(__file__
).replace('v:', '\\\\vboxsrv\\vbox') 
 200         cmdline 
= '%s %s -s -b %s' % (sys
.executable
, fn
, options
.bind
) 
 201         win_install_service(SVCNAME
, cmdline
) 
 204     if options
.action 
== 'uninstall': 
 205         win_uninstall_service(SVCNAME
) 
 208     if options
.action 
== 'service': 
 209         win_service_start(SVCNAME
, main
) 
 212     host
, port_str 
= options
.bind
.split(':') 
 215     print('Listening on %s:%d' % (host
, port
)) 
 216     srv 
= BuildHTTPServer((host
, port
), BuildHTTPRequestHandler
) 
 217     thr 
= threading
.Thread(target
=srv
.serve_forever
) 
 219     input('Press ENTER to shut down') 
 225     for name 
in os
.listdir(path
): 
 226         fname 
= os
.path
.join(path
, name
) 
 227         if os
.path
.isdir(fname
): 
 230             os
.chmod(fname
, 0o666) 
 234 #============================================================================== 
 237 class BuildError(Exception): 
 238     def __init__(self
, output
, code
=500): 
 246 class HTTPError(BuildError
): 
 250 class PythonBuilder(object): 
 251     def __init__(self
, **kwargs
): 
 252         pythonVersion 
= kwargs
.pop('python', '2.7') 
 254             key 
= _winreg
.OpenKey(_winreg
.HKEY_LOCAL_MACHINE
, r
'SOFTWARE\Python\PythonCore\%s\InstallPath' % pythonVersion
) 
 256                 self
.pythonPath
, _ 
= _winreg
.QueryValueEx(key
, '') 
 258                 _winreg
.CloseKey(key
) 
 260             raise BuildError('No such Python version: %s' % pythonVersion
) 
 262         super(PythonBuilder
, self
).__init
__(**kwargs
) 
 265 class GITInfoBuilder(object): 
 266     def __init__(self
, **kwargs
): 
 268             self
.user
, self
.repoName 
= kwargs
['path'][:2] 
 269             self
.rev 
= kwargs
.pop('rev') 
 271             raise BuildError('Invalid path') 
 272         except KeyError as e
: 
 273             raise BuildError('Missing mandatory parameter "%s"' % e
.args
[0]) 
 275         path 
= os
.path
.join(os
.environ
['APPDATA'], 'Build archive', self
.repoName
, self
.user
) 
 276         if not os
.path
.exists(path
): 
 278         self
.basePath 
= tempfile
.mkdtemp(dir=path
) 
 279         self
.buildPath 
= os
.path
.join(self
.basePath
, 'build') 
 281         super(GITInfoBuilder
, self
).__init
__(**kwargs
) 
 284 class GITBuilder(GITInfoBuilder
): 
 287             subprocess
.check_output(['git', 'clone', 'git://github.com/%s/%s.git' % (self
.user
, self
.repoName
), self
.buildPath
]) 
 288             subprocess
.check_output(['git', 'checkout', self
.rev
], cwd
=self
.buildPath
) 
 289         except subprocess
.CalledProcessError 
as e
: 
 290             raise BuildError(e
.output
) 
 292         super(GITBuilder
, self
).build() 
 295 class YoutubeDLBuilder(object): 
 296     authorizedUsers 
= ['fraca7', 'phihag', 'rg3', 'FiloSottile'] 
 298     def __init__(self
, **kwargs
): 
 299         if self
.repoName 
!= 'youtube-dl': 
 300             raise BuildError('Invalid repository "%s"' % self
.repoName
) 
 301         if self
.user 
not in self
.authorizedUsers
: 
 302             raise HTTPError('Unauthorized user "%s"' % self
.user
, 401) 
 304         super(YoutubeDLBuilder
, self
).__init
__(**kwargs
) 
 308             subprocess
.check_output([os
.path
.join(self
.pythonPath
, 'python.exe'), 'setup.py', 'py2exe'], 
 310         except subprocess
.CalledProcessError 
as e
: 
 311             raise BuildError(e
.output
) 
 313         super(YoutubeDLBuilder
, self
).build() 
 316 class DownloadBuilder(object): 
 317     def __init__(self
, **kwargs
): 
 318         self
.handler 
= kwargs
.pop('handler') 
 319         self
.srcPath 
= os
.path
.join(self
.buildPath
, *tuple(kwargs
['path'][2:])) 
 320         self
.srcPath 
= os
.path
.abspath(os
.path
.normpath(self
.srcPath
)) 
 321         if not self
.srcPath
.startswith(self
.buildPath
): 
 322             raise HTTPError(self
.srcPath
, 401) 
 324         super(DownloadBuilder
, self
).__init
__(**kwargs
) 
 327         if not os
.path
.exists(self
.srcPath
): 
 328             raise HTTPError('No such file', 404) 
 329         if os
.path
.isdir(self
.srcPath
): 
 330             raise HTTPError('Is a directory: %s' % self
.srcPath
, 401) 
 332         self
.handler
.send_response(200) 
 333         self
.handler
.send_header('Content-Type', 'application/octet-stream') 
 334         self
.handler
.send_header('Content-Disposition', 'attachment; filename=%s' % os
.path
.split(self
.srcPath
)[-1]) 
 335         self
.handler
.send_header('Content-Length', str(os
.stat(self
.srcPath
).st_size
)) 
 336         self
.handler
.end_headers() 
 338         with open(self
.srcPath
, 'rb') as src
: 
 339             shutil
.copyfileobj(src
, self
.handler
.wfile
) 
 341         super(DownloadBuilder
, self
).build() 
 344 class CleanupTempDir(object): 
 347             rmtree(self
.basePath
) 
 348         except Exception as e
: 
 349             print('WARNING deleting "%s": %s' % (self
.basePath
, e
)) 
 351         super(CleanupTempDir
, self
).build() 
 355     def __init__(self
, **kwargs
): 
 368 class Builder(PythonBuilder
, GITBuilder
, YoutubeDLBuilder
, DownloadBuilder
, CleanupTempDir
, Null
): 
 372 class BuildHTTPRequestHandler(BaseHTTPRequestHandler
): 
 373     actionDict 
= {'build': Builder
, 'download': Builder
}  # They're the same, no more caching. 
 376         path 
= urlparse
.urlparse(self
.path
) 
 377         paramDict 
= dict([(key
, value
[0]) for key
, value 
in urlparse
.parse_qs(path
.query
).items()]) 
 378         action
, _
, path 
= path
.path
.strip('/').partition('/') 
 380             path 
= path
.split('/') 
 381             if action 
in self
.actionDict
: 
 383                     builder 
= self
.actionDict
[action
](path
=path
, handler
=self
, **paramDict
) 
 389                 except BuildError 
as e
: 
 390                     self
.send_response(e
.code
) 
 391                     msg 
= unicode(e
).encode('UTF-8') 
 392                     self
.send_header('Content-Type', 'text/plain; charset=UTF-8') 
 393                     self
.send_header('Content-Length', len(msg
)) 
 395                     self
.wfile
.write(msg
) 
 396                 except HTTPError 
as e
: 
 397                     self
.send_response(e
.code
, str(e
)) 
 399                 self
.send_response(500, 'Unknown build method "%s"' % action
) 
 401             self
.send_response(500, 'Malformed URL') 
 403 #============================================================================== 
 405 if __name__ 
== '__main__':