summaryrefslogtreecommitdiff
path: root/beinc_weechat.py
diff options
context:
space:
mode:
authorSimeon Simeonov2017-05-12 13:25:43 +0200
committerSimeon Simeonov2017-05-12 13:25:43 +0200
commitdf917f55d6fbbe899942c5c4947444702b0b3fcd (patch)
tree670dac84d222ef0f263f8e2612fc536bc0eac5b3 /beinc_weechat.py
parent1336daa7a6383bc4ed723c914fa5ee8b4e967b26 (diff)
Make beinc_weechat.py BEINC 3 compatible
Diffstat (limited to 'beinc_weechat.py')
-rw-r--r--beinc_weechat.py191
1 files changed, 42 insertions, 149 deletions
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