1
|
|
|
#!/usr/bin/env python |
2
|
|
|
# -*- coding: utf-8 -*- |
3
|
|
|
|
4
|
|
|
import logging |
5
|
|
|
logger = logging.getLogger(__name__) |
6
|
|
|
logger.debug("%s loaded", __name__) |
7
|
|
|
|
8
|
|
|
from BaseHTTPServer import HTTPServer |
9
|
|
|
from SocketServer import ThreadingMixIn |
10
|
|
|
|
11
|
|
|
from random import randrange |
|
|
|
|
12
|
|
|
|
13
|
|
|
import doorpi |
14
|
|
|
from doorpi.action.base import SingleAction |
15
|
|
|
|
16
|
|
|
from doorpi.status.webserver_lib.session_handler import SessionHandler |
17
|
|
|
from doorpi.status.webserver_lib.request_handler import DoorPiWebRequestHandler |
18
|
|
|
|
19
|
|
|
class WebServerStartupAction(SingleAction): pass |
20
|
|
|
class WebServerFakeRequestAction(SingleAction): pass |
21
|
|
|
class WebServerShutdownAction(SingleAction): pass |
22
|
|
|
class WebServerInformUrl(SingleAction): pass |
23
|
|
|
|
24
|
|
|
DOORPIWEB_SECTION = 'DoorPiWeb' |
25
|
|
|
CONF_AREA_PREFIX = 'AREA_' |
26
|
|
|
|
27
|
|
|
def load_webserver(): |
28
|
|
|
ip = doorpi.DoorPi().config.get(DOORPIWEB_SECTION, 'ip', '') |
29
|
|
|
port = doorpi.DoorPi().config.get_int(DOORPIWEB_SECTION, 'port', 80) |
30
|
|
|
|
31
|
|
|
doorpiweb_object = False |
32
|
|
|
|
33
|
|
|
possible_ports = [port, 80, 8080, 0] |
34
|
|
|
for single_port in possible_ports: |
35
|
|
|
try: |
36
|
|
|
server_address = (ip, single_port) |
37
|
|
|
doorpiweb_object = DoorPiWeb(server_address, DoorPiWebRequestHandler) |
38
|
|
|
logger.info('Initiating WebService at ip %s and port %s', ip, single_port) |
39
|
|
|
doorpiweb_object.start() |
40
|
|
|
if single_port is not port: |
41
|
|
|
doorpi.DoorPi().event_handler.register_action('OnTimeSecondEvenNumber', doorpiweb_object.inform_own_url) |
42
|
|
|
return doorpiweb_object |
43
|
|
|
except Exception as exp: |
44
|
|
|
logger.warning('failed to initiating WebService at ip %s and port %s (%s)', ip, single_port, exp) |
45
|
|
|
|
46
|
|
|
return doorpiweb_object |
47
|
|
|
|
48
|
|
|
def check_config(config): |
49
|
|
|
errors = [] |
50
|
|
|
warnings = [] |
51
|
|
|
infos = [] |
52
|
|
|
|
53
|
|
|
groups_with_write_permissions = config.get_keys('WritePermission') |
54
|
|
|
groups_with_read_permissions = config.get_keys('ReadPermission') |
55
|
|
|
groups = config.get_keys('Group') |
56
|
|
|
users = config.get_keys('User') |
57
|
|
|
|
58
|
|
|
if len(groups) == 0: |
59
|
|
|
errors.append('no groups in configfile!') |
60
|
|
|
|
61
|
|
|
if len(groups_with_write_permissions) == 0: |
62
|
|
|
errors.append("no WritePermission found") |
63
|
|
|
|
64
|
|
|
for group in groups_with_write_permissions: |
65
|
|
|
if group not in groups: warnings.append("group %s doesn't exist but is assigned to WritePermission" % group) |
66
|
|
|
|
67
|
|
|
if len(groups_with_read_permissions) == 0: |
68
|
|
|
warnings.append("no ReadPermission found") |
69
|
|
|
|
70
|
|
|
for group in groups_with_read_permissions: |
71
|
|
|
if group not in groups: warnings.append("group %s doesn't exist but is assigned to ReadPermission" % group) |
72
|
|
|
|
73
|
|
|
for group in groups: |
74
|
|
|
users_in_group = config.get_list('Group', group) |
75
|
|
|
for user_in_group in users_in_group: |
76
|
|
|
if user_in_group not in users: |
77
|
|
|
warnings.append("user %s is assigned to group %s but doesn't exist as user" % (user_in_group, group)) |
78
|
|
|
|
79
|
|
|
config_section = config.get_sections() |
80
|
|
|
|
81
|
|
|
for group in groups_with_write_permissions: |
82
|
|
|
modules = config.get_list('WritePermission', group) |
83
|
|
|
for module in modules: |
84
|
|
|
if CONF_AREA_PREFIX+module not in config_section: |
85
|
|
|
warnings.append("module %s doesn't exist but is assigned to group %s in WritePermission" % (module, group)) |
86
|
|
|
|
87
|
|
|
for group in groups_with_read_permissions: |
88
|
|
|
modules = config.get_list('ReadPermission', group) |
89
|
|
|
for module in modules: |
90
|
|
|
if CONF_AREA_PREFIX+module not in config_section: |
91
|
|
|
warnings.append("module %s doesn't exist but is assigned to group %s in ReadPermission" % (module, group)) |
92
|
|
|
|
93
|
|
|
for info in infos: logger.info(info) |
94
|
|
|
for warning in warnings: logger.error(warning) |
95
|
|
|
for error in errors: logger.error(error) |
96
|
|
|
|
97
|
|
|
return {'infos': infos, 'warnings': warnings, 'errors': errors} |
98
|
|
|
|
99
|
|
|
class DoorPiWeb(ThreadingMixIn, HTTPServer): |
100
|
|
|
keep_running = True |
101
|
|
|
|
102
|
|
|
www = None |
103
|
|
|
indexfile = None |
104
|
|
|
loginfile = None |
105
|
|
|
base_url = None |
106
|
|
|
area_public_name = None |
107
|
|
|
online_fallback = None |
108
|
|
|
|
109
|
|
|
@property |
110
|
|
|
def config_status(self): return check_config(self.config) |
111
|
|
|
|
112
|
|
|
@property |
113
|
|
|
def own_url(self): |
114
|
|
|
if self.server_port is 80: |
115
|
|
|
return "http://%s/"%self.server_name |
116
|
|
|
else: |
117
|
|
|
return "http://%s:%s/"%(self.server_name, self.server_port) |
118
|
|
|
|
119
|
|
|
def inform_own_url(self): logger.info('DoorPiWeb URL is %s', self.own_url) |
120
|
|
|
|
121
|
|
|
@property |
122
|
|
|
def sessions(self): |
123
|
|
|
if not self._session_handler and self.keep_running: |
124
|
|
|
logger.debug('no session handler - creating it now') |
125
|
|
|
self._session_handler = SessionHandler() |
126
|
|
|
return self._session_handler |
127
|
|
|
_session_handler = None |
128
|
|
|
|
129
|
|
|
@property |
130
|
|
|
def config(self): return doorpi.DoorPi().config |
131
|
|
|
|
132
|
|
|
def start(self): |
133
|
|
|
doorpi.DoorPi().event_handler.register_event('OnWebServerStart', __name__) |
134
|
|
|
doorpi.DoorPi().event_handler.register_event('OnWebServerStop', __name__) |
135
|
|
|
|
136
|
|
|
self.www = doorpi.DoorPi().config.get_string_parsed(DOORPIWEB_SECTION, 'www', '!BASEPATH!/../DoorPiWeb') |
137
|
|
|
self.indexfile = doorpi.DoorPi().config.get_string_parsed(DOORPIWEB_SECTION, 'indexfile', 'index.html') |
138
|
|
|
self.loginfile = doorpi.DoorPi().config.get_string_parsed(DOORPIWEB_SECTION, 'loginfile', 'login.html') |
139
|
|
|
self.area_public_name = doorpi.DoorPi().config.get_string_parsed(DOORPIWEB_SECTION, 'public', 'AREA_public') |
140
|
|
|
# https://raw.githubusercontent.com/motom001/DoorPiWeb/master/ or http://motom001.github.io/DoorPiWeb/ |
141
|
|
|
self.online_fallback = doorpi.DoorPi().config.get_string_parsed(DOORPIWEB_SECTION, 'online_fallback', 'http://motom001.github.io/DoorPiWeb') |
142
|
|
|
check_config(self.config) |
143
|
|
|
|
144
|
|
|
doorpi.DoorPi().event_handler.register_action('OnWebServerStart', WebServerStartupAction(self.handle_while_not_shutdown)) |
145
|
|
|
doorpi.DoorPi().event_handler.register_action('OnShutdown', WebServerShutdownAction(self.init_shutdown)) |
146
|
|
|
doorpi.DoorPi().event_handler('OnWebServerStart', __name__) |
147
|
|
|
|
148
|
|
|
DoorPiWebRequestHandler.prepare() |
149
|
|
|
return self |
150
|
|
|
|
151
|
|
|
def handle_while_not_shutdown(self): |
152
|
|
|
while self.keep_running: self.handle_request() |
153
|
|
|
|
154
|
|
|
def fake_request(self): |
155
|
|
|
try: |
156
|
|
|
from urllib2 import urlopen as fake_request |
157
|
|
|
fake_request("http://%s:%s/"%(self.server_name, self.server_port), timeout = 0) |
158
|
|
|
except: pass |
159
|
|
|
|
160
|
|
|
def init_shutdown(self): |
161
|
|
|
doorpi.DoorPi().event_handler('OnWebServerStop', __name__) |
162
|
|
|
self.keep_running = False |
163
|
|
|
if self.sessions: self.sessions.destroy() |
164
|
|
|
DoorPiWebRequestHandler.destroy() |
165
|
|
|
self.fake_request() |
166
|
|
|
doorpi.DoorPi().event_handler.unregister_source(__name__, True) |
167
|
|
|
|