PageRenderTime 26ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/CodeSnippets/PyDown/tags/v0.5.2/PyDown/PyDown.py

https://github.com/jedie/python-code-snippets
Python | 459 lines | 446 code | 5 blank | 8 comment | 0 complexity | 6287d0146560dbe5d33277b4f7fb5c22 MD5 | raw file
  1. #!/usr/bin/python
  2. # -*- coding: UTF-8 -*-
  3. """
  4. PyDown - A small Python Download Center...
  5. Info zur Installation/Benutzung: PyDown_readme.txt
  6. """
  7. # copyleft: jensdiemer.de (GPL v2 or above)
  8. __author__ = "Jens Diemer (www.jensdiemer.de)"
  9. __license__ = "GNU General Public License v2 or above - http://www.opensource.org/licenses/gpl-license.php"
  10. __url__ = "http://www.jensdiemer.de/Programmieren/Python/PyDown"
  11. __version__ = "v0.5.2"
  12. __info__ = """<a href="%s">PyDown %s</a>""" % (__url__, __version__)
  13. __history__ = """
  14. v0.5.2
  15. - Windows bugfixes
  16. v0.5.1
  17. - Bugfix in path.py (wrong Links in newestFiles)
  18. v0.5
  19. - NEU: newestFile-List
  20. v0.4.3
  21. - NEU: eMail Benachrichtigung beim Upload
  22. v0.4.2
  23. - NEU: Temp-Verz. wird aufgeräumt
  24. v0.4.1
  25. - Anpassungen an colubrid 1.0
  26. v0.4.0
  27. - NEU: upload
  28. - NEU: Download von einzelnen Dateien möglich
  29. - NEU: filesystemencoding
  30. v0.3.1
  31. - Patch für Windows Downloads: Setzt stdout auf binary vor dem Download
  32. v0.3
  33. - Dynamische Bandbreitenanpassung möglich
  34. v0.2
  35. - NEU: einen Info Bereich
  36. - Kräftig aufgeräumt
  37. v0.1.1
  38. - Bugfixes for running under Windows
  39. v0.1
  40. - erste Version
  41. """
  42. # Basis Config Einstellung
  43. # Diese _nicht_ hier ändern, sondern in der ../PyDown_config.py!!!
  44. cfg = {
  45. # Nur diese Usernamen haben zugriff, wenn only_auth_users==True
  46. "allowes_user": (),
  47. # Username mit dem sich der Admin einloggt
  48. "admin_username": "",
  49. # Datei-Endungsfilter, nur diese Dateien werden beachtet
  50. "ext_whitelist": (".mp3",),
  51. # Basis-Pfad, der "Rekursiv-Freigegeben" werden soll.
  52. # unter Windows:
  53. # -normalen slash "/" verwenden!
  54. # -ganze Laufwerke mit abschließendem slash!
  55. "base_path": "/tmp",
  56. # Nur HTTPs Verbindungen erlauben?
  57. "only_https": True,
  58. # Zugriff nur eingeloggte User, durch Apache's .htaccess-Auth erlauben?
  59. "only_auth_users": True,
  60. # Zugriffe nur von bestimmten IP's zulassen
  61. "IP_range": ["127.0.0.1","192.168.*.*"],
  62. # Debugausgaben einblenden?
  63. "debug": False,
  64. # Ab welcher Anzahl von Verzeichnissen sollen Buchstaben eingeblendet werden?
  65. "min_letters" : 12,
  66. # Anzahl der Log-Einträge die angezeigt werden sollen
  67. "last_log_count": 50,
  68. # Anzahl der Einträge die bei "newest Files" angezeigt werden
  69. "newestfiles_count": 30,
  70. # Temp-Verz., bei None, wird das System-Temp-Verz. genommen
  71. "temp": None,
  72. "temp_prefix": "PyDown_",
  73. # Max. alter einer Temp-Datei in Sec. bevor sie automatisch gelöscht wird
  74. "temp_max_old": 60,
  75. #__________________________________
  76. # Upload
  77. "upload_bufsize" : 8192,
  78. "upload_dir" : "uploads",
  79. # Soll nach dem Upload eine eMail verschickt werden?
  80. "upload_email_notify" : False,
  81. # Die Absender-Adresse
  82. "email_from" : "pydown@localhost",
  83. # Die Ziel-Adresse
  84. "upload_to" : "administator@localhost",
  85. }
  86. # Standart Python Module
  87. import os, sys, urllib, cgi
  88. import posixpath, subprocess, stat, time, locale
  89. # Leitet alle print Ausgaben an stderr weiter
  90. # Geht allerdings nicht unter Windows mit Apache!!!
  91. if sys.platform!="win32":
  92. sys.stdout = sys.stderr
  93. #_____________________________________________________________________________
  94. ## Eigene Module
  95. # Eigene Ausnahmen
  96. from exceptions import *
  97. # SQL-Wrapper mit speziellen PyDown-DB-Zugriffsmethoden
  98. from PyDownDB import PyDownDB
  99. # Path-Klasse für das request-Objekt
  100. from path import path
  101. #_____________________________________________________________________________
  102. # local explizit auf deutsch stellen (nur Linux!), für:
  103. # - den tausender-Punkt bei Bytes-Angaben
  104. # - die richtige sortierung mit locale.strcoll()
  105. try:
  106. locale.setlocale(locale.LC_ALL, "de_DE.UTF-8")
  107. except:
  108. pass
  109. #~ if sys.getdefaultencoding()!="utf-8":
  110. #~ # Hack, damit defaultencoding auf UTF-8 gesetzt wird.
  111. #~ try:
  112. #~ reload(sys)
  113. #~ sys.setdefaultencoding("utf-8")
  114. #~ except:
  115. #~ pass
  116. # Colubrid import's werden das erste mal im Request-Handler vorgenommen und
  117. # sind dort mit einem except und Info-Text versehen
  118. from colubrid import BaseApplication
  119. from colubrid import HttpResponse
  120. # Der key im environ-Dict mit dem das request-Object eingefügt ist
  121. requestObjectKey = "colubrid.request"
  122. # Jinja Template engine
  123. import jinja
  124. """
  125. ##~ request.GET -> request.args
  126. ##~ request.REQUEST -> request.values
  127. ##~ request.POST -> request.form
  128. ##~ request.FILES -> request.files
  129. ##~ request.COOKIES -> request.cookies
  130. http://trac.pocoo.org/browser/colubrid/webpage/documentation/applications/regex.txt
  131. http://trac.pocoo.org/browser/colubrid/webpage/documentation/request.txt
  132. http://trac.pocoo.org/browser/colubrid/webpage/documentation/response.txt
  133. """
  134. class PyDown(BaseApplication):
  135. """
  136. Klasse die die Programmlogik zusammenstellt
  137. """
  138. charset = 'utf-8'
  139. #~ urls = [
  140. #~ (r'^$', 'index'),
  141. #~ (r'^browse/(.*?)$', "PyDown.browse.index"),
  142. #~ (r'^download/(.*?)$', "PyDown.download.index"),
  143. #~ (r'^upload/(.*?)$', "PyDown.upload.index"),
  144. #~ (r'^info/status/$', "PyDown.info.status"),
  145. #~ (r'^info/setup/$', "PyDown.info.setup"),
  146. #~ ]
  147. slash_append = True
  148. naviTABs = [
  149. { "url": "info/status", "title": "info"},
  150. { "url": "browse/", "title": "download"},
  151. { "url": "upload/", "title": "upload"},
  152. { "url": "newest/", "title": "newest files"},
  153. ]
  154. def __init__(self, *args):
  155. super(PyDown, self).__init__(*args)
  156. self.response = HttpResponse()
  157. # Config-Dict an Request-Objekt und der Debugausgabe anhängen
  158. self.request.cfg = cfg
  159. self.cfg = cfg
  160. # Datenbankverbindung herstellen und dem request-Objekt anhängen
  161. self.setup_db()
  162. def setup_db(self):
  163. """
  164. Datenbankverbindung herstellen und dem request-Objekt anhängen
  165. """
  166. try:
  167. self.request.db = PyDownDB(
  168. self.request,
  169. dbtyp="sqlite",
  170. databasename="SQLiteDB/PyDownSQLite.db"
  171. )
  172. except Exception, e:
  173. raise Exception("%s\n --- Have you run install_DB.py first?" % e)
  174. def check_rights(self):
  175. """
  176. Überprüft only_https und only_auth_users
  177. """
  178. def check_ip(mask, IP):
  179. mask = mask.split(".")
  180. IP = IP.split(".")
  181. for mask_part, ip_part in zip(mask, IP):
  182. if mask_part != ip_part and mask_part != '*':
  183. return False
  184. return True
  185. def check_ip_list(mask_list, IP):
  186. for mask in mask_list:
  187. if check_ip(mask, IP):
  188. return True
  189. return False
  190. if self.request.cfg["IP_range"]:
  191. if not check_ip_list(self.request.cfg["IP_range"], self.request.environ["REMOTE_ADDR"]):
  192. raise PermissionDenied("Permission denied!")
  193. if self.request.cfg["only_https"]:
  194. # Nur https Verbindungen "erlauben"
  195. if self.request.environ.get("HTTPS", False) != "on":
  196. raise PermissionDenied("Only HTTPs connections allow!")
  197. if self.request.cfg["only_auth_users"]:
  198. # Der User muß über Apache's basic auth eingeloggt sein
  199. if not (self.request.environ.has_key("AUTH_TYPE") and \
  200. self.request.environ.has_key("REMOTE_USER")):
  201. raise PermissionDenied("Only identified users allow!")
  202. if not self.request.environ["REMOTE_USER"] in self.request.cfg["allows_user"]:
  203. raise PermissionDenied("Permission denied!")
  204. #~ raise PermissionDenied(
  205. #~ "Permission denied: %s - %s " % (
  206. #~ self.request.environ["REMOTE_USER"],
  207. #~ self.request.cfg["allows_user"]
  208. #~ )
  209. #~ )
  210. if not self.request.environ.has_key("REMOTE_USER"):
  211. self.request.environ["REMOTE_USER"] = "anonymous"
  212. def process_request(self):
  213. """
  214. Abarbeiten des eigentlichen Request. Die ObjectApplication ruft
  215. automatisch die eigentlichen Programmteile auf
  216. """
  217. self.check_rights() # Als erstes die Rechte checken!
  218. self.setup_naviTABs()
  219. self.setup_request_objects()
  220. self.setup_context()
  221. pathInfo = self.request.environ.get('PATH_INFO', '/')
  222. pathInfo = urllib.unquote(pathInfo)
  223. try:
  224. pathInfo = unicode(pathInfo, "utf-8")
  225. except:
  226. pass
  227. if pathInfo in ("", "/"):
  228. # Weiterleitung zum browser
  229. url = posixpath.join(self.request.environ['APPLICATION_REQUEST'], "browse")
  230. self.response.write("<h1>PathInfo: '%s'</h1>" % pathInfo)
  231. self.response.write("<h1>url: '%s'</h1>" % url)
  232. raise HttpMoved(url)
  233. elif pathInfo.startswith("/info"):
  234. # Informations-Seite
  235. import info
  236. info.info(self.request, self.response)
  237. elif pathInfo.startswith("/browse"):
  238. # Browser/Download
  239. path = pathInfo.lstrip("/browse")
  240. import browse
  241. FileIter = browse.browser(self.request, self.response, path).get()
  242. if FileIter != None:
  243. return FileIter
  244. elif pathInfo.startswith("/upload/status"):
  245. from upload import UploadStatus
  246. UploadStatus(self.request, self.response)
  247. elif pathInfo.startswith("/upload"):
  248. import upload
  249. upload.Uploader(self.request, self.response)
  250. elif pathInfo.startswith("/newest"):
  251. import newestFiles
  252. newestFiles.NewestFiles(self.request, self.response)
  253. else:
  254. self.response.write("<h1>PathInfo: '%s'</h1>" % pathInfo)
  255. return self.response
  256. #~ def on_regular_close(self):
  257. def close(self):
  258. if hasattr(self.request, 'downloadFileObj'):
  259. self.request.downloadFileObj.close()
  260. def on_access_denied(self, args):
  261. """
  262. Überschreibt die original Ausgabe und ergänzt diese mit
  263. einem Hinweis, warum der Access Denied ist ;)
  264. """
  265. self.response.write("<h1>403 Forbidden</h1>")
  266. self.response.write("<h3>%s</h3>" % " ".join(args))
  267. #_________________________________________________________________________
  268. # zusätzliche Request-Objekte
  269. def setup_naviTABs(self):
  270. """
  271. relative TAB-Links absolut machen
  272. """
  273. for link in self.naviTABs:
  274. link["url"] = posixpath.join(
  275. self.request.environ["SCRIPT_ROOT"], link["url"]
  276. )
  277. def setup_request_objects(self):
  278. """
  279. Hängt Objekte an das globale request-Objekt
  280. """
  281. # Jinja-Context anhängen
  282. self.request.context = {}
  283. # jinja-Render-Methode anhängen
  284. self.request.jinja = self.jinja
  285. self.request.render = self.render
  286. # Path-Klasse anhängen
  287. self.request.path = path(self.request, self.response)
  288. def setup_context(self):
  289. # ein paar Angaben
  290. self.request.context = {
  291. "naviTABs" : self.naviTABs,
  292. "cfg" : self.request.cfg,
  293. "__info__" : __info__,
  294. "filesystemencoding": sys.getfilesystemencoding(),
  295. "username" : self.request.environ["REMOTE_USER"],
  296. "is_admin" : self.request.environ["REMOTE_USER"] in \
  297. self.request.cfg["admin_username"],
  298. }
  299. usernames = self.request.db.last_users()
  300. usernames = ",".join(usernames)
  301. self.request.context["serverInfo"] = {
  302. "totalBandwith" : int(round(self.request.db.get_bandwith())),
  303. "availableBandwith" : int(round(self.request.db.available_bandwith())),
  304. "downloadCount" : self.request.db.download_count(),
  305. "uploadCount" : self.request.db.upload_count(),
  306. "user" : usernames,
  307. }
  308. #_________________________________________________________________________
  309. def jinja(self, templatename, context, suffix=".html"):
  310. #~ try:
  311. #~ loader = jinja.CachedFileSystemLoader('templates', suffix=suffix, charset='utf-8')
  312. #~ template = jinja.Template(templatename, loader)
  313. #~ except:# EOFError, ImportError:
  314. #~ self.response.write("<small>(jinja FileSystemLoader fallback)</small>")
  315. loader = jinja.FileSystemLoader('templates', suffix=suffix, charset='utf-8')
  316. template = jinja.Template(templatename, loader)
  317. context = jinja.Context(context, charset='utf-8')
  318. content = template.render(context)
  319. if isinstance(content, unicode):
  320. content = content.encode("utf-8")
  321. else:
  322. self.response.write("<small>(Content not unicode)</small><br />")
  323. return content
  324. def render(self, templatename):
  325. """
  326. Template mit jinja rendern, dabei wird self.request.context verwendet
  327. """
  328. if cfg["debug"]:
  329. self.response.write("<small>Debug: ON</small><br />")
  330. content = self.jinja(templatename, self.request.context)
  331. self.response.write(content)
  332. class UploadCallback:
  333. def __init__(self, request, environ, filename):
  334. self.request = request
  335. self.environ = environ
  336. self.filename = filename
  337. self.db = request.db
  338. #~ print "init", filename
  339. def start(self, contentLength):
  340. #~ print "Start", contentLength
  341. self.id = self.db.insert_upload(
  342. self.filename, contentLength, current_bytes=0
  343. )
  344. def update(self, pos):
  345. self.db.update_upload(self.id, pos)
  346. #~ print "pos:", pos
  347. def finished(self):
  348. #~ self.db.update_upload(self.id, pos)
  349. #~ print "finished", self.filename
  350. pass
  351. app = PyDown
  352. # Middleware, die den Tag <script_duration /> ersetzt
  353. from ReplacerMiddleware import replacer
  354. app = replacer(app)
  355. # callback Funktion für Uploads
  356. from uploadMiddleware import ProgressMiddleware
  357. app = ProgressMiddleware(
  358. app, UploadCallback, requestObjectKey, threshold=512*1024
  359. )