Advertisement
Guest User

Untitled

a guest
Apr 23rd, 2018
109
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.04 KB | None | 0 0
  1. import os
  2. import re
  3. import html
  4.  
  5. import cherrypy
  6. from jinja2 import Template
  7.  
  8. from db.client import DBClient
  9. from db.model import Model, TextField, ValidationError
  10. from torrent_format.torrent_file import TorrentFile, PrivateTorrentFile, InvalidTorrentFileError
  11. from utils import generate_uid, get_base_of_passw
  12.  
  13. LOGIN_PATTERN = re.compile("^[a-zA-Z0-9_-]{3,50}$")
  14.  
  15.  
  16. class UserError(Exception):
  17.     def __init__(self, error_message):
  18.         self.error_message = error_message
  19.  
  20.  
  21. class User(Model):
  22.     login = TextField(50)
  23.     password_base = TextField(256)
  24.     uid = TextField(32)
  25.  
  26.     @staticmethod
  27.     def get_user_by_id(uid):
  28.         users = User.filter(uid=uid)
  29.         if not users:
  30.             return None
  31.         return users[0]
  32.  
  33.  
  34. class Cookie:
  35.     def __init__(self, uid, login, password, max_age=60 * 60):
  36.         self.uid = uid
  37.         self.max_age = max_age
  38.         self.secret = Cookie.generate_cookie_secret(uid, login, get_base_of_passw(password))
  39.  
  40.     @staticmethod
  41.     def generate_cookie_secret(uid, login, password_base):
  42.         return get_base_of_passw(login + password_base + uid)
  43.  
  44.     @staticmethod
  45.     def is_valid(cookie_uid, cookie_secret):
  46.         if not cookie_uid.isdigit():
  47.             return False
  48.         user = User.get_user_by_id(cookie_uid)
  49.         if user is None:
  50.             return False
  51.         if Cookie.generate_cookie_secret(user.uid, user.login, user.password_base) != cookie_secret:
  52.             return False
  53.         return True
  54.  
  55.  
  56. def load_templates():
  57.     templates_dict = {}
  58.     for template_name in os.listdir('webserver/templates'):
  59.         with open('webserver/templates/{}'.format(template_name)) as template_file:
  60.             templates_dict[template_name] = Template(template_file.read())
  61.     return templates_dict
  62.  
  63.  
  64. def validate_login(login):
  65.     if re.match(LOGIN_PATTERN, login) is None:
  66.         raise UserError('Username "{}" is invalid. Username must match regex {}.'.format(login, LOGIN_PATTERN.pattern))
  67.  
  68.  
  69. BAD_CHARS = [
  70.     '\\',
  71.     '\"',
  72.     '%',
  73.     '_'
  74. ]
  75.  
  76.  
  77. def adjust_search_filter(filter_req: str):
  78.     for bad_char in BAD_CHARS:
  79.         filter_req = filter_req.replace(bad_char, '\\' + bad_char)
  80.     return filter_req.replace("'", "''")
  81.  
  82.  
  83. def register(login, password):
  84.     validate_login(login)
  85.     users = User.filter(login=login)
  86.     if users:
  87.         raise UserError('Username "{}" is already exists.'.format(login))
  88.     User.create(uid=generate_uid(), login=login, password_base=get_base_of_passw(password))
  89.  
  90.  
  91. def authenticate(login, password):
  92.     validate_login(login)
  93.     users = User.filter(login=login)
  94.     if not users:
  95.         raise UserError("User {} doesn't exist.".format(login))
  96.     user = users[0]
  97.     if user.password_base != get_base_of_passw(password):
  98.         raise UserError("Wrong password.")
  99.     return user.uid
  100.  
  101.  
  102. LINES_FOR_QUERY = 20
  103.  
  104.  
  105. def get_client_hash():
  106.     return hash((cherrypy.request.remote.ip, cherrypy.request.headers.get("User-Agent", "")))
  107.  
  108.  
  109. def error_page_404(status, message, traceback, version):
  110.     return "404 Error!"
  111.  
  112.  
  113. def get_torrent_files(fields_filter, page_number):
  114.     if fields_filter:
  115.         files = TorrentFile.filter(name__contains=fields_filter)
  116.     else:
  117.         files = TorrentFile.all(page_number * LINES_FOR_QUERY, LINES_FOR_QUERY)
  118.     return files
  119.  
  120.  
  121. def get_authorized_user():
  122.     request_cookie = dict(cherrypy.request.cookie)
  123.     if ('uid' in request_cookie) and ('secret' in request_cookie):
  124.         secret = request_cookie['secret'].value
  125.         uid = request_cookie['uid'].value
  126.         if Cookie.is_valid(uid, secret):
  127.             return User.get_user_by_id(uid)
  128.     return None
  129.  
  130.  
  131. def set_cookie(login, password):
  132.     user_id = authenticate(login, password)
  133.     user_cookie = Cookie(user_id, login, password)
  134.     response_cookie = cherrypy.response.cookie
  135.     response_cookie['uid'] = user_cookie.uid
  136.     response_cookie['uid']['max-age'] = user_cookie.max_age
  137.     response_cookie['secret'] = user_cookie.secret
  138.     response_cookie['secret']['max-age'] = user_cookie.max_age
  139.  
  140.  
  141. def reset_cookie():
  142.     response_cookie = cherrypy.response.cookie
  143.     response_cookie['uid'] = ''
  144.     response_cookie['uid']['max-age'] = 0
  145.     response_cookie['secret'] = ''
  146.     response_cookie['secret']['max-age'] = 0
  147.  
  148.  
  149. class RequestHandler:
  150.     def __init__(self):
  151.         self.templates_dict = load_templates()
  152.         self.errors = {}
  153.  
  154.     def set_error(self, error):
  155.         self.errors[get_client_hash()] = error
  156.  
  157.     def reset_error(self):
  158.         client_hash = get_client_hash()
  159.         if client_hash in self.errors:
  160.             del self.errors[client_hash]
  161.  
  162.     @property
  163.     def error(self):
  164.         return self.errors.get(get_client_hash(), "")
  165.  
  166.     def get_template(self, template_name):
  167.         return self.templates_dict[template_name]
  168.  
  169.     @cherrypy.expose
  170.     def signin(self, login, password):
  171.         login = html.escape(login)
  172.         try:
  173.             set_cookie(login, password)
  174.             self.reset_error()
  175.             raise cherrypy.HTTPRedirect('/')
  176.         except UserError as user_error:
  177.             self.set_error(str(user_error))
  178.             raise cherrypy.HTTPRedirect('/signin_page')
  179.  
  180.     @cherrypy.expose
  181.     def signup(self, login, password):
  182.         login = html.escape(login)
  183.         if len(password) > 192:
  184.             self.set_error("Password must be shorter than 193 characters.")
  185.             raise cherrypy.HTTPRedirect('/signup_page')
  186.         else:
  187.             password = html.escape(password)
  188.         try:
  189.             register(login, password)
  190.             set_cookie(login, password)
  191.             self.reset_error()
  192.             raise cherrypy.HTTPRedirect('/')
  193.         except UserError as user_error:
  194.             self.set_error(user_error)
  195.             raise cherrypy.HTTPRedirect('/signup_page')
  196.  
  197.     @cherrypy.expose
  198.     def signup_page(self):
  199.         rendered_template = self.get_template('signup.html').render(error_message=self.error)
  200.         self.reset_error()
  201.         return rendered_template
  202.  
  203.     @cherrypy.expose
  204.     def signin_page(self):
  205.         rendered_template = self.get_template('signin.html').render(error_message=self.error)
  206.         self.reset_error()
  207.         return rendered_template
  208.  
  209.     @cherrypy.expose
  210.     def signout(self):
  211.         reset_cookie()
  212.         self.reset_error()
  213.         raise cherrypy.HTTPRedirect('/')
  214.  
  215.     @cherrypy.expose
  216.     def index(self):
  217.         self.reset_error()
  218.         user = get_authorized_user()
  219.         authed = "" if user is None else user.login
  220.         return self.get_template('main.html').render(authed=authed)
  221.  
  222.     @cherrypy.expose
  223.     def default(self, _):
  224.         return self.get_template('page_404.html').render()
  225.  
  226.     @cherrypy.expose
  227.     def storage(self, search_filter="", page_number=0):
  228.         self.reset_error()
  229.         user = get_authorized_user()
  230.         if user is None:
  231.             raise cherrypy.HTTPRedirect("/index")
  232.         else:
  233.             authed = user.login
  234.         if not search_filter:
  235.             max_page = TorrentFile.get_count() // LINES_FOR_QUERY
  236.             if (int(page_number) < 0) or (int(page_number) > max_page):
  237.                 raise cherrypy.HTTPError(404)
  238.         else:
  239.             max_page = -1
  240.             page_number = 0
  241.         return self.get_template('files_storage.html').render(
  242.             model_fields=TorrentFile.get_field_names(),
  243.             files=get_torrent_files(adjust_search_filter(search_filter), int(page_number)),
  244.             authed=authed,
  245.             value=search_filter,
  246.             page_number=page_number,
  247.             max_page=max_page,
  248.         )
  249.  
  250.     @cherrypy.expose
  251.     def private_storage(self, search_filter="", page_number="0"):
  252.         self.reset_error()
  253.         user = get_authorized_user()
  254.         if user is None:
  255.             raise cherrypy.HTTPRedirect('/index')
  256.         if search_filter:
  257.             max_page = -1
  258.             files = PrivateTorrentFile.filter(
  259.                 name__contains=adjust_search_filter(search_filter),
  260.                 upload_by=user.login
  261.             )
  262.         else:
  263.             max_page = (PrivateTorrentFile.get_count(upload_by=user.login)) // LINES_FOR_QUERY
  264.             files = PrivateTorrentFile.filter(
  265.                 lower_bound=int(page_number) * LINES_FOR_QUERY,
  266.                 count=LINES_FOR_QUERY,
  267.                 upload_by=user.login
  268.             )
  269.         return self.get_template('private_files_storage.html').render(
  270.             model_fields=PrivateTorrentFile.get_field_names(),
  271.             files=files,
  272.             authed=user.login,
  273.             value=search_filter,
  274.             max_page=max_page,
  275.             page_number=page_number,
  276.         )
  277.  
  278.     @cherrypy.expose
  279.     def upload(self, upload_file):
  280.         user = get_authorized_user()
  281.         if user is None:
  282.             raise cherrypy.HTTPError(401)
  283.         raw_torrent_info_file = bytearray()
  284.         while True:
  285.             data = upload_file.file.read(8192)
  286.             if not data:
  287.                 break
  288.             raw_torrent_info_file += data
  289.         user_login = get_authorized_user().login
  290.         try:
  291.             TorrentFile(bytes(raw_torrent_info_file), upload_by=user_login).save()
  292.             self.reset_error()
  293.             raise cherrypy.HTTPRedirect('/storage')
  294.         except (InvalidTorrentFileError, ValidationError) as error:
  295.             normalized_name = upload_file.filename
  296.             if len(normalized_name) > 25:
  297.                 normalized_name = normalized_name[:26]
  298.             self.set_error('File "{}" is invalid .torrent file: {}'.format(normalized_name, error))
  299.             raise cherrypy.HTTPRedirect("/upload_file")
  300.  
  301.     @cherrypy.expose
  302.     def upload_file(self):
  303.         user = get_authorized_user()
  304.         if user is None:
  305.             raise cherrypy.HTTPRedirect('/')
  306.         rendered_template = self.get_template('upload_file.html').render(authed=user.login, error_message=self.error)
  307.         self.reset_error()
  308.         return rendered_template
  309.  
  310.     @cherrypy.expose
  311.     def download(self, file_id):
  312.         tfiles = TorrentFile.filter(uid=file_id)
  313.         if (len(tfiles) > 1) or (not tfiles):
  314.             raise cherrypy.HTTPError(404)
  315.         tfile = tfiles[0]
  316.         cherrypy.response.headers['Content-Type'] = 'application/x-bittorrent'
  317.         cherrypy.response.headers['Content-Disposition'] = 'attachment; filename="{}.torrent"'.format(tfile.name)
  318.         return tfile.get_data()
  319.  
  320.     @cherrypy.expose
  321.     def download_private(self, file_id):
  322.         user = get_authorized_user()
  323.         if user is None:
  324.             raise cherrypy.HTTPError(404)
  325.         tfiles = PrivateTorrentFile.filter(uid=file_id)
  326.         if (len(tfiles) > 1) or (not tfiles):
  327.             raise cherrypy.HTTPError(404)
  328.         tfile = tfiles[0]
  329.         if user.login != tfile.upload_by:
  330.             raise cherrypy.HTTPError(404)
  331.         cherrypy.response.headers['Content-Type'] = 'application/x-bittorrent'
  332.         cherrypy.response.headers['Content-Disposition'] = 'attachment; filename="{}.torrent"'.format(tfile.name)
  333.         return tfile.get_data()
  334.  
  335.     @cherrypy.expose
  336.     def upload_private(self, upload_file):
  337.         user = get_authorized_user()
  338.         if user is None:
  339.             raise cherrypy.HTTPError(401)
  340.         raw_torrent_info_file = bytearray()
  341.         while True:
  342.             data = upload_file.file.read(8192)
  343.             if not data:
  344.                 break
  345.             raw_torrent_info_file += data
  346.         user_login = get_authorized_user().login
  347.         try:
  348.             PrivateTorrentFile(bytes(raw_torrent_info_file), upload_by=user_login).save()
  349.             self.reset_error()
  350.             raise cherrypy.HTTPRedirect('/private_storage')
  351.         except (InvalidTorrentFileError, ValidationError) as error:
  352.             self.set_error('File "{}" is invalid .torrent file: {}'.format(upload_file.filename, error))
  353.             raise cherrypy.HTTPRedirect("/upload_file")
  354.  
  355.  
  356. def start_web_server():
  357.     with DBClient():
  358.         request_handler = RequestHandler()
  359.         cherrypy.quickstart(request_handler, '/', config='webserver/webserver.config')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement