def read_write_directory(directory): if os.path.exists(directory): if os.access(directory, os.W_OK and os.R_OK): return directory else: error('The output is not readable and/or writable') else: error('The specified directory does not exist')
def handler(signal, frame): print() error('Exiting!')
def home(path, message=''): #kill the server if args.k: if request.args.get('stop') is not None: print() error('User requested kill server!') #only upload if args.u: return render_template('upload.html', version=VERSION, killable=args.k) #only serving a specific file option if fileToServe: return serveFile(fileToServe, False) # If there is a path parameter and it is valid if path and is_valid_subpath(path, base_directory): # Take off the trailing '/' path = os.path.normpath(path) requested_path = os.path.join(base_directory, path) # If directory if os.path.isdir(requested_path): back = get_parent_directory(requested_path, base_directory) is_subdirectory = True if args.z: if request.args.get('zip') is not None: tmp = tempfile.NamedTemporaryFile(mode='w+b', suffix='.zip', delete=True) zipf = zipfile.ZipFile(tmp.name, 'w', zipfile.ZIP_DEFLATED) zipdir(requested_path, zipf) zipf.close() success('Directory zipped: %s' % requested_path) return serveFile(tmp.name, True) # If file elif os.path.isfile(requested_path): if args.z: if request.args.get('zip') is not None: tmp = tempfile.NamedTemporaryFile(mode='w+b', suffix='.zip', delete=True) zipf = zipfile.ZipFile(tmp.name, 'w', zipfile.ZIP_DEFLATED) zipf.write(requested_path, arcname=os.path.basename(requested_path), compress_type=zipfile.ZIP_DEFLATED) zipf.close() success('File zipped: %s' % requested_path) return serveFile(tmp.name, True) # Check if the view flag is set if request.args.get('view') is None: send_as_attachment = True else: send_as_attachment = False return serveFile(requested_path, send_as_attachment) else: # Root home configuration is_subdirectory = False requested_path = base_directory back = '' if os.path.exists(requested_path): # Read the files try: directory_files = process_files( os.scandir(requested_path), base_directory, imageOnly=(args.g and request.args.get('gallery') is not None)) except PermissionError: abort(403, 'Read Permission Denied: ' + requested_path) homeHtml = 'home.html' if args.l: homeHtml = 'lite.html' if args.g: if request.args.get('gallery') is not None: info('gallery mode') homeHtml = 'gallery.html' pathsList = split_path(requested_path, base_directory) requested_path = requested_path.replace('\\', '/') return render_template(homeHtml, files=directory_files, back=back, galleryAllow=args.g, directory=requested_path, is_subdirectory=is_subdirectory, version=VERSION, killable=args.k, zipAllow=args.z, canExecute=args.x, canModify=args.m, paths=pathsList[1], directories=pathsList[0], len=len(pathsList[0])) else: return redirect('/')
def main(): args = parse_arguments() app = Flask(__name__) app.secret_key = os.urandom(16) auth = HTTPBasicAuth() global base_directory base_directory = args.directory fileToServe = '' if args.file: fileToServe = os.path.join(base_directory, args.file) # Deal with Favicon requests @app.route('/favicon.ico') def favicon(): if fileToServe or args.l: return abort(500, 'No icon') return send_from_directory(os.path.join(app.root_path, 'static'), 'images/favicon.ico', mimetype='image/vnd.microsoft.icon') ############################################ # File Browsing and Download Functionality # ############################################ @app.route('/', defaults={'path': None}, methods=['GET']) @app.route('/<path:path>', methods=['GET']) @auth.login_required def home(path, message=''): #kill the server if args.k: if request.args.get('stop') is not None: print() error('User requested kill server!') #only upload if args.u: return render_template('upload.html', version=VERSION, killable=args.k) #only serving a specific file option if fileToServe: return serveFile(fileToServe, False) # If there is a path parameter and it is valid if path and is_valid_subpath(path, base_directory): # Take off the trailing '/' path = os.path.normpath(path) requested_path = os.path.join(base_directory, path) # If directory if os.path.isdir(requested_path): back = get_parent_directory(requested_path, base_directory) is_subdirectory = True if args.z: if request.args.get('zip') is not None: tmp = tempfile.NamedTemporaryFile(mode='w+b', suffix='.zip', delete=True) zipf = zipfile.ZipFile(tmp.name, 'w', zipfile.ZIP_DEFLATED) zipdir(requested_path, zipf) zipf.close() success('Directory zipped: %s' % requested_path) return serveFile(tmp.name, True) # If file elif os.path.isfile(requested_path): if args.z: if request.args.get('zip') is not None: tmp = tempfile.NamedTemporaryFile(mode='w+b', suffix='.zip', delete=True) zipf = zipfile.ZipFile(tmp.name, 'w', zipfile.ZIP_DEFLATED) zipf.write(requested_path, arcname=os.path.basename(requested_path), compress_type=zipfile.ZIP_DEFLATED) zipf.close() success('File zipped: %s' % requested_path) return serveFile(tmp.name, True) # Check if the view flag is set if request.args.get('view') is None: send_as_attachment = True else: send_as_attachment = False return serveFile(requested_path, send_as_attachment) else: # Root home configuration is_subdirectory = False requested_path = base_directory back = '' if os.path.exists(requested_path): # Read the files try: directory_files = process_files( os.scandir(requested_path), base_directory, imageOnly=(args.g and request.args.get('gallery') is not None)) except PermissionError: abort(403, 'Read Permission Denied: ' + requested_path) homeHtml = 'home.html' if args.l: homeHtml = 'lite.html' if args.g: if request.args.get('gallery') is not None: info('gallery mode') homeHtml = 'gallery.html' pathsList = split_path(requested_path, base_directory) requested_path = requested_path.replace('\\', '/') return render_template(homeHtml, files=directory_files, back=back, galleryAllow=args.g, directory=requested_path, is_subdirectory=is_subdirectory, version=VERSION, killable=args.k, zipAllow=args.z, canExecute=args.x, canModify=args.m, paths=pathsList[1], directories=pathsList[0], len=len(pathsList[0])) else: return redirect('/') ################################################# # Send message back and return to previous page # ################################################# def returnWithMessage(message): flash(message) return redirect(request.referrer) ############################## # File Actions Functionality # ############################## @app.route('/fileAction', methods=['POST']) @auth.login_required def fileAction(): #if it's upload only or server single file only, accept no file action if fileToServe or args.u: abort(403, 'Permission denied') if request.method == 'POST': if 'action' not in request.form or 'path' not in request.form: #invalid path or action return returnWithMessage('Invalid action.') filename = request.form['file'] full_path = os.path.join(request.form['path'], filename) # Prevent access to paths outside of base directory if not is_valid_upload_path(request.form['path'], base_directory): flash('Not a valid path.') return redirect(request.referrer) full_path = os.path.realpath(full_path) if request.form['action'] == 'newFolder' and args.m: if not os.path.exists(full_path): os.mkdir(full_path) return returnWithMessage('Directory created') return returnWithMessage( 'Folder already exists with that name.') if not os.path.exists(full_path): abort(404, 'File not found: %s' % full_path) #execute the file if request.form['action'] == 'execute' and args.x: runCommand = 'sh ' + full_path if os.name == 'nt': runCommand = 'start ' + full_path os.system(runCommand) flash('File executed.') return redirect(request.referrer) #allow file modifications if not args.m: flash('File modifications not allowed.') return redirect(request.referrer) #delete the file if request.form['action'] == 'delete': #if is file if os.path.isfile(full_path): os.remove(full_path) else: #if is directory shutil.rmtree(full_path) flash('Deleted.') return redirect(request.referrer) if 'newName' not in request.form: #no newName present flash('No new name added.') return redirect(request.referrer) #from this point on, needs new name if request.form['newName'] == '': #invalid newName flash('No new name returned.') return redirect(request.referrer) #options bellow expect field newName newfilename = secure_filename(request.form['newName']) new_full_path = os.path.join(request.form['path'], newfilename) if new_full_path == full_path: #same name, ignore flash('Same file?') return redirect(request.referrer) #rename the file to a new name if request.form['action'] == 'rename': shutil.move(full_path, new_full_path) flash('File renamed.') return redirect(request.referrer) #copy the file if request.form['action'] == 'copy': if os.path.isdir(full_path): if os.path.isdir(new_full_path): shutil.copytree(full_path, new_full_path) else: if not os.path.isdir(new_full_path): shutil.copyfile(full_path, new_full_path) flash('Copied file.') return redirect(request.referrer) ############################# # File Upload Functionality # ############################# @app.route('/upload', methods=['POST']) @auth.login_required def upload(): if fileToServe: abort(403, 'Permission denied') #only if file modifications are allowed if not args.m and not args.u: info('Attempt to upload file withou permissions') return redirect(request.referrer) if request.method == 'POST': # No file part - needs to check before accessing the files['file'] if 'file' not in request.files: return redirect(request.referrer) if 'path' not in request.form or request.form[ 'path'] == '' or request.form[ 'path'] == '.' or request.form['path'] == '/': path = base_directory else: path = request.form['path'] # Prevent file upload to paths outside of base directory if not is_valid_upload_path(path, base_directory): return redirect(request.referrer) for file in request.files.getlist('file'): # No filename attached if file.filename == '': return redirect(request.referrer) # Assuming all is good, process and save out the file if file: filename = secure_filename(file.filename) full_path = os.path.join(path, filename) #if it's upload UI only, and there's already a file with that name, change name if args.u: while os.path.exists(full_path): filename = 'cp.' + filename full_path = os.path.join(path, filename) # if not Upload only then it assumes the user wanted to overwrite the file try: info('File upload: %s' % full_path) file.save(full_path) success('Uploaded') except PermissionError: abort(403, 'Write Permission Denied: ' + full_path) return redirect(request.referrer) # Password functionality is without username users = {'': generate_password_hash(args.password)} @auth.verify_password def verify_password(username, password): if args.password: if username in users: return check_password_hash(users.get(username), password) return False else: return True # Inform user before server goes up if fileToServe: if os.path.isfile(fileToServe): success('Serving file %s...' % fileToServe) else: error('File %s not found' % fileToServe) else: success('Serving {}...'.format(args.directory, args.port)) #exit coded def handler(signal, frame): print() error('Exiting!') signal.signal(signal.SIGINT, handler) if args.q: localIp = get_ip() # '0.0.0.0' else: localIp = '0.0.0.0' ssl_context = None if args.ssl: if args.cert != '' and args.pKey != '': if not os.path.exists(args.pKey) or not os.path.exists(args.cert): print() error('Files provided as CERT or PrivKey do not exist') keyFile = args.pKey crtFile = args.cert else: certsPath = os.path.join(app.root_path, 'certs') crtFile = os.path.join(certsPath, 'local.crt') keyFile = os.path.join(certsPath, 'local.key') #on the first run, create the file if not os.path.exists(certsPath): os.mkdir(certsPath) hostName = args.hostname create_self_signed_cert(crtFile, keyFile, hostName) success('Certificates created for the first time for %s' % hostName) ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) ssl_context.load_cert_chain(crtFile, keyFile) protocol = 'https://' url = protocol + localIp if args.port != 443: url = url + ":" + str(args.port) else: protocol = 'http://' url = protocol + localIp if args.port != 80: url = url + ":" + str(args.port) #show QRCode in console if args.q: qr = QRCode.getMinimumQRCode(url, ErrorCorrectLevel.M) qr.setErrorCorrectLevel(ErrorCorrectLevel.M) qr.make() qr.printQr() #multicast-dns if args.mc: infoMulticast = get_service_info(args.mc) if infoMulticast is None: register_service(args.mc, args.port) success('Registered Multicast ' + protocol + args.mc + '.local') else: error('Multicast ' + args.mc + ' already registered.') #handle logs logger = logging.getLogger('werkzeug') if args.logFile: fileHandlerLog = logging.FileHandler(args.logFile, 'w') logger.addHandler(fileHandlerLog) print(localIp + ' - ' + str(args.port)) # localIp = '127.0.0.1' run_simple(localIp, args.port, app, ssl_context=ssl_context)