| 34 | |
| 35 | |
| 36 | class UpdateHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
| 37 | def __init__(self, catalog_dir, *args): |
| 38 | self.catalog_dir = catalog_dir |
| 39 | BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args) |
| 40 | |
| 41 | def do_GET(self): |
| 42 | request_path = urlparse.urlparse(self.path) |
| 43 | if request_path.path == '/_catalog': |
| 44 | err = '' |
| 45 | query = urlparse.urlparse(self.path).query |
| 46 | try: |
| 47 | op = urlparse.parse_qs(query)['op'][0] |
| 48 | if op == 'list': |
| 49 | try: |
| 50 | path = urlparse.parse_qs(query)['path'][0] |
| 51 | except KeyError: |
| 52 | path = '.' |
| 53 | full_path = os.path.join(self.catalog_dir, path) |
| 54 | if os.path.commonprefix((os.path.realpath(full_path), self.catalog_dir)) == self.catalog_dir: |
| 55 | self.__send_dir(full_path) |
| 56 | result = True |
| 57 | else: |
| 58 | err = 'Path traversal detected' |
| 59 | result = False |
| 60 | else: |
| 61 | err = '{0} unknown operation'.format(op) |
| 62 | result = False |
| 63 | except KeyError: |
| 64 | err = '{0} invaid catalog request'.format(self.path) |
| 65 | result = False |
| 66 | if not result: |
| 67 | logger.info(err) |
| 68 | self.send_response(httplib.FORBIDDEN, err) |
| 69 | self.end_headers() |
| 70 | else: |
| 71 | self.__send_file(self.path) |
| 72 | |
| 73 | def __check_header(self): |
| 74 | ex_headers_templ = ['x-*-STA-MAC', 'x-*-AP-MAC', 'x-*-FREE-SPACE', 'x-*-SKETCH-SIZE', 'x-*-SKETCH-MD5', 'x-*-CHIP-SIZE', 'x-*-SDK-VERSION'] |
| 75 | ex_headers = [] |
| 76 | ua = re.match('(ESP8266|ESP32)-http-Update', self.headers.get('User-Agent')) |
| 77 | if ua: |
| 78 | arch = ua.group().split('-')[0] |
| 79 | ex_headers = list(imap(lambda x: x.replace('*', arch), ex_headers_templ)) |
| 80 | else: |
| 81 | logger.info('User-Agent {0} is not HTTPUpdate'.format(ua)) |
| 82 | return False |
| 83 | for ex_header in ex_headers: |
| 84 | if ex_header not in self.headers: |
| 85 | logger.info('Missing header {0} to identify a legitimate request'.format(ex_header)) |
| 86 | return False |
| 87 | return True |
| 88 | |
| 89 | def __send_file(self, path): |
| 90 | if not self.__check_header(): |
| 91 | self.send_response(httplib.FORBIDDEN, 'The request available only from ESP8266 or ESP32 http updater.') |
| 92 | self.end_headers() |
| 93 | return |