def getPasswords(self, path): path = h.cleanPath(path) passwordsFile = h.makePath(self.basePath, path, ".password") if not os.path.exists(passwordsFile): return set() passwordsCacheKey = h.makeKeyFromArguments(path) lh, lf = None, h.makePath(h.LOCKS_FOLDER, "_sfl_password_%s" % h.clean(path)) try: self.passwordsLock.acquire() if passwordsCacheKey in self.passwordsCache: pc = self.passwordsCache[passwordsCacheKey] if h.getFileModified(passwordsFile) == pc["date"]: return pc["passwords"] lh = h.getLockShared(lf, 5) passwords = set([ p for p in h.readFromFile( h.makePath(self.basePath, path, ".password")).split("\n") if p != "" ]) self.passwordsCache[passwordsCacheKey] = { "passwords": passwords, "date": h.getFileModified(passwordsFile) } return passwords finally: self.passwordsLock.release() if lh is not None: h.releaseLock(lh)
def getItems(self, path, r=None, asAdmin=False, overrideListingForbidden=False, overrideNoShow=False) -> (List[item], List[item]): path = h.cleanPath(path) if not self.doesItemExists(path): return [], [] if not overrideListingForbidden and not asAdmin and self.ap.listingForbidden( path): return [], [] fullPath = self.getFullPath(path) items = h.listDirectoryItems(fullPath) itemsContainers = [] for item in items: if not os.path.isdir(item): continue itemPath = item.replace(self.basePath, "") if itemPath.lstrip("/").startswith("_sf"): continue isForbidden = self.ap.isForbidden(itemPath) if not asAdmin and isForbidden: continue showForbidden = self.ap.showForbidden(itemPath) if not overrideNoShow and not asAdmin and showForbidden: continue i = self.getItem(itemPath, r, asAdmin) if i is not None: itemsContainers.append(i) itemsLeafs = [] for item in items: if not os.path.isfile(item): continue if self.isHiddenForListings(item): continue itemPath = item.replace(self.basePath, "") if itemPath.lstrip("/").startswith("_sf"): continue isForbidden = self.ap.isForbidden(itemPath) if not asAdmin and isForbidden: continue i = self.getItem(itemPath, r, asAdmin) if i is not None: itemsLeafs.append(i) return itemsContainers, itemsLeafs
def setUserPassword(self, path, password, r, response=None): path = h.cleanPath(path) cookieKey = "_sf_pass_%s" % h.clean(path if path != "" else "-") r.cookies = dict(r.cookies) r.cookies[cookieKey] = password if response is not None: response.set_cookie(cookieKey, password, max_age=COOKIE_DURATION)
def remove(self, path): path = h.cleanPath(path) if self.doesItemExists(path) and self.ap.isEditAllowed( self.getParent(path)): h.delete(self.getFullPath(path)) return True else: return False
def getLowerProtectedPath(self, path): path = h.cleanPath(path) relativePath = path lowerPath = False paths = relativePath.split("/") for i in range(len(paths)): currentPath = h.makePath(*paths[0:len(paths) - i]) if currentPath == "": currentPath = "/" currentRealPath = os.path.abspath( h.makePath(self.basePath, currentPath)) if os.path.isfile(h.makePath(currentRealPath, ".password")): lowerPath = currentRealPath break if os.path.isfile(h.makePath(currentRealPath, ".nopassword")): lowerPath = False break if lowerPath == False: return False return h.cleanPath(lowerPath.replace(self.basePath, ""))
def _routeAdmin(self, path="/"): path = h.cleanPath(path) if request.form.get("password-submit", False): response = make_response() self.ap.setAdminPassword(request.form.get("password-admin", ""), request, response) return self._redirect("/admin", response) if not self.ap.isAdmin(request): return self._makeTemplate("password-admin") return self._routeItemsAdmin(path)
def _routeList(self, path="/"): if request.headers.get("password") != ap.adminPassword: return {"result": 403} path = h.cleanPath(self._aliasPath(path)) if not ip.doesItemExists(path): return {"result": 500, "error": "path %s does not exist" % path} containers, leafs = ip.getItems(path, request) containers, leafs = [c.__dict__ for c in containers], [l.__dict__ for l in leafs] return { "result": 200, "path": path, "containers": containers, "leafs": leafs }
def addLeaf(self, path, file): path = h.cleanPath(path) try: if not self.doesItemExists(path): return False, "Container does not exists" filename = self.getPotentialLeafName(file) if self.isItemLeaf(h.makePath(path, filename)): return False, "Item already exists" if self.isItemContainer(h.makePath(path, filename)): return False, "Item already exists" filePath = h.makePath(self.basePath, path, filename) file.save(filePath) h.touchFile(filePath) return True, filename except: le, lt = h.getLastExceptionAndTrace() return False, le
def addNewPassword(self, path, password): path = h.cleanPath(path) if password is None: return False if self.passwordEditForbidden(path): return False lh, lf = None, h.makePath(h.LOCKS_FOLDER, "_sfl_password_%s" % h.clean(path)) try: passwordFile = h.makePath(self.basePath, path, ".password") requiredPasswords = list(self.getPasswords(path)) requiredPasswords.append(password) lh = h.getLockExclusive(lf, 5) h.writeToFile(passwordFile, "\n".join(list(set(requiredPasswords)))) return True except: print(h.getLastExceptionAndTrace()) return False finally: if lh is not None: h.releaseLock(lh)
def getItem(self, path, r=None, asAdmin=False) -> item: path = h.cleanPath(path) if not self.doesItemExists(path): return None fullPath = h.makePath(self.basePath, path) isLeaf = self.isItemLeaf(path) isContainer = not isLeaf if r is not None: protected, requiredPasswords, savedPassword, isAuthorized, lowerProtectedPath, protectedFromParent = self.ap.isAuthorized( path, r) else: isAuthorized, requiredPasswords, protected, savedPassword, lowerProtectedPath, protectedFromParent = True, "", False, "", "", False if asAdmin: isAuthorized = True if isLeaf: extension = os.path.splitext(path)[-1].replace(".", "") size = os.path.getsize(fullPath) nbItems = 0 isTmpFolder = False else: extension = "" size = 0 nbItems = len(h.listDirectoryItems(fullPath)) isTmpFolder = path.lstrip("/") == self.tmpFolder isForbidden = self.ap.isForbidden(path) listingForbidden = self.ap.listingForbidden(path) shareForbidden = self.ap.shareForbidden(path) showForbidden = self.ap.showForbidden(path) editAllowed = self.ap.isEditAllowed(path) downloadForbidden = self.ap.downloadForbidden(path) passwordEditForbidden = self.ap.passwordEditForbidden(path) expires = h.getFileModified(h.makePath(self.basePath, path)) + self.tmpFolderDuration return item(path, os.path.basename(path), os.stat(fullPath).st_mtime, isAuthorized, protected, isForbidden, showForbidden, listingForbidden, downloadForbidden, shareForbidden, passwordEditForbidden, isTmpFolder, editAllowed, isLeaf, isContainer, lowerProtectedPath, nbItems, requiredPasswords, expires, size, extension, savedPassword, protectedFromParent)
def getZipFile(self, path, r): path = h.cleanPath(path) addedSize = 0 if not self.doesItemExists(path): return None fullPath = self.getFullPath(path) zipFilePath = h.makePath(h.TMP_FOLDER, h.uniqueID()) try: zipf = zipfile.ZipFile(zipFilePath, "w", zipfile.ZIP_DEFLATED) for root, dirs, files in os.walk(fullPath): dirs[:] = [ d for d in dirs if not self.ap.isForbidden(d.replace(self.basePath, "")) ] dirs[:] = [ d for d in dirs if self.ap.isAuthorized(d.replace(self.basePath, ""), r)[3] ] dirs[:] = [ d for d in dirs if not self.ap.downloadForbidden(d.replace(self.basePath, "")) ] for f in files: fpath = h.makePath(root, f) frpath = fpath.replace(self.basePath, "") if self.ap.isForbidden(frpath): continue addedSize += h.getFileSize(fpath) if addedSize > self.maxZipSize: break zipf.write(fpath, arcname=frpath) if addedSize > self.maxZipSize: h.logDebug("Max zip file size reached", path, self.maxZipSize) break return zipFilePath except Exception as e: os.remove(zipFilePath) raise (e)
def _doTrack(self, path, address, isProtected, isAuthotirzed, passwordProvided, shareID, shareTag): try: self.trackingsLock.acquire() path = h.cleanPath(path) if shareID is not None: path = "%s - sid: %s" % (path, shareID) if shareTag is not None: path = "%s - stag: %s" % (path, shareTag) if self.locationEnabled: location = self._getLocationFromIP(address) else: location = address self.trackings.append( tracking( path, passwordProvided if passwordProvided is not None else "", isAuthotirzed, address, h.now(), isProtected, location)) if len(self.trackings) > self.maxSize: nbLines = len(self.trackings) offset = int(nbLines - self.maxSize / 2) if offset > 0: self.trackings = self.trackings[offset:nbLines] h.logDebug("Trimed trackings", nbLines, len(self.trackings)) self.trackingsSaved = False finally: self.trackingsLock.release()
def listingForbidden(self, path): path = h.cleanPath(path) return h.isfile(h.makePath(self.basePath, path, ".nolist"))
def isItemContainer(self, path): path = h.cleanPath(path) fullPath = self.getFullPath(path) if not os.path.exists(fullPath): return False return os.path.isdir(self.getFullPath(path))
def getParent(self, path): path = h.cleanPath(path) return os.path.dirname(path)
def _isFullPathWithinBaseFolder(self, fullPath): return h.cleanPath(self.basePath) in fullPath
def isItemLeaf(self, path): path = h.cleanPath(path) fullPath = self.getFullPath(path) if not os.path.exists(fullPath): return False return os.path.isfile(fullPath)
def downloadForbidden(self, path): path = h.cleanPath(path) if path == "": return True return h.isfile(h.makePath(self.basePath, path, ".nodownload")) or self.showForbidden( path) or self.listingForbidden(path)
def _routeShare(self, shareIDAndPath): shareID = shareIDAndPath.split("/")[0] query = str(request.query_string, "utf8") if not self.sp.shareExists(shareID): return self._makeTemplate("not-found", path=shareID) if request.form.get("password-submit", False): response = make_response() self.ap.setSharePassword(shareID, request.form.get("password-share", ""), request, response) return self._redirect(h.makeURL("/share=%s" % shareID, query), response) isAdmin = self.ap.isAdmin(request) subPath = h.cleanPath( os.path.normpath(shareIDAndPath.replace(shareID, ""))).lstrip(".") s, hint = self.sp.getShare(shareID, asAdmin=isAdmin, r=request) if s is None: raise Exception("Can't get Share %s, %s" % (shareID, hint)) baseFilesIndexes = { s.files[i].split("/")[-1]: i for i in range(len(s.files)) } subPathBits = subPath.split("/") if not isAdmin and len(s.files) == 1 and subPath == "": return self._redirect( h.makeURL( "/share=%s/%s" % (shareID, s.files[0].split("/")[-1]), query)) elif subPath == "": baseFile, indexFile = None, -1 else: baseFile = subPathBits.pop(0) indexFile = baseFilesIndexes.get(baseFile, -2) isAuthorized, savedPassword = self.ap.isShareAuthorized(s, request) if not isAdmin: self.sp.addView( s, h.makePath(*subPathBits), baseFile, request.remote_addr if request is not None else None, s.tag, isAuthorized) if indexFile == -2: return self._makeTemplate("not-found", path="%s" % shareID) if isAdmin and request.args.get("view") is None: return self._makeTemplate("share-admin", shareID=shareID, share=s) if indexFile == -1: path, displayPath, shareBasePath = "", shareID, "" else: shareBasePath = s.files[indexFile] path = h.cleanPath(h.makePath(shareBasePath, *subPathBits)) if baseFile is not None: shareBasePath = shareBasePath.replace(baseFile, "") displayPath = shareID if baseFile is not None: displayPath = h.makePath(displayPath, baseFile) if subPath != "": displayPath = h.makePath(displayPath, *subPathBits) if h.TRACKING and not isAdmin: self.tp.track(path, request, ap.isShareProtected(s), isAuthorized, savedPassword, shareID, s.tag) if not ip.doesItemExists(path): return self._makeTemplate("not-found", path=displayPath) if not isAdmin and not isAuthorized: return self._makeTemplate("share-password", displayPath=displayPath, share=s) if indexFile == -1: isLeaf, readme = False, None containers, leafs = [], [] for file in s.files: item = ip.getItem(file) if item is None: continue item.path = item.path.split("/")[-1] if ip.isItemContainer(file): containers.append(item) else: leafs.append(item) else: isLeaf = ip.isItemLeaf(path) if not isLeaf: containers, leafs = self.ip.getItems( path, overrideListingForbidden=True, overrideNoShow=True) readme = ip.getReadme(path) else: containers, leafs, readme = None, None, None if isLeaf: return send_from_directory(h.DATA_FOLDER, path) else: return self._makeTemplate("share", displayPath=displayPath, shareBasePath=shareBasePath, subPath=subPath, share=s, containers=containers, leafs=leafs, alerts=[], readme=readme, indexFile=indexFile, query=query)
def shareForbidden(self, path): path = h.cleanPath(path) return h.isfile(h.makePath(self.basePath, path, ".noshare"))
def setDownloadForbidden(self, path): path = h.cleanPath(path) h.writeToFile(h.makePath(self.basePath, path, ".nodownload"), "")
def passwordEditForbidden(self, path): path = h.cleanPath(path) return h.isfile(h.makePath(self.basePath, path, ".nopasswordedit"))
def getReadmeAdmin(self, path): path = h.cleanPath(path) readmeFile = h.makePath(self.getFullPath(path), "README.admin.md") if not os.path.exists(readmeFile): return self.getReadme(path) return markdown2.markdown(h.readFromFile(readmeFile))
def getFullPath(self, path): path = h.cleanPath(path) fullPath = os.path.abspath(h.makePath(self.basePath, path)) if not self._isFullPathWithinBaseFolder(fullPath): return self.basePath return fullPath
def _routeItems(self, path="/"): path = h.cleanPath(self._aliasPath(path)) if self.ap.isAdmin(request): return self._routeAdmin(path) if not ip.doesItemExists(path): return self._makeTemplate("not-found", path=path) item = self.ip.getItem(path, request) if item is None: return self._makeTemplate("error", path=path, e="Can't get item") if request.form.get("password-submit", False): response = make_response() self.ap.setUserPassword(item.lowerProtectedPath, request.form.get("password", ""), request, response) return self._redirect(path, response) if h.TRACKING: self.tp.track(path, request, item.protected, item.isAuthorized, item.savedPassword) alerts = [] if item.isAuthorized: if ip.isItemLeaf(path): if item.forbidden: return self._makeTemplate("forbidden", path=path) return send_from_directory(h.DATA_FOLDER, path) else: if item.forbidden: return self._makeTemplate("forbidden", path=path) if item.listingForbidden: return self._makeTemplate("forbidden", path=path) if "download" in request.args and not item.downloadForbidden: return self._downloadAndDeleteFile( ip.getZipFile(path, request), "%s.zip" % (os.path.basename(path) if path != "" else "root")) containers, leafs = ip.getItems(path, request) readme = ip.getReadme(path) return self._makeTemplate( "items", containers=containers, leafs=leafs, path=path, readme=readme, downloadAllowed=not self.ap.downloadForbidden(path), currentURLWithoutURI=path, alerts=alerts) else: if item.protected: if item.savedPassword is not None and item.savedPassword != "": alerts.append([ "Can't authenticate", "The password you provided (%s) is not valid." % item.savedPassword ]) else: alerts.append([ "Can't authenticate", "You did not provide a password." ]) return self._makeTemplate( "password", path=path, lowerProtectedPath=item.lowerProtectedPath, alerts=alerts)
def setListingForbidden(self, path): path = h.cleanPath(path) h.writeToFile(h.makePath(self.basePath, path, ".nolist"), "")
def passwordProtected(self, path): path = h.cleanPath(path) return h.isfile(h.makePath(self.basePath, path, ".password"))
def doesItemExists(self, path): path = h.cleanPath(path) return os.path.exists(self.getFullPath(path))
def _aliasPath(self, path): path = h.cleanPath(path) if self.aliases is None: return path return self.aliases.get(path, path)
def setShareForbidden(self, path): path = h.cleanPath(path) h.writeToFile(h.makePath(self.basePath, path, ".noshare"), "")