Esempio n. 1
0
File: views.py Progetto: quru/qis
def make_304_response(image_attrs, is_original, last_modified_time):
    """
    Returns a HTTP 304 "Not Modified" Flask response object for the given image.

    image_attrs - An ImageAttrs containing the image specification.
    is_original - Whether to count this response as a "download original" function.
                  If True, logs download statistics instead of a view.
    last_modified_time - The image's last modification time as number of
                         seconds since the epoch.
    """
    # Create a blank response with no content
    response = flask.Response(status=304)

    # We have to set the same caching headers again
    # http://stackoverflow.com/a/4393499/1671320
    _add_http_caching_headers(
        response,
        image_attrs,
        last_modified_time,
        image_engine._get_expiry_secs(image_attrs)
    )

    if app.config['DEBUG']:
        logger.debug(
            'Sending 304 Not Modified for ' + str(image_attrs)
        )

    _log_stats(image_attrs.database_id(), 0, is_original, False)
    return response
Esempio n. 2
0
File: views.py Progetto: quru/qis
def _public_image_limits_post_image_checks(req_width, req_height, req_template,
                                           image_data, image_format):
    """
    To be called when no one is logged in, checks that the image actually
    generated conforms to the limits defined by the PUBLIC_MAX_IMAGE_WIDTH
    and PUBLIC_MAX_IMAGE_HEIGHT settings.

    As an optimisation, this function only has any effect for the conditions
    that would not have been caught by the "pre-image" checks.
    Specifically, this is when either:

    * only PUBLIC_MAX_IMAGE_WIDTH is set, but only an image height was given
    or
    * only PUBLIC_MAX_IMAGE_HEIGHT is set, but only an image width was given

    Raises a ValueError if the generated image dimensions have exceeded the
    defined limits.
    """
    if not req_template:
        limit_w = app.config['PUBLIC_MAX_IMAGE_WIDTH'] or 0
        limit_h = app.config['PUBLIC_MAX_IMAGE_HEIGHT'] or 0
        # We have to inspect the image, so only do this for the 2 conditions
        # that the pre-image checks couldn't do
        if (limit_w and not limit_h and req_height and not req_width) or \
           (limit_h and not limit_w and req_width and not req_height):
            logger.debug('Public image limits, checking generated image dimensions')
            image_w, image_h = image_engine.get_image_data_dimensions(
                image_data, image_format
            )
            logger.debug('Public image limits, generated image is %d x %d' % (image_w, image_h))
            if image_w and image_h:
                if limit_w and image_w > limit_w:
                    raise ValueError('width: exceeds public image limit')
                if limit_h and image_h > limit_h:
                    raise ValueError('height: exceeds public image limit')
Esempio n. 3
0
def mine():
    global index
    request_body = request.get_json()
    logger.debug("request_body: %s", request_body)
    index += 1
    block = Block(index, request_body)
    blockchain.add_block(block)
    return jsonify({"response": "success"})
Esempio n. 4
0
File: views.py Progetto: quru/qis
def image():
    logger.debug(request.method + ' ' + request.url)
    try:
        logged_in = session_logged_in()
        allow_uncache = app.config['BENCHMARKING'] or app.config['DEBUG']
        args = request.args

        # Get URL parameters for the image
        src         = args.get('src', '')
        page        = args.get('page', None)
        iformat     = args.get('format', None)
        template    = args.get('tmp', None)
        width       = args.get('width', None)
        height      = args.get('height', None)
        halign      = args.get('halign', None)
        valign      = args.get('valign', None)
        autosizefit = args.get('autosizefit', None)
        rotation    = args.get('angle', None)
        flip        = args.get('flip', None)
        top         = args.get('top', None)
        left        = args.get('left', None)
        bottom      = args.get('bottom', None)
        right       = args.get('right', None)
        autocropfit = args.get('autocropfit', None)
        fill        = args.get('fill', None)
        quality     = args.get('quality', None)
        sharpen     = args.get('sharpen', None)
        ov_src      = args.get('overlay', None)
        ov_size     = args.get('ovsize', None)
        ov_opacity  = args.get('ovopacity', None)
        ov_pos      = args.get('ovpos', None)
        icc_profile = args.get('icc', None)
        icc_intent  = args.get('intent', None)
        icc_bpc     = args.get('bpc', None)
        colorspace  = args.get('colorspace', None)
        strip       = args.get('strip', None)
        dpi         = args.get('dpi', None)
        tile        = args.get('tile', None)
        # Get URL parameters for handling options
        attach      = args.get('attach', None)
        xref        = args.get('xref', None)
        stats       = args.get('stats', None)
        # Get protected admin/internal parameters
        cache       = args.get('cache', '1') if logged_in or allow_uncache else '1'
        recache     = args.get('recache', None) if allow_uncache else None

        # eRez compatibility mode
        src = erez_params_compat(src)

        # Tweak strings as necessary and convert non-string parameters
        # to the correct data types
        try:
            # Image options
            if page is not None:
                page = parse_int(page)
            if iformat is not None:
                iformat = iformat.lower()
            if template is not None:
                template = template.lower()
            if width is not None:
                width = parse_int(width)
            if height is not None:
                height = parse_int(height)
            if halign is not None:
                halign = halign.lower()
            if valign is not None:
                valign = valign.lower()
            if autosizefit is not None:
                autosizefit = parse_boolean(autosizefit)
            if rotation is not None:
                rotation = parse_float(rotation)
            if flip is not None:
                flip = flip.lower()
            if top is not None:
                top = parse_float(top)
            if left is not None:
                left = parse_float(left)
            if bottom is not None:
                bottom = parse_float(bottom)
            if right is not None:
                right = parse_float(right)
            if autocropfit is not None:
                autocropfit = parse_boolean(autocropfit)
            if fill is not None:
                fill = parse_colour(fill)
            if quality is not None:
                quality = parse_int(quality)
            if sharpen is not None:
                sharpen = parse_int(sharpen)
            if ov_size is not None:
                ov_size = parse_float(ov_size)
            if ov_pos is not None:
                ov_pos = ov_pos.lower()
            if ov_opacity is not None:
                ov_opacity = parse_float(ov_opacity)
            if icc_profile is not None:
                icc_profile = icc_profile.lower()
            if icc_intent is not None:
                icc_intent = icc_intent.lower()
            if icc_bpc is not None:
                icc_bpc = parse_boolean(icc_bpc)
            if colorspace is not None:
                colorspace = colorspace.lower()
            if strip is not None:
                strip = parse_boolean(strip)
            if dpi is not None:
                dpi = parse_int(dpi)
            if tile is not None:
                tile = parse_tile_spec(tile)
            # Handling options
            if attach is not None:
                attach = parse_boolean(attach)
            if xref is not None:
                validate_string(xref, 0, 1024)
            if stats is not None:
                stats = parse_boolean(stats)
            # Admin/internal options
            if cache is not None:
                cache = parse_boolean(cache)
            if recache is not None:
                recache = parse_boolean(recache)
        except (ValueError, TypeError) as e:
            raise httpexc.BadRequest(unicode(e))

        # Package and validate the parameters
        try:
            # #2694 Enforce public image limits - perform easy parameter checks
            if not logged_in:
                width, height, autosizefit = _public_image_limits_pre_image_checks(
                    width, height, autosizefit, tile, template
                )
            # Store and normalise all the parameters
            image_attrs = ImageAttrs(src, -1, page, iformat, template,
                                     width, height, halign, valign,
                                     rotation, flip,
                                     top, left, bottom, right, autocropfit,
                                     autosizefit, fill, quality, sharpen,
                                     ov_src, ov_size, ov_pos, ov_opacity,
                                     icc_profile, icc_intent, icc_bpc,
                                     colorspace, strip, dpi, tile)
            image_engine.finalise_image_attrs(image_attrs)
        except ValueError as e:
            raise httpexc.BadRequest(unicode(e))

        # Get/create the database ID (from cache, validating path on create)
        image_id = data_engine.get_or_create_image_id(
            image_attrs.filename(),
            return_deleted=False,
            on_create=on_image_db_create_anon_history
        )
        if (image_id == 0):
            raise DoesNotExistError()  # Deleted
        elif (image_id < 0):
            raise DBError('Failed to add image to database')
        image_attrs.set_database_id(image_id)

        # Require view permission or file admin
        permissions_engine.ensure_folder_permitted(
            image_attrs.folder_path(),
            FolderPermission.ACCESS_VIEW,
            get_session_user()
        )
        # Ditto for overlays
        if ov_src:
            permissions_engine.ensure_folder_permitted(
                filepath_parent(ov_src),
                FolderPermission.ACCESS_VIEW,
                get_session_user()
            )

        # v1.17 If this is a conditional request with an ETag, see if we can just return a 304
        if 'If-None-Match' in request.headers and not recache:
            etag_valid, modified_time = _etag_is_valid(
                image_attrs,
                request.headers['If-None-Match'],
                False
            )
            if etag_valid:
                # Success HTTP 304
                return make_304_response(image_attrs, False, modified_time)

        # Get the requested image data
        image_wrapper = image_engine.get_image(
            image_attrs,
            'refresh' if recache else cache
        )
        if (image_wrapper is None):
            raise DoesNotExistError()

        # #2694 Enforce public image limits - check the dimensions
        #       of images that passed the initial parameter checks
        if not logged_in:
            try:
                _public_image_limits_post_image_checks(
                    image_attrs.width(),
                    image_attrs.height(),
                    image_attrs.template(),
                    image_wrapper.data(),
                    image_wrapper.attrs().format()
                )
            except ValueError as e:
                raise httpexc.BadRequest(unicode(e))  # As for the pre-check

        # Success HTTP 200
        return make_image_response(image_wrapper, False, stats, attach, xref)
    except httpexc.HTTPException:
        # Pass through HTTP 4xx and 5xx
        raise
    except ServerTooBusyError:
        logger.warn(u'503 Too busy for ' + request.url)
        raise httpexc.ServiceUnavailable()
    except ImageError as e:
        logger.warn(u'415 Invalid image file \'' + src + '\' : ' + unicode(e))
        raise httpexc.UnsupportedMediaType(unicode(e))
    except SecurityError as e:
        if app.config['DEBUG']:
            raise
        log_security_error(e, request)
        raise httpexc.Forbidden()
    except DoesNotExistError as e:
        # First time around the ID will be set. Next time around it
        # won't but we should check whether the disk file now exists.
        if image_attrs.database_id() > 0 or path_exists(image_attrs.filename(), require_file=True):
            image_engine.reset_image(image_attrs)
        logger.warn(u'404 Not found: ' + unicode(e))
        raise httpexc.NotFound(unicode(e))
    except Exception as e:
        if app.config['DEBUG']:
            raise
        logger.error(u'500 Error for ' + request.url + '\n' + unicode(e))
        raise httpexc.InternalServerError(unicode(e))
Esempio n. 5
0
File: views.py Progetto: quru/qis
def _public_image_limits_pre_image_checks(req_width, req_height, req_autosizefit,
                                          req_tile, req_template):
    """
    To be called when no one is logged in, enforces the image dimension limits
    defined by the PUBLIC_MAX_IMAGE_WIDTH and PUBLIC_MAX_IMAGE_HEIGHT settings.
    If a template is specified, the template dimensions take precedence.
    Or if no dimensions were requested, returns default value(s) for them.

    Returns a tuple of replacement (width, height, autosizefit) values that
    should be used for the rest of the image request.

    Raises a ValueError if the requested image dimensions would exceed the
    defined limits.
    """
    limit_w = app.config['PUBLIC_MAX_IMAGE_WIDTH'] or 0
    limit_h = app.config['PUBLIC_MAX_IMAGE_HEIGHT'] or 0
    if (limit_w or limit_h) and req_tile is None:
        logger.debug(
            'Public image limits, checking parameters vs %d x %d limit' % (limit_w, limit_h)
        )

        # For v1 only, v2 will get these from a default template
        default_w = limit_w
        default_h = limit_h

        # If we're using a template, get the template dimensions
        template_w = 0
        template_h = 0
        if req_template:
            try:
                templ = image_engine.get_template(req_template)
                template_w = templ.image_attrs.width() or 0
                template_h = templ.image_attrs.height() or 0
            except KeyError:
                # Validation (yet to come) will reject the bad template name
                pass

        # v1.32.1 - if template contradicts the limit, template takes precedence
        if limit_w and template_w and template_w > limit_w:
            limit_w = template_w
        if limit_h and template_h and template_h > limit_h:
            limit_h = template_h

        # Check the requested size vs the limits
        if req_width and limit_w and req_width > limit_w:
            raise ValueError('width: exceeds public image limit')
        if req_height and limit_h and req_height > limit_h:
            raise ValueError('height: exceeds public image limit')

        # Check if we need to size-limit an otherwise unlimited image request
        # Note: In v2 this will be done with new default values in ImageAttrs
        if not req_width and not req_height and not template_w and not template_h:
            req_width = default_w if default_w else 0
            req_height = default_h if default_h else 0
            # Unless explicitly set otherwise, prevent padding
            if req_width and req_height and req_autosizefit is None:
                req_autosizefit = True
            logger.debug(
                'Public image limits, unsized image set as %d x %d' % (req_width, req_height)
            )

    return req_width, req_height, req_autosizefit
Esempio n. 6
0
File: views.py Progetto: quru/qis
def make_image_response(image_wrapper, is_original, stats=None, as_attachment=None, xref=None):
    """
    Returns a Flask response object for the given image and response options,
    handles the tracking ID if there is one, and writes view statistics for the
    image.

    image_wrapper - An ImageWrapper containing the image data to return.
    is_original - Whether to count this response as a "download original" function.
                  If True, logs download statistics instead of a view.
    stats - Optional override for whether to enable or disable image statistics.
            Uses the setting in image_wrapper when None.
    as_attachment - Optional override for whether to provide the
                    Content-Disposition HTTP header (with filename).
                    Uses the setting in image_wrapper when None.
    xref - Optional external URL to call.
    """
    image_attrs = image_wrapper.attrs()

    # Process xref if there is one
    if xref:
        handle_image_xref(xref)

    # Create the HTTP response
    if _USE_SENDFILE:
        response = send_file(
            StringIO.StringIO(image_wrapper.data()),
            image_attrs.mime_type()
        )
    else:
        response = make_response(image_wrapper.data())
        response.mimetype = image_attrs.mime_type()

    # Set the browser caching headers
    _add_http_caching_headers(
        response,
        image_attrs,
        image_wrapper.last_modified_time(),
        image_wrapper.client_expiry_time()
    )

    # Set custom cache info header
    response.headers['X-From-Cache'] = str(image_wrapper.is_from_cache())

    # URL attachment param overrides what the returned object wants
    attach = as_attachment if (as_attachment is not None) else \
             image_wrapper.is_attachment()
    if is_original or attach:
        fname = image_attrs.filename(with_path=False, replace_format=True)
        fname = unicode_to_utf8(fname)
        cd_type = 'attachment' if attach else 'inline'
        response.headers['Content-Disposition'] = cd_type + '; filename="' + fname + '"'

    if app.config['DEBUG']:
        logger.debug(
            'Sending ' + str(len(image_wrapper.data())) + ' bytes for ' + str(image_attrs)
        )

    _log_stats(
        image_attrs.database_id(),
        len(image_wrapper.data()),
        is_original,
        image_wrapper.is_from_cache(),
        image_wrapper.record_stats() if stats is None else stats
    )
    return response
Esempio n. 7
0
File: views.py Progetto: quru/qis
def original():
    logger.debug('GET ' + request.url)
    try:
        # Get URL parameters for the image
        src = request.args.get('src', '')
        # Get URL parameters for handling options
        attach  = request.args.get('attach', None)
        xref    = request.args.get('xref', None)
        stats   = request.args.get('stats', None)

        # Validate the parameters
        try:
            if attach is not None:
                attach = parse_boolean(attach)
            if xref is not None:
                validate_string(xref, 0, 1024)
            if stats is not None:
                stats = parse_boolean(stats)

            image_attrs = ImageAttrs(src)
            image_attrs.validate()
        except ValueError as e:
            raise httpexc.BadRequest(unicode(e))

        # Get/create the database ID (from cache, validating path on create)
        image_id = data_engine.get_or_create_image_id(
            image_attrs.filename(),
            return_deleted=False,
            on_create=on_image_db_create_anon_history
        )
        if (image_id == 0):
            raise DoesNotExistError()  # Deleted
        elif (image_id < 0):
            raise DBError('Failed to add image to database')
        image_attrs.set_database_id(image_id)

        # Require download permission or file admin
        permissions_engine.ensure_folder_permitted(
            image_attrs.folder_path(),
            FolderPermission.ACCESS_DOWNLOAD,
            get_session_user()
        )

        # v1.17 If this is a conditional request with an ETag, see if we can just return a 304
        if 'If-None-Match' in request.headers:
            etag_valid, modified_time = _etag_is_valid(
                image_attrs,
                request.headers['If-None-Match'],
                True
            )
            if etag_valid:
                # Success HTTP 304
                return make_304_response(image_attrs, True, modified_time)

        # Read the image file
        image_wrapper = image_engine.get_image_original(
            image_attrs
        )
        if (image_wrapper is None):
            raise DoesNotExistError()

        # Success HTTP 200
        return make_image_response(image_wrapper, True, stats, attach, xref)
    except httpexc.HTTPException:
        # Pass through HTTP 4xx and 5xx
        raise
    except ServerTooBusyError:
        logger.warn(u'503 Too busy for ' + request.url)
        raise httpexc.ServiceUnavailable()
    except ImageError as e:
        logger.warn(u'415 Invalid image file \'' + src + '\' : ' + unicode(e))
        raise httpexc.UnsupportedMediaType(unicode(e))
    except SecurityError as e:
        if app.config['DEBUG']:
            raise
        log_security_error(e, request)
        raise httpexc.Forbidden()
    except DoesNotExistError as e:
        # First time around the ID will be set. Next time around it
        # won't but we should check whether the disk file now exists.
        if image_attrs.database_id() > 0 or path_exists(image_attrs.filename(), require_file=True):
            image_engine.reset_image(image_attrs)
        logger.warn(u'404 Not found: ' + src)
        raise httpexc.NotFound(src)
    except Exception as e:
        if app.config['DEBUG']:
            raise
        logger.error(u'500 Error for ' + request.url + '\n' + unicode(e))
        raise httpexc.InternalServerError(unicode(e))
Esempio n. 8
0
import json
index = 0


def serialize(obj):
    """JSON serializer for objects not serializable by default json code"""

    if isinstance(obj, Block):
        return obj.__dict__


@app.route('/chain', methods=['GET'])
def chain():
    return json.dumps(blockchain.chain, default=serialize)


@app.route('/mine', methods=['POST'])
def mine():
    global index
    request_body = request.get_json()
    logger.debug("request_body: %s", request_body)
    index += 1
    block = Block(index, request_body)
    blockchain.add_block(block)
    return jsonify({"response": "success"})


if __name__ == '__main__':
    logger.debug("Running on 127.0.0.1, PORT 9900")
    app.run(host='127.0.0.1', port=9900, debug=True)