Status: | Superseded |
---|---|
Proposed branch: | lp:~raoul-snyman/openlp/ssl |
Merge into: | lp:openlp |
Diff against target: |
658 lines (+302/-96) 3 files modified
openlp/core/utils/__init__.py (+7/-3) openlp/plugins/remotes/lib/httpserver.py (+164/-43) openlp/plugins/remotes/lib/remotetab.py (+131/-50) |
To merge this branch: | bzr merge lp:~raoul-snyman/openlp/ssl |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Bentley | Pending | ||
Review via email: mp+106535@code.launchpad.net |
This proposal supersedes a proposal from 2012-05-18.
Commit message
Description of the change
Implement an HTTPS server for the web remote and the Android remote.
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
The code should not generate a certificate. You must generate your own (we'll put the generation of certs into installers and/or build scripts).
Here's how to generate a self-signed certificate:
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Thanks but till a bug as it should not crash if no certificate.
Jonathan Corwin (j-corwin) wrote : Posted in a previous version of this proposal | # |
It shouldn't crash if no certificate exists though, either disable the service and also prevent SSL being turned on in the first place if no certificate exists
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Generated certificate and key file Ok.
Now Stage view gives me the web page source
The normal page gives my the html but no css or js
Raoul Snyman (raoul-snyman) wrote : | # |
The missing-cert-crash "bug" has not been looked at yet from a code perspective. We will generate the cert when the package is installed (or when built, in the case of the OS X and Windows builds).
Raoul Snyman (raoul-snyman) wrote : | # |
i.e. I'll still look at it, this is just a sanity-check for the other stuff.
- 1964. By Raoul Snyman
-
Check to see if there is an SSL certificate available, and if not disable the HTTPS server.
Unmerged revisions
- 1964. By Raoul Snyman
-
Check to see if there is an SSL certificate available, and if not disable the HTTPS server.
- 1963. By Raoul Snyman
-
HTTPS server is now optional.
Both HTTP and HTTPS servers are restarted if the configuration changes. - 1962. By Raoul Snyman
-
HEAD
- 1961. By Raoul Snyman
-
Add the entry on the settings tab for the HTTPS server.
- 1960. By Raoul Snyman
-
Fixed an unnecessary import.
Downgraded the log.error in sslErrors to log.warn. - 1959. By Raoul Snyman
-
Added a new "SharedData" enumeration for /usr/share/openlp on Linux/BSD, defaults to the same place as the LanguageDir on Windows and OS X.
Added fetching an SSL certificate from the aforementioned "SharedData" enumeration.
Some code didn't need to be there.
Some code needed to be tidied up. - 1958. By Raoul Snyman
-
Last fix to make HTTPS work. YAHOO!
- 1957. By Raoul Snyman
-
Trying various things to get the SSL socket to actually return data.
- 1956. By Raoul Snyman
-
Implementing an HTTPS server. So close and yet so far.
Preview Diff
1 | === modified file 'openlp/core/utils/__init__.py' |
2 | --- openlp/core/utils/__init__.py 2012-05-01 12:58:22 +0000 |
3 | +++ openlp/core/utils/__init__.py 2012-05-20 16:30:25 +0000 |
4 | @@ -88,6 +88,7 @@ |
5 | VersionDir = 5 |
6 | CacheDir = 6 |
7 | LanguageDir = 7 |
8 | + SharedData = 8 |
9 | |
10 | # Base path where data/config/cache dir is located |
11 | BaseDir = None |
12 | @@ -151,7 +152,8 @@ |
13 | if dir_type == AppLocation.DataDir: |
14 | return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), |
15 | u'openlp', u'data') |
16 | - elif dir_type == AppLocation.LanguageDir: |
17 | + elif dir_type == AppLocation.LanguageDir or \ |
18 | + dir_type == AppLocation.SharedData: |
19 | return os.path.split(openlp.__file__)[0] |
20 | return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), |
21 | u'openlp') |
22 | @@ -159,12 +161,14 @@ |
23 | if dir_type == AppLocation.DataDir: |
24 | return os.path.join(unicode(os.getenv(u'HOME'), encoding), |
25 | u'Library', u'Application Support', u'openlp', u'Data') |
26 | - elif dir_type == AppLocation.LanguageDir: |
27 | + elif dir_type == AppLocation.LanguageDir or \ |
28 | + dir_type == AppLocation.SharedData: |
29 | return os.path.split(openlp.__file__)[0] |
30 | return os.path.join(unicode(os.getenv(u'HOME'), encoding), |
31 | u'Library', u'Application Support', u'openlp') |
32 | else: |
33 | - if dir_type == AppLocation.LanguageDir: |
34 | + if dir_type == AppLocation.LanguageDir or \ |
35 | + dir_type == AppLocation.SharedData: |
36 | return os.path.join(u'/usr', u'share', u'openlp') |
37 | if XDG_BASE_AVAILABLE: |
38 | if dir_type == AppLocation.ConfigDir: |
39 | |
40 | === modified file 'openlp/plugins/remotes/lib/httpserver.py' |
41 | --- openlp/plugins/remotes/lib/httpserver.py 2012-04-20 19:36:10 +0000 |
42 | +++ openlp/plugins/remotes/lib/httpserver.py 2012-05-20 16:30:25 +0000 |
43 | @@ -27,8 +27,8 @@ |
44 | |
45 | """ |
46 | The :mod:`http` module contains the API web server. This is a lightweight web |
47 | -server used by remotes to interact with OpenLP. It uses JSON to communicate with |
48 | -the remotes. |
49 | +server used by remotes to interact with OpenLP. It uses JSON to communicate |
50 | +with the remotes. |
51 | |
52 | *Routes:* |
53 | |
54 | @@ -126,6 +126,7 @@ |
55 | |
56 | log = logging.getLogger(__name__) |
57 | |
58 | + |
59 | class HttpResponse(object): |
60 | """ |
61 | A simple object to encapsulate a pseudo-http response. |
62 | @@ -144,14 +145,53 @@ |
63 | self.code = code |
64 | |
65 | |
66 | -class HttpServer(object): |
67 | +class SslServer(QtNetwork.QTcpServer): |
68 | + """ |
69 | + SslServer is a class that implements an HTTPS server. |
70 | + """ |
71 | + sslCertificate = None |
72 | + sslPrivateKey = None |
73 | + connections = [] |
74 | + |
75 | + def incomingConnection(self, socket_descriptor): |
76 | + """ |
77 | + This method overrides the default one in :method:`incomingConnection` |
78 | + to provide the SSL socket support needed for HTTPS. |
79 | + """ |
80 | + log.debug(u'Incoming HTTPS connection') |
81 | + cert_path = AppLocation.get_directory(AppLocation.SharedData) |
82 | + if not SslServer.sslCertificate: |
83 | + ssl_cert_data = QtCore.QByteArray( |
84 | + open(os.path.join(cert_path, u'openlp.crt'), u'rb').read()) |
85 | + SslServer.sslCertificate = QtNetwork.QSslCertificate(ssl_cert_data) |
86 | + if not SslServer.sslPrivateKey: |
87 | + ssl_key_data = QtCore.QByteArray( |
88 | + open(os.path.join(cert_path, u'openlp.key'), u'rb').read()) |
89 | + SslServer.sslPrivateKey = QtNetwork.QSslKey(ssl_key_data, |
90 | + QtNetwork.QSsl.Rsa) |
91 | + server_socket = QtNetwork.QSslSocket() |
92 | + if server_socket.setSocketDescriptor(socket_descriptor): |
93 | + server_socket.setPrivateKey(SslServer.sslPrivateKey) |
94 | + server_socket.setLocalCertificate(SslServer.sslCertificate) |
95 | + server_socket.setPeerVerifyMode(QtNetwork.QSslSocket.VerifyNone) |
96 | + server_socket.startServerEncryption() |
97 | + self.connections.append(server_socket) |
98 | + self.addPendingConnection(server_socket) |
99 | + |
100 | + |
101 | +class HttpServer(QtCore.QObject): |
102 | """ |
103 | Ability to control OpenLP via a web browser. |
104 | """ |
105 | + |
106 | + http_server = None |
107 | + https_server = None |
108 | + |
109 | def __init__(self, plugin): |
110 | """ |
111 | Initialise the httpserver, and start the server. |
112 | """ |
113 | + QtCore.QObject.__init__(self) |
114 | log.debug(u'Initialise httpserver') |
115 | self.plugin = plugin |
116 | self.html_dir = os.path.join( |
117 | @@ -160,33 +200,81 @@ |
118 | self.connections = [] |
119 | self.current_item = None |
120 | self.current_slide = None |
121 | - self.start_tcp() |
122 | - |
123 | - def start_tcp(self): |
124 | - """ |
125 | - Start the http server, use the port in the settings default to 4316. |
126 | - Listen out for slide and song changes so they can be broadcast to |
127 | - clients. Listen out for socket connections. |
128 | - """ |
129 | - log.debug(u'Start TCP server') |
130 | - port = QtCore.QSettings().value( |
131 | - self.plugin.settingsSection + u'/port', |
132 | - QtCore.QVariant(4316)).toInt()[0] |
133 | - address = QtCore.QSettings().value( |
134 | - self.plugin.settingsSection + u'/ip address', |
135 | - QtCore.QVariant(u'0.0.0.0')).toString() |
136 | - self.server = QtNetwork.QTcpServer() |
137 | - self.server.listen(QtNetwork.QHostAddress(address), port) |
138 | QtCore.QObject.connect(Receiver.get_receiver(), |
139 | QtCore.SIGNAL(u'slidecontroller_live_changed'), |
140 | self.slide_change) |
141 | QtCore.QObject.connect(Receiver.get_receiver(), |
142 | QtCore.SIGNAL(u'slidecontroller_live_started'), |
143 | self.item_change) |
144 | - QtCore.QObject.connect(self.server, |
145 | + QtCore.QObject.connect(Receiver.get_receiver(), |
146 | + QtCore.SIGNAL(u'remotes_config_updated'), |
147 | + self.update_servers) |
148 | + self.start_tcp() |
149 | + if QtCore.QSettings().value(self.plugin.settingsSection + \ |
150 | + u'/https enabled', QtCore.QVariant(False)).toBool(): |
151 | + self.start_ssl() |
152 | + |
153 | + def update_servers(self): |
154 | + """ |
155 | + Restart the server(s) if the configuration changes. |
156 | + """ |
157 | + log.debug(u'Config changed, updating servers') |
158 | + https_enabled = QtCore.QSettings().value( |
159 | + self.plugin.settingsSection + u'/https enabled', |
160 | + QtCore.QVariant(False)).toBool() |
161 | + # Restart the HTTP server |
162 | + self.http_server.close() |
163 | + self.http_server = None |
164 | + self.start_tcp() |
165 | + # Check the status of the HTTPS server and restart it |
166 | + if not self.https_server and https_enabled: |
167 | + self.start_ssl() |
168 | + elif https_enabled and self.https_server: |
169 | + self.https_server.close() |
170 | + self.https_server = None |
171 | + self.start_ssl() |
172 | + elif self.https_server and not https_enabled: |
173 | + self.https_server.close() |
174 | + self.https_server = None |
175 | + |
176 | + def start_tcp(self): |
177 | + """ |
178 | + Start the HTTP server, use the port in the settings default to 4316. |
179 | + Listen out for slide and song changes so they can be broadcast to |
180 | + clients. Listen out for socket connections. |
181 | + """ |
182 | + log.debug(u'Start TCP server') |
183 | + settings = QtCore.QSettings() |
184 | + settings.beginGroup(self.plugin.settingsSection) |
185 | + port = settings.value(u'port', QtCore.QVariant(4316)).toInt()[0] |
186 | + address = settings.value( |
187 | + u'ip address', QtCore.QVariant(u'0.0.0.0')).toString() |
188 | + settings.endGroup() |
189 | + self.http_server = QtNetwork.QTcpServer() |
190 | + self.http_server.listen(QtNetwork.QHostAddress(address), port) |
191 | + QtCore.QObject.connect(self.http_server, |
192 | QtCore.SIGNAL(u'newConnection()'), self.new_connection) |
193 | log.debug(u'TCP listening on port %d' % port) |
194 | |
195 | + def start_ssl(self): |
196 | + """ |
197 | + Start the HTTPS server, use the port in the settings default to 4317. |
198 | + Listen out for slide and song changes so they can be broadcast to |
199 | + clients. Listen out for socket connections. |
200 | + """ |
201 | + log.debug(u'Start SSL server') |
202 | + settings = QtCore.QSettings() |
203 | + settings.beginGroup(self.plugin.settingsSection) |
204 | + port = settings.value(u'ssl port', QtCore.QVariant(4317)).toInt()[0] |
205 | + address = settings.value( |
206 | + u'ip address', QtCore.QVariant(u'0.0.0.0')).toString() |
207 | + settings.endGroup() |
208 | + self.https_server = SslServer() |
209 | + self.https_server.listen(QtNetwork.QHostAddress(address), port) |
210 | + QtCore.QObject.connect(self.https_server, |
211 | + QtCore.SIGNAL(u'newConnection()'), self.new_connection) |
212 | + log.debug(u'SSL listening on port %d' % port) |
213 | + |
214 | def slide_change(self, row): |
215 | """ |
216 | Slide change listener. Store the item and tell the clients. |
217 | @@ -205,7 +293,8 @@ |
218 | communication. |
219 | """ |
220 | log.debug(u'new http connection') |
221 | - socket = self.server.nextPendingConnection() |
222 | + server = self.sender() |
223 | + socket = server.nextPendingConnection() |
224 | if socket: |
225 | self.connections.append(HttpConnection(self, socket)) |
226 | |
227 | @@ -213,7 +302,7 @@ |
228 | """ |
229 | The connection has been closed. Clean up |
230 | """ |
231 | - log.debug(u'close http connection') |
232 | + log.debug(u'close connection') |
233 | if connection in self.connections: |
234 | self.connections.remove(connection) |
235 | |
236 | @@ -222,7 +311,9 @@ |
237 | Close down the http server. |
238 | """ |
239 | log.debug(u'close http server') |
240 | - self.server.close() |
241 | + self.http_server.close() |
242 | + if self.https_server: |
243 | + self.https_server.close() |
244 | |
245 | |
246 | class HttpConnection(object): |
247 | @@ -252,10 +343,17 @@ |
248 | (r'^/api/(.*)/live$', self.go_live), |
249 | (r'^/api/(.*)/add$', self.add_to_service) |
250 | ] |
251 | - QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'), |
252 | - self.ready_read) |
253 | - QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'disconnected()'), |
254 | - self.disconnected) |
255 | + if isinstance(socket, QtNetwork.QSslSocket): |
256 | + QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'encrypted()'), |
257 | + self.encrypted) |
258 | + QtCore.QObject.connect(self.socket, |
259 | + QtCore.SIGNAL(u'sslErrors(const QList<QSslError> &)'), |
260 | + self.sslErrors) |
261 | + else: |
262 | + QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'), |
263 | + self.ready_read) |
264 | + QtCore.QObject.connect(self.socket, |
265 | + QtCore.SIGNAL(u'disconnected()'), self.disconnected) |
266 | self.translate() |
267 | |
268 | def _get_service_items(self): |
269 | @@ -309,17 +407,32 @@ |
270 | 'options': translate('RemotePlugin.Mobile', 'Options') |
271 | } |
272 | |
273 | + def encrypted(self): |
274 | + """ |
275 | + Only setup these slots when the data is encrypted. |
276 | + """ |
277 | + QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'), |
278 | + self.ready_read) |
279 | + QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'disconnected()'), |
280 | + self.disconnected) |
281 | + |
282 | + def sslErrors(self, errors): |
283 | + for error in errors: |
284 | + log.warn(unicode(error.errorString())) |
285 | + self.socket.ignoreSslErrors() |
286 | + |
287 | def ready_read(self): |
288 | """ |
289 | Data has been sent from the client. Respond to it |
290 | """ |
291 | - log.debug(u'ready to read socket') |
292 | + log.debug(u'Ready to read socket') |
293 | if self.socket.canReadLine(): |
294 | data = str(self.socket.readLine()) |
295 | try: |
296 | - log.debug(u'received: ' + data) |
297 | + log.debug(u'Received: ' + data) |
298 | except UnicodeDecodeError: |
299 | # Malicious request containing non-ASCII characters. |
300 | + self.send_response(HttpResponse(code='400 Bad Request')) |
301 | self.close() |
302 | return |
303 | words = data.split(' ') |
304 | @@ -342,23 +455,28 @@ |
305 | else: |
306 | self.send_response(HttpResponse(code='404 Not Found')) |
307 | self.close() |
308 | + else: |
309 | + return |
310 | |
311 | def serve_file(self, filename=None): |
312 | """ |
313 | - Send a file to the socket. For now, just a subset of file types |
314 | - and must be top level inside the html folder. |
315 | - If subfolders requested return 404, easier for security for the present. |
316 | + Send a file to the socket. For now, only a subset of file types will |
317 | + be send, and all files must be at the top level inside the html folder. |
318 | + If sub-folders are requested return 404, easier for security for the |
319 | + present. |
320 | |
321 | Ultimately for i18n, this could first look for xx/file.html before |
322 | falling back to file.html... where xx is the language, e.g. 'en' |
323 | """ |
324 | - log.debug(u'serve file request %s' % filename) |
325 | + log.debug(u'serve file request (original) %s' % filename) |
326 | if not filename: |
327 | filename = u'index.html' |
328 | elif filename == u'stage': |
329 | filename = u'stage.html' |
330 | + log.debug(u'serve file request (updated) %s' % filename) |
331 | path = os.path.normpath(os.path.join(self.parent.html_dir, filename)) |
332 | if not path.startswith(self.parent.html_dir): |
333 | + log.debug(u'File not found, returning 404') |
334 | return HttpResponse(code=u'404 Not Found') |
335 | ext = os.path.splitext(filename)[1] |
336 | html = None |
337 | @@ -404,8 +522,8 @@ |
338 | u'slide': self.parent.current_slide or 0, |
339 | u'item': self.parent.current_item._uuid \ |
340 | if self.parent.current_item else u'', |
341 | - u'twelve':QtCore.QSettings().value( |
342 | - u'remotes/twelve hour', QtCore.QVariant(True)).toBool(), |
343 | + u'twelve': QtCore.QSettings().value( |
344 | + u'remotes/twelve hour', QtCore.QVariant(True)).toBool(), |
345 | u'blank': self.parent.plugin.liveController.blankScreen.\ |
346 | isChecked(), |
347 | u'theme': self.parent.plugin.liveController.themeScreen.\ |
348 | @@ -436,7 +554,7 @@ |
349 | try: |
350 | text = json.loads( |
351 | self.url_params[u'data'][0])[u'request'][u'text'] |
352 | - except KeyError, ValueError: |
353 | + except (KeyError, ValueError): |
354 | return HttpResponse(code=u'400 Bad Request') |
355 | text = urllib.unquote(text) |
356 | Receiver.send_message(u'alerts_text', [text]) |
357 | @@ -484,7 +602,7 @@ |
358 | if self.url_params and self.url_params.get(u'data'): |
359 | try: |
360 | data = json.loads(self.url_params[u'data'][0]) |
361 | - except KeyError, ValueError: |
362 | + except (KeyError, ValueError): |
363 | return HttpResponse(code=u'400 Bad Request') |
364 | log.info(data) |
365 | # This slot expects an int within a list. |
366 | @@ -500,14 +618,16 @@ |
367 | event = u'servicemanager_%s' % action |
368 | if action == u'list': |
369 | return HttpResponse( |
370 | - json.dumps({u'results': {u'items': self._get_service_items()}}), |
371 | + json.dumps({ |
372 | + u'results': {u'items': self._get_service_items()} |
373 | + }), |
374 | {u'Content-Type': u'application/json'}) |
375 | else: |
376 | event += u'_item' |
377 | if self.url_params and self.url_params.get(u'data'): |
378 | try: |
379 | data = json.loads(self.url_params[u'data'][0]) |
380 | - except KeyError, ValueError: |
381 | + except (KeyError, ValueError): |
382 | return HttpResponse(code=u'400 Bad Request') |
383 | Receiver.send_message(event, data[u'request'][u'id']) |
384 | else: |
385 | @@ -543,7 +663,7 @@ |
386 | """ |
387 | try: |
388 | text = json.loads(self.url_params[u'data'][0])[u'request'][u'text'] |
389 | - except KeyError, ValueError: |
390 | + except (KeyError, ValueError): |
391 | return HttpResponse(code=u'400 Bad Request') |
392 | text = urllib.unquote(text) |
393 | plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type) |
394 | @@ -562,7 +682,7 @@ |
395 | """ |
396 | try: |
397 | id = json.loads(self.url_params[u'data'][0])[u'request'][u'id'] |
398 | - except KeyError, ValueError: |
399 | + except (KeyError, ValueError): |
400 | return HttpResponse(code=u'400 Bad Request') |
401 | plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type) |
402 | if plugin.status == PluginStatus.Active and plugin.mediaItem: |
403 | @@ -575,7 +695,7 @@ |
404 | """ |
405 | try: |
406 | id = json.loads(self.url_params[u'data'][0])[u'request'][u'id'] |
407 | - except KeyError, ValueError: |
408 | + except (KeyError, ValueError): |
409 | return HttpResponse(code=u'400 Bad Request') |
410 | plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type) |
411 | if plugin.status == PluginStatus.Active and plugin.mediaItem: |
412 | @@ -590,6 +710,7 @@ |
413 | http += '\r\n' |
414 | self.socket.write(http) |
415 | self.socket.write(response.content) |
416 | + self.socket.flush() |
417 | |
418 | def disconnected(self): |
419 | """ |
420 | |
421 | === modified file 'openlp/plugins/remotes/lib/remotetab.py' |
422 | --- openlp/plugins/remotes/lib/remotetab.py 2012-03-18 09:33:05 +0000 |
423 | +++ openlp/plugins/remotes/lib/remotetab.py 2012-05-20 16:30:25 +0000 |
424 | @@ -41,7 +41,9 @@ |
425 | def setupUi(self): |
426 | self.setObjectName(u'RemoteTab') |
427 | SettingsTab.setupUi(self) |
428 | + # Main server settings |
429 | self.serverSettingsGroupBox = QtGui.QGroupBox(self.leftColumn) |
430 | + self.serverSettingsGroupBox.setContentsMargins(8, 8, 8, 8) |
431 | self.serverSettingsGroupBox.setObjectName(u'serverSettingsGroupBox') |
432 | self.serverSettingsLayout = QtGui.QFormLayout( |
433 | self.serverSettingsGroupBox) |
434 | @@ -60,27 +62,63 @@ |
435 | self.twelveHourCheckBox = QtGui.QCheckBox(self.serverSettingsGroupBox) |
436 | self.twelveHourCheckBox.setObjectName(u'twelveHourCheckBox') |
437 | self.serverSettingsLayout.addRow(self.twelveHourCheckBox) |
438 | - self.portLabel = QtGui.QLabel(self.serverSettingsGroupBox) |
439 | - self.portLabel.setObjectName(u'portLabel') |
440 | - self.portSpinBox = QtGui.QSpinBox(self.serverSettingsGroupBox) |
441 | - self.portSpinBox.setMaximum(32767) |
442 | - self.portSpinBox.setObjectName(u'portSpinBox') |
443 | - QtCore.QObject.connect(self.portSpinBox, |
444 | - QtCore.SIGNAL(u'valueChanged(int)'), self.setUrls) |
445 | - self.serverSettingsLayout.addRow(self.portLabel, self.portSpinBox) |
446 | - self.remoteUrlLabel = QtGui.QLabel(self.serverSettingsGroupBox) |
447 | - self.remoteUrlLabel.setObjectName(u'remoteUrlLabel') |
448 | - self.remoteUrl = QtGui.QLabel(self.serverSettingsGroupBox) |
449 | - self.remoteUrl.setObjectName(u'remoteUrl') |
450 | - self.remoteUrl.setOpenExternalLinks(True) |
451 | - self.serverSettingsLayout.addRow(self.remoteUrlLabel, self.remoteUrl) |
452 | - self.stageUrlLabel = QtGui.QLabel(self.serverSettingsGroupBox) |
453 | - self.stageUrlLabel.setObjectName(u'stageUrlLabel') |
454 | - self.stageUrl = QtGui.QLabel(self.serverSettingsGroupBox) |
455 | - self.stageUrl.setObjectName(u'stageUrl') |
456 | - self.stageUrl.setOpenExternalLinks(True) |
457 | - self.serverSettingsLayout.addRow(self.stageUrlLabel, self.stageUrl) |
458 | self.leftLayout.addWidget(self.serverSettingsGroupBox) |
459 | + self.httpSettingsGroupBox = QtGui.QGroupBox(self.leftColumn) |
460 | + self.httpSettingsGroupBox.setObjectName(u'httpSettingsGroupBox') |
461 | + self.httpSettingsLayout = QtGui.QFormLayout( |
462 | + self.httpSettingsGroupBox) |
463 | + self.httpSettingsLayout.setObjectName(u'httpSettingsLayout') |
464 | + self.httpPortLabel = QtGui.QLabel(self.httpSettingsGroupBox) |
465 | + self.httpPortLabel.setObjectName(u'httpPortLabel') |
466 | + self.httpPortSpinBox = QtGui.QSpinBox(self.httpSettingsGroupBox) |
467 | + self.httpPortSpinBox.setMaximum(32767) |
468 | + self.httpPortSpinBox.setObjectName(u'httpPortSpinBox') |
469 | + self.httpSettingsLayout.addRow( |
470 | + self.httpPortLabel, self.httpPortSpinBox) |
471 | + self.remoteHttpUrlLabel = QtGui.QLabel(self.httpSettingsGroupBox) |
472 | + self.remoteHttpUrlLabel.setObjectName(u'remoteHttpUrlLabel') |
473 | + self.remoteHttpUrl = QtGui.QLabel(self.httpSettingsGroupBox) |
474 | + self.remoteHttpUrl.setObjectName(u'remoteHttpUrl') |
475 | + self.remoteHttpUrl.setOpenExternalLinks(True) |
476 | + self.httpSettingsLayout.addRow( |
477 | + self.remoteHttpUrlLabel, self.remoteHttpUrl) |
478 | + self.stageHttpUrlLabel = QtGui.QLabel(self.httpSettingsGroupBox) |
479 | + self.stageHttpUrlLabel.setObjectName(u'stageHttpUrlLabel') |
480 | + self.stageHttpUrl = QtGui.QLabel(self.httpSettingsGroupBox) |
481 | + self.stageHttpUrl.setObjectName(u'stageHttpUrl') |
482 | + self.stageHttpUrl.setOpenExternalLinks(True) |
483 | + self.httpSettingsLayout.addRow( |
484 | + self.stageHttpUrlLabel, self.stageHttpUrl) |
485 | + self.leftLayout.addWidget(self.httpSettingsGroupBox) |
486 | + self.httpsSettingsGroupBox = QtGui.QGroupBox(self.leftColumn) |
487 | + self.httpsSettingsGroupBox.setCheckable(True) |
488 | + self.httpsSettingsGroupBox.setChecked(False) |
489 | + self.httpsSettingsGroupBox.setObjectName(u'httpsSettingsGroupBox') |
490 | + self.httpsSettingsLayout = QtGui.QFormLayout( |
491 | + self.httpsSettingsGroupBox) |
492 | + self.httpsSettingsLayout.setObjectName(u'httpsSettingsLayout') |
493 | + self.httpsPortLabel = QtGui.QLabel(self.httpsSettingsGroupBox) |
494 | + self.httpsPortLabel.setObjectName(u'httpsPortLabel') |
495 | + self.httpsPortSpinBox = QtGui.QSpinBox(self.httpsSettingsGroupBox) |
496 | + self.httpsPortSpinBox.setMaximum(32767) |
497 | + self.httpsPortSpinBox.setObjectName(u'httpsPortSpinBox') |
498 | + self.httpsSettingsLayout.addRow( |
499 | + self.httpsPortLabel, self.httpsPortSpinBox) |
500 | + self.remoteHttpsUrl = QtGui.QLabel(self.httpsSettingsGroupBox) |
501 | + self.remoteHttpsUrl.setObjectName(u'remoteHttpsUrl') |
502 | + self.remoteHttpsUrl.setOpenExternalLinks(True) |
503 | + self.remoteHttpsUrlLabel = QtGui.QLabel(self.httpsSettingsGroupBox) |
504 | + self.remoteHttpsUrlLabel.setObjectName(u'remoteHttpsUrlLabel') |
505 | + self.httpsSettingsLayout.addRow( |
506 | + self.remoteHttpsUrlLabel, self.remoteHttpsUrl) |
507 | + self.stageHttpsUrlLabel = QtGui.QLabel(self.httpsSettingsGroupBox) |
508 | + self.stageHttpsUrlLabel.setObjectName(u'stageHttpsUrlLabel') |
509 | + self.stageHttpsUrl = QtGui.QLabel(self.httpsSettingsGroupBox) |
510 | + self.stageHttpsUrl.setObjectName(u'stageHttpsUrl') |
511 | + self.stageHttpsUrl.setOpenExternalLinks(True) |
512 | + self.httpsSettingsLayout.addRow( |
513 | + self.stageHttpsUrlLabel, self.stageHttpsUrl) |
514 | + self.leftLayout.addWidget(self.httpsSettingsGroupBox) |
515 | self.androidAppGroupBox = QtGui.QGroupBox(self.rightColumn) |
516 | self.androidAppGroupBox.setObjectName(u'androidAppGroupBox') |
517 | self.rightLayout.addWidget(self.androidAppGroupBox) |
518 | @@ -99,6 +137,10 @@ |
519 | self.qrLayout.addWidget(self.qrDescriptionLabel) |
520 | self.leftLayout.addStretch() |
521 | self.rightLayout.addStretch() |
522 | + QtCore.QObject.connect(self.httpPortSpinBox, |
523 | + QtCore.SIGNAL(u'valueChanged(int)'), self.setUrls) |
524 | + QtCore.QObject.connect(self.httpsPortSpinBox, |
525 | + QtCore.SIGNAL(u'valueChanged(int)'), self.setUrls) |
526 | QtCore.QObject.connect(self.twelveHourCheckBox, |
527 | QtCore.SIGNAL(u'stateChanged(int)'), |
528 | self.onTwelveHourCheckBoxChanged) |
529 | @@ -108,11 +150,21 @@ |
530 | translate('RemotePlugin.RemoteTab', 'Server Settings')) |
531 | self.addressLabel.setText(translate('RemotePlugin.RemoteTab', |
532 | 'Serve on IP address:')) |
533 | - self.portLabel.setText(translate('RemotePlugin.RemoteTab', |
534 | - 'Port number:')) |
535 | - self.remoteUrlLabel.setText(translate('RemotePlugin.RemoteTab', |
536 | - 'Remote URL:')) |
537 | - self.stageUrlLabel.setText(translate('RemotePlugin.RemoteTab', |
538 | + self.httpSettingsGroupBox.setTitle( |
539 | + translate('RemotePlugin.RemoteTab', 'HTTP Server')) |
540 | + self.httpPortLabel.setText(translate('RemotePlugin.RemoteTab', |
541 | + 'Port number:')) |
542 | + self.remoteHttpUrlLabel.setText(translate('RemotePlugin.RemoteTab', |
543 | + 'Remote URL:')) |
544 | + self.stageHttpUrlLabel.setText(translate('RemotePlugin.RemoteTab', |
545 | + 'Stage view URL:')) |
546 | + self.httpsSettingsGroupBox.setTitle( |
547 | + translate('RemotePlugin.RemoteTab', 'HTTPS Server')) |
548 | + self.httpsPortLabel.setText(translate('RemotePlugin.RemoteTab', |
549 | + 'Port number:')) |
550 | + self.remoteHttpsUrlLabel.setText(translate('RemotePlugin.RemoteTab', |
551 | + 'Remote URL:')) |
552 | + self.stageHttpsUrlLabel.setText(translate('RemotePlugin.RemoteTab', |
553 | 'Stage view URL:')) |
554 | self.twelveHourCheckBox.setText( |
555 | translate('RemotePlugin.RemoteTab', |
556 | @@ -125,7 +177,7 @@ |
557 | 'download</a> to install the Android app from the Market.')) |
558 | |
559 | def setUrls(self): |
560 | - ipAddress = u'localhost' |
561 | + ip_address = u'localhost' |
562 | if self.addressEdit.text() == ZERO_URL: |
563 | ifaces = QtNetwork.QNetworkInterface.allInterfaces() |
564 | for iface in ifaces: |
565 | @@ -138,41 +190,70 @@ |
566 | ip = addr.ip() |
567 | if ip.protocol() == 0 and \ |
568 | ip != QtNetwork.QHostAddress.LocalHost: |
569 | - ipAddress = ip.toString() |
570 | + ip_address = ip.toString() |
571 | break |
572 | else: |
573 | - ipAddress = self.addressEdit.text() |
574 | - url = u'http://%s:%s/' % (ipAddress, self.portSpinBox.value()) |
575 | - self.remoteUrl.setText(u'<a href="%s">%s</a>' % (url, url)) |
576 | - url = url + u'stage' |
577 | - self.stageUrl.setText(u'<a href="%s">%s</a>' % (url, url)) |
578 | + ip_address = self.addressEdit.text() |
579 | + http_url = u'http://%s:%s/' % \ |
580 | + (ip_address, self.httpPortSpinBox.value()) |
581 | + https_url = u'https://%s:%s/' % \ |
582 | + (ip_address, self.httpsPortSpinBox.value()) |
583 | + self.remoteHttpUrl.setText(u'<a href="%s">%s</a>' % \ |
584 | + (http_url, http_url)) |
585 | + self.remoteHttpsUrl.setText(u'<a href="%s">%s</a>' % \ |
586 | + (https_url, https_url)) |
587 | + http_url += u'stage' |
588 | + https_url += u'stage' |
589 | + self.stageHttpUrl.setText(u'<a href="%s">%s</a>' % \ |
590 | + (http_url, http_url)) |
591 | + self.stageHttpsUrl.setText(u'<a href="%s">%s</a>' % \ |
592 | + (https_url, https_url)) |
593 | |
594 | def load(self): |
595 | - self.portSpinBox.setValue( |
596 | - QtCore.QSettings().value(self.settingsSection + u'/port', |
597 | - QtCore.QVariant(4316)).toInt()[0]) |
598 | + settings = QtCore.QSettings() |
599 | + settings.beginGroup(self.settingsSection) |
600 | + self.httpPortSpinBox.setValue( |
601 | + settings.value(u'port', QtCore.QVariant(4316)).toInt()[0]) |
602 | + self.httpsPortSpinBox.setValue( |
603 | + settings.value(u'ssl port', QtCore.QVariant(4317)).toInt()[0]) |
604 | self.addressEdit.setText( |
605 | - QtCore.QSettings().value(self.settingsSection + u'/ip address', |
606 | - QtCore.QVariant(ZERO_URL)).toString()) |
607 | - self.twelveHour = QtCore.QSettings().value( |
608 | - self.settingsSection + u'/twelve hour', |
609 | - QtCore.QVariant(True)).toBool() |
610 | + settings.value( |
611 | + u'ip address', QtCore.QVariant(ZERO_URL)).toString()) |
612 | + self.twelveHour = settings.value( |
613 | + u'twelve hour', QtCore.QVariant(True)).toBool() |
614 | + self.httpsSettingsGroupBox.setChecked(settings.value(u'https enabled', |
615 | + QtCore.QVariant(False)).toBool()) |
616 | + settings.endGroup() |
617 | self.twelveHourCheckBox.setChecked(self.twelveHour) |
618 | self.setUrls() |
619 | |
620 | def save(self): |
621 | changed = False |
622 | - if QtCore.QSettings().value(self.settingsSection + u'/ip address', |
623 | - QtCore.QVariant(ZERO_URL).toString() != self.addressEdit.text() or |
624 | - QtCore.QSettings().value(self.settingsSection + u'/port', |
625 | - QtCore.QVariant(4316).toInt()[0]) != self.portSpinBox.value()): |
626 | + settings = QtCore.QSettings() |
627 | + settings.beginGroup(self.settingsSection) |
628 | + ip_address = settings.value( |
629 | + u'ip address', QtCore.QVariant(ZERO_URL).toString()) |
630 | + http_port = settings.value( |
631 | + u'port', QtCore.QVariant(4316).toInt()[0]) |
632 | + https_port = settings.value( |
633 | + u'ssl port', QtCore.QVariant(4317).toInt()[0]) |
634 | + https_enabled = settings.value( |
635 | + u'https enabled', QtCore.QVariant(False)).toBool() |
636 | + if ip_address != self.addressEdit.text() or \ |
637 | + http_port != self.httpPortSpinBox.value() or\ |
638 | + https_port != self.httpsPortSpinBox.value() or \ |
639 | + https_enabled != self.httpsSettingsGroupBox.isChecked(): |
640 | changed = True |
641 | - QtCore.QSettings().setValue(self.settingsSection + u'/port', |
642 | - QtCore.QVariant(self.portSpinBox.value())) |
643 | - QtCore.QSettings().setValue(self.settingsSection + u'/ip address', |
644 | - QtCore.QVariant(self.addressEdit.text())) |
645 | - QtCore.QSettings().setValue(self.settingsSection + u'/twelve hour', |
646 | - QtCore.QVariant(self.twelveHour)) |
647 | + settings.setValue( |
648 | + u'port', QtCore.QVariant(self.httpPortSpinBox.value())) |
649 | + settings.setValue( |
650 | + u'ssl port', QtCore.QVariant(self.httpsPortSpinBox.value())) |
651 | + settings.setValue( |
652 | + u'ip address', QtCore.QVariant(self.addressEdit.text())) |
653 | + settings.setValue(u'twelve hour', QtCore.QVariant(self.twelveHour)) |
654 | + settings.setValue(u'https enabled', |
655 | + QtCore.QVariant(self.httpsSettingsGroupBox.isChecked())) |
656 | + settings.endGroup() |
657 | if changed: |
658 | Receiver.send_message(u'remotes_config_updated') |
659 |
264 commented out code.
Tried to run ssl with not certificate and got this.
Traceback (most recent call last): timali/ dev/projects/ openlp/ ssl/openlp/ plugins/ remotes/ lib/httpserver. py", line 165, in incomingConnection os.path. join(cert_ path, u'openlp.crt'), u'rb').read()) share/openlp/ openlp. crt'
File "/home/
open(
IOError: [Errno 2] No such file or directory: u'/usr/
How do I get a certificate?