summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimeon Simeonov2015-04-15 22:34:26 +0200
committerSimeon Simeonov2015-04-15 22:34:26 +0200
commit0ce648a0fd2cc55c18ca9f20b8f67bbb932d3293 (patch)
tree1a1adbdc0b42431a5ef6ebd507f2139fb135ae4e
parent65b5e0fdb857febed888eff7674d80925515c7d5 (diff)
beinc_poller.py v.2.0 + few bugfixes
-rwxr-xr-xbeinc_poller.py221
-rwxr-xr-xbeinc_poller_xmlrpc.py248
-rwxr-xr-xbeinc_server.py (renamed from beinc_server_.py)0
3 files changed, 155 insertions, 314 deletions
diff --git a/beinc_poller.py b/beinc_poller.py
index 2bdfe29..b5701ce 100755
--- a/beinc_poller.py
+++ b/beinc_poller.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,16 +21,13 @@
21import argparse 21import argparse
22import errno 22import errno
23import getpass 23import getpass
24import httplib 24import httplib # for Python < 2.7.9
25import json
26import os 25import os
27import sched 26import socket # for Python < 2.7.9
28import socket
29import ssl 27import ssl
30import sys 28import sys
31import time 29import time
32import urllib 30import xmlrpclib
33import urllib2
34 31
35try: 32try:
36 import pynotify 33 import pynotify
@@ -50,38 +47,74 @@ except ImportError as e:
50 47
51 48
52__author__ = 'Simeon Simeonov' 49__author__ = 'Simeon Simeonov'
53__version__ = '1.0' 50__version__ = '2.0'
54__license__ = 'GPL3' 51__license__ = 'GPL3'
55 52
56 53
57class ValidHTTPSConnection(httplib.HTTPConnection): 54BEINC_SSL_METHODS = {'SSLv3': ssl.PROTOCOL_SSLv3,
58 """ 55 'TLSv1': ssl.PROTOCOL_TLSv1}
59 Implements a simple CERT verification functionality 56try:
57 BEINC_SSL_METHODS.update({'TLSv1_1': ssl.PROTOCOL_TLSv1_1})
58 BEINC_SSL_METHODS.update({'TLSv1_2': ssl.PROTOCOL_TLSv1_2})
59except:
60 pass
61
62
63class BEINCCustomHTTPSConnection(httplib.HTTPConnection):
60 """ 64 """
65 This class allows communication via SSL.
61 66
67 It is a reimplementation of httplib.HTTPSConnection and
68 allows the server certificate to be validated against CA
69 This functionality lacks in Python < 2.7.9
70 """
62 default_port = httplib.HTTPS_PORT 71 default_port = httplib.HTTPS_PORT
63 72
64 def __init__(self, *args, **kwargs): 73 def __init__(self, host, port=None, key_file=None, cert_file=None,
65 httplib.HTTPConnection.__init__(self, *args, **kwargs) 74 strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
75 source_address=None, custom_ssl_options={}):
76 httplib.HTTPConnection.__init__(self, host, port, strict, timeout,
77 source_address)
78 self.key_file = key_file
79 self.cert_file = cert_file
80 self.custom_ssl_options = custom_ssl_options
66 81
67 def connect(self): 82 def connect(self):
83 "Connect to a host on a given (SSL) port."
68 sock = socket.create_connection((self.host, self.port), 84 sock = socket.create_connection((self.host, self.port),
69 self.timeout, self.source_address) 85 self.timeout, self.source_address)
70 if self._tunnel_host: 86 if self._tunnel_host:
71 self.sock = sock 87 self.sock = sock
72 self._tunnel() 88 self._tunnel()
73 self.sock = ssl.wrap_socket(sock, 89 self.sock = ssl.wrap_socket(sock,
74 ca_certs=global_beinc_cert_file, 90 self.key_file,
75 cert_reqs=ssl.CERT_REQUIRED) 91 self.cert_file,
92 **self.custom_ssl_options)
76 93
77 94
78class ValidHTTPSHandler(urllib2.HTTPSHandler): 95class BEINCCustomSafeTransport(xmlrpclib.Transport):
79 """ 96
80 Implements a simple CERT verification functionality 97 def __init__(self, use_datetime=0, custom_ssl_options={}):
81 """ 98 xmlrpclib.Transport.__init__(self, use_datetime=use_datetime)
99 self.custom_ssl_options = custom_ssl_options
82 100
83 def https_open(self, req): 101 def make_connection(self, host):
84 return self.do_open(ValidHTTPSConnection, req) 102 if self._connection and host == self._connection[0]:
103 return self._connection[1]
104 try:
105 HTTPS = BEINCCustomHTTPSConnection
106 except AttributeError:
107 raise NotImplementedError(
108 "your version of httplib doesn't support HTTPS"
109 )
110 else:
111 chost, self._extra_headers, x509 = self.get_host_info(host)
112 self._connection = host, HTTPS(
113 chost,
114 None,
115 custom_ssl_options=self.custom_ssl_options,
116 **(x509 or {}))
117 return self._connection[1]
85 118
86 119
87def display_notification(args, title, message): 120def display_notification(args, title, message):
@@ -141,23 +174,35 @@ def poll_notifications(scheduler, args):
141 args: the argparse processed command-line arguments 174 args: the argparse processed command-line arguments
142 """ 175 """
143 try: 176 try:
144 post_values = {'password': args.password} 177 ssl_version = BEINC_SSL_METHODS.get(args.ssl_version,
145 data = urllib.urlencode(post_values) 178 ssl.PROTOCOL_SSLv23)
146 req = urllib2.Request(args.url, data) 179 if sys.hexversion >= 0x20709f0:
147 if args.cert: # check for cert validity 180 # Python >= 2.7.9
148 global global_beinc_cert_file # ugly hack 181 context = ssl.SSLContext(ssl_version)
149 global_beinc_cert_file = args.cert 182 context.verify_mode = ssl.CERT_REQUIRED
150 opener = urllib2.build_opener(ValidHTTPSHandler) 183 if args.no_cert_validate:
151 response = opener.open(req) 184 context.verify_mode = ssl.CERT_NONE
152 else: # ... or don't 185 context.check_hostname = bool(not args.disable_hostname_check)
153 response = urllib2.urlopen(req) 186 if args.cert and not args.no_cert_validate:
154 res_code = response.code 187 context.load_verify_locations(os.path.expanduser(args.cert))
155 res_str = response.read() 188 if args.ciphers:
156 if res_code == 200 and args.debug: 189 context.set_ciphers(args.ciphers)
157 print('Server responded: OK') 190 transport = xmlrpclib.SafeTransport(context=context)
158 print('Body:\n{0}'.format(res_str)) 191 else:
159 response.close() 192 # Python < 2.7.9
160 res_list = json.loads(res_str) 193 ssl_options = {}
194 ssl_options['ssl_version'] = ssl_version
195 if args.cert and not args.no_cert_validate:
196 ssl_options['ca_certs'] = os.path.expanduser(args.cert)
197 if not args.no_cert_validate:
198 ssl_options['cert_reqs'] = ssl.CERT_REQUIRED
199 if args.ciphers:
200 ssl_options['ciphers'] = args.ciphers
201 transport = BEINCCustomSafeTransport(
202 custom_ssl_options=ssl_options)
203 server = xmlrpclib.ServerProxy(args.url,
204 transport=transport)
205 res_list = server.pull(args.rname, args.password)
161 for entry in res_list: 206 for entry in res_list:
162 title = entry.get('title', '') 207 title = entry.get('title', '')
163 message = entry.get('message', '') 208 message = entry.get('message', '')
@@ -166,11 +211,16 @@ def poll_notifications(scheduler, args):
166 1, 211 1,
167 poll_notifications, 212 poll_notifications,
168 (scheduler, args)) 213 (scheduler, args))
169 except urllib2.HTTPError as e: 214 except xmlrpclib.Fault as fault:
170 sys.stderr.write('BEINC-server error ({0} - {1})\n'.format(e.code, 215 sys.stderr.write(
171 e.reason)) 216 'BEINC server answered with errorCode={0}: {1}\n'.format(
217 fault.faultCode,
218 fault.faultString))
219 except ssl.SSLError as e:
220 sys.stderr.write('BEINC SSL/TLS error: {0}\n'.format(e))
221 sys.exit(errno.EPERM)
172 except Exception as e: 222 except Exception as e:
173 sys.stderr.write('BEINC-poller error: {0}\nTerminating...'.format(e)) 223 sys.stderr.write('BEINC generic client error: {0}\n'.format(e))
174 sys.exit(errno.EPERM) 224 sys.exit(errno.EPERM)
175 225
176 226
@@ -181,21 +231,14 @@ def main():
181 'url', 231 'url',
182 metavar='URL', 232 metavar='URL',
183 type=str, 233 type=str,
184 help='BEINC destination URL') 234 help='BEINC server destination URL')
185 parser.add_argument( 235 parser.add_argument(
186 '-a', '--align', 236 '-a', '--align',
187 metavar='ALIGNMENT', 237 metavar='ALIGNMENT',
188 type=str, 238 type=str,
189 dest='alignment', 239 dest='alignment',
190 default='left', 240 default='left',
191 help='Alignment for "pyosd" (default: "left")') 241 help='Alignment for "pyosd": "left" (default), "center", "right"')
192 parser.add_argument(
193 '-c', '--cert-file',
194 metavar='FILE',
195 type=str,
196 dest='cert',
197 default='',
198 help='BEINC CA-cert to check the server-cert against (default: None)')
199 parser.add_argument( 242 parser.add_argument(
200 '-C', '--color', 243 '-C', '--color',
201 metavar='COLOR', 244 metavar='COLOR',
@@ -204,18 +247,27 @@ def main():
204 default='blue', 247 default='blue',
205 help='Color for "pyosd" (default: "blue")') 248 help='Color for "pyosd" (default: "blue")')
206 parser.add_argument( 249 parser.add_argument(
207 '-d', '--debug', 250 '-c', '--cert-file',
208 action='store_true', 251 metavar='FILE',
209 dest='debug', 252 type=str,
210 default=False, 253 dest='cert',
211 help='Run the poller-process in debug-mode') 254 default='',
255 help='BEINC CA-cert to check the server-cert against '
256 '(default: Check disabled)')
212 parser.add_argument( 257 parser.add_argument(
213 '-f', '--frequency', 258 '--ciphers',
214 metavar='SECONDS', 259 metavar='CIPHERS',
215 type=int, 260 type=str,
216 dest='frequency', 261 dest='ciphers',
217 default=10, 262 default='',
218 help='Polling frequency in seconds (default: 10)') 263 help='Preferred ciphers list (default: auto)')
264 if sys.hexversion >= 0x20709f0:
265 parser.add_argument(
266 '--disable-hostname-check',
267 action='store_true',
268 dest='disable_hostname_check',
269 default=False,
270 help='Do not check whether server cert matches server hostname')
219 parser.add_argument( 271 parser.add_argument(
220 '--font', 272 '--font',
221 metavar='FONT', 273 metavar='FONT',
@@ -224,6 +276,13 @@ def main():
224 default=None, 276 default=None,
225 help='Custom font for "pyosd" (default: Default font)') 277 help='Custom font for "pyosd" (default: Default font)')
226 parser.add_argument( 278 parser.add_argument(
279 '-f', '--frequency',
280 metavar='SECONDS',
281 type=int,
282 dest='frequency',
283 default=10,
284 help='Polling frequency in seconds (default: 10)')
285 parser.add_argument(
227 '--h-offset', 286 '--h-offset',
228 metavar='OFFSET', 287 metavar='OFFSET',
229 type=int, 288 type=int,
@@ -231,14 +290,27 @@ def main():
231 default=30, 290 default=30,
232 help='Horizontal offset for "pyosd" (default: 30)') 291 help='Horizontal offset for "pyosd" (default: 30)')
233 parser.add_argument( 292 parser.add_argument(
293 '-n', '--resource-name',
294 metavar='NAME',
295 type=str,
296 dest='rname',
297 required=True,
298 help='The name of the BEINC-resource on the remote server')
299 parser.add_argument(
300 '--no-cert-validate',
301 action='store_true',
302 dest='no_cert_validate',
303 default=False,
304 help='Do not validate server certificate')
305 parser.add_argument(
234 '-o', '--osd-system', 306 '-o', '--osd-system',
235 metavar='SYSTEM', 307 metavar='SYSTEM',
236 type=str, 308 type=str,
237 dest='osd_sys', 309 dest='osd_sys',
238 default='pynotify', 310 default='pynotify',
239 help='BEINC osd-system ("pynotify" or "pyosd") (default: pynotify)') 311 help='BEINC osd-system: "pynotify" (default), "pyosd"')
240 parser.add_argument( 312 parser.add_argument(
241 '-P', '--password', 313 '-p', '--password',
242 metavar='PASSWORD[FILE]', 314 metavar='PASSWORD[FILE]',
243 type=str, 315 type=str,
244 dest='password', 316 dest='password',
@@ -246,12 +318,29 @@ def main():
246 help='BEINC taget-password / text-file containing the target password' 318 help='BEINC taget-password / text-file containing the target password'
247 ' (default & recommended: prompt for passwd)') 319 ' (default & recommended: prompt for passwd)')
248 parser.add_argument( 320 parser.add_argument(
249 '-p', '--position', 321 '-P', '--position',
250 metavar='POSITION', 322 metavar='POSITION',
251 type=str, 323 type=str,
252 dest='position', 324 dest='position',
253 default='bottom', 325 default='bottom',
254 help='Position for "pyosd" (default: "bottom")') 326 help='Position for "pyosd": "top", "middle", "bottom" (default)')
327 if sys.hexversion >= 0x20709f0:
328 parser.add_argument(
329 '-s', '--ssl-version',
330 metavar='VERSION',
331 type=str,
332 dest='ssl_version',
333 default='auto',
334 help='Use SSL version: "auto" (default), '
335 '"SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2"')
336 else:
337 parser.add_argument(
338 '-s', '--ssl-version',
339 metavar='VERSION',
340 type=str,
341 dest='ssl_version',
342 default='auto',
343 help='Use SSL version: "auto" (default), "SSLv3", "TLSv1"')
255 parser.add_argument( 344 parser.add_argument(
256 '-t', '--osd-timeout', 345 '-t', '--osd-timeout',
257 metavar='SECONDS', 346 metavar='SECONDS',
diff --git a/beinc_poller_xmlrpc.py b/beinc_poller_xmlrpc.py
deleted file mode 100755
index 2efd7df..0000000
--- a/beinc_poller_xmlrpc.py
+++ /dev/null
@@ -1,248 +0,0 @@
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Blackmore's Enhanced IRC-Notification Collection (BEINC) v2.0
5# Copyright (C) 2013-2015 Simeon Simeonov
6
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
21import argparse
22import errno
23import getpass
24import httplib # for Python < 2.7.9
25import os
26import socket # for Python < 2.7.9
27import ssl
28import sys
29import xmlrpclib
30
31
32__author__ = 'Simeon Simeonov'
33__version__ = '2.0'
34__license__ = 'GPL3'
35
36
37BEINC_SSL_METHODS = {'SSLv3': ssl.PROTOCOL_SSLv3,
38 'TLSv1': ssl.PROTOCOL_TLSv1}
39try:
40 BEINC_SSL_METHODS.update({'TLSv1_1': ssl.PROTOCOL_TLSv1_1})
41 BEINC_SSL_METHODS.update({'TLSv1_2': ssl.PROTOCOL_TLSv1_2})
42except:
43 pass
44
45
46class BEINCCustomHTTPSConnection(httplib.HTTPConnection):
47 """
48 This class allows communication via SSL.
49
50 It is a reimplementation of httplib.HTTPSConnection and
51 allows the server certificate to be validated against CA
52 This functionality lacks in Python < 2.7.9
53 """
54 default_port = httplib.HTTPS_PORT
55
56 def __init__(self, host, port=None, key_file=None, cert_file=None,
57 strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
58 source_address=None, custom_ssl_options={}):
59 httplib.HTTPConnection.__init__(self, host, port, strict, timeout,
60 source_address)
61 self.key_file = key_file
62 self.cert_file = cert_file
63 self.custom_ssl_options = custom_ssl_options
64
65 def connect(self):
66 "Connect to a host on a given (SSL) port."
67 sock = socket.create_connection((self.host, self.port),
68 self.timeout, self.source_address)
69 if self._tunnel_host:
70 self.sock = sock
71 self._tunnel()
72 self.sock = ssl.wrap_socket(sock,
73 self.key_file,
74 self.cert_file,
75 **self.custom_ssl_options)
76
77
78class BEINCCustomSafeTransport(xmlrpclib.Transport):
79
80 def __init__(self, use_datetime=0, custom_ssl_options={}):
81 xmlrpclib.Transport.__init__(self, use_datetime=use_datetime)
82 self.custom_ssl_options = custom_ssl_options
83
84 def make_connection(self, host):
85 if self._connection and host == self._connection[0]:
86 return self._connection[1]
87 try:
88 HTTPS = BEINCCustomHTTPSConnection
89 except AttributeError:
90 raise NotImplementedError(
91 "your version of httplib doesn't support HTTPS"
92 )
93 else:
94 chost, self._extra_headers, x509 = self.get_host_info(host)
95 self._connection = host, HTTPS(
96 chost,
97 None,
98 custom_ssl_options=self.custom_ssl_options,
99 **(x509 or {}))
100 return self._connection[1]
101
102
103def action_execute(args):
104 """
105 """
106 try:
107 ssl_version = BEINC_SSL_METHODS.get(args.ssl_version,
108 ssl.PROTOCOL_SSLv23)
109 if sys.hexversion >= 0x20709f0:
110 # Python >= 2.7.9
111 context = ssl.SSLContext(ssl_version)
112 context.verify_mode = ssl.CERT_REQUIRED
113 if args.no_cert_validate:
114 context.verify_mode = ssl.CERT_NONE
115 context.check_hostname = bool(not args.disable_hostname_check)
116 if args.cert and not args.no_cert_validate:
117 context.load_verify_locations(os.path.expanduser(args.cert))
118 if args.ciphers:
119 context.set_ciphers(args.ciphers)
120 transport = xmlrpclib.SafeTransport(context=context)
121 else:
122 # Python < 2.7.9
123 ssl_options = {}
124 ssl_options['ssl_version'] = ssl_version
125 if args.cert and not args.no_cert_validate:
126 ssl_options['ca_certs'] = os.path.expanduser(args.cert)
127 if not args.no_cert_validate:
128 ssl_options['cert_reqs'] = ssl.CERT_REQUIRED
129 if args.ciphers:
130 ssl_options['ciphers'] = args.ciphers
131 transport = BEINCCustomSafeTransport(
132 custom_ssl_options=ssl_options)
133 server = xmlrpclib.ServerProxy(args.url,
134 transport=transport)
135 if args.pull:
136 print(server.pull(args.rname, args.password))
137 else:
138 print(server.push(args.rname,
139 args.password,
140 args.title,
141 args.message))
142 except xmlrpclib.Fault as fault:
143 sys.stderr.write(
144 'BEINC server answered with errorCode={0}: {1}\n'.format(
145 fault.faultCode,
146 fault.faultString))
147 except ssl.SSLError as e:
148 sys.stderr.write('BEINC SSL/TLS error: {0}\n'.format(e))
149 sys.exit(errno.EPERM)
150 except Exception as e:
151 sys.stderr.write('BEINC generic client error: {0}\n'.format(e))
152 sys.exit(errno.EPERM)
153
154
155def main():
156 parser = argparse.ArgumentParser(
157 description='The following options are available')
158 parser.add_argument('url',
159 metavar='URL',
160 type=str,
161 help='Destination URL')
162 parser.add_argument('-c', '--cert-file',
163 metavar='FILE',
164 type=str,
165 dest='cert',
166 default='',
167 help='BEINC CA-cert to check the server-cert against')
168 parser.add_argument('-C', '--ciphers',
169 metavar='CIPHERS',
170 type=str,
171 dest='ciphers',
172 default='',
173 help='Preferred ciphers list (default: auto)')
174 parser.add_argument('-m', '--message',
175 metavar='MESSAGE',
176 type=str,
177 dest='message',
178 default='BEINC message',
179 help='BEINC message')
180 parser.add_argument('-n', '--resource-name',
181 metavar='NAME',
182 type=str,
183 dest='rname',
184 required=True,
185 help='The name of the BEINC-resource on '
186 'the remote server')
187 parser.add_argument('-p', '--password',
188 metavar='PASSWORD',
189 type=str,
190 dest='password',
191 default='',
192 help='Password')
193 if sys.hexversion >= 0x20709f0:
194 parser.add_argument('-s', '--ssl-version',
195 metavar='VERSION',
196 type=str,
197 dest='ssl_version',
198 default='auto',
199 help='Use SSL version: auto (default), '
200 'SSLv3, TLSv1, TLSv1_1, TLSv1_2')
201 else:
202 parser.add_argument('-s', '--ssl-version',
203 metavar='VERSION',
204 type=str,
205 dest='ssl_version',
206 default='auto',
207 help='Use SSL version: auto (default), '
208 'SSLv3, TLSv1')
209 parser.add_argument('-t', '--title',
210 metavar='TITLE',
211 type=str,
212 dest='title',
213 default='BEINC title',
214 help='BEINC title')
215 parser.add_argument('-v', '--version',
216 action='version',
217 version='%(prog)s {0}'.format(__version__),
218 help='display program-version and exit')
219 if sys.hexversion >= 0x20709f0:
220 parser.add_argument('--disable-hostname-check',
221 action='store_true',
222 dest='disable_hostname_check',
223 default=False,
224 help='Do not check whether server cert '
225 'matches server hostname')
226 parser.add_argument('--no-cert-validate',
227 action='store_true',
228 dest='no_cert_validate',
229 default=False,
230 help='Do not validate server certificate')
231 parser.add_argument('--pull',
232 action='store_true',
233 dest='pull',
234 default=False,
235 help='Perform a pull operation (default: push)')
236 args = parser.parse_args()
237 if not args.password:
238 try:
239 args.password = getpass.getpass()
240 except Exception as e:
241 sys.stderr.write('Prompt terminated\n')
242 sys.exit(errno.EACCES)
243 action_execute(args)
244 sys.exit(0)
245
246
247if __name__ == '__main__':
248 main()
diff --git a/beinc_server_.py b/beinc_server.py
index cf4f7c7..cf4f7c7 100755
--- a/beinc_server_.py
+++ b/beinc_server.py