def macro_call(self, param: UniParam): p = param.params if self._have_variable(p[1], param): return MacroResult(self._get_variable(p[1], param)) elif p[1] in param.interpreter.handler: param.params = param[1:] return MacroResult(param.interpreter.handler(param))
def unquote(self, text: str, param: UniParam, do_parse=True) -> MacroResult: quote_level = 0 position = 0 length = len(text) while position < length: mark0 = text[position:position + 2] if mark0 == '{:': quote_level += 1 if quote_level == 1: text = replace_str_at_position(text, position, '{:', '') length -= 2 continue elif mark0 == ':}': quote_level -= 1 if quote_level == 0: text = replace_str_at_position(text, position, ':}', '') length -= 2 continue position += 1 # text = text.replace('{:', '').replace(':}', '') # Deep copy param, prevent modifying original one uni_param = UniParam(param.params, interpreter=param.interpreter, request=param.request, filelist=param.filelist, statistics=param.statistics) return self.parse_text(text, uni_param) if do_parse else MacroResult(text)
def section_to_page(self, section_name, param: UniParam): # Deep copy param, prevent modifying original one uni_param = UniParam(param.params, interpreter=param.interpreter, request=param.request, filelist=param.filelist, statistics=param.statistics) section = self.get_section(section_name, uni_param, True, True) if section == None: return self.get_page( 'error-page', UniParam(['not found', 404], interpreter=self, request=param.request)) status = 200 return Page(section.content, status, section.headers)
def exec_macro(self, params: list, param: UniParam) -> MacroResult: params = self.to_normal_macro(params) if params[-1].split('/')[-1] == params[0] and len(params) > 2: params[-1] = '/'.join(params[-1].split('/')[0:-1]) # params = [strip_starting_spaces(x) for x in params] param.params = params result = self.handler[params[0]](param) return result
def unauth_response(self, request: PHFSRequest) -> Response: page = self.interpreter.get_page( 'error-page', UniParam(['unauthorized', 403], interpreter=self.interpreter, request=request, statistics=self.statistics)) return Response(page.content, page.status, page.headers, mimetype=mimeLib.getmime('*.html'))
def breadcrumbs(self, param: UniParam): t = param.interpreter.unquote(param[1], param, False).content r = [] paths = param.request.path.split('/')[:-1] bread_url = '' bread_name = '' for i in range(len(paths)): bread_name = paths[i] bread_url += paths[i] + '/' symbols = concat_dict(param.symbols, { 'bread-name': lambda p: MacroResult(bread_name), 'bread-url': lambda p: MacroResult(bread_url) }) param.symbols = concat_dict(param.symbols, symbols) c = param.interpreter.parse_text(t, param) r.append(c.content) return MacroResult(''.join(r))
def get_section(self, section_name: str, param: UniParam, do_parse=True, force=False) -> MacroResult: """ Get a section from template. What this returns is a `MacroResult`. `section_name`: Name of section. `param`: `UniParam` for parsing macros and symbols. `do_parse`: Parse the content? `force`: Get this section even if not public? """ section: TplSection = self.sections.get(section_name, None) if section == None: return None param.symbols = concat_dict(param.symbols, section.symbols) return self.parse_text(section.content, param) if do_parse else MacroResult( section.content)
def get_page(self, page_name: str, param: UniParam) -> Page: uni_param = param if page_name == '': page = self.section_to_page('', param) page.content = replace_str( page.content, '%build-time%', str(round(time.time() - param.request.build_time_start, 3))) return page elif page_name == 'files': if param.filelist.count == 0: nofiles = self.get_section('nofiles', param, True, True) return Page(nofiles.content, 200) return self.section_to_page('files', param) elif page_name == 'list': return self.get_list(uni_param) elif page_name == 'upload': return self.section_to_page('upload', param) elif page_name == 'upload-results': page = self.section_to_page('upload-results', param) _success = self.get_section('upload-success', uni_param, False, True) _failed = self.get_section('upload-failed', uni_param, False, True) uploaded_files = [] upload_result = param.params[0] assert type(upload_result) == dict, 'param.params[0] is not a dict' for i in upload_result: result = upload_result[i] if result[0] == True: uploaded_files.append( self.parse_text( _success.content, UniParam( [], symbols={ 'item-name': lambda p: MacroResult(i), 'item-size': lambda p: MacroResult( smartsize( os.stat(param.request.path_real + i ).st_size)), 'speed': lambda p: MacroResult('0') }, interpreter=self, request=param.request)).content) else: uploaded_files.append( self.parse_text( _failed.content, UniParam( [], symbols={ 'item-name': lambda p: MacroResult(i), 'reason': lambda p: MacroResult(result[1]) }, interpreter=self, request=param.request)).content) page.content = replace_str(page.content, '%uploaded-files%', ''.join(uploaded_files)) return page elif page_name == 'error-page': error_type = param.params[0] error_status = param.params[1] base_page = self.get_section( 'error-page', UniParam([], symbols={}, request=param.request, interpreter=self, statistics=param.statistics)) content = self.get_section( error_type, UniParam([], symbols={}, request=param.request, interpreter=self, statistics=param.statistics)) headers = concat_dict(base_page.headers, content.headers) return Page( replace_str(base_page.content, '%content%', content.content), error_status, headers)
def __init__(self, tpl_file='hfs.tpl'): self.handler = Commands() self.uptime_start = time.time() self.cached_pages = {} self.symbols = { 'style': self.handler.sym_style, 'user': self.handler.sym_user, 'login-link': lambda p: self.get_section('login-link', p, True, True), 'loggedin': lambda p: self.get_section('loggedin', p, True, True), 'ip': lambda p: MacroResult(p.request.host), 'version': lambda p: MacroResult(Config.version), 'timestamp': lambda p: MacroResult(str(datetime.datetime.now())), 'uptime': lambda p: MacroResult( str( datetime.timedelta(seconds=round(time.time() - self. uptime_start)))), 'connections': lambda p: MacroResult('0'), 'speed-out': lambda p: MacroResult('0'), 'speed-in': lambda p: MacroResult('0'), 'total-out': lambda p: MacroResult('0'), 'total-in': lambda p: MacroResult('0'), 'total-downloads': lambda p: MacroResult('0'), 'total-uploads': lambda p: MacroResult('0'), 'number-addresses': lambda p: MacroResult('0'), 'number-addresses-downloading': lambda p: MacroResult('0'), 'build': lambda p: MacroResult(Config.build), 'sequencial': lambda p: MacroResult('0'), 'number-addresses-ever': lambda p: MacroResult('0'), 'port': lambda p: MacroResult(Config.port), 'folder': lambda p: MacroResult((p.request.path_virtual_dir + '/') if p.request != None else ''), 'encoded-folder': lambda p: MacroResult( purify(join_path(p.request.path_virtual_dir, '/')) if p.request != None else '') } self.sections = object_from_dict({ '_empty': TplSection('', [], {}), '': TplSection( '', ['public'], { 'files': lambda p: self.get_page('files', p), 'up': lambda p: self.get_section('up', p, True, True), 'upload-link': lambda p: self.get_section('upload-link', p, True, True), 'host': lambda p: MacroResult(p.request.host), 'number': lambda p: MacroResult(str(p.filelist.count)), 'number-files': lambda p: MacroResult(str(p.filelist.count_files)), 'number-folders': lambda p: MacroResult(str(p.filelist.count_folders)), 'total-size': lambda p: MacroResult( smartsize( sum([ os.stat(x.path).st_size for x in p.filelist.items ]))), 'total-kbytes': lambda p: MacroResult( str( sum([ os.stat(x.path).st_size for x in p.filelist.items ]) // 1024)), 'total-bytes': lambda p: MacroResult( str( sum([ os.stat(x.path).st_size for x in p.filelist.items ]))), 'list': self.get_list, 'folder-item-comment': lambda p: MacroResult('') }), 'files': TplSection( '', [], { 'list': self.get_list, 'item-archive': lambda p: self.get_section('item-archive', p, True, True), }), 'nofiles': TplSection('', [], {}), 'upload': TplSection( '', [], { 'diskfree': lambda p: MacroResult( smartsize( shutil.disk_usage(p.request.path_real_dir).free)), }) }) f = open(tpl_file, 'r', encoding='utf-8') c = '\n' + f.read() f.close() s = c.split('\n[') for i in s: t = i.split(']\n', 1) if len(t) <= 1: continue plus = False prepend = False if t[0][0:1] == '+': plus = True t[0] = t[0][1:] elif t[0][0:1] == '^': prepend = True t[0] = t[0][1:] p = t[0].split('|') for j in p[0].split('='): j = j.strip() if j not in self.sections: self.sections[j] = TplSection('', [], {}) if plus: self.sections[j].content += t[1].strip('\n') + '\n' elif prepend: self.sections[j].content = t[1] + self.sections[ j].content.strip('\n') + '\n' else: self.sections[j].content = t[1].strip('\n') + '\n' self.sections[j].params = p[1:] self.translations = {} for i in self.sections.get( 'special:strings', self.sections['_empty']).content.split('\n'): pair = i.split('=', 1) if len(pair) < 2: continue if pair[0] not in self.translations: self.translations[pair[0]] = pair[1] alias_from_txt = read_ini('alias.txt') for i in alias_from_txt: self.handler[i] = MacroToCallable(alias_from_txt[i], UniParam([], interpreter=self), True) for i in self.sections.get( 'special:alias', self.sections['_empty']).content.split('\n'): pair = i.split('=', 1) if len(pair) < 2: continue self.handler[pair[0]] = MacroToCallable( pair[1], UniParam([], interpreter=self), True) return
#!/usr/bin/python3 import os from tplLib import Interpreter from classesLib import UniParam os.chdir(os.path.dirname(__file__) or '.') itp = Interpreter() print( itp.parse_text('{.save|1.txt|1.}', UniParam([], interpreter=itp)).content)
def wsgi(self, environ, start_response): request_initial = Request(environ) request_initial.path = request_initial.path.replace('/..', '') response = Response('bad request', 400) page = Page('', 400) path = request_initial.path resource = join_path(Config.base_path, path) request = PHFSRequest(environ, path, resource) self.request = request if os.path.isdir(resource): # If there's a index.html inside folder, show that for i in self.indexes: if os.path.isfile(join_path(resource, i)): path = join_path(path, i) resource = join_path(resource, i) uni_param = UniParam([], interpreter=self.interpreter, request=request, filelist=FileList([]), statistics=self.statistics) levels_virtual = path.split('/') levels_real = resource.split('/') if resource[-1:] != '/' and os.path.isdir(resource): response = Response('', 302, {'Location': path + '/'}) return self.return_response(request, response, environ, start_response) if not Account.can_access( self.get_current_account(request)[0], resource, True): response = self.unauth_response(request) return self.return_response(request, response, environ, start_response) if request.args.get('tpl', '') == 'list': uni_param.interpreter = self.itp_filelist if request.method == 'POST': if len(request.files) > 0: # File upload if not if_upload_allowed_in(request.path_real, Config): response = Response('forbidden', 403) else: upload_result = {} for i in request.files: single_file = request.files[i] filename = purify_filename(single_file.filename) if filename == '': continue elif filename in self.indexes: upload_result[filename] = ( False, I18n.get_string( 'file_name_or_extension_forbidden')) continue try: single_file.save(join_path(resource, filename)) upload_result[filename] = (True, '') except Exception as e: upload_result[filename] = (False, str(e)) page = self.interpreter.get_page( 'upload-results', UniParam([upload_result], interpreter=self.interpreter, request=request, statistics=self.statistics)) response = Response(page.content, page.status, page.headers, mimetype=mimeLib.getmime('*.html')) return self.return_response(request, response, environ, start_response) if request.form.get('action', '') == 'delete': if not Account.can_access( self.get_current_account(request)[0], resource, False): response = Response('forbidden', 403) else: filelist = request.form.getlist('selection') try: for i in filelist: smartremove(Config.base_path + i) response = Response('ok', 200) except Exception as err: response = Response(str(err), 500) return self.return_response(request, response, environ, start_response) if 'mode' in request.args: # urlvar mode mode = request.args['mode'] if mode == 'section': section_name = request.args.get('id', '') page = uni_param.interpreter.section_to_page( section_name, uni_param) response = Response(page.content, page.status, page.headers, mimetype=mimeLib.getmime(section_name)) elif mode == 'login': account_name = request.form.get('user') token_hash = request.form.get('passwordSHA256') expected_hash = hashLib.BaseHashToTokenHash( Account.get_account_detail(account_name)[0], request.cookies.get('HFS_SID_', '')).get() if account_name not in Account.accounts: response = Response('username not found', 200) elif token_hash != expected_hash: response = Response('bad password', 200) else: sid = hashlib.sha256( bytes([random.randint(0, 255) for _ in range(32)])).hexdigest() self.statistics.accounts[sid] = (account_name, request.host) response = Response('ok', 200) response.headers.add_header( 'Set-Cookie', 'HFS_SID_=%s; HttpOnly; Max-Age=0' % request.cookies.get('HFS_SID_', '')) response.headers.add_header('Set-Cookie', 'HFS_SID_=%s; HttpOnly' % sid) elif mode == 'logout': sid = request.cookies.get('HFS_SID_', '') if sid in self.statistics.accounts: del self.statistics.accounts[sid] response = Response( 'ok', 200, {'Set-Cookie': 'HFS_SID_=%s; HttpOnly; Max-Age=0' % sid}) elif mode == 'archive': tmp = tempfile.TemporaryFile(mode='w+b') tar = tarfile.open(mode='w', fileobj=tmp) path_real = request.path_real_dir + '/' filelist = request.form.getlist('selection') final_list_without_dots = [ ((Config.base_path if x[0:1] == '/' else path_real) + x) for x in filelist if x[0:1] != '.' ] final_list_with_dots = [ ((Config.base_path if x[0:1] == '/' else path_real) + x) for x in filelist ] shown_files = final_list_without_dots if Config.hide_dots else final_list_with_dots for i in shown_files: is_recursive = 'recursive' in request.args or bool( Config.recur_archive) tar.add(i, i, recursive=is_recursive) tar.close() # Pointer is at the end of file tmp.seek(0) # Read at start response = send_file( tmp, environ, mimetype=mimeLib.getmime('*.tar'), as_attachment=True, download_name=os.path.basename(request.path_virtual_dir) + '.selection.tar') return self.return_response(request, response, environ, start_response) elif 'search' in request.args: # Search, with re.findall directory = request.path_real_dir if not os.path.isdir(directory): response = self.not_found_response(request) return self.return_response(request, response, environ, start_response) pattern = re.compile(wildcard2re(request.args['search']), re.I) recursive = 'recursive' in request.args or bool( Config.recur_search) items_folder = [] items_file = [] if recursive: for dirpath, dirnames, filenames in os.walk(directory): for i in dirnames: if re.findall(pattern, i): items_folder.append(os.path.join(dirpath, i)) for i in filenames: if re.findall(pattern, i): items_file.append(os.path.join(dirpath, i)) else: for i in os.scandir(directory): if re.findall(pattern, i.name): if i.is_dir: items_folder.append(i.path) else: items_file.append(i.path) path_real_dir = request.path_real_dir + '/' shown_files = [ x for x in (items_folder + items_file) if x[0:1] != '.' ] if Config.hide_dots else (items_folder + items_file) paths = [x.replace('\\', '/') for x in shown_files] items = [ItemEntry(x, x, path_real_dir) for x in paths] filelist = FileList(items) uni_param.filelist = filelist page = uni_param.interpreter.get_page('', uni_param) response = Response(page.content, page.status, page.headers, mimetype=mimeLib.getmime('*.html')) return self.return_response(request, response, environ, start_response) elif 'filter' in request.args: # Filter, with re.fullmatch directory = request.path_real_dir if not os.path.isdir(directory): response = self.not_found_response(request) return self.return_response(request, response, environ, start_response) pattern = re.compile(wildcard2re(request.args['filter']), re.I) recursive = 'recursive' in request.args or bool( Config.recur_search) items_folder = [] items_file = [] if recursive: for dirpath, dirnames, filenames in os.walk(directory): for i in dirnames: if re.fullmatch(pattern, i): items_folder.append(os.path.join(dirpath, i)) for i in filenames: if re.fullmatch(pattern, i): items_file.append(os.path.join(dirpath, i)) else: for i in os.scandir(directory): if re.fullmatch(pattern, i.name): if i.is_dir: items_folder.append(i.path) else: items_file.append(i.path) path_real_dir = request.path_real_dir + '/' shown_files = [ x for x in (items_folder + items_file) if x[0:1] != '.' ] if Config.hide_dots else (items_folder + items_file) paths = [x.replace('\\', '/') for x in shown_files] items = [ItemEntry(x, x, path_real_dir) for x in paths] filelist = FileList(items) uni_param.filelist = filelist page = uni_param.interpreter.get_page('', uni_param) response = Response(page.content, page.status, page.headers, mimetype=mimeLib.getmime('*.html')) return self.return_response(request, response, environ, start_response) elif levels_virtual[-1][0:1] == '~': # Command command = levels_virtual[-1][1:] if len(levels_virtual) == 2: # Section call, only at root global builtin_sections section = uni_param.interpreter.get_section( command, uni_param, True, False) if section != None: page = Page(section.content, 200) response = Response(page.content, page.status, page.headers, mimetype=mimeLib.getmime(path)) elif command in builtin_sections: response = send_file(command, environ, mimeLib.getmime(command)) else: response = self.not_found_response(request) if command == 'upload' and if_upload_allowed_in( request.path_real, Config): page = uni_param.interpreter.get_page('upload', uni_param) response = Response(page.content, page.status, page.headers, mimetype=mimeLib.getmime('*.html')) elif command == 'folder.tar': tmp = tempfile.TemporaryFile(mode='w+b') tar = tarfile.open(mode='w', fileobj=tmp) path_real = request.path_real_dir + '/' shown_files = [ x for x in os.listdir(request.path_real_dir) if x[0:1] != '.' ] if Config.hide_dots else os.listdir(request.path_real_dir) for i in shown_files: is_recursive = 'recursive' in request.args or bool( Config.recur_archive) tar.add(path_real + i, i, recursive=is_recursive) tar.close() # Pointer is at the end of file tmp.seek(0) # Read at start response = send_file(tmp, environ, mimetype=mimeLib.getmime('*.tar')) elif command == 'files.lst': uni_param.interpreter = self.itp_filelist path_real_dir = request.path_real_dir + '/' shown_files = [ x for x in os.listdir(request.path_real_dir) if x[0:1] != '.' ] if Config.hide_dots else os.listdir(request.path_real_dir) paths = [join_path(path_real_dir, x) for x in shown_files] items = [ItemEntry(x, x, path_real_dir) for x in paths] filelist = FileList(items) uni_param.filelist = filelist page = uni_param.interpreter.get_page('', uni_param) response = Response(page.content, page.status, page.headers, mimetype=mimeLib.getmime('*.txt')) return self.return_response(request, response, environ, start_response) elif resource != None: # Filelist or send file or 404 if os.path.exists(resource): if os.path.isdir(resource): # List files if 'no list' not in uni_param.interpreter.sections[ ''].params: path_real_dir = request.path_real_dir + '/' shown_files = [ x for x in os.listdir(path_real_dir) if x[0:1] != '.' ] if Config.hide_dots else os.listdir(path_real_dir) paths = [ join_path(path_real_dir, x) for x in shown_files ] items = [ItemEntry(x, x, path_real_dir) for x in paths] filelist = FileList(items) uni_param.filelist = filelist page = uni_param.interpreter.get_page('', uni_param) response = Response(page.content, page.status, page.headers, mimetype=mimeLib.getmime( '*.txt' if uni_param.interpreter == self.itp_filelist else '*.html')) elif levels_real[-1].lower().endswith( '.zip') and Config.preview_zip: # Preview zip file if configured if resource not in self.cached_zip_files: try: zip_file = zipfile.ZipFile(resource, 'r') except zipfile.BadZipFile: response = Response( I18n.get_string('zip_file_is_broken'), 202) return self.return_response( request, response, environ, start_response) self.cached_zip_files[resource] = (zip_file, ) zip_file_data = self.cached_zip_files[resource] if 'getitem' in request.args: zip_file = zip_file_data[0] filename = request.args['getitem'] try: response = Response( zip_file.read(filename), 200, mimetype=mimeLib.getmime(filename)) except KeyError: response = self.not_found_response(request) else: items = [ ZipItemEntry(x, resource, path) for x in zip_file_data[0].filelist if x.filename[-1:] != '/' ] filelist = FileList(items) uni_param.filelist = filelist page = uni_param.interpreter.get_page('', uni_param) response = Response( page.content, page.status, page.headers, mimetype=mimeLib.getmime( '*.txt' if uni_param.interpreter == self.itp_filelist else '*.html')) else: # A file response = send_file(resource, environ) else: # 404 response = self.not_found_response(request) return self.return_response(request, response, environ, start_response) else: response = self.not_found_response(request) return self.return_response(request, response, environ, start_response)
#!/usr/bin/python3 import sys from tplLib import Interpreter from classesLib import UniParam itp = Interpreter() print(itp.parse_text(sys.argv[1], UniParam([], interpreter=itp)).content)