diff options
| author | Simeon Simeonov | 2015-04-14 23:08:21 +0200 |
|---|---|---|
| committer | Simeon Simeonov | 2015-04-14 23:08:21 +0200 |
| commit | a5a26cba29ba37f3ef7c4f60c6cbf3f663161b27 (patch) | |
| tree | b7fa2ed496cee78ad67c14338039a08a30742814 | |
| parent | 36dda1cca9503fc00c15ed22c4c5969e80edc1f7 (diff) | |
beinc_generic_client.py v.2.0 almost fone
| -rwxr-xr-x | beinc_generic_client.py | 129 | ||||
| -rwxr-xr-x | beinc_server_.py | 13 |
2 files changed, 91 insertions, 51 deletions
diff --git a/beinc_generic_client.py b/beinc_generic_client.py index b46d3fd..da671f2 100755 --- a/beinc_generic_client.py +++ b/beinc_generic_client.py | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
| 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
| 3 | 3 | ||
| 4 | # Blackmore's Enhanced IRC-Notification Collection (BEINC) v1.0 | 4 | # Blackmore's Enhanced IRC-Notification Collection (BEINC) v2.0 |
| 5 | # Copyright (C) 2013-2015 Simeon Simeonov | 5 | # Copyright (C) 2013-2015 Simeon Simeonov |
| 6 | 6 | ||
| 7 | # This program is free software: you can redistribute it and/or modify | 7 | # This program is free software: you can redistribute it and/or modify |
| @@ -21,12 +21,12 @@ | |||
| 21 | import argparse | 21 | import argparse |
| 22 | import errno | 22 | import errno |
| 23 | import getpass | 23 | import getpass |
| 24 | import httplib | 24 | import httplib # for Python < 2.7.9 |
| 25 | import socket | 25 | import os |
| 26 | import socket # for Python < 2.7.9 | ||
| 26 | import ssl | 27 | import ssl |
| 27 | import sys | 28 | import sys |
| 28 | import urllib | 29 | import xmlrpclib |
| 29 | import urllib2 | ||
| 30 | 30 | ||
| 31 | 31 | ||
| 32 | __author__ = 'Simeon Simeonov' | 32 | __author__ = 'Simeon Simeonov' |
| @@ -34,62 +34,91 @@ __version__ = '1.0' | |||
| 34 | __license__ = 'GPL3' | 34 | __license__ = 'GPL3' |
| 35 | 35 | ||
| 36 | 36 | ||
| 37 | class ValidHTTPSConnection(httplib.HTTPConnection): | 37 | class BEINCCustomHTTPSConnection(httplib.HTTPConnection): |
| 38 | """ | ||
| 39 | Implements a simple CERT verification functionality | ||
| 40 | """ | 38 | """ |
| 39 | This class allows communication via SSL. | ||
| 41 | 40 | ||
| 41 | It is a reimplementation of httplib.HTTPSConnection and | ||
| 42 | allows the server certificate to be validated against CA | ||
| 43 | This functionality lacks in Python < 2.7.9 | ||
| 44 | """ | ||
| 42 | default_port = httplib.HTTPS_PORT | 45 | default_port = httplib.HTTPS_PORT |
| 43 | 46 | ||
| 44 | def __init__(self, *args, **kwargs): | 47 | def __init__(self, host, port=None, key_file=None, cert_file=None, |
| 45 | httplib.HTTPConnection.__init__(self, *args, **kwargs) | 48 | strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, |
| 49 | source_address=None, ca_cert=None): | ||
| 50 | httplib.HTTPConnection.__init__(self, host, port, strict, timeout, | ||
| 51 | source_address) | ||
| 52 | self.key_file = key_file | ||
| 53 | self.cert_file = cert_file | ||
| 54 | self.ca_cert = ca_cert | ||
| 46 | 55 | ||
| 47 | def connect(self): | 56 | def connect(self): |
| 57 | "Connect to a host on a given (SSL) port." | ||
| 48 | sock = socket.create_connection((self.host, self.port), | 58 | sock = socket.create_connection((self.host, self.port), |
| 49 | self.timeout, self.source_address) | 59 | self.timeout, self.source_address) |
| 50 | if self._tunnel_host: | 60 | if self._tunnel_host: |
| 51 | self.sock = sock | 61 | self.sock = sock |
| 52 | self._tunnel() | 62 | self._tunnel() |
| 53 | self.sock = ssl.wrap_socket(sock, | 63 | self.sock = ssl.wrap_socket(sock, |
| 54 | ca_certs=global_beinc_cert_file, | 64 | self.key_file, |
| 55 | cert_reqs=ssl.CERT_REQUIRED) | 65 | self.cert_file, |
| 56 | 66 | cert_reqs=ssl.CERT_REQUIRED, | |
| 67 | ca_certs=self.ca_cert) | ||
| 57 | 68 | ||
| 58 | class ValidHTTPSHandler(urllib2.HTTPSHandler): | 69 | class BEINCCustomSafeTransport(xmlrpclib.Transport): |
| 59 | """ | ||
| 60 | Implements a simple CERT verification functionality | ||
| 61 | """ | ||
| 62 | 70 | ||
| 63 | def https_open(self, req): | 71 | def __init__(self, use_datetime=0, ca_cert=None): |
| 64 | return self.do_open(ValidHTTPSConnection, req) | 72 | xmlrpclib.Transport.__init__(self, use_datetime=use_datetime) |
| 73 | self.ca_cert = ca_cert | ||
| 74 | |||
| 75 | def make_connection(self, host): | ||
| 76 | if self._connection and host == self._connection[0]: | ||
| 77 | return self._connection[1] | ||
| 78 | try: | ||
| 79 | HTTPS = BEINCCustomHTTPSConnection | ||
| 80 | except AttributeError: | ||
| 81 | raise NotImplementedError( | ||
| 82 | "your version of httplib doesn't support HTTPS" | ||
| 83 | ) | ||
| 84 | else: | ||
| 85 | chost, self._extra_headers, x509 = self.get_host_info(host) | ||
| 86 | self._connection = host, HTTPS(chost, | ||
| 87 | None, | ||
| 88 | ca_cert=self.ca_cert, | ||
| 89 | **(x509 or {})) | ||
| 90 | return self._connection[1] | ||
| 65 | 91 | ||
| 66 | 92 | ||
| 67 | def action_push(args): | 93 | def action_execute(args): |
| 68 | """ | 94 | """ |
| 69 | """ | 95 | """ |
| 70 | try: | 96 | try: |
| 71 | post_values = {'title': args.title, | 97 | if sys.hexversion >= 0x20709f0: |
| 72 | 'message': args.message, | 98 | # Python >= 2.7.9 |
| 73 | 'password': args.password} | 99 | context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) |
| 74 | data = urllib.urlencode(post_values) | 100 | context.verify_mode = ssl.CERT_REQUIRED |
| 75 | req = urllib2.Request(args.url, data) | 101 | context.check_hostname = False |
| 76 | if args.cert: # check for cert validity | 102 | context.load_verify_locations(os.path.expanduser(args.cert)) |
| 77 | global global_beinc_cert_file # ugly hack | 103 | transport = xmlrpclib.SafeTransport(context=context) |
| 78 | global_beinc_cert_file = args.cert | 104 | else: |
| 79 | opener = urllib2.build_opener(ValidHTTPSHandler) | 105 | # Python < 2.7.9 |
| 80 | response = opener.open(req) | 106 | transport = BEINCCustomSafeTransport( |
| 81 | else: # ... or don't | 107 | ca_cert=os.path.expanduser(args.cert)) |
| 82 | response = urllib2.urlopen(req) | 108 | server = xmlrpclib.ServerProxy(args.url, |
| 83 | res_code = response.code | 109 | transport=transport) |
| 84 | if res_code == 200: | 110 | if args.pull: |
| 85 | print('Server responded: OK') | 111 | print(server.pull(args.rname, args.password)) |
| 86 | else: | 112 | else: |
| 87 | print('Server responded: {0}'.format(res_code)) | 113 | print(server.push(args.rname, |
| 88 | print('Body:\n{0}'.format(response.read())) | 114 | args.password, |
| 89 | response.close() | 115 | args.title, |
| 90 | except urllib2.HTTPError as e: | 116 | args.message)) |
| 91 | sys.stderr.write('BEINC-server error ({0} - {1})\n'.format(e.code, | 117 | except xmlrpclib.Fault as fault: |
| 92 | e.reason)) | 118 | sys.stderr.write( |
| 119 | 'BEINC server answered with errorCode={0}: {1}\n'.format( | ||
| 120 | fault.faultCode, | ||
| 121 | fault.faultString)) | ||
| 93 | except Exception as e: | 122 | except Exception as e: |
| 94 | sys.stderr.write('BEINC generic client error: {0}\n'.format(e)) | 123 | sys.stderr.write('BEINC generic client error: {0}\n'.format(e)) |
| 95 | sys.exit(errno.EPERM) | 124 | sys.exit(errno.EPERM) |
| @@ -101,8 +130,6 @@ def main(): | |||
| 101 | parser.add_argument('url', | 130 | parser.add_argument('url', |
| 102 | metavar='URL', | 131 | metavar='URL', |
| 103 | type=str, | 132 | type=str, |
| 104 | #dest='url', | ||
| 105 | #required=True, | ||
| 106 | help='Destination URL') | 133 | help='Destination URL') |
| 107 | parser.add_argument('-c', '--cert-file', | 134 | parser.add_argument('-c', '--cert-file', |
| 108 | metavar='FILE', | 135 | metavar='FILE', |
| @@ -116,12 +143,24 @@ def main(): | |||
| 116 | dest='message', | 143 | dest='message', |
| 117 | default='BEINC message', | 144 | default='BEINC message', |
| 118 | help='BEINC message') | 145 | help='BEINC message') |
| 146 | parser.add_argument('-n', '--resource-name', | ||
| 147 | metavar='NAME', | ||
| 148 | type=str, | ||
| 149 | dest='rname', | ||
| 150 | required=True, | ||
| 151 | help='The name of the BEINC-resource on ' | ||
| 152 | 'the remote server') | ||
| 119 | parser.add_argument('-p', '--password', | 153 | parser.add_argument('-p', '--password', |
| 120 | metavar='PASSWORD', | 154 | metavar='PASSWORD', |
| 121 | type=str, | 155 | type=str, |
| 122 | dest='password', | 156 | dest='password', |
| 123 | default='', | 157 | default='', |
| 124 | help='Password') | 158 | help='Password') |
| 159 | parser.add_argument('--pull', | ||
| 160 | action='store_true', | ||
| 161 | dest='pull', | ||
| 162 | default=False, | ||
| 163 | help='Perform a pull operation (default: push)') | ||
| 125 | parser.add_argument('-t', '--title', | 164 | parser.add_argument('-t', '--title', |
| 126 | metavar='TITLE', | 165 | metavar='TITLE', |
| 127 | type=str, | 166 | type=str, |
| @@ -139,9 +178,9 @@ def main(): | |||
| 139 | except Exception as e: | 178 | except Exception as e: |
| 140 | sys.stderr.write('Prompt terminated\n') | 179 | sys.stderr.write('Prompt terminated\n') |
| 141 | sys.exit(errno.EACCES) | 180 | sys.exit(errno.EACCES) |
| 142 | action_push(args) | 181 | action_execute(args) |
| 143 | sys.exit(0) | 182 | sys.exit(0) |
| 144 | 183 | ||
| 145 | 184 | ||
| 146 | if __name__ == '__main__': | 185 | if __name__ == '__main__': |
| 147 | main() | 186 | main() |
diff --git a/beinc_server_.py b/beinc_server_.py index 36cbcd5..ec920c2 100755 --- a/beinc_server_.py +++ b/beinc_server_.py | |||
| @@ -31,7 +31,7 @@ from functools import wraps | |||
| 31 | 31 | ||
| 32 | from twisted.web import xmlrpc, server | 32 | from twisted.web import xmlrpc, server |
| 33 | from twisted.internet import protocol, reactor, ssl | 33 | from twisted.internet import protocol, reactor, ssl |
| 34 | from twisted.python.filepath import FilePath | 34 | from twisted.python import filepath, log |
| 35 | 35 | ||
| 36 | try: | 36 | try: |
| 37 | import pynotify | 37 | import pynotify |
| @@ -188,9 +188,9 @@ class BEINCInstance(object): | |||
| 188 | """ | 188 | """ |
| 189 | Reruens a json representation of the message queue | 189 | Reruens a json representation of the message queue |
| 190 | """ | 190 | """ |
| 191 | jstr = json.dumps(self.__message_queue) | 191 | r_value = self.__message_queue |
| 192 | self.__message_queue = list() | 192 | self.__message_queue = list() |
| 193 | return jstr | 193 | return r_value |
| 194 | 194 | ||
| 195 | def __send_pynotify_messaage(self, title, message): | 195 | def __send_pynotify_messaage(self, title, message): |
| 196 | """ | 196 | """ |
| @@ -339,13 +339,14 @@ def main(): | |||
| 339 | action='version', | 339 | action='version', |
| 340 | version='%(prog)s {0}'.format(__version__), | 340 | version='%(prog)s {0}'.format(__version__), |
| 341 | help='Display program-version and exit') | 341 | help='Display program-version and exit') |
| 342 | log.startLogging(sys.stdout) | ||
| 342 | args = parser.parse_args() | 343 | args = parser.parse_args() |
| 343 | try: | 344 | try: |
| 344 | with open(args.config_file, 'r') as fp: | 345 | with open(args.config_file, 'r') as fp: |
| 345 | config_dict = json.load(fp) | 346 | config_dict = json.load(fp) |
| 346 | except Exception as e: | 347 | except Exception as e: |
| 347 | sys.stderr.write('Unable to parse {0}: {1}\n'.format(args.config_file, | 348 | sys.stderr.write('Unable to parse {0}: {1}\n'.format(args.config_file, |
| 348 | e)) | 349 | e)) |
| 349 | sys.exit(errno.EIO) | 350 | sys.exit(errno.EIO) |
| 350 | try: | 351 | try: |
| 351 | if config_dict.get('config_version') != 2: | 352 | if config_dict.get('config_version') != 2: |
| @@ -364,8 +365,8 @@ def main(): | |||
| 364 | beinc_server = XMLRPCNotifyServer(config_dict) | 365 | beinc_server = XMLRPCNotifyServer(config_dict) |
| 365 | if ssl_certificate and ssl_private_key: | 366 | if ssl_certificate and ssl_private_key: |
| 366 | # SSL connection | 367 | # SSL connection |
| 367 | cert_path = FilePath(ssl_certificate) | 368 | cert_path = filepath.FilePath(ssl_certificate) |
| 368 | key_path = FilePath(ssl_private_key) | 369 | key_path = filepath.FilePath(ssl_private_key) |
| 369 | private_certificate = ssl.PrivateCertificate.loadPEM( | 370 | private_certificate = ssl.PrivateCertificate.loadPEM( |
| 370 | key_path.getContent() + cert_path.getContent()) | 371 | key_path.getContent() + cert_path.getContent()) |
| 371 | options = private_certificate.options() | 372 | options = private_certificate.options() |
