Пример #1
0
 def test_ims(self):
     """ SendFile: If-Modified-Since"""
     request.environ['HTTP_IF_MODIFIED_SINCE'] = bottle.http_date(time.time())
     res = static_file(basename, root=root)
     self.assertEqual(304, res.status_code)
     self.assertEqual(int(os.stat(__file__).st_mtime), parse_date(res.headers['Last-Modified']))
     self.assertAlmostEqual(int(time.time()), parse_date(res.headers['Date']))
     request.environ['HTTP_IF_MODIFIED_SINCE'] = bottle.http_date(100)
     self.assertEqual(open(__file__,'rb').read(), static_file(basename, root=root).body.read())
Пример #2
0
 def test_ims(self):
     """ SendFile: If-Modified-Since"""
     request.environ["HTTP_IF_MODIFIED_SINCE"] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
     res = static_file(os.path.basename(__file__), root="./")
     self.assertEqual(304, res.status)
     self.assertEqual(int(os.stat(__file__).st_mtime), parse_date(res.headers["Last-Modified"]))
     self.assertAlmostEqual(int(time.time()), parse_date(res.headers["Date"]))
     request.environ["HTTP_IF_MODIFIED_SINCE"] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(100))
     self.assertEqual(open(__file__, "rb").read(), static_file(os.path.basename(__file__), root="./").output.read())
Пример #3
0
 def test_ims(self):
     """ SendFile: If-Modified-Since"""
     request.environ['HTTP_IF_MODIFIED_SINCE'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
     res = static_file(os.path.basename(__file__), root='./')
     self.assertEqual(304, res.status)
     self.assertEqual(int(os.stat(__file__).st_mtime), parse_date(res.headers['Last-Modified']))
     self.assertAlmostEqual(int(time.time()), parse_date(res.headers['Date']))
     request.environ['HTTP_IF_MODIFIED_SINCE'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(100))
     self.assertEqual(open(__file__,'rb').read(), static_file(os.path.basename(__file__), root='./').output.read())
Пример #4
0
 def test_ims(self):
     """ SendFile: If-Modified-Since"""
     request.environ['HTTP_IF_MODIFIED_SINCE'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
     res = static_file(os.path.basename(__file__), root='./')
     self.assertEqual(304, res.status_code)
     self.assertEqual(int(os.stat(__file__).st_mtime), parse_date(res.headers['Last-Modified']))
     self.assertAlmostEqual(int(time.time()), parse_date(res.headers['Date']))
     request.environ['HTTP_IF_MODIFIED_SINCE'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(100))
     self.assertEqual(open(__file__,'rb').read(), static_file(os.path.basename(__file__), root='./').body.read())
Пример #5
0
def render_js(filename):
    """ Render javascript template to insert a translation
        :param filename: name of the file to be translated
    """

    headers = {}

    # check the template file
    path = bottle.SimpleTemplate.search("javascript/%s" % filename,
                                        bottle.TEMPLATE_PATH)
    if not path:
        return bottle.HTTPError(404, "File does not exist.")

    # test last modification date (mostly copied from bottle.py)
    stats = os.stat(path)
    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                       time.gmtime(stats.st_mtime))
    headers['Last-Modified'] = lm

    ims = bottle.request.environ.get('HTTP_IF_MODIFIED_SINCE')
    if ims:
        ims = bottle.parse_date(ims.split(";")[0].strip())
    if ims is not None and ims >= int(stats.st_mtime):
        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                                        time.gmtime())
        return bottle.HTTPResponse(status=304, **bottle.response.headers)

    # set the content type to javascript
    headers['Content-Type'] = "application/javascript; charset=UTF-8"

    body = bottle.template("javascript/%s" % filename)
    # TODO if you are sadistic enough you can try to minify the content

    return bottle.HTTPResponse(body, **headers)
Пример #6
0
 def test_rfc850(self):
     """DateParser: RFC 850 format"""
     ts = time.time()
     t = time.gmtime(ts)
     rs = time.strftime("%%s, %d-%%s-%y %H:%M:%S GMT",
                        t) % (weekday_full[t.tm_wday], month_abbr[t.tm_mon])
     self.assertEqual(int(ts), int(parse_date(rs)))
Пример #7
0
 def test_asctime(self):
     """DateParser: asctime format"""
     ts = time.time()
     t = time.gmtime(ts)
     rs = time.strftime("%%s %%s %d %H:%M:%S %Y",
                        t) % (weekday_abbr[t.tm_wday], month_abbr[t.tm_mon])
     self.assertEqual(int(ts), int(parse_date(rs)))
Пример #8
0
def get_thumbnail(userfolder, file_name):

    filename = check_path(os.path.join(get_userfolder_path(userfolder), IMG_DIR), file_name)

    headers = {}
    stats = os.stat(filename)

    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
    response.set_header('Last-Modified', lm)

    ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
    if ims:
        ims = parse_date(ims.split(";")[0].strip())
    if ims is not None and ims >= int(stats.st_mtime):
        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
        return HTTPResponse(status=304, **headers)

    response.content_type = 'image/png'

    img = Image.open(filename, 'r')
    img.thumbnail((70, 70), Image.ANTIALIAS)
    buffered = IO()
    img.save(buffered, format='PNG')

    ret = buffered.getvalue()
    response.set_header('Content-Length', len(ret))

    return ret
Пример #9
0
def handle_zip_directory_listing(zip,
                                 archivefile,
                                 subarchivepath,
                                 format=None):
    """List contents in a directory.
    """
    # ensure directory has trailing '/'
    pathname = get_pathname()
    if not pathname.endswith('/'):
        parts = request.urlparts
        new_parts = (parts[0], parts[1], quote(pathname) + '/', parts[3],
                     parts[4])
        new_url = urlunsplit(new_parts)
        return redirect(new_url)

    from bottle import parse_date, tob

    headers = {}

    # check 304
    stats = os.stat(archivefile)
    headers['Last-Modified'] = email.utils.formatdate(stats.st_mtime,
                                                      usegmt=True)
    headers['Cache-Control'] = 'no-cache'

    getenv = request.environ.get

    etag = '%d:%d:%d:%d:%s' % (stats.st_dev, stats.st_ino, stats.st_mtime,
                               stats.st_size, archivefile)
    etag = hashlib.sha1(tob(etag)).hexdigest()

    headers['ETag'] = etag
    check = getenv('HTTP_IF_NONE_MATCH')
    if check and check == etag:
        return HTTPResponse(status=304, **headers)

    if not check:
        ims = getenv('HTTP_IF_MODIFIED_SINCE')
        if ims:
            ims = parse_date(ims.split(";")[0].strip())
        if ims is not None and ims >= int(stats.st_mtime):
            return HTTPResponse(status=304, **headers)

    subentries = util.zip_listdir(zip, subarchivepath)

    try:
        body = template(
            'index.tpl',
            sitename=runtime['name'],
            is_local=is_local_access(),
            base=get_base(),
            path=request.path,
            subarchivepath=subarchivepath,
            subentries=subentries,
        )
        return HTTPResponse(body, **headers)
    except util.ZipDirNotFoundError:
        return http_error(404, "File does not exist.", format=format)
Пример #10
0
 def test_expires_header(self):
     import datetime
     response = BaseResponse()
     now = datetime.datetime.now()
     response.expires = now
     self.assertEqual(0, int((response.expires - now).total_seconds()))
     now2 = datetime.datetime.utcfromtimestamp(
         parse_date(response.headers['Expires']))
     self.assertEqual(0, int((now - now2).total_seconds()))
Пример #11
0
 def wrapper(*args, **kwargs):
     ims = request.environ.get("HTTP_IF_MODIFIED_SINCE")
     if ims:
         ims = parse_date(ims.split(";", 1)[0].strip())
     if ims is not None and ims > state().last_updated_float:
         return HTTPResponse(
             status=304,
             Date=datetime.utcnow().strftime("%a, %d %b %y %T GMT"))
     return handler_f(*args, **kwargs)
Пример #12
0
def data():
    resp = "{}"
    headers = dict()
    last = 0
    sensors = [fetch.SENSOR_1_NAME, fetch.SENSOR_2_NAME]
    for fn in sensors:
        lu = fetch.lastupdate(fn)
        if not lu:
            continue
        if not last:
            last = lu
        else:
            last = max(last, lu)
    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(last))
    headers['Last-Modified'] = lm
    ims = bottle.request.environ.get('HTTP_IF_MODIFIED_SINCE')
    if ims:
        ims = bottle.parse_date(ims.split(";")[0].strip())
    if ims is not None and ims >= last:
        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                                        time.gmtime())
        return bottle.HTTPResponse(status=304, **headers)
    resp = {}
    # ensure we match readouts
    for fn in sensors:
        data = fetch.rrdfetch(fn)
        if "time" not in resp:
            resp["time"] = data["time"]
        resp[fn] = data
    # does time diverge?
    time1 = set(resp[sensors[0]]["time"])
    time2 = set(resp[sensors[1]]["time"])
    for fn in sensors:
        sensordata = resp[fn]
        sensordata.pop("time")
        # Remove outliers
        for name in sensordata.keys():
            data = sensordata[name]
            if name == "volt":
                eps = 0.01
            else:
                eps = 2
            outliers = dbscan(data, eps, 3)
            for i in outliers:
                data[i] = None

    # todo: time might diverge +/- one tick
    #if time1 != time2:
    #    print(time1-time2, time2-time1)
    #    return bottle.HTTPResponse(status=500)

    resp = json.dumps(resp).encode("utf-8")
    headers['Content-Length'] = len(resp)
    return bottle.HTTPResponse(resp, **headers)
 def test_expires_header(self):
     import datetime
     response = BaseResponse()
     now = datetime.datetime.now()
     response.expires = now
     
     def seconds(a, b):
         td = max(a,b) - min(a,b)
         return td.days*360*24 + td.seconds
     
     self.assertEqual(0, seconds(response.expires, now))
     now2 = datetime.datetime.utcfromtimestamp(
         parse_date(response.headers['Expires']))
     self.assertEqual(0, seconds(now, now2))
Пример #14
0
def gzip_cache(path):
    """
    Another GZIP handler for Bottle functions. This may be used to cache the
    files statically on the disc on given `path`.

    If the browser accepts GZIP and there is file at ``path + ".gz"``, this
    file is returned, correct headers are set (Content-Encoding, Last-Modified,
    Content-Length, Date and so on).

    If the browser doesn't accept GZIP or there is no ``.gz`` file at same
    path, normal file is returned.

    Args:
        path (str): Path to the cached file.

    Returns:
        obj: Opened file.
    """
    accept_enc = request.get_header("Accept-Encoding")

    if accept_enc and "gzip" in accept_enc and os.path.exists(path + ".gz"):
        path = path + ".gz"
        response.set_header("Content-Encoding", "gzip")

    stats = os.stat(path)

    headers = dict()
    headers['Content-Length'] = stats.st_size
    headers['Last-Modified'] = time.strftime(
        "%a, %d %b %Y %H:%M:%S GMT",
        time.gmtime(stats.st_mtime)
    )

    # I need to set `headers` dict for optional HTTPResponse use, but also set
    # hedears using `response.set_header()` for normal use
    for key, val in headers.iteritems():
        response.set_header(key, val)

    modified_since = request.environ.get('HTTP_IF_MODIFIED_SINCE')
    if modified_since:
        modified_since = parse_date(modified_since.split(";")[0].strip())

    if modified_since is not None and modified_since >= int(stats.st_mtime):
        headers['Date'] = time.strftime(
            "%a, %d %b %Y %H:%M:%S GMT",
            time.gmtime()
        )
        return HTTPResponse(status=304, **headers)

    return open(path)
Пример #15
0
    def test_expires_header(self):
        import datetime
        response = BaseResponse()
        now = datetime.datetime.now()
        response.expires = now

        def seconds(a, b):
            td = max(a, b) - min(a, b)
            return td.days * 360 * 24 + td.seconds

        self.assertEqual(0, seconds(response.expires, now))
        now2 = datetime.datetime.utcfromtimestamp(
            parse_date(response.headers['Expires']))
        self.assertEqual(0, seconds(now, now2))
Пример #16
0
def send_file(content, filename, size, timestamp):
    """ Send a file represented by file object

    The code is partly based on ``bottle.static_file``.

    :param content:     file-like object
    :param filename:    filename to use
    :param size:        file size in bytes
    :param timestamp:   file's timestamp seconds since epoch
    """
    headers = {}
    ctype = get_mimetype(filename)

    if ctype.startswith('text/'):
        # We expect and assume all text files are encoded UTF-8. It's
        # broadcaster's job to ensure this is true.
        ctype += '; charset=%s' % CHARSET

    # Set basic headers
    headers['Content-Type'] = ctype
    headers['Content-Length'] = size
    headers['Last-Modified'] = format_ts(timestamp)

    # Check if If-Modified-Since header is in request and respond early if so
    modsince = request.environ.get('HTTP_IF_MODIFIED_SINCE')
    modsince = modsince and parse_date(modsince.split(';')[0].strip())
    if modsince is not None and modsince >= timestamp:
        headers['Date'] = format_ts()
        return HTTPResponse(status=304, **headers)

    if request.method == 'HEAD':
        # Request is a HEAD, so remove any content body
        content = ''


    headers['Accept-Ranges'] = 'bytes'
    ranges = request.environ.get('HTTP_RANGE')
    if ranges:
        ranges = list(parse_range_header(ranges, size))
        if not ranges:
            return HTTPError(416, "Request Range Not Satisfiable")
        start, end = ranges[0]
        headers['Content-Range'] = 'bytes %d-%d/%d' % (start, end - 1, size)
        headers['Content-Length'] = str(end - start)
        content = iter_read_range(content, start, end - start)
    return HTTPResponse(content, **headers)
Пример #17
0
def send_file(content, filename, size, timestamp):
    """ Send a file represented by file object

    The code is partly based on ``bottle.static_file``.

    :param content:     file-like object
    :param filename:    filename to use
    :param size:        file size in bytes
    :param timestamp:   file's timestamp seconds since epoch
    """
    headers = {}
    ctype = get_mimetype(filename)

    if ctype.startswith('text/'):
        # We expect and assume all text files are encoded UTF-8. It's
        # broadcaster's job to ensure this is true.
        ctype += '; charset=%s' % CHARSET

    # Set basic headers
    headers['Content-Type'] = ctype
    headers['Content-Length'] = size
    headers['Last-Modified'] = format_ts(timestamp)

    # Check if If-Modified-Since header is in request and respond early if so
    modsince = request.environ.get('HTTP_IF_MODIFIED_SINCE')
    modsince = modsince and parse_date(modsince.split(';')[0].strip())
    if modsince is not None and modsince >= timestamp:
        headers['Date'] = format_ts()
        return HTTPResponse(status=304, **headers)

    if request.method == 'HEAD':
        # Request is a HEAD, so remove any content body
        content = ''

    headers['Accept-Ranges'] = 'bytes'
    ranges = request.environ.get('HTTP_RANGE')
    if ranges:
        ranges = list(parse_range_header(ranges, size))
        if not ranges:
            return HTTPError(416, "Request Range Not Satisfiable")
        start, end = ranges[0]
        headers['Content-Range'] = 'bytes %d-%d/%d' % (start, end - 1, size)
        headers['Content-Length'] = str(end - start)
        content = iter_read_range(content, start, end - start)
    return HTTPResponse(content, **headers)
Пример #18
0
def handle_markdown_output(filepath, filename):
    """Output processed markdown.
    """
    from bottle import parse_date, tob

    headers = {}

    # check 304
    stats = os.stat(filename)
    headers['Last-Modified'] = email.utils.formatdate(stats.st_mtime,
                                                      usegmt=True)

    getenv = request.environ.get

    etag = '%d:%d:%d:%d:%s' % (stats.st_dev, stats.st_ino, stats.st_mtime,
                               stats.st_size, filename)
    etag = hashlib.sha1(tob(etag)).hexdigest()

    headers['ETag'] = etag
    check = getenv('HTTP_IF_NONE_MATCH')
    if check and check == etag:
        return HTTPResponse(status=304, **headers)

    if not check:
        ims = getenv('HTTP_IF_MODIFIED_SINCE')
        if ims:
            ims = parse_date(ims.split(";")[0].strip())
        if ims is not None and ims >= int(stats.st_mtime):
            return HTTPResponse(status=304, **headers)

    # output processed content
    with open(filename, 'r', encoding='UTF-8') as f:
        body = f.read()
        f.close()

    body = template(
        'markdown',
        sitename=runtime['name'],
        is_local=is_local_access(),
        base=get_base(),
        path=request.path,
        content=commonmark.commonmark(body),
    )

    return HTTPResponse(body, **headers)
Пример #19
0
def custom_static_file(filename, root, mimetype='auto', download=False, custom_headers=None):
    if custom_headers is None:
        custom_headers = {}

    root = os.path.abspath(root) + os.sep
    filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
    header = dict()

    if not filename.startswith(root):
        return HTTPError(403, "Access denied.")
    if not os.path.exists(filename) or not os.path.isfile(filename):
        return HTTPError(404, "File does not exist.")
    if not os.access(filename, os.R_OK):
        return HTTPError(403, "You do not have permission to access this file.")

    if mimetype == 'auto':
        mimetype, encoding = mimetypes.guess_type(filename)
        if mimetype: header['Content-Type'] = mimetype
        if encoding: header['Content-Encoding'] = encoding
    elif mimetype:
        header['Content-Type'] = mimetype

    if download:
        download = os.path.basename(filename if download == True else download)
        header['Content-Disposition'] = 'attachment; filename="%s"' % download

    stats = os.stat(filename)
    header['Content-Length'] = stats.st_size
    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
    header['Last-Modified'] = lm

    ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
    if ims:
        ims = parse_date(ims.split(";")[0].strip())
    if ims is not None and ims >= int(stats.st_mtime):
        header['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
        return HTTPResponse(status=304, header=header)

    if custom_headers:
        header.update(custom_headers)

    body = '' if request.method == 'HEAD' else open(filename, 'rb')
    return HTTPResponse(body, header=header)
Пример #20
0
    def get_item_file(self, id):
        item = self.lib.get_item(id)
        if item is None:
            return HTTPError(404, 'File does not exist.')

        if not os.access(item.path, os.R_OK):
            return HTTPError(403, 'You do not have permission to access this file.')

        stats = os.stat(item.path)
        headers = {
            'Content-Type': 'application/octet-stream',
            'Content-Length': stats.st_size,
            'Last-Modified': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)),
            'Date': time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
        }

        ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
        if ims:
            ims = parse_date(ims.split(";")[0].strip())
        if ims is not None and ims >= int(stats.st_mtime):
            return HTTPResponse(status=304, **headers)

        body = '' if request.method == 'HEAD' else open(item.path, 'rb')

        headers["Accept-Ranges"] = "bytes"
        range_header = request.environ.get('HTTP_RANGE')
        if range_header:
            ranges = list(parse_range_header(range_header, stats.st_size))
            if not ranges:
                return HTTPError(416, "Requested Range Not Satisfiable")

            offset, end = ranges[0]
            headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1, stats.st_size)
            headers["Content-Length"] = str(end - offset)
            if body:
                body = _file_iter_range(body, offset, end - offset)

            return HTTPResponse(body, status=206, **headers)

        return HTTPResponse(body, **headers)
Пример #21
0
    def serve(self, root_path, download=False, charset='UTF-8'):
        filename = os.path.join(root_path, self.filepath)
        headers = dict()
        if not os.path.exists(filename) or not os.path.isfile(filename):
            return False, 404, "File does not exist."
        if not os.access(filename, os.R_OK):
            return False, 403, "You do not have permission to access this file."

        mimetype, encoding = mimetypes.guess_type(filename)
        if encoding: headers['Content-Encoding'] = encoding

        if mimetype:
            if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype:
                mimetype += '; charset=%s' % charset
            headers['Content-Type'] = mimetype

        if download:
            download = os.path.basename(filename if download == True else download)
            headers['Content-Disposition'] = 'attachment; filename="%s"' % download

        stats = os.stat(filename)
        headers['Content-Length'] = stats.st_size
        lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
        headers['Last-Modified'] = lm

        ims = self.environ.get('HTTP_IF_MODIFIED_SINCE')
        if ims:
            ims = bottle.parse_date(ims.split(";")[0].strip())
        if ims is not None and ims >= int(stats.st_mtime):
            headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
            return True, '', 304, headers

        if self.method == 'HEAD':
            body = ''
        else:
            with open(filename, 'rb') as f:
                body = f.read()

        return True, body, None, headers
Пример #22
0
def serve_picture():
    renew_picture()
    _picture = picture
    
    # from bottle.static_file
    headers = dict()
    headers['Content-Type'] = 'image/jpeg'

    headers['Content-Length'] = clen = len(_picture.content)
    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(_picture.time))
    headers['Last-Modified'] = lm

    ims = request.get_header('If-Modified-Since')
    if not ims:
      ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
    if ims:
        ims = parse_date(ims.split(";")[0].strip())
    if ims is not None and ims >= int(_picture.time):
        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
        return HTTPResponse(status=304, **headers)

    body = '' if request.method == 'HEAD' else _picture.content
    return HTTPResponse(body, **headers)
Пример #23
0
def static_file(filename, root, mimetype="auto", download=False, charset="UTF-8"):
    """ Open a file in a safe way and return :exc:`HTTPResponse` with status
        code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``,
        ``Content-Length`` and ``Last-Modified`` headers are set if possible.
        Special support for ``If-Modified-Since``, ``Range`` and ``HEAD``
        requests.

        :param filename: Name or path of the file to send.
        :param root: Root path for file lookups. Should be an absolute directory
            path.
        :param mimetype: Defines the content-type header (default: guess from
            file extension)
        :param download: If True, ask the browser to open a `Save as...` dialog
            instead of opening the file with the associated program. You can
            specify a custom filename as a string. If not specified, the
            original filename is used (default: False).
        :param charset: The charset to use for files with a ``text/*``
            mime-type. (default: UTF-8)
    """

    root = os.path.abspath(root) + os.sep
    filename = os.path.abspath(os.path.join(root, filename.strip("/\\")))
    headers = dict()

    if not filename.startswith(root):
        return HTTPError(403, "Access denied.")
    if not os.path.exists(filename) or not os.path.isfile(filename):
        return HTTPError(404, "File does not exist.")
    if not os.access(filename, os.R_OK):
        return HTTPError(403, "You do not have permission to access this file.")

    if mimetype == "auto":
        mimetype, encoding = mimetypes.guess_type(filename)
        if encoding:
            headers["Content-Encoding"] = encoding

    if mimetype:
        if mimetype[:5] == "text/" and charset and "charset" not in mimetype:
            mimetype += "; charset=%s" % charset
        headers["Content-Type"] = mimetype

    if download:
        download = os.path.basename(filename if download == True else download)
        headers["Content-Disposition"] = 'attachment; filename="%s"' % download

    stats = os.stat(filename)
    headers["Content-Length"] = clen = stats.st_size
    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
    headers["Last-Modified"] = lm

    ### Added here:
    headers["Cache-Control"] = "max-age=3600, public"

    ims = request.environ.get("HTTP_IF_MODIFIED_SINCE")
    if ims:
        ims = parse_date(ims.split(";")[0].strip())
    if ims is not None and ims >= int(stats.st_mtime):
        headers["Date"] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
        return HTTPResponse(status=304, **headers)

    body = "" if request.method == "HEAD" else open(filename, "rb")

    headers["Accept-Ranges"] = "bytes"
    ranges = request.environ.get("HTTP_RANGE")
    if "HTTP_RANGE" in request.environ:
        ranges = list(parse_range_header(request.environ["HTTP_RANGE"], clen))
        if not ranges:
            return HTTPError(416, "Requested Range Not Satisfiable")
        offset, end = ranges[0]
        headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1, clen)
        headers["Content-Length"] = str(end - offset)
        if body:
            body = _file_iter_range(body, offset, end - offset)
        return HTTPResponse(body, status=206, **headers)
    return HTTPResponse(body, **headers)
Пример #24
0
def send_fileobj(filename, root, guessmime=True, mimetype=None, download=False):
    """ replacement for bottle's static_file
        with more support for sending string, or file object like StringIO
        special keyword for root : ':string', ':file'
        when passing other data through filename
    """
    header = dict()
    if root in (':string',':file') : fileobj = filename
    elif root==':json' :
        fileobj = bottle.json_dumps(filename)
        mimetype = 'application/json'

    if fileobj :
        filename = download if isinstance(download,basestring) else 'noname'
    else :
        root = os.path.abspath(root) + os.sep
        filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))

        if not filename.startswith(root):
            return bottle.HTTPError(403, "Access denied.")
        if not os.path.exists(filename) or not os.path.isfile(filename):
            return bottle.HTTPError(404, "File does not exist.")
        if not os.access(filename, os.R_OK):
            return bottle.HTTPError(403, "You do not have permission to access this file.")

    if not mimetype and guessmime:
        header['Content-Type'] = bottle.mimetypes.guess_type(filename)[0]
    else:
        header['Content-Type'] = mimetype if mimetype else 'text/plain'

    if download == True:
        download = os.path.basename(filename)
    if download:
        header['Content-Disposition'] = 'attachment; filename="%s"' % download
    if fileobj:
        l = None
        if isinstance(fileobj,basestring):
            l = fileobj.__sizeof__() - type(fileobj)().__sizeof__()
        elif hasattr(fileobj,'len') :
            l = fileobj.len
        elif hasattr(fileobj,'tell') and hasattr(fileobj,'seek') :
            old = fileobj.tell()
            fileobj.seek(0,2)
            l = fileobj.tell()
            fileobj.seek(old)
        if l : header['Content-Length'] = l
    else:
        stats = os.stat(filename)
        lm = bottle.time.strftime("%a, %d %b %Y %H:%M:%S GMT", bottle.time.gmtime(stats.st_mtime))
        header['Last-Modified'] = lm
        ims = bottle.request.environ.get('HTTP_IF_MODIFIED_SINCE')
        if ims:
            ims = ims.split(";")[0].strip() # IE sends "<date>; length=146"
            ims = bottle.parse_date(ims)
            if ims is not None and ims >= int(stats.st_mtime):
                header['Date'] = bottle.time.strftime("%a, %d %b %Y %H:%M:%S GMT", bottle.time.gmtime())
                return bottle.HTTPResponse(status=304, header=header)
        header['Content-Length'] = stats.st_size

    if bottle.request.method == 'HEAD':
        return bottle.HTTPResponse('', header=header)
    if not fileobj : fileobj = open(filename, 'rb')
    return bottle.HTTPResponse(fileobj, header=header)
Пример #25
0
 def test_rfc1123(self):
     """DateParser: RFC 1123 format"""
     ts = time.time()
     rs = bottle.http_date(ts)
     self.assertEqual(int(ts), int(parse_date(rs)))
Пример #26
0
 def test_rfc1123(self):
     """DateParser: RFC 1123 format"""
     ts = time.time()
     rs = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(ts))
     self.assertEqual(int(ts), int(parse_date(rs)))
Пример #27
0
 def test_asctime(self):
     """DateParser: asctime format"""
     ts = time.time()
     rs = time.strftime("%a %b %d %H:%M:%S %Y", time.gmtime(ts))
     self.assertEqual(int(ts), int(parse_date(rs)))
Пример #28
0
 def test_bad(self):
     """DateParser: Bad format"""
     self.assertEqual(None, parse_date('Bad 123'))
Пример #29
0
def send_file(fd, filename=None, size=None, timestamp=None, ctype=None,
              charset=CHARSET, attachment=False, wrapper=DEFAULT_WRAPPER):
    """ Send a file represented by file object

    This function constcuts a HTTPResponse object that uses a file descriptor
    as response body. The file descriptor is suppled as ``fd`` argument and it
    must have a ``read()`` method. ``ValueError`` is raised when this is not
    the case. It supports `byte serving`_ using Range header, and makes the
    best effort to set all appropriate headers. It also supports HEAD queries.

    Because we are dealing with file descriptors and not physical files, the
    user must also supply the file metadata such as filename, size, and
    timestamp.

    The ``filename`` argument is an arbitrary filename. It is used to guess the
    content type, and also to set the content disposition in case of
    attachments.

    The ``size`` argument is the payload size in bytes. If it is omitted, the
    content length header is not set, and byte serving does not work.

    The ``timestamp`` argument is the number of seconds since Unix epoch when
    the file was created or last modified. If this argument is omitted,
    If-Modified-Since request headers cannot be honored.

    To explicitly specify the content type, the ``ctype`` argument can be used.
    This should be a valid MIME type of the payload.

    Default encoding (used as charset parameter in Content-Type header) is
    'UTF-8'. This can be overridden by using the ``charset`` argument.

    The ``attachment`` argumnet can be set to ``True`` to add the
    Content-Dispositon response header. Value of the header is then set to the
    filename.

    The ``wrapper`` argument is used to wrap the file descriptor when doing
    byte serving. The default is to use ``fdsend.rangewrapper.RangeWrapper``
    class, but there are alternatives as ``fdsend.rangewrapper.range_iter`` and
    ``bottle._file_iter_range``. The wrappers provided by this package are
    written to specifically handle file handles that do not have a ``seek()``
    method. If this is not your case, you may safely use the bottle's wrapper.

    The primary difference between ``fdsend.rangewrapper.RangeWrapper`` and
    ``fdsend.rangewrapper.range_iter`` is that the former returns a file-like
    object with ``read()`` method, which may or may not increase performance
    when used on a WSGI server that supports ``wsgi.file_wrapper`` feature. The
    latter returns an iterator and the response is returned as is without the
    use of a ``file_wrapper``. This may have some benefits when it comes to
    memory usage.

    Benchmarking and profiling is the best way to determine which wrapper you
    want to use, or you need to implement your own.

    To implement your own wrapper, you need to create a callable or a class
    that takes the following arguments:

    - file descriptor
    - offset (in bytes from start of the file)
    - length (total number of bytes in the range)

    The return value of the wrapper must be either an iterable or file-like
    object that implements ``read()`` and ``close()`` methods with the usual
    semantics.

    The code is partly based on ``bottle.static_file``.

    .. _byte serving: https://tools.ietf.org/html/rfc2616#page-138
    """
    if not hasattr(fd, 'read'):
        raise ValueError("Object '{}' has no read() method".format(fd))

    headers = {}
    status = 200

    if not ctype and filename is not None:
        ctype, enc = mimetypes.guess_type(filename)
        if enc:
            headers['Content-Encoding'] = enc

    if ctype:
        if ctype.startswith('text/'):
            # We expect and assume all text files are encoded UTF-8. It's
            # broadcaster's job to ensure this is true.
            ctype += '; charset=%s' % charset
        headers['Content-Type'] = ctype

    if size:
        headers['Content-Length'] = size
        headers['Accept-Ranges'] = 'bytes'

    if timestamp:
        headers['Last-Modified'] = format_ts(timestamp)

        # Check if If-Modified-Since header is in request and respond early
        modsince = request.environ.get('HTTP_IF_MODIFIED_SINCE')
        print(modsince)
        modsince = modsince and parse_date(modsince.split(';')[0].strip())
        if modsince is not None and modsince >= timestamp:
            headers['Date'] = format_ts()
            return HTTPResponse(status=304, **headers)

    if attachment and filename:
        headers['Content-Disposition'] = 'attachment; filename="%s"' % filename

    if request.method == 'HEAD':
        # Request is a HEAD, so remove any fd body
        fd = ''

    ranges = request.environ.get('HTTP_RANGE')
    if size and ranges:
        ranges = list(parse_range_header(ranges, size))
        if not ranges:
            return HTTPError(416, 'Request Range Not Satisfiable')
        start, end = ranges[0]
        headers['Content-Range'] = 'bytes %d-%d/%d' % (start, end - 1, size)
        length = end - start
        headers['Content-Length'] = str(length)
        fd = wrapper(fd, start, length)
        status = 206

    return HTTPResponse(fd, status=status, **headers)
Пример #30
0
 def test_rfc850(self):
     """DateParser: RFC 850 format"""
     ts = time.time()
     rs = time.strftime("%A, %d-%b-%y %H:%M:%S GMT", time.gmtime(ts))
     self.assertEqual(int(ts), int(parse_date(rs)))
Пример #31
0
    def static_file(filename,
                    root,
                    mimetype=True,
                    download=False,
                    charset='UTF-8',
                    etag=None):
        """ Open a file in a safe way and return an instance of :exc:`HTTPResponse`
            that can be sent back to the client.
            :param filename: Name or path of the file to send, relative to ``root``.
            :param root: Root path for file lookups. Should be an absolute directory
                path.
            :param mimetype: Provide the content-type header (default: guess from
                file extension)
            :param download: If True, ask the browser to open a `Save as...` dialog
                instead of opening the file with the associated program. You can
                specify a custom filename as a string. If not specified, the
                original filename is used (default: False).
            :param charset: The charset for files with a ``text/*`` mime-type.
                (default: UTF-8)
            :param etag: Provide a pre-computed ETag header. If set to ``False``,
                ETag handling is disabled. (default: auto-generate ETag header)
            While checking user input is always a good idea, this function provides
            additional protection against malicious ``filename`` parameters from
            breaking out of the ``root`` directory and leaking sensitive information
            to an attacker.
            Read-protected files or files outside of the ``root`` directory are
            answered with ``403 Access Denied``. Missing files result in a
            ``404 Not Found`` response. Conditional requests (``If-Modified-Since``,
            ``If-None-Match``) are answered with ``304 Not Modified`` whenever
            possible. ``HEAD`` and ``Range`` requests (used by download managers to
            check or continue partial downloads) are also handled automatically.
        """

        root = os.path.join(os.path.abspath(root), '')
        filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
        headers = dict()

        if not filename.startswith(root):
            return HTTPError(403, "Access denied.")
        if not os.path.exists(filename) or not os.path.isfile(filename):
            return HTTPError(404, "File does not exist.")
        if not os.access(filename, os.R_OK):
            return HTTPError(
                403, "You do not have permission to access this file.")

        if mimetype is True:
            if download and download is not True:
                mimetype, encoding = mimetypes.guess_type(download)
            else:
                mimetype, encoding = mimetypes.guess_type(filename)
            if encoding: headers['Content-Encoding'] = encoding

        if mimetype:
            if (mimetype[:5] == 'text/' or mimetype == 'application/javascript')\
            and charset and 'charset' not in mimetype:
                mimetype += '; charset=%s' % charset
            headers['Content-Type'] = mimetype

        if download:
            download = os.path.basename(
                filename if download is True else download)
            headers[
                'Content-Disposition'] = 'attachment; filename="%s"' % download

        stats = os.stat(filename)
        headers['Content-Length'] = clen = stats.st_size
        headers['Last-Modified'] = email.utils.formatdate(stats.st_mtime,
                                                          usegmt=True)
        headers['Date'] = email.utils.formatdate(time.time(), usegmt=True)

        getenv = request.environ.get

        if etag is None:
            etag = '%d:%d:%d:%d:%s' % (stats.st_dev, stats.st_ino,
                                       stats.st_mtime, clen, filename)
            etag = hashlib.sha1(tob(etag)).hexdigest()

        if etag:
            headers['ETag'] = etag
            check = getenv('HTTP_IF_NONE_MATCH')
            if check and check == etag:
                return HTTPResponse(status=304, **headers)

        if not (etag and check):
            ims = getenv('HTTP_IF_MODIFIED_SINCE')
            if ims:
                ims = parse_date(ims.split(";")[0].strip())
            if ims is not None and ims >= int(stats.st_mtime):
                return HTTPResponse(status=304, **headers)

        body = '' if request.method == 'HEAD' else open(filename, 'rb')

        headers["Accept-Ranges"] = "bytes"
        range_header = getenv('HTTP_RANGE')
        if range_header:
            ranges = list(parse_range_header(range_header, clen))
            if not ranges:
                return HTTPError(416, "Requested Range Not Satisfiable")
            offset, end = ranges[0]
            headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1,
                                                           clen)
            headers["Content-Length"] = str(end - offset)
            if body: body = _file_iter_range(body, offset, end - offset)
            return HTTPResponse(body, status=206, **headers)
        return HTTPResponse(body, **headers)
Пример #32
0
def handle_subarchive_path(archivefile,
                           subarchivepath,
                           mimetype,
                           encoding,
                           download=False,
                           charset='UTF-8',
                           etag=None,
                           format=None):
    """Show content of a path in a zip file.
    """
    from bottle import parse_range_header, parse_date, _file_iter_range, tob

    if not os.access(archivefile, os.R_OK):
        return http_error(403,
                          "You do not have permission to access this file.",
                          format=format)

    try:
        zip = zipfile.ZipFile(archivefile)
    except:
        return http_error(500, "Unable to open the ZIP file.", format=format)

    try:
        # KeyError is raised if subarchivepath does not exist
        info = zip.getinfo(subarchivepath)
    except KeyError:
        # subarchivepath does not exist
        # possibility a missing directory entry?
        return handle_zip_directory_listing(zip, archivefile, subarchivepath)

    fh = zip.open(subarchivepath, 'r')

    headers = dict()

    if encoding: headers['Content-Encoding'] = encoding

    if mimetype is True:
        if download and download is not True:
            mimetype, encoding = mimetypes.guess_type(download)
        else:
            mimetype, encoding = mimetypes.guess_type(subarchivepath)
        if encoding: headers['Content-Encoding'] = encoding

    if mimetype:
        if (mimetype[:5] == 'text/' or mimetype == 'application/javascript')\
        and charset and 'charset' not in mimetype:
            mimetype += '; charset=%s' % charset
        headers['Content-Type'] = mimetype

    if download:
        download = os.path.basename(
            subarchivepath if download is True else download)
        headers['Content-Disposition'] = 'attachment; filename="%s"' % download

    headers['Content-Length'] = clen = info.file_size

    lm = info.date_time
    epoch = int(
        time.mktime((lm[0], lm[1], lm[2], lm[3], lm[4], lm[5], 0, 0, -1)))
    headers['Last-Modified'] = email.utils.formatdate(epoch, usegmt=True)

    headers['Date'] = email.utils.formatdate(time.time(), usegmt=True)

    getenv = request.environ.get

    if etag is None:
        etag = '%d:%d:%s' % (epoch, clen, subarchivepath)
        etag = hashlib.sha1(tob(etag)).hexdigest()

    if etag:
        headers['ETag'] = etag
        check = getenv('HTTP_IF_NONE_MATCH')
        if check and check == etag:
            return HTTPResponse(status=304, **headers)

    if not (etag and check):
        ims = getenv('HTTP_IF_MODIFIED_SINCE')
        if ims:
            ims = parse_date(ims.split(";")[0].strip())
        if ims is not None and ims >= int(epoch):
            return HTTPResponse(status=304, **headers)

    body = '' if request.method == 'HEAD' else fh

    headers["Accept-Ranges"] = "bytes"
    range_header = getenv('HTTP_RANGE')
    if range_header:
        ranges = list(parse_range_header(range_header, clen))
        if not ranges:
            return http_error(416, "Requested Range Not Satisfiable")
        offset, end = ranges[0]
        headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1, clen)
        headers["Content-Length"] = str(end - offset)
        if body: body = _file_iter_range(body, offset, end - offset)
        return HTTPResponse(body, status=206, **headers)
    return HTTPResponse(body, **headers)
Пример #33
0
 def test_rfc850(self):
     """DateParser: RFC 850 format"""
     ts = time.time()
     rs = time.strftime("%A, %d-%b-%y %H:%M:%S GMT", time.gmtime(ts))
     self.assertEqual(int(ts), int(parse_date(rs)))
Пример #34
0
 def last_mod_header(self, lm):
     self.lm = lm
     self.lm_epoch = bottle.parse_date(lm) or 0
Пример #35
0
 def test_bad(self):
     """DateParser: Bad format"""
     self.assertEqual(None, parse_date('Bad 123'))
Пример #36
0
 def test_rfc1123(self):
     """DateParser: RFC 1123 format"""
     ts = time.time()
     rs = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(ts))
     self.assertEqual(int(ts), int(parse_date(rs)))
Пример #37
0
    def post_cc_data(self, session):

        headers = dict()

        # data shall always send 'If-Modified-Since' header ...
        ims = bottle.request.environ.get('HTTP_IF_MODIFIED_SINCE')

        headers['Last-Modified'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                                                 time.gmtime(time.time()))
        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                                        time.gmtime())

        if ims:
            ims = bottle.parse_date(ims.split(";")[0].strip())

        #print(session['node'])

        # print(session.id + ' / position -> ')

        try:
            node = self.nodes[session['node']]
        except KeyError:
            self.sessions.delete_session(session)
            raise bottle.HTTPError(400)

        status = session['status']

        # print(status)
        if status != 'cc_ok':

            # if node.config.connect is False:
            #     return bottle.HTTPResponse(202)    # Accepted

            # This raises in case of issues
            self.connect_card_to_node(session, node)

            # Being here there's a valid connection!
            session['status'] = 'cc_ok'

        if node.controller and not node.controller.is_alive():
            # Try to reconnect - once!
            node.controller.reconnect()

        # This is an issue...
        if not node.controller or not node.controller.is_alive():
            raise bottle.HTTPError(404)

        # ...

        ret = {}

        # create an uuid unique to a fp
        # to allow the cc client to distinguish cards for cumulation
        fp = None
        with contextlib.suppress(Exception):
            fp = node.controller.fingerprint

        if fp is not None and len(fp) == 40:
            if fp not in self.fingerprints:
                self.fingerprints[fp] = uuid.uuid4().hex
            ret['representing'] = self.fingerprints[fp]

        # ret['style'] = 'readonly'
        ret['dd'] = ''

        ret['label'] = node.nickname
        ret['version'] = node.controller.version_short

        # if ims and ims < self.vm.Tor.last_modified:
        ret['latest'] = self.version.Tor.stable

        # ret['latest'] = '0.4.0.6'
        ret['versionflag'] = node.controller.version_current

        node_mode = 'Client'
        if node.controller.get_conf('BridgeRelay', None) == '1':
            node_mode = 'Bridge'
        elif node.controller.get_conf('ORPort', None):
            node_mode = 'Relay'
        ret['mode'] = node_mode

        if True == True:

            # the first flag is always the placeholder for the nodes mode data
            # ret['flags'] = ['mode']
            ret['flags'] = []

            if node_mode == 'Bridge':
                # node.controller.flags fails for a bridge!
                try:
                    oo = node.onionoo
                    d = oo.details('flags')
                    ret['flags'].extend(d)
                except:
                    pass
            else:
                f = node.controller.flags
                if f is not None and len(f) > 0 and f[0] != 'unknown':
                    ret['flags'].extend(f)

            # We add an icon in case of Hibernation!
            try:
                accs = node.controller.get_accounting_stats()
            except:
                pass
            else:
                if accs.status != "awake":
                    ret['flags'].append(accs.status)

        else:

            ret['flags'] = [
                'mode', 'Authority', 'BadExit', 'BadDirectory', 'Exit', 'Fast',
                'Guard', 'HSDir', 'Named', 'Stable', 'Running', 'Unnamed',
                'Valid', 'V2Dir', 'V3Dir', 'soft', 'hard', 'unknown'
            ]

        ret['details'] = True

        last_ts = session['lastTS']

        rv = node.bandwidth.get_data(interval='1s', since_timestamp=last_ts)
        # print(len(rv))
        if len(rv) > 0:
            ret['bw'] = rv

        session['lastTS'] = time.time() * 1000

        # Connection
        conn = ''
        conn += 'h' if node.config.is_default_node else ''
        conn += 'p' if node.controller.auth_password else ''
        conn += 'c' if node.controller.with_cookie else ''
        conn += 'x' if node.controller.via_proxy else ''
        ret['conn'] = conn

        # set the headers
        for header, value in headers.items():
            # print(header, value)
            bottle.response.set_header(header, value)

        return json.JSONEncoder().encode(ret)
Пример #38
0
 def test_asctime(self):
     """DateParser: asctime format"""
     ts = time.time()
     rs = time.strftime("%a %b %d %H:%M:%S %Y", time.gmtime(ts))
     self.assertEqual(int(ts), int(parse_date(rs)))
Пример #39
0
def send_file(content, filename, size=None, timestamp=None):
    """
    Convert file data into an HTTP response.

    This method is used when the file data does not exist on disk, such as when
    it is dynamically generated.

    Because the file does not exist on disk, the basic metadata which is
    usually read from the file itself must be supplied as arguments. The
    ``filename`` argument is the supposed filename of the file data. It is only
    used to set the Content-Type header, and you may safely pass in just the
    extension with leading period.

    The ``size`` argument is the payload size in bytes. For streaming files,
    this can be particularly important as the ranges are calculated baed on
    content length. If ``size`` is omitted, then support for ranges is not
    advertise and ranges are never returned.

    ``timestamp`` is expected to be in seconds since UNIX epoch, and is used to
    calculate Last-Modified HTTP headers, as well as handle If-Modified-Since
    header. If omitted, current time is used, and If-Modified-Since is never
    checked.

    .. note::
        The returned response is a completely new response object.
        Modifying the reponse object in the current request context is not
        going to affect the object returned by this function. You should modify
        the object returned by this function instead.

    Example::

        def some_handler():
            import StringIO
            f = StringIO.StringIO('foo')
            return send_file(f, 'file.txt', 3, 1293281312)

    The code is partly based on ``bottle.static_file``, with the main
    difference being the use of file-like objects instead of files on disk.
    """
    headers = {}
    ctype = get_mimetype(filename)

    if ctype.startswith('text/'):
        # We expect and assume all text files are encoded UTF-8. It's
        # user's job to ensure this is true.
        ctype += '; charset=UTF-8'

    # Set basic headers
    headers['Content-Type'] = ctype
    if size:
        headers['Content-Length'] = size
    headers['Last-Modified'] = format_ts(timestamp)

    # Check if If-Modified-Since header is in request and respond early if so
    if timestamp:
        modsince = request.environ.get('HTTP_IF_MODIFIED_SINCE')
        modsince = modsince and parse_date(modsince.split(';')[0].strip())
        if modsince is not None and modsince >= timestamp:
            headers['Date'] = format_ts()
            return HTTPResponse(status=304, **headers)

    if request.method == 'HEAD':
        # Request is a HEAD, so remove any content body
        content = ''

    if size:
        headers['Accept-Ranges'] = 'bytes'

    ranges = request.environ.get('HTTP_RANGE')
    if ranges and size:
        ranges = list(parse_range_header(ranges, size))
        if not ranges:
            return HTTPError(416, "Request Range Not Satisfiable")
        start, end = ranges[0]
        headers['Content-Range'] = 'bytes %d-%d/%d' % (start, end - 1, size)
        headers['Content-Length'] = str(end - start)
        content = iter_read_range(content, start, end - start)
    return HTTPResponse(content, **headers)
Пример #40
0
def git_static_file(filename,
                    mimetype='auto',
                    download=False,
                    charset='UTF-8'):
    """ This method is derived from bottle.static_file:

        Open [a file] and return :exc:`HTTPResponse` with status
        code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``,
        ``Content-Length`` and ``Last-Modified`` headers are set if possible.
        Special support for ``If-Modified-Since`` [...].

        :param filename: Name or path of the file to send.
        :param mimetype: Defines the content-type header (default: guess from
            file extension)
        :param download: If True, ask the browser to open a `Save as...` dialog
            instead of opening the file with the associated program. You can
            specify a custom filename as a string. If not specified, the
            original filename is used (default: False).
        :param charset: The charset to use for files with a ``text/*``
            mime-type. (default: UTF-8)
    """

    # root = os.path.abspath(root) + os.sep
    # filename = os.path.abspath(pathjoin(root, filename.strip('/\\')))
    filename = filename.strip('/\\')
    headers = dict()

    FS = request.app.config['pgs.FS']
    # if not filename.startswith(root):
    #    return HTTPError(403, "Access denied.")
    if not FS.exists(filename):
        return HTTPError(404, "Not found.")
    # if not os.access(filename, os.R_OK):
    # return HTTPError(403, "You do not have permission to access this file.")

    if mimetype == 'auto':
        if download and download is not True:
            mimetype, encoding = mimetypes.guess_type(download)
        else:
            mimetype, encoding = mimetypes.guess_type(filename)
        if encoding:
            headers['Content-Encoding'] = encoding

    if mimetype:
        if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype:
            mimetype += '; charset=%s' % charset
        headers['Content-Type'] = mimetype

    if download:
        download = os.path.basename(filename if download else download)
        headers['Content-Disposition'] = 'attachment; filename="%s"' % download

    # stats = os.stat(filename)
    info = FS.getinfo(filename)
    headers['Content-Length'] = clen = info['size']
    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                       time.gmtime(info['modified_time']))
    headers['Last-Modified'] = lm

    ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
    if ims:
        ims = parse_date(ims.split(";")[0].strip())
    mtime = info['modified_time']
    if mtime and ims is not None and ims >= int(mtime):
        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                                        time.gmtime())
        return HTTPResponse(status=304, **headers)

    body = '' if request.method == 'HEAD' else FS.get_fileobj(filename)

    clen
    # headers["Accept-Ranges"] = "bytes"
    # ranges = request.environ.get('HTTP_RANGE')
    # if 'HTTP_RANGE' in request.environ:
    #    ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen))
    #     if not ranges:
    #         return HTTPError(416, "Requested Range Not Satisfiable")
    #    offset, end = ranges[0]
    #    headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1, clen)
    #    headers["Content-Length"] = str(end - offset)
    #    if body: body = _file_iter_range(body, offset, end - offset)
    #     return HTTPResponse(body, status=206, **headers)
    return HTTPResponse(body, **headers)
Пример #41
0
    def post_cc_ping(self, session):

        headers = {
            'Last-Modified':
            time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                          time.gmtime(self.cc.last_modified)),
            'Date':
            time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
        }

        # ping shall always send 'If-Modified-Since' header ...
        ims = bottle.request.environ.get('HTTP_IF_MODIFIED_SINCE', None)
        if ims is None:
            # As said: ping shall always send I-M-S
            return bottle.HTTPError(400)

        ims = bottle.parse_date(ims.split(";")[0].strip())
        if ims >= int(self.cc.last_modified):
            return bottle.HTTPResponse(status=304, **headers)

        # set the headers
        for header, value in headers.items():
            # print(header, value)
            bottle.response.set_header(header, value)

        # ['cards'] holds the session.id's for the cards of this session
        if session['cards'] is None:
            # First Card = DefaultNode
            card = self.sessions.create_session(bottle.request, 'cc_new')
            card['node'] = 'theonionbox'
            session['cards'] = [card.id]

            # if the frame (cc) session knows a password, this is the one for the default node!
            card['password'] = session.get('password', None)

        cards = [session['cards'][0]]

        # walk through the sections defined in the cc configuration file
        for section in self.cc:

            # The session = card representing this section
            card = None

            # the card's node
            node = None

            # check if there's a card = session, that holds a node with the name of this configuration section
            for card_id in session['cards']:
                s = self.sessions.get_session(card_id, bottle.request)
                if s is not None:
                    # If s IS None, the session is expired!
                    node = self.nodes[s['node']]
                    if section.name == node.config.name:
                        card = s
                        break

            if card is not None:
                # if the config changed...
                if int(section.last_modified) > ims:
                    # disco the node
                    node = self.nodes[card['node']]
                    node.disconnect()

                    # and delete the session = card
                    self.sessions.delete_session(card)
                    card = None

                    # card will now be recreated & node reconnected

            # If there's None, create a new session = a new card
            if card is None:

                # get the node representing this section
                node = self.nodes.get_name(section.name)

                # if node is None: create it from section!
                if node is None:
                    id = self.nodes.node_add(section)
                    node = self.nodes.get(id)

                # then create a new session & connect both
                if node is not None:
                    card = self.sessions.create_session(
                        bottle.request, 'cc_new')
                    if card is not None:
                        card['node'] = node.id
                        card['controlled_by'] = session.id
                        card['password'] = section.password

                        # session['cards'].append(card.id)

            if card is not None:
                cards.append(card.id)

        # This eliminates all expired sessions from ['cards']
        session['cards'] = cards

        # Now json everything... and return it!
        return json.JSONEncoder().encode({'cards': cards})