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 macro_count(self, param: UniParam): counts = param.request.counts if param[1] in counts: counts[param[1]] += 1 return MacroResult(str(counts[param[1]])) else: counts[param[1]] = 0 return MacroResult('0')
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 lesser_or_equal_than(self, param: UniParam): p = param.params status = True for i in range(1, len(p) - 1): if float(p[i]) > float(p[i + 1]): status = False return MacroResult(self._bool(status))
def pos(self, param: UniParam): optional_params = self._get_optional_params(param, 'var') str_to_find = param[2] if 'var' in optional_params: str_to_find = self._get_variable(optional_params['var'], param) position = str_to_find.find(param[1]) + 1 return MacroResult(self.FALSE if position == 0 else str(position))
def time(self, param: UniParam): optional_params = self._get_optional_params(param, 'format', 'when') time_format = optional_params.get('format', 'yyyy-mm-dd hh:MM:ss') when = float(optional_params.get('when', time.time())) f = datetime.datetime.fromtimestamp(when) result = time_format.replace('c', str(f)).replace('yyyy', '%04d' % f.year).replace('mm', '%02d' % f.month).replace('dd', '%02d' % f.day).replace('hh', '%02d' % f.hour).replace('MM', '%02d' % f.minute).replace('ss', '%02d' % f.second) return MacroResult(result)
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 macro_dir(self, param: UniParam): optional_params = self._get_optional_params(param, 'separator') separator = optional_params.get('separator', '|') path = self._get_existing_filename(param[1]) result = '' if path != None: result = separator.join(os.listdir(path)) return MacroResult(result)
def macro_chr(self, param: UniParam): result = '' for i in param.params[1:]: if i[0:1].lower() == 'x': result = result + chr(int(i[1:], 16)) else: result = result + chr(int(i, 10)) return MacroResult(result)
def cookie(self, param: UniParam): optional_params = self._get_optional_params(param, 'value', 'expires', 'path') result = '' headers = {} if 'value' not in optional_params: result = param.request.cookies.get(param[1], self.FALSE) else: headers['Set-Cookie'] = '; '.join(('%s=%s' % (param[1], optional_params['value']), 'expires=' + optional_params.get('expires', ''), 'path=' + optional_params.get('path', '/'))) return MacroResult(result, headers=headers)
def macro_for_each(self, param: UniParam): p = param.params var_name = p[1] var_range = p[2:-1] macro_body = param.interpreter.unquote(p[-1], param, False).content result = [] for i in var_range: self._set_variable(var_name, i, param) result.append(param.interpreter.parse_text(macro_body, param)) return MacroResult(''.join([x.content for x in result]))
def save(self, param: UniParam): optional_params = self._get_optional_params(param, 'var') path = self._predict_filename(param[1]) f = open(path or param[1], 'w', encoding='utf-8') if optional_params.get('var', '') == '': f.write(param[2]) else: f.write(param.request.variables[optional_params['var']]) f.close() return MacroResult(self.TRUE2)
def cut(self, param: UniParam): if len(param[1]) == 0: param[1] = '0' if len(param[2]) == 0: param[2] = '0' start = int(param[1]) end = int(param[2]) if start < 0 and end < 0 and start > end: start, end = end, start string = param[3] return MacroResult(string[start:end])
def macro_for(self, param: UniParam): p = param.params var_name = p[1] var_step = int(p[4]) if len(p) > 5 else 1 var_range = range(int(p[2]), int(p[3]) + 1, var_step) macro_body = param.interpreter.unquote(p[5] if len(p) > 5 else p[4], param, False).content result = [] for i in var_range: self._set_variable(var_name, str(i), param) result.append(param.interpreter.parse_text(macro_body, param)) return MacroResult(''.join([x.content for x in result]))
def macro_while(self, param: UniParam): is_variable = not param.interpreter.is_quoted(param[1]) result = [] while True: if is_variable: if not self._judge(self._get_variable(param[1], param)): break else: if not self._judge(param.interpreter.unquote(param[1], param, True).content): break result.append(param.interpreter.unquote(param[2], param, True)) return MacroResult(''.join([x.content for x in result]))
def not_equal(self, param: UniParam): p = param.params status = True for i in range(1, len(p) - 1): first: str = p[i] second: str = p[i + 1] if is_number(first) and is_number(second): first = float_to_str(float(first)) second = float_to_str(float(second)) if first == second: status = False break return MacroResult(self._bool(status))
def dialog(self, param: UniParam): l = len(param.params) exitcode = 0 buttons = param[2].split(' ') if l == 2: exitcode = smartmsgbox(param[1], [], '') elif l == 3: exitcode = smartmsgbox(param[1], buttons, '') elif l == 4: exitcode = smartmsgbox(param[1], buttons, param[3]) result = self._bool(exitcode == 1 or exitcode == 6) # OK/Yes if exitcode == 2 and 'yesnocancel' in buttons: result = 'cancel' return MacroResult(result)
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 load(self, param: UniParam): optional_params = self._get_optional_params(param, 'from', 'size', 'to', 'var') p_from = 0 p_to = 0 p_size = 0 data = '' path = self._get_existing_filename(param[1]) if path != None: p_from = int(optional_params.get('from', '0')) p_to = int(optional_params.get('to', str(os.path.getsize(path)))) p_size = int(optional_params.get('size', str(p_to - p_from))) f = open(path, 'rb') f.seek(p_from) data = f.read(p_size).decode('utf-8') f.close() if optional_params.get('var', '') != '': param.request.variables[optional_params['var']] = data data = '' return MacroResult(data)
def substring(self, param: UniParam): # Case sensitivity always true string: str = param[3] start = string.find(param[1]) + len(param[1]) end = string.find(param[2]) if start in (0, len(string) - 1): start = 0 if end in (0, -1): end = len(string) optional_params = self._get_optional_params(param, 'include') include = optional_params.get('include', '1') result = string[start:end] if include == '1': result = param[1] + result elif include == '2': result = result + param[2] elif include == '1+2': result = param[1] + result + param[2] return MacroResult(result)
def get(self, param: UniParam): # TODO key = param[1] result = self.FALSE if key in ('can recur', 'can archive', 'stop spiders'): result = self.TRUE elif key in ('can access'): account_name = param.statistics.accounts.get(param.request.cookies.get('HFS_SID_', ''), ('', ''))[0] result = self._bool(Account.can_access(account_name, param.request.path_real, True)) elif key in ('can delete'): account_name = param.statistics.accounts.get(param.request.cookies.get('HFS_SID_', ''), ('', ''))[0] result = self._bool(Account.can_access(account_name, param.request.path_real, False)) elif key == 'can upload': result = self._bool(if_upload_allowed_in(param.request.path_real, Config)) elif key == 'accounts': result = Config.accounts.replace('|', ';') elif key == 'protocolon': result = 'https://' if param.request.is_secure else 'http://' elif key == 'speed limit': result = '0' elif key == 'agent': result = str(param.request.user_agent.browser) # Maybe None, also differ from oHFS return MacroResult(result)
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
def regexp(self, param: UniParam): regex = param[1] result = re.match(regex, param[2], re.I | re.M) return MacroResult(self._bool(result))
def match(self, param: UniParam): regex = wildcard2re(param[1]) result = re.match(regex, param[2], re.I | re.M) return MacroResult(self._bool(result))
def add_header(self, param: UniParam): header = param[1].split(':') header_dict = { header[0]: header[1].strip() } return MacroResult('', headers=header_dict)
def disconnect(self, param: UniParam): return MacroResult('', disconnect=True)
def parse_text(self, text: str, param: UniParam) -> MacroResult: """ Parse a string and apply symbols and macros """ text = self.parse_symbols(text, param, param.symbols, self.symbols) macro_level = 0 quote_level = 0 position = 0 newlines = 0 last_newline_at = 0 length = len(text) full_macro = [''] broken = False disconnect = False headers = {} while position < length: # We parse content char by char and find tokens last_macro_at = position char0 = text[position] mark0 = text[position:position + 2] if char0 == '\n': newlines += 1 last_newline_at = position if mark0 == '{.' and quote_level == 0: macro_level += 1 if macro_level > 100: raise TooManyRecurs( 'Too many recurs at line %d, column %d' % (newlines, position - last_newline_at)) elif mark0 == '.}' and quote_level == 0: # Macros are executed inner to outer, so when we find the first CLOSE, # we reverse parse the content and find the first OPEN, # and collect parameters, then execute & replace the macro # Also note, many things have inverted logic here :) while position >= 0: char1 = text[position] mark1 = text[position:position + 2] if char1 == '\n': newlines -= 1 last_newline_at = position if mark1 == '{.' and quote_level == 0: macro_level -= 1 full_macro = list(reversed(full_macro)) full_macro[0] = full_macro[0][1:] full_macro[-1] = full_macro[-1][0:-1] result = MacroResult('') if not (broken or disconnect): result = self.exec_macro(full_macro, param) if result.do_break: broken = True if result.disconnect: disconnect = True return MacroResult('', do_break=True, disconnect=True, headers=headers) for i in result.headers: headers[i] = result.headers[i] macro_str = '{.' + '|'.join(full_macro) + '.}' text = replace_str(text, macro_str, result.content) text = self.parse_symbols(text, param, param.symbols, self.symbols) length = len(text) full_macro = [''] break elif mark1 == '{:': quote_level -= 1 elif mark1 == ':}': quote_level += 1 if char1 == '|' and quote_level == 0: full_macro.append('') else: full_macro[-1] = char1 + full_macro[-1] if position == 0: raise MacroNotClosedProperly( 'Macro not closed properly at line %d, column %d' % (newlines, last_macro_at - last_newline_at)) position -= 1 continue elif mark0 == '{:': quote_level += 1 elif mark0 == ':}': quote_level -= 1 position += 1 if macro_level != 0: raise MacroNotClosedProperly( 'Macro not closed properly at level %i' % macro_level) # text = self.unquote(text, param, False).content return MacroResult(text, do_break=broken, disconnect=disconnect, headers=headers)
def translation(self, param: UniParam): t = param.interpreter p = param.params return MacroResult(t.translations[p[1]] if p[1] in t.translations else (p[2] if len(p) > 2 else p[1]))
def length(self, param: UniParam): return MacroResult(str(len(param[1])))