Clean up, remove compat with py < 3.6
[pyi2ncommon] / src / web_interface.py
1 # The software in this package is distributed under the GNU General
2 # Public License version 2 (with a special exception described below).
3 #
4 # A copy of GNU General Public License (GPL) is included in this distribution,
5 # in the file COPYING.GPL.
6 #
7 # As a special exception, if other files instantiate templates or use macros
8 # or inline functions from this file, or you compile this file and link it
9 # with other works to produce a work based on this file, this file
10 # does not by itself cause the resulting work to be covered
11 # by the GNU General Public License.
12 #
13 # However the source code for this file must still be made available
14 # in accordance with section (3) of the GNU General Public License.
15 #
16 # This exception does not invalidate any other reasons why a work based
17 # on this file might be covered by the GNU General Public License.
18 #
19 # Copyright (c) 2016-2018 Intra2net AG <info@intra2net.com>
20
21 """
22
23 SUMMARY
24 ------------------------------------------------------
25 Utility for HTTP based interaction with the arnied web page.
26
27 Copyright: Intra2net AG
28
29
30 INTERFACE
31 ------------------------------------------------------
32
33 """
34
35 import re
36 import ssl
37 import http.client as client
38 import urllib.parse as parse
39 import socket
40 import logging
41 from .arnied_wrapper import accept_licence
42
43 log = logging.getLogger('pyi2ncommon.web_interface')
44
45
46 #: FQDN of local machine
47 LOCALHOST = socket.gethostname()
48
49
50 def find_in_form(regex, form="status", escape=False, check_certs=True):
51     """
52     Find a regex in given I2N web page form.
53
54     :param str regex: regular expression to find
55     :param str form: form name to open
56     :param bool escape: whether to escape the regex
57     :param check_certs: forwarded to :py:func:`web_page_request`, see doc there
58     :returns: whether the regex was found
59     :rtype: bool
60     """
61     accept_licence()
62     data = web_page_request(method="GET", url="/arnie?form=" + form,
63                             check_certs=check_certs)
64     if escape:
65         regex = re.escape(regex)
66     if re.search(regex, data):
67         return True
68     else:
69         log.debug("'%s' could not be found in:\n%s", regex, data)
70         return False
71
72
73 def web_page_request(method="GET", url="/", body=None, check_certs=True):
74     """
75     Send an HTTPS request and return any response data.
76
77     SSL certificates are checked against a default set of certificates
78     installed on you system. This can be disabled (not recommended, security
79     implications!) by setting `check_certs` to `False`. To allow a secure
80     connection to host with e.g. a self-signed certificate, the caller can
81     load this certificate by specifying `check_certs=/path/to/cert.pem`.
82     (see also: :py:meth:`ssl.SSLContext.load_verify_locations`). Note that the
83     certificate has to be issued for the same server name that we try to
84     access, i.e. :py:data:`LOCALHOST`.
85
86     :param str method: GET or POST method for the request
87     :param str url: url location within the remote host
88     :param body: dictionary to be parsed and added to the url
89     :type body: {str, str} or None
90     :param check_certs: Whether or not to check ssl certificates for connection
91                         or file name to a certificate file
92     :type check_certs: bool or str
93     :returns: data from the response if any
94     :rtype: str
95     """
96     body = parse.urlencode(body) if body is not None else ""
97     headers = {"Content-Type": "application/x-www-form-urlencoded",
98                "Accept": "text/plain"}
99
100     if isinstance(check_certs, str):
101         context = ssl.create_default_context(cafile=check_certs)
102     elif check_certs:
103         context = ssl.create_default_context()
104     else:
105         # disable certificate checks
106         context = ssl._create_unverified_context()
107     conn = client.HTTPSConnection(LOCALHOST, context=context)
108     conn.request(method, url, body, headers)
109     resp = conn.getresponse()
110     logging.info("Request status %s and response %s",
111                  resp.status, resp.reason)
112     if resp.status != 200:
113         raise client.HTTPException("POST request failed.")
114     data = resp.read().decode()
115     conn.close()
116
117     return data