diff options
Diffstat (limited to 'beinc_server.py')
| -rwxr-xr-x | beinc_server.py | 148 |
1 files changed, 77 insertions, 71 deletions
diff --git a/beinc_server.py b/beinc_server.py index 8687da3..d4dd3c3 100755 --- a/beinc_server.py +++ b/beinc_server.py | |||
| @@ -1,18 +1,42 @@ | |||
| 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 | ||
| 5 | # Copyright (C) 2013-2014 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 | |||
| 4 | import argparse | 21 | import argparse |
| 22 | import errno | ||
| 5 | import getpass | 23 | import getpass |
| 6 | import json | 24 | import json |
| 7 | import os | 25 | import os |
| 26 | import random | ||
| 8 | import sys | 27 | import sys |
| 9 | 28 | ||
| 10 | import cherrypy | 29 | import cherrypy |
| 11 | 30 | ||
| 31 | try: | ||
| 32 | import pynotify | ||
| 33 | except ImportError as e: | ||
| 34 | pynotify = None | ||
| 35 | |||
| 12 | 36 | ||
| 13 | __author__ = 'Simeon Simeonov' | 37 | __author__ = 'Simeon Simeonov' |
| 14 | __version__ = '1.0-beta' | 38 | __version__ = '1.0' |
| 15 | __license__ = "GPL3" | 39 | __license__ = 'GPL3' |
| 16 | 40 | ||
| 17 | 41 | ||
| 18 | BEINC_OSD_TYPE_NONE = 0 | 42 | BEINC_OSD_TYPE_NONE = 0 |
| @@ -33,40 +57,35 @@ class BEINCInstance(object): | |||
| 33 | self.__osd_type = BEINC_OSD_TYPE_NONE | 57 | self.__osd_type = BEINC_OSD_TYPE_NONE |
| 34 | self.__osd_notification = None | 58 | self.__osd_notification = None |
| 35 | 59 | ||
| 36 | try: | 60 | self.__name = instance_dict.get('name') |
| 37 | self.__name = instance_dict['name'] | 61 | self.__password = instance_dict.get('password', '') |
| 38 | self.__password = instance_dict['password'] | 62 | self.__queue_size = int(instance_dict.get('queue_size', 3)) |
| 39 | self.__queue_size = int(instance_dict['queue_size']) | ||
| 40 | |||
| 41 | except Exception as e: | ||
| 42 | sys.stderr.write( | ||
| 43 | 'Instance processing error {0}:\n{1}\n'.format(self.__name, | ||
| 44 | e)) | ||
| 45 | sys.exit(1) | ||
| 46 | 63 | ||
| 47 | if instance_dict['osd_system'].lower() == 'pynotify': | 64 | if instance_dict['osd_system'].lower() == 'pynotify': |
| 65 | self.__queue_size = 0 # disable queueing | ||
| 48 | if not pynotify: | 66 | if not pynotify: |
| 49 | sys.stderr.write( | 67 | sys.stderr.write( |
| 50 | 'This server does not possess pynotify capability\n') | 68 | 'This server does not possess pynotify capability\n') |
| 51 | sys.stderr.write( | 69 | sys.stderr.write( |
| 52 | 'Remove the instance {0}'.format(self.__name)) | 70 | 'Remove the instance {0}'.format(self.__name)) |
| 53 | sys.stderr.write("or define it with 'osd_system': 'none'\n") | 71 | sys.stderr.write("or define it with 'osd_system': 'none'\n") |
| 54 | sys.exit(1) | 72 | sys.exit(errno.EPERM) |
| 55 | 73 | ||
| 56 | try: | 74 | try: |
| 57 | self.__osd_notification = pynotify.Notification(' ') | 75 | self.__osd_notification = pynotify.Notification(' ') |
| 58 | self.__osd_notification.set_timeout( | 76 | self.__osd_notification.set_timeout( |
| 59 | instance_dict['osd_timeout']) | 77 | int(instance_dict.get('osd_timeout', 5000))) |
| 60 | self.__osd_notification.set_property( | 78 | self.__osd_notification.set_property( |
| 61 | 'app_name', | 79 | 'app_name', |
| 62 | '{0} {1}'.format(sys.argv[0], __version__)) | 80 | '{0} {1}'.format(sys.argv[0], __version__)) |
| 63 | except Exception as e: | 81 | except Exception as e: |
| 64 | sys.stderr.write( | 82 | sys.stderr.write( |
| 65 | 'Unable to set up a notification object for {0} ({1})\n') | 83 | 'Unable to set up a notification object for {0} ({1})\n') |
| 66 | sys.exit(1) | 84 | sys.exit(errno.EPERM) |
| 67 | 85 | ||
| 68 | self.__osd_type = BEINC_OSD_TYPE_PYNOTIFY | 86 | self.__osd_type = BEINC_OSD_TYPE_PYNOTIFY |
| 69 | 87 | ||
| 88 | |||
| 70 | @property | 89 | @property |
| 71 | def name(self): | 90 | def name(self): |
| 72 | """ | 91 | """ |
| @@ -129,34 +148,25 @@ def beinc_instance_login(method): | |||
| 129 | """ | 148 | """ |
| 130 | decorator for checking login credentials | 149 | decorator for checking login credentials |
| 131 | """ | 150 | """ |
| 132 | from functools import wraps | ||
| 133 | 151 | ||
| 134 | @wraps(method) | 152 | def wrapper(self, *args, **kwargs): |
| 135 | def tmp_func(self, *args, **kwargs): | ||
| 136 | 153 | ||
| 137 | if not args: | 154 | if not args: |
| 138 | raise cherrypy.HTTPError(400) | 155 | raise cherrypy.HTTPError(status = 404) |
| 139 | |||
| 140 | print('args: {0}'.format(args)) | ||
| 141 | print('kwargs: {0}'.format(kwargs)) | ||
| 142 | print(cherrypy.request.config) | ||
| 143 | print('args[0]: {0} ({1})'.format(args[0], str(type(args[0])))) | ||
| 144 | print('instances: {0}'.format(str(self.__instances))) | ||
| 145 | 156 | ||
| 146 | try: | 157 | try: |
| 147 | instance = self.__instances[args[0]] | 158 | instance = self.instances[args[0]] |
| 148 | except Exception as e: | 159 | except Exception as e: |
| 149 | sys.stderr.write('Wrong instance or password: {0}\n'.format(e)) | 160 | raise cherrypy.HTTPError(status = 401, |
| 150 | raise cherrypy.HTTPError('403 Forbidden', | 161 | message = 'Wrong instance or password') |
| 151 | 'Wrong instance or password') | ||
| 152 | 162 | ||
| 153 | if not instance.password_match(kwargs.get('password')): | 163 | if not instance.password_match(kwargs.get('password')): |
| 154 | raise cherrypy.HTTPError('403 Forbidden', | 164 | raise cherrypy.HTTPError(status = 401, |
| 155 | 'Wrong instance or password') | 165 | message = 'Wrong instance or password') |
| 156 | 166 | ||
| 157 | return method(self, *args, **kwargs) | 167 | return method(self, *args, **kwargs) |
| 158 | 168 | ||
| 159 | return tmp_func | 169 | return wrapper |
| 160 | 170 | ||
| 161 | 171 | ||
| 162 | class WebNotifyServer(object): | 172 | class WebNotifyServer(object): |
| @@ -167,15 +177,32 @@ class WebNotifyServer(object): | |||
| 167 | self.__config = config | 177 | self.__config = config |
| 168 | self.__instances = dict() | 178 | self.__instances = dict() |
| 169 | 179 | ||
| 180 | # initialize pynotify if the module exists and if needed | ||
| 181 | if pynotify: | ||
| 182 | for instance in self.__config['server']['instances']: | ||
| 183 | # check if we have at least one instance that uses pynotify | ||
| 184 | # before initializing it | ||
| 185 | if instance.get('osd_system', '').lower() == 'pynotify': | ||
| 186 | if not pynotify.init('BEINC Notify'): | ||
| 187 | sys.stderr.write('pynotify.init failed! Exiting...\n') | ||
| 188 | sys.exit(1) | ||
| 189 | break | ||
| 170 | try: | 190 | try: |
| 171 | for instance in self.__config['server']['instances']: | 191 | for instance in self.__config['server']['instances']: |
| 172 | self.__instances[instance['name']] = BEINCInstance(instance) | 192 | self.__instances[instance['name']] = BEINCInstance(instance) |
| 173 | print('Instance "{0}" added'.format(instance['name'])) | 193 | print('Instance "{0}" added'.format(instance['name'])) |
| 174 | 194 | ||
| 175 | except Exception as e: | 195 | except Exception as e: |
| 176 | sys.stderr.write('Unable to initialize queues: {0}\n'.format(e)) | 196 | sys.stderr.write('Unable to create instance "{0}": {1}\n'.format( |
| 197 | instance['name'], | ||
| 198 | e)) | ||
| 177 | sys.exit(1) | 199 | sys.exit(1) |
| 178 | 200 | ||
| 201 | |||
| 202 | @property | ||
| 203 | def instances(self): | ||
| 204 | return self.__instances | ||
| 205 | |||
| 179 | @cherrypy.expose | 206 | @cherrypy.expose |
| 180 | def index(self): | 207 | def index(self): |
| 181 | """ | 208 | """ |
| @@ -190,33 +217,14 @@ class WebNotifyServer(object): | |||
| 190 | """ | 217 | """ |
| 191 | return 'default' | 218 | return 'default' |
| 192 | 219 | ||
| 220 | |||
| 193 | @cherrypy.expose | 221 | @cherrypy.expose |
| 222 | @beinc_instance_login | ||
| 194 | def push(self, *args, **kwargs): | 223 | def push(self, *args, **kwargs): |
| 195 | print('push called') | ||
| 196 | |||
| 197 | if not args: | ||
| 198 | raise cherrypy.HTTPError(400) | ||
| 199 | |||
| 200 | # print('args: {0}'.format(args)) | ||
| 201 | # print('kwargs: {0}'.format(unicode(kwargs))) | ||
| 202 | # print(cherrypy.request.config) | ||
| 203 | # print('args[0]: {0} ({1})'.format(args[0], str(type(args[0])))) | ||
| 204 | # print('instances: {0}'.format(str(self.__instances))) | ||
| 205 | |||
| 206 | try: | ||
| 207 | instance = self.__instances[args[0]] | ||
| 208 | except Exception as e: | ||
| 209 | sys.stderr.write('Wrong instance or password\n') | ||
| 210 | #print('DEBUG: {0}'.format(e)) | ||
| 211 | raise cherrypy.HTTPError('403 Forbidden', | ||
| 212 | 'Wrong instance or password') | ||
| 213 | 224 | ||
| 214 | if not instance.password_match(kwargs.get('password')): | 225 | instance = self.__instances[args[0]] |
| 215 | sys.stderr.write('Wrong instance or password\n') | ||
| 216 | raise cherrypy.HTTPError('403 Forbidden', | ||
| 217 | 'Wrong instance or password') | ||
| 218 | 226 | ||
| 219 | # instance = self.__instances[args[0]] | 227 | print('**kwargs: {0}'.format(str(kwargs))) |
| 220 | title = kwargs.get('title', '') | 228 | title = kwargs.get('title', '') |
| 221 | message = kwargs.get('message', '') | 229 | message = kwargs.get('message', '') |
| 222 | try: | 230 | try: |
| @@ -229,12 +237,19 @@ class WebNotifyServer(object): | |||
| 229 | e)) | 237 | e)) |
| 230 | raise cherrypy.HTTPError(500, 'Unable to send message') | 238 | raise cherrypy.HTTPError(500, 'Unable to send message') |
| 231 | 239 | ||
| 240 | |||
| 232 | @cherrypy.expose | 241 | @cherrypy.expose |
| 233 | @beinc_instance_login | 242 | @beinc_instance_login |
| 234 | def pull(self, *args, **kwargs): | 243 | def pull(self, *args, **kwargs): |
| 235 | 244 | ||
| 236 | instance = self.__instances(args[0]) | 245 | instance = self.__instances[args[0]] |
| 237 | return 'OK' | 246 | if not instance.queueable: |
| 247 | raise cherrypy.HTTPError( | ||
| 248 | status = 405, | ||
| 249 | message = 'BEINC instance "{0}" does not support queuing'.format( | ||
| 250 | instance.name)) | ||
| 251 | |||
| 252 | return instance.get_queue() | ||
| 238 | 253 | ||
| 239 | 254 | ||
| 240 | def main(): | 255 | def main(): |
| @@ -283,7 +298,7 @@ def main(): | |||
| 283 | except Exception as e: | 298 | except Exception as e: |
| 284 | sys.stderr.write('Unable to parse {0}: {1}'.format(args.config_file, | 299 | sys.stderr.write('Unable to parse {0}: {1}'.format(args.config_file, |
| 285 | e)) | 300 | e)) |
| 286 | sys.exit(1) | 301 | sys.exit(errno.EIO) |
| 287 | 302 | ||
| 288 | cherrypy.config.update({ | 303 | cherrypy.config.update({ |
| 289 | 'server.socket_host': args.hostname, | 304 | 'server.socket_host': args.hostname, |
| @@ -292,20 +307,11 @@ def main(): | |||
| 292 | 'server.ssl_certificate': config_dict['server']['general']['ssl_certificate'], | 307 | 'server.ssl_certificate': config_dict['server']['general']['ssl_certificate'], |
| 293 | 'server.ssl_private_key': config_dict['server']['general']['ssl_private_key'], | 308 | 'server.ssl_private_key': config_dict['server']['general']['ssl_private_key'], |
| 294 | 'tools.encode.on': True, | 309 | 'tools.encode.on': True, |
| 295 | 'tools.encode.encoding': 'utf-8' | 310 | 'tools.encode.encoding': 'utf-8', |
| 311 | 'tools.log_tracebacks.on': False, | ||
| 312 | 'request.show_tracebacks': False | ||
| 296 | }) | 313 | }) |
| 297 | 314 | ||
| 298 | global pynotify | ||
| 299 | try: | ||
| 300 | import pynotify | ||
| 301 | if not pynotify.init('BEINC Notify'): | ||
| 302 | sys.stderr.write('pynotify.init failed! Exiting...\n') | ||
| 303 | sys.exit(1) | ||
| 304 | except Exception as e: | ||
| 305 | sys.stderr.write( | ||
| 306 | 'Notice: pynotify support unavailable ({0})\n'.format(e)) | ||
| 307 | pynotify = False | ||
| 308 | |||
| 309 | try: | 315 | try: |
| 310 | cherrypy.quickstart(WebNotifyServer(config_dict)) | 316 | cherrypy.quickstart(WebNotifyServer(config_dict)) |
| 311 | 317 | ||
