def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) client_dir = client_id_dir(client_id) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) # TODO: if validator is too tight we should accept rejects here # and then make sure that such rejected fields are never printed if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) path = accepted['path'][-1] current_dir = accepted['current_dir'][-1] # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath(os.path.join(configuration.user_home, client_dir)) + os.sep # the client can choose to specify the path of the target directory with # current_dir + "/" + path, instead of specifying the complete path in # subdirs. This is usefull from ls.py where a hidden html control makes it # possible to target the directory from the current dir. title_entry = find_entry(output_objects, 'title') title_entry['text'] = '%s file web editor' % configuration.short_title title_entry['style'] = advanced_editor_css_deps() title_entry['javascript'] = advanced_editor_js_deps() title_entry['javascript'] += lock_info('this file', -1) output_objects.append({'object_type': 'header', 'text' : 'Editing file in %s home directory' % \ configuration.short_title }) if not path: now = time.gmtime() path = 'noname-%s.txt' % time.strftime('%d%m%y-%H%M%S', now) output_objects.append({'object_type': 'text', 'text' : 'No path supplied - creating new file in %s' % path}) path = os.path.normpath(current_dir + path) real_path = os.path.abspath(base_dir + current_dir + path) if not valid_user_path(real_path, base_dir): logger.warning('%s tried to %s restricted path %s ! (%s)' % (client_id, op_name, real_path, path)) output_objects.append( {'object_type': 'error_text', 'text' : "Invalid path! (%s expands to an illegal path)" % path}) return (output_objects, returnvalues.CLIENT_ERROR) (owner, time_left) = acquire_edit_lock(real_path, client_id) if owner == client_id: javascript = \ '''<script type="text/javascript"> setTimeout("newcountdown('%s', %d)", 1) </script> '''\ % (path, time_left / 60) output_objects.append({'object_type': 'html_form', 'text' : javascript}) html = edit_file(path, real_path) output_objects.append({'object_type': 'html_form', 'text' : html}) else: output_objects.append( {'object_type': 'error_text', 'text' : '%s acquired the editing lock for %s! (timeout in %d seconds)' % (owner, path, time_left)}) return (output_objects, returnvalues.CLIENT_ERROR) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) client_dir = client_id_dir(client_id) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) if not correct_handler('POST'): output_objects.append( {'object_type': 'error_text', 'text' : 'Only accepting POST requests to prevent unintended updates'}) return (output_objects, returnvalues.CLIENT_ERROR) path = accepted['path'][-1] chosen_newline = accepted['newline'][-1] submitjob = accepted['submitjob'][-1] # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath(os.path.join(configuration.user_home, client_dir)) + os.sep # HTML spec dictates newlines in forms to be MS style (\r\n) # rather than un*x style (\n): change if requested. form_newline = '\r\n' allowed_newline = {'unix': '\n', 'mac': '\r', 'windows': '\r\n'} output_objects.append({'object_type': 'header', 'text' : 'Saving changes to edited file'}) if not chosen_newline in allowed_newline.keys(): output_objects.append( {'object_type': 'error_text', 'text' : 'Unsupported newline style supplied: %s (must be one of %s)' % (chosen_newline, ', '.join(allowed_newline.keys()))}) return (output_objects, returnvalues.CLIENT_ERROR) saved_newline = allowed_newline[chosen_newline] # Check directory traversal attempts before actual handling to avoid # leaking information about file system layout while allowing consistent # error messages real_path = '' unfiltered_match = glob.glob(base_dir + path) for server_path in unfiltered_match: real_path = os.path.abspath(server_path) if not valid_user_path(real_path, base_dir, True): logger.warning('%s tried to %s restricted path %s ! (%s)' % (client_id, op_name, real_path, path)) output_objects.append( {'object_type': 'error_text', 'text' : "Invalid path! (%s expands to an illegal path)" % path}) return (output_objects, returnvalues.CLIENT_ERROR) if real_path == '': real_path = base_dir + path if not valid_user_path(real_path, base_dir, True): logger.warning('%s tried to %s restricted path %s ! (%s)' % (client_id, op_name, real_path, path)) output_objects.append( {'object_type': 'error_text', 'text' : "Invalid path! (%s expands to an illegal path)" % path}) return (output_objects, returnvalues.CLIENT_ERROR) (owner, time_left) = acquire_edit_lock(real_path, client_id) if owner != client_id: output_objects.append({'object_type': 'error_text', 'text' : "You don't have the lock for %s!" % path}) return (output_objects, returnvalues.CLIENT_ERROR) try: fh = open(real_path, 'w+') fh.write(user_arguments_dict['editarea' ][0].replace(form_newline, saved_newline)) fh.close() # everything ok output_objects.append({'object_type': 'text', 'text' : 'Saved changes to %s.' % path}) release_edit_lock(real_path, client_id) except Exception, exc: # Don't give away information about actual fs layout output_objects.append({'object_type': 'error_text', 'text' : '%s could not be written! (%s)' % (path, str(exc).replace(base_dir, '' ))}) return (output_objects, returnvalues.SYSTEM_ERROR)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) client_dir = client_id_dir(client_id) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) path = accepted['path'][-1] chosen_newline = accepted['newline'][-1] submitjob = accepted['submitjob'][-1] if not safe_handler(configuration, 'post', op_name, client_id, get_csrf_limit(configuration), accepted): output_objects.append({ 'object_type': 'error_text', 'text': '''Only accepting CSRF-filtered POST requests to prevent unintended updates''' }) return (output_objects, returnvalues.CLIENT_ERROR) if not configuration.site_enable_jobs and submitjob: output_objects.append({ 'object_type': 'error_text', 'text': '''Job execution is not enabled on this system''' }) return (output_objects, returnvalues.SYSTEM_ERROR) # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath( os.path.join(configuration.user_home, client_dir)) + os.sep # HTML spec dictates newlines in forms to be MS style (\r\n) # rather than un*x style (\n): change if requested. form_newline = '\r\n' allowed_newline = {'unix': '\n', 'mac': '\r', 'windows': '\r\n'} output_objects.append({ 'object_type': 'header', 'text': 'Saving changes to edited file' }) if not chosen_newline in allowed_newline.keys(): output_objects.append({ 'object_type': 'error_text', 'text': 'Unsupported newline style supplied: %s (must be one of %s)' % (chosen_newline, ', '.join(allowed_newline.keys())) }) return (output_objects, returnvalues.CLIENT_ERROR) saved_newline = allowed_newline[chosen_newline] # Check directory traversal attempts before actual handling to avoid # leaking information about file system layout while allowing consistent # error messages abs_path = '' unfiltered_match = glob.glob(base_dir + path) for server_path in unfiltered_match: # IMPORTANT: path must be expanded to abs for proper chrooting abs_path = os.path.abspath(server_path) if not valid_user_path(configuration, abs_path, base_dir, True): logger.warning('%s tried to %s restricted path %s ! (%s)' % (client_id, op_name, abs_path, path)) output_objects.append({ 'object_type': 'error_text', 'text': "Invalid path! (%s expands to an illegal path)" % path }) return (output_objects, returnvalues.CLIENT_ERROR) if abs_path == '': # IMPORTANT: path must be expanded to abs for proper chrooting abs_path = os.path.abspath(os.path.join(base_dir, path.lstrip(os.sep))) if not valid_user_path(configuration, abs_path, base_dir, True): logger.warning('%s tried to %s restricted path %s ! (%s)' % (client_id, op_name, abs_path, path)) output_objects.append({ 'object_type': 'error_text', 'text': "Invalid path! (%s expands to an illegal path)" % path }) return (output_objects, returnvalues.CLIENT_ERROR) if not check_write_access(abs_path, parent_dir=True): logger.warning('%s called without write access: %s' % \ (op_name, abs_path)) output_objects.append( {'object_type': 'error_text', 'text': 'cannot edit "%s": inside a read-only location!' % \ path}) status = returnvalues.CLIENT_ERROR return (output_objects, returnvalues.CLIENT_ERROR) (owner, time_left) = acquire_edit_lock(abs_path, client_id) if owner != client_id: output_objects.append({ 'object_type': 'error_text', 'text': "You don't have the lock for %s!" % path }) return (output_objects, returnvalues.CLIENT_ERROR) try: fh = open(abs_path, 'w+') fh.write(user_arguments_dict['editarea'][0].replace( form_newline, saved_newline)) fh.close() # everything ok output_objects.append({ 'object_type': 'text', 'text': 'Saved changes to %s.' % path }) logger.info('saved changes to %s' % path) release_edit_lock(abs_path, client_id) except Exception, exc: # Don't give away information about actual fs layout output_objects.append({ 'object_type': 'error_text', 'text': '%s could not be written! (%s)' % (path, str(exc).replace(base_dir, '')) }) return (output_objects, returnvalues.SYSTEM_ERROR)