summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimeon Simeonov2017-05-12 13:25:43 +0200
committerSimeon Simeonov2017-05-12 13:25:43 +0200
commitdf917f55d6fbbe899942c5c4947444702b0b3fcd (patch)
tree670dac84d222ef0f263f8e2612fc536bc0eac5b3
parent1336daa7a6383bc4ed723c914fa5ee8b4e967b26 (diff)
Make beinc_weechat.py BEINC 3 compatible
-rwxr-xr-xbeinc_poller.py132
-rw-r--r--beinc_weechat.py191
2 files changed, 73 insertions, 250 deletions
diff --git a/beinc_poller.py b/beinc_poller.py
index aaf2d05..e84ad71 100755
--- a/beinc_poller.py
+++ b/beinc_poller.py
@@ -1,4 +1,4 @@
1#!/usr/bin/env python 1#!/usr/bin/env python3
2# -*- coding: utf-8 -*- 2# -*- coding: utf-8 -*-
3 3
4# Blackmore's Enhanced IRC-Notification Collection (BEINC) v3.0 4# Blackmore's Enhanced IRC-Notification Collection (BEINC) v3.0
@@ -21,7 +21,7 @@
21import argparse 21import argparse
22import errno 22import errno
23import getpass 23import getpass
24import httplib 24import json
25import os 25import os
26import sched 26import sched
27import socket 27import socket
@@ -29,6 +29,9 @@ import ssl
29import sys 29import sys
30import time 30import time
31 31
32from urllib.parse import urlencode
33from urllib.request import urlopen
34
32try: 35try:
33 import notify2 as pynotify 36 import notify2 as pynotify
34except ImportError as e: 37except ImportError as e:
@@ -40,66 +43,6 @@ __version__ = '3.0'
40__license__ = 'GPL3' 43__license__ = 'GPL3'
41 44
42 45
43class BEINCCustomHTTPSConnection(httplib.HTTPConnection):
44 """
45 This class allows communication via SSL.
46
47 It is a reimplementation of httplib.HTTPSConnection and
48 allows the server certificate to be validated against CA
49 This functionality lacks in Python < 2.7.9
50 """
51 default_port = httplib.HTTPS_PORT
52
53 def __init__(self, host, port=None, key_file=None, cert_file=None,
54 strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
55 source_address=None, custom_ssl_options={}):
56 httplib.HTTPConnection.__init__(self, host, port, strict, timeout,
57 source_address)
58 self.key_file = key_file
59 self.cert_file = cert_file
60 self.custom_ssl_options = custom_ssl_options
61
62 def connect(self):
63 "Connect to a host on a given (SSL) port."
64 sock = socket.create_connection((self.host, self.port),
65 self.timeout, self.source_address)
66 if self._tunnel_host:
67 self.sock = sock
68 self._tunnel()
69 self.sock = ssl.wrap_socket(sock,
70 self.key_file,
71 self.cert_file,
72 **self.custom_ssl_options)
73
74
75class BEINCCustomSafeTransport(xmlrpclib.Transport):
76 """
77 Provides an alternative HTTPS transport for clients running on Python<2.7.9
78 """
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 display_notification(args, title, message): 46def display_notification(args, title, message):
104 """ 47 """
105 A wrapper function for displaying a single notification 48 A wrapper function for displaying a single notification
@@ -116,7 +59,8 @@ def display_notification(args, title, message):
116 'Terminating...\n') 59 'Terminating...\n')
117 if not pynotify.init('BEINC Notify'): 60 if not pynotify.init('BEINC Notify'):
118 raise Exception('There was a problem with libnotify\n') 61 raise Exception('There was a problem with libnotify\n')
119 notification_obj = pynotify.Notification(' ') 62 notification_obj = pynotify.Notification(summary=title,
63 message=message)
120 notification_obj.timeout = 1000 * args.osd_timeout 64 notification_obj.timeout = 1000 * args.osd_timeout
121 notification_obj.set_category('im.received') 65 notification_obj.set_category('im.received')
122 notification_obj.show() 66 notification_obj.show()
@@ -133,46 +77,34 @@ def poll_notifications(scheduler, args):
133 args: the argparse processed command-line arguments 77 args: the argparse processed command-line arguments
134 """ 78 """
135 try: 79 try:
136 ssl_version = BEINC_SSL_METHODS.get(args.ssl_version, 80 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
137 ssl.PROTOCOL_SSLv23) 81 context.verify_mode = ssl.CERT_NONE
138 if sys.hexversion >= 0x20709f0: 82 if args.cert:
139 # Python >= 2.7.9 83 context.verify_mode = ssl.CERT_REQUIRED
140 context = ssl.SSLContext(ssl_version) 84 context.load_verify_locations(cafile=os.path.expanduser(args.cert))
141 context.verify_mode = ssl.CERT_NONE 85 context.check_hostname = bool(not args.disable_hostname_check)
142 if args.cert: 86 if args.ciphers:
143 context.verify_mode = ssl.CERT_REQUIRED 87 context.set_ciphers(args.ciphers)
144 context.load_verify_locations(os.path.expanduser(args.cert)) 88 response = urlopen(
145 context.check_hostname = bool(not args.disable_hostname_check) 89 args.url,
146 if args.ciphers: 90 data=urlencode(
147 context.set_ciphers(args.ciphers) 91 (
148 transport = xmlrpclib.SafeTransport(context=context) 92 ('resource_name', args.rname),
149 else: 93 ('password', args.password)
150 # Python < 2.7.9 94 )).encode('utf-8'),
151 ssl_options = {} 95 timeout=args.socket_timeout,
152 ssl_options['ssl_version'] = ssl_version 96 context=context)
153 if args.cert: 97 response_dict = json.loads(response.read().decode('utf-8'))
154 ssl_options['ca_certs'] = os.path.expanduser(args.cert) 98 if response.code != 200:
155 ssl_options['cert_reqs'] = ssl.CERT_REQUIRED 99 raise socket.error(response_dict.get('message', ''))
156 if args.ciphers: 100 for entry in response_dict['data']['messages']:
157 ssl_options['ciphers'] = args.ciphers 101 display_notification(args,
158 transport = BEINCCustomSafeTransport( 102 entry.get('title', ''),
159 custom_ssl_options=ssl_options) 103 entry.get('message', ''))
160 server = xmlrpclib.ServerProxy(args.url,
161 transport=transport)
162 res_list = server.pull(args.rname, args.password)
163 for entry in res_list:
164 title = entry.get('title', '')
165 message = entry.get('message', '')
166 display_notification(args, title, message)
167 scheduler.enter(args.frequency, 104 scheduler.enter(args.frequency,
168 1, 105 1,
169 poll_notifications, 106 poll_notifications,
170 (scheduler, args)) 107 (scheduler, args))
171 except xmlrpclib.Fault as fault:
172 sys.stderr.write(
173 'BEINC server answered with errorCode={0}: {1}\n'.format(
174 fault.faultCode,
175 fault.faultString))
176 except ssl.SSLError as e: 108 except ssl.SSLError as e:
177 sys.stderr.write('BEINC SSL/TLS error: {0}\n'.format(e)) 109 sys.stderr.write('BEINC SSL/TLS error: {0}\n'.format(e))
178 sys.exit(errno.EPERM) 110 sys.exit(errno.EPERM)
@@ -277,8 +209,6 @@ def main():
277 except Exception as e: 209 except Exception as e:
278 sys.stderr.write('Unable to open password file: {0}'.format(e)) 210 sys.stderr.write('Unable to open password file: {0}'.format(e))
279 sys.exit(1) 211 sys.exit(1)
280 if args.socket_timeout:
281 socket.setdefaulttimeout(args.socket_timeout)
282 scheduler = sched.scheduler(time.time, time.sleep) 212 scheduler = sched.scheduler(time.time, time.sleep)
283 scheduler.enter(args.frequency, 213 scheduler.enter(args.frequency,
284 1, 214 1,
diff --git a/beinc_weechat.py b/beinc_weechat.py
index a71258c..23d4db8 100644
--- a/beinc_weechat.py
+++ b/beinc_weechat.py
@@ -1,7 +1,7 @@
1# -*- coding: utf-8 -*- 1# -*- coding: utf-8 -*-
2 2
3# Blackmore's Enhanced IRC-Notification Collection (BEINC) v2.0 3# Blackmore's Enhanced IRC-Notification Collection (BEINC) v3.0
4# Copyright (C) 2013-2015 Simeon Simeonov 4# Copyright (C) 2013-2017 Simeon Simeonov
5 5
6# This program is free software: you can redistribute it and/or modify 6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by 7# it under the terms of the GNU General Public License as published by
@@ -18,19 +18,21 @@
18 18
19 19
20import datetime 20import datetime
21import httplib
22import json 21import json
23import os 22import os
24import socket 23import socket
25import ssl 24import ssl
26import sys 25import sys
27import xmlrpclib 26
27from urllib import urlencode
28from urllib2 import urlopen
29
28 30
29import weechat 31import weechat
30 32
31 33
32__author__ = 'Simeon Simeonov' 34__author__ = 'Simeon Simeonov'
33__version__ = '2.0' 35__version__ = '3.0'
34__license__ = 'GPL3' 36__license__ = 'GPL3'
35 37
36 38
@@ -43,74 +45,6 @@ BEINC_POLICY_ALL = 1
43BEINC_POLICY_LIST_ONLY = 2 45BEINC_POLICY_LIST_ONLY = 2
44BEINC_CURRENT_CONFIG_VERSION = 2 46BEINC_CURRENT_CONFIG_VERSION = 2
45 47
46BEINC_SSL_METHODS = {'TLSv1': ssl.PROTOCOL_TLSv1}
47try:
48 BEINC_SSL_METHODS.update({'TLSv1_1': ssl.PROTOCOL_TLSv1_1})
49 BEINC_SSL_METHODS.update({'TLSv1_2': ssl.PROTOCOL_TLSv1_2})
50except:
51 pass
52
53
54class BEINCCustomHTTPSConnection(httplib.HTTPConnection):
55 """
56 This class allows communication via SSL.
57
58 It is a reimplementation of httplib.HTTPSConnection and
59 allows the server certificate to be validated against CA
60 This functionality lacks in Python < 2.7.9
61 """
62
63 default_port = httplib.HTTPS_PORT
64
65 def __init__(self, host, port=None, key_file=None, cert_file=None,
66 strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
67 source_address=None, custom_ssl_options={}):
68 httplib.HTTPConnection.__init__(self, host, port, strict, timeout,
69 source_address)
70 self.key_file = key_file
71 self.cert_file = cert_file
72 self.custom_ssl_options = custom_ssl_options
73
74 def connect(self):
75 "Connect to a host on a given (SSL) port."
76 sock = socket.create_connection((self.host, self.port),
77 self.timeout, self.source_address)
78 if self._tunnel_host:
79 self.sock = sock
80 self._tunnel()
81 self.sock = ssl.wrap_socket(sock,
82 self.key_file,
83 self.cert_file,
84 **self.custom_ssl_options)
85
86
87class BEINCCustomSafeTransport(xmlrpclib.Transport):
88 """
89 Provides an alternative HTTPS transport for clients running on Python<2.7.9
90 """
91
92 def __init__(self, use_datetime=0, custom_ssl_options={}):
93 xmlrpclib.Transport.__init__(self, use_datetime=use_datetime)
94 self.custom_ssl_options = custom_ssl_options
95
96 def make_connection(self, host):
97 if self._connection and host == self._connection[0]:
98 return self._connection[1]
99 try:
100 HTTPS = BEINCCustomHTTPSConnection
101 except AttributeError:
102 raise NotImplementedError(
103 "your version of httplib doesn't support HTTPS"
104 )
105 else:
106 chost, self._extra_headers, x509 = self.get_host_info(host)
107 self._connection = host, HTTPS(
108 chost,
109 None,
110 custom_ssl_options=self.custom_ssl_options,
111 **(x509 or {}))
112 return self._connection[1]
113
114 48
115class WeechatTarget(object): 49class WeechatTarget(object):
116 """ 50 """
@@ -163,8 +97,8 @@ class WeechatTarget(object):
163 target_dict.get('disable-hostname-check', False)) 97 target_dict.get('disable-hostname-check', False))
164 self.__ssl_version = target_dict.get('ssl_version', 'auto') 98 self.__ssl_version = target_dict.get('ssl_version', 'auto')
165 self.__last_message = None # datetime.datetime instance 99 self.__last_message = None # datetime.datetime instance
166 self.__connection = None # xmlrpclib.ServerProxy instance 100 self.__context = None
167 self.__connection_setup() 101 self.__context_setup()
168 102
169 @property 103 @property
170 def name(self): 104 def name(self):
@@ -222,13 +156,6 @@ class WeechatTarget(object):
222 """ 156 """
223 self.__enabled = value 157 self.__enabled = value
224 158
225 @property
226 def connected(self):
227 """
228 The target's connection status (read-only property)
229 """
230 return bool(self.__connection)
231
232 def __repr__(self): 159 def __repr__(self):
233 """ 160 """
234 """ 161 """
@@ -343,64 +270,32 @@ class WeechatTarget(object):
343 'BEINC DEBUG: send_broadcast_notification-ERROR ' 270 'BEINC DEBUG: send_broadcast_notification-ERROR '
344 'for "{0}": {1}'.format(self.__name, e)) 271 'for "{0}": {1}'.format(self.__name, e))
345 272
346 def __connection_setup(self): 273 def __context_setup(self):
347 """ 274 """
348 """ 275 """
349 if self.__connection is not None: 276 if self.__context is not None:
350 return True 277 return True
351 if self.__debug:
352 beinc_prnt(
353 'BEINC DEBUG: Establishing proxy-connection to: {0}'.format(
354 self.__url))
355 try: 278 try:
356 ssl_version = BEINC_SSL_METHODS.get(self.__ssl_version, 279 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
357 ssl.PROTOCOL_SSLv23) 280 context.verify_mode = ssl.CERT_NONE
358 if sys.hexversion >= 0x20709f0: 281 if self.__cert_file:
359 # Python >= 2.7.9 282 context.verify_mode = ssl.CERT_REQUIRED
360 context = ssl.SSLContext(ssl_version) 283 context.load_verify_locations(cafile=os.path.expanduser(
361 context.verify_mode = ssl.CERT_NONE 284 self.__cert_file))
362 if self.__cert_file: 285 context.check_hostname = bool(
363 context.verify_mode = ssl.CERT_REQUIRED 286 not self.__disable_hostname_check)
364 context.load_verify_locations(os.path.expanduser( 287 if self.__ssl_ciphers and self.__ssl_ciphers != 'auto':
365 self.__cert_file)) 288 context.set_ciphers(self.__ssl_ciphers)
366 context.check_hostname = bool( 289 self.__context = context
367 not self.__disable_hostname_check)
368 if self.__ssl_ciphers and self.__ssl_ciphers != 'auto':
369 context.set_ciphers(self.__ssl_ciphers)
370 transport = xmlrpclib.SafeTransport(context=context)
371 else:
372 # Python < 2.7.9
373 ssl_options = {}
374 ssl_options['ssl_version'] = ssl_version
375 if self.__cert_file:
376 ssl_options['ca_certs'] = os.path.expanduser(
377 self.__cert_file)
378 ssl_options['cert_reqs'] = ssl.CERT_REQUIRED
379 if self.__ssl_ciphers and self.__ssl_ciphers != 'auto':
380 ssl_options['ciphers'] = self.__ssl_ciphers
381 transport = BEINCCustomSafeTransport(
382 custom_ssl_options=ssl_options)
383 self.__connection = xmlrpclib.ServerProxy(self.__url,
384 transport=transport)
385 return True 290 return True
386 except xmlrpclib.Fault as fault:
387 if self.__debug:
388 beinc_prnt(
389 'BEINC DEBUG: Server answered with '
390 'errorCode={0}: {1}\n'.format(
391 fault.faultCode,
392 fault.faultString))
393 except ssl.SSLError as e: 291 except ssl.SSLError as e:
394 if self.__debug: 292 if self.__debug:
395 beinc_prnt('BEINC DEBUG: SSL/TLS error: {0}\n'.format(e)) 293 beinc_prnt('BEINC DEBUG: SSL/TLS error: {0}\n'.format(e))
396 except socket.error as e:
397 if self.__debug:
398 beinc_prnt('BEINC DEBUG: Connection error: {0}\n'.format(e))
399 except Exception as e: 294 except Exception as e:
400 if self.__debug: 295 if self.__debug:
401 beinc_prnt('BEINC DEBUG: Generic client error: {0}\n'.format( 296 beinc_prnt('BEINC DEBUG: Generic context error: {0}\n'.format(
402 e)) 297 e))
403 self.__connection = None 298 self.__context = None
404 return False 299 return False
405 300
406 def __fetch_formatted_str(self, template, values): 301 def __fetch_formatted_str(self, template, values):
@@ -427,28 +322,30 @@ class WeechatTarget(object):
427 def __send_beinc_message(self, title, message): 322 def __send_beinc_message(self, title, message):
428 """ 323 """
429 the function implements the BEINC "protocol" by generating a simple 324 the function implements the BEINC "protocol" by generating a simple
430 XMLRPC request 325 POST request
431 """ 326 """
432 try: 327 try:
433 if self.__connection is None and not self.__connection_setup(): 328 if self.__context is None and not self.__context_setup():
434 return False 329 return False
435 if self.__socket_timeout: 330 response = urlopen(
436 socket.setdefaulttimeout(self.__socket_timeout) 331 self.__url,
437 result = self.__connection.push(self.__name, 332 data=urlencode(
438 self.__password, 333 (
439 title, 334 ('resource_name', self.__name),
440 message) 335 ('password', self.__password),
336 ('title', title),
337 ('message', message)
338 )),
339 timeout=self.__socket_timeout,
340 context=self.__context)
341 response_dict = json.loads(response.read())
342 if response.code != 200:
343 raise socket.error(response_dict.get('message', ''))
441 if self.__debug: 344 if self.__debug:
442 beinc_prnt('BEINC DEBUG: Server responded: {0}'.format(result)) 345 beinc_prnt('BEINC DEBUG: Server responded: {0}'.format(
346 response_dict.get('message')))
443 self.__last_message = datetime.datetime.now() 347 self.__last_message = datetime.datetime.now()
444 return True 348 return True
445 except xmlrpclib.Fault as fault:
446 if self.__debug:
447 beinc_prnt(
448 'BEINC DEBUG: Server answered with '
449 'errorCode={0}: {1}\n'.format(
450 fault.faultCode,
451 fault.faultString))
452 except ssl.SSLError as e: 349 except ssl.SSLError as e:
453 if self.__debug: 350 if self.__debug:
454 beinc_prnt('BEINC DEBUG: SSL/TLS error: {0}\n'.format(e)) 351 beinc_prnt('BEINC DEBUG: SSL/TLS error: {0}\n'.format(e))
@@ -459,10 +356,6 @@ class WeechatTarget(object):
459 if self.__debug: 356 if self.__debug:
460 beinc_prnt('BEINC DEBUG: Unable to send message: {0}\n'.format( 357 beinc_prnt('BEINC DEBUG: Unable to send message: {0}\n'.format(
461 e)) 358 e))
462 finally:
463 if self.__socket_timeout:
464 socket.setdefaulttimeout(None)
465 self.__connection = None
466 return False 359 return False
467 360
468 361