def file_view(request): """ Show an uploaded file. .../{action}/{identifier} """ TEMP_FOLDER_NAME = "temp" try: action = request.matchdict["action"] identifier = request.matchdict["identifier"] except KeyError: raise HTTPNotFound() # Check if the action is valid if not (action == "view" or action == "download"): raise HTTPNotFound() # Check if the identifier is valid if validate_uuid(identifier) is False: raise HTTPNotFound() # Try to find necessary information of the file (mime-type) db_file_query = Session.query(File).filter(File.identifier == identifier) try: db_file = db_file_query.one() except NoResultFound: raise HTTPNotFound() except MultipleResultsFound: # This should actually never happen since we are dealing with UUIDs raise HTTPNotFound() # Get file extension extension = get_valid_file_extension(request, db_file.mime) if extension is None: # This should also never happen because files without valid mime type # should not have been uploaded in the first place raise HTTPNotFound() # Put together the filename filename = "%s%s" % (identifier, extension) # Try to find the file on disk upload_path = upload_directory_path(request) folder1, folder2 = get_folders_from_identifier(str(identifier)) filepath = os.path.join(upload_path, folder1, folder2, filename) # Check that the file is on the disk temporaryFile = False if not os.path.exists(filepath): # If the file was not found in its proper directory, try to find it in # the temporary upload directory. This means the Activity was not (yet) # submitted and the file is only visible for logged in users. filepath = os.path.join(upload_path, TEMP_FOLDER_NAME, filename) if not os.path.exists(filepath): # If it is still not found, raise error raise HTTPNotFound() # TODO: Authentication: Handle it properly if "system.Authenticated" not in effective_principals(request): raise HTTPForbidden() temporaryFile = True # If the file is not in the temporary folder, find its Activity versions by # searching for its A_Value (filename|UID). if temporaryFile is False: a_value = "%s|%s" % (db_file.name, db_file.identifier) a_db_query = ( Session.query(Activity) .join(A_Tag_Group, A_Tag_Group.fk_activity == Activity.id) .join(A_Tag, A_Tag.fk_tag_group == A_Tag_Group.id) .join(A_Key, A_Tag.fk_key == A_Key.id) .join(A_Value, A_Tag.fk_value == A_Value.id) .filter(A_Key.type.ilike("File")) .filter(A_Value.value.ilike(a_value)) ) # Files for Activities are always visible if there is an active version # having the file attached. # Files for pending versions of Activities are only visible if the # current user is moderator or edited at least one pending version. showFile = False for a in a_db_query.all(): if a.fk_status == 2: # Active: There is an active version with the file, show it showFile = True break if a.fk_status == 1 and request.user is not None: # Pending: Check if user is moderator or created the version. if "group:moderators" in effective_principals(request): # Moderator showFile = True break if a.changeset.user == request.user: # Editor of a version showFile = True break if showFile is False: raise HTTPForbidden() # Open the file file = open(filepath, "rb").read() response = Response(body=file, content_type=str(db_file.mime)) if action == "download": response.content_disposition = "attachment; filename=%s" % db_file.name return response
def check_file_location_name(request, filevaluestring): """ Check if the files in a filevaluestring of the format cleanfilename|fileidentifier,cleanfilename|fileidentifier are still in the temporary upload directory (meaning that they were just recently uploaded). If so, move them to their proper directory (create it if necessary). (Also check if the file was renamed. In this case, update the database entry.) - see comments below """ TEMP_FOLDER_NAME = "temp" # A database query is needed to find out the mime-type (for the file # extension) and the name of the file. fileidentifiers = [] filenames = [] fileobjects = filevaluestring.split(",") for file in fileobjects: # Collect all fileidentifiers to query the database only once f = file.split("|") if len(f) != 2: # Something is wrong with the filevaluestring: skip it continue fileidentifiers.append(f[1]) filenames.append(f[0]) files_query = Session.query(File).filter(File.identifier.in_(fileidentifiers)).all() for i, fileidentifier in enumerate(fileidentifiers): # Loop all the file identifiers for f_db in files_query: # For each file identifier, try to find it in the database query if str(f_db.identifier) != fileidentifier: continue # Check if the file needs to be moved. This is the case if the file # is found in the temporary upload directory. extension = get_valid_file_extension(request, f_db.mime) if extension is None: # This should also never happen because files without valid # mime type should not have been uploaded in the first place continue # Put together the filename filename = "%s%s" % (fileidentifier, extension) # Try to find the file in the temporary directory upload_path = upload_directory_path(request) filepath = os.path.join(upload_path, TEMP_FOLDER_NAME, filename) if os.path.exists(filepath): # If the file is still in the temporary directory, move it folder1, folder2 = get_folders_from_identifier(fileidentifier) new_location = os.path.join(upload_path, folder1, folder2) # If the directory does not yet exist, create it if not os.path.exists(new_location): os.makedirs(new_location) # Rename works to move the file os.rename(filepath, os.path.join(new_location, filename)) log.debug( "Moved file %s from temporary folder to new location at \ %s." % (f_db.name, new_location) ) # Return the file information, use only the valid ones rets = [] for i, fileidentifier in enumerate(fileidentifiers): rets.append("%s|%s" % (filenames[i], fileidentifier)) return ",".join(rets)
def handle_upload(request, filedict): """ Handle the upload of a new file. http://code.google.com/p/file-uploader/ """ TEMP_FOLDER_NAME = "temp" ret = {"success": False, "msg": ""} filename = None filetype = None file = None try: filename = filedict["filename"] file = filedict["fp"] filetype = filedict["mimetype"] except: ret["msg"] = _("Not all necessary values were provided.") valid = False if filename is None or file is None or filetype is None: ret["msg"] = "Uploaded file not found." # Check upload directory upload_path = upload_directory_path(request) if upload_path is None or not os.path.exists(upload_path): ret["msg"] = _("Upload directory not specified or not found.") return ret # Check filetype fileextension = get_valid_file_extension(request, filetype) if fileextension is None: ret["msg"] = _("File type is not valid.") return ret # Check filesize size = get_file_size(file) if size > upload_max_file_size(request): ret["msg"] = _("File is too big.") return ret # Do the actual file processing # Strip leading path from file name to avoid directory traversal # attacks old_filename = os.path.basename(filename) # Internet Explorer will attempt to provide full path for filename # fix old_filename = old_filename.split("\\")[-1] # Remove the extension and check the filename clean_filename = ".".join(old_filename.split(".")[:-1]) clean_filename = _clean_filename(clean_filename) # Make sure the filename is not too long if len(clean_filename) > 500: clean_filename = clean_filename[:500] # Append the predefined file extension clean_filename = "%s%s" % (clean_filename, fileextension) # Use a randomly generated UUID as filename file_identifier = uuid.uuid4() new_filename = "%s%s" % (file_identifier, fileextension) # Check if the directories already exist. If not, create them. if not os.path.exists(os.path.join(upload_path, TEMP_FOLDER_NAME)): os.makedirs(os.path.join(upload_path, TEMP_FOLDER_NAME)) new_filepath = os.path.join(upload_path, TEMP_FOLDER_NAME, new_filename) # Open the new file for writing f = open(new_filepath, "wb", 10000) datalength = 0 # Read the file in chunks for chunk in _file_buffer(file): f.write(chunk) datalength += len(chunk) f.close() # Open the file again to get the hash hash = get_file_hash(new_filepath) # Database values db_file = File(identifier=file_identifier, name=clean_filename, mime=filetype, size=datalength, hash=hash) Session.add(db_file) log.debug("The uploaded file (%s) was saved as %s at %s" % (clean_filename, new_filename, new_filepath)) ret["filename"] = clean_filename ret["fileidentifier"] = str(file_identifier) ret["msg"] = _("File was successfully uploaded") ret["success"] = True localizer = get_localizer(request) ret["msg"] = localizer.translate(ret["msg"]) return ret