Пример #1
0
    def _render_GET_thread(self, request: Request) -> bytes:
        """ GET request /graphviz/full.{format}
            Returns the rendered graph file
        """
        set_cors(request, 'GET')

        tx_storage = self.manager.tx_storage

        graphviz = GraphvizVisualizer(tx_storage)
        if b'weight' in request.args:
            graphviz.show_weight = self.parse_bool_arg(
                request.args[b'weight'][0].decode('utf-8'))
        if b'acc_weight' in request.args:
            graphviz.show_acc_weight = self.parse_bool_arg(
                request.args[b'acc_weight'][0].decode('utf-8'))
        if b'verifications' in request.args:
            graphviz.include_verifications = self.parse_bool_arg(
                request.args[b'verifications'][0].decode('utf-8'))
        if b'funds' in request.args:
            graphviz.include_funds = self.parse_bool_arg(
                request.args[b'funds'][0].decode('utf-8'))
        if b'only_blocks' in request.args:
            graphviz.only_blocks = self.parse_bool_arg(
                request.args[b'only_blocks'][0].decode('utf-8'))
        dot = graphviz.dot(format=self.format.dot)

        request.setHeader(b'content-type', self.format.content_type)
        if self.format is FileFormat.DOT:
            return str(dot).encode('utf-8')
        return dot.pipe()
Пример #2
0
    def _render_GET_thread(self, request: Request) -> bytes:
        """ GET request /graphviz/neighbours.{format}
            Returns the rendered graph file
        """
        set_cors(request, 'GET')

        tx_storage = self.manager.tx_storage

        tx_hex = request.args[b'tx'][0].decode('utf-8')
        success, message = validate_tx_hash(tx_hex, tx_storage)
        if not success:
            return json.dumps({
                'success': False,
                'message': message
            }, indent=4).encode('utf-8')

        graph_type = request.args[b'graph_type'][0].decode('utf-8')
        max_level = min(int(request.args[b'max_level'][0]),
                        settings.MAX_GRAPH_LEVEL)
        tx = tx_storage.get_transaction(bytes.fromhex(tx_hex))

        graphviz = GraphvizVisualizer(tx_storage)
        dot = graphviz.tx_neighborhood(tx,
                                       format=self.format.dot,
                                       max_level=max_level,
                                       graph_type=graph_type)

        request.setHeader(b'content-type', self.format.content_type)
        if self.format is FileFormat.DOT:
            return str(dot).encode('utf-8')
        return dot.pipe()
Пример #3
0
    async def _async_render_GET(self, request: Request) -> None:
        set_cors_headers(request)
        request.setHeader(
            b"Content-Security-Policy",
            b"sandbox;"
            b" default-src 'none';"
            b" script-src 'none';"
            b" plugin-types application/pdf;"
            b" style-src 'unsafe-inline';"
            b" media-src 'self';"
            b" object-src 'self';",
        )
        request.setHeader(
            b"Referrer-Policy",
            b"no-referrer",
        )
        server_name, media_id, name = parse_media_id(request)
        if server_name == self.server_name:
            await self.media_repo.get_local_media(request, media_id, name)
        else:
            allow_remote = parse_boolean(request, "allow_remote", default=True)
            if not allow_remote:
                logger.info(
                    "Rejecting request for remote media %s/%s due to allow_remote",
                    server_name,
                    media_id,
                )
                respond_404(request)
                return

            await self.media_repo.get_remote_media(request, server_name,
                                                   media_id, name)
Пример #4
0
    def render_GET(self, request: Request) -> bytes:
        """ GET request for /thin_wallet/address_history/
            Expects 'addresses[]' as request args
            'addresses[]' is an array of address

            Returns an array of WalletIndex for each address

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        wallet_index = self.manager.tx_storage.wallet_index

        if not wallet_index:
            request.setResponseCode(503)
            return json.dumps({'success': False}, indent=4).encode('utf-8')

        addresses = request.args[b'addresses[]']

        history = []
        seen: Set[bytes] = set()
        for address_to_decode in addresses:
            address = address_to_decode.decode('utf-8')
            for tx_hash in wallet_index.get_from_address(address):
                tx = self.manager.tx_storage.get_transaction(tx_hash)
                if tx_hash not in seen:
                    seen.add(tx_hash)
                    history.append(tx.to_json_extended())

        data = {'history': history}
        return json.dumps(data, indent=4).encode('utf-8')
Пример #5
0
def textReply(request: Request, status: int, message: str) -> bytes:
    """Replies to an HTTP request with a plain text message.
    The reply body is returned.
    """
    request.setResponseCode(status)
    request.setHeader(b'Content-Type', b'text/plain; charset=UTF-8')
    return message.encode()
Пример #6
0
    def render_GET(self, request: Request) -> bytes:
        """ GET request for /thin_wallet/token/

            Expects 'id' (hash) as GET parameter of the queried token

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        if not self.manager.tx_storage.tokens_index:
            request.setResponseCode(503)
            return json.dumps({'success': False}).encode('utf-8')

        if b'id' in request.args:
            try:
                token_uid_str = request.args[b'id'][0].decode('utf-8')
                token_uid = bytes.fromhex(token_uid_str)
            except (ValueError, AttributeError):
                return json.dumps({
                    'success': False,
                    'message': 'Invalid token id'
                }).encode('utf-8')

            data = self.get_one_token_data(token_uid)
        else:
            data = self.get_list_token_data()

        return json.dumps(data).encode('utf-8')
Пример #7
0
def emptyReply(request: Request) -> bytes:
    """Replies to an HTTP request with a no-body response.
    An empty bytes sequence is returned.
    """
    request.setResponseCode(204)
    request.setHeader(b'Content-Length', b'0')
    return b''
Пример #8
0
    def render_POST(self, request: Request) -> bytes:
        """ POST request for /thin_wallet/address_history/

            It has the same behaviour as the GET request but when using the GET
            we have a limit of addresses to put as query param, otherwise we end up
            reaching the HTTP content length limit
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'POST')

        if not self.manager.tx_storage.indexes.addresses:
            request.setResponseCode(503)
            return json.dumps({'success': False}, indent=4).encode('utf-8')

        raw_body_bytes = request.content.read() or b''
        raw_body_str = raw_body_bytes.decode('utf-8')
        try:
            post_data = json.loads(raw_body_str)
        except json.JSONDecodeError:
            return get_missing_params_msg('invalid json')

        if 'addresses' not in post_data:
            return get_missing_params_msg('addresses')

        return self.get_address_history(post_data.get('addresses'),
                                        post_data.get('hash'))
Пример #9
0
    def render_GET(self, request: TxRequest):
        request.setHeader('Content-Type', 'application/json; charset=UTF-8')
        request.setHeader('Content-Encoding', 'UTF-8')
        headers = {}
        for k, v in request.requestHeaders.getAllRawHeaders():
            headers[str(k, 'utf-8')] = str(v[0], 'utf-8')

        return bytes(json.dumps(headers), 'utf-8')
Пример #10
0
    def render_GET(self, request: TxRequest):
        request.setHeader('Content-Type', 'application/json; charset=UTF-8')
        request.setHeader('Content-Encoding', 'UTF-8')

        query_params = {}
        for k, v in request.args.items():
            query_params[str(k, 'utf-8')] = str(v[0], 'utf-8')

        return bytes(json.dumps(query_params), 'utf-8')
Пример #11
0
    def render_GET(self, request: Request) -> bytes:
        """ GET request for /thin_wallet/token/

            Expects 'id' (hash) as GET parameter of the queried token

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        if not self.manager.tx_storage.tokens_index:
            request.setResponseCode(503)
            return json.dumps({'success': False}).encode('utf-8')

        if b'id' not in request.args:
            return get_missing_params_msg('id')

        try:
            token_uid_str = request.args[b'id'][0].decode('utf-8')
            token_uid = bytes.fromhex(token_uid_str)
        except (ValueError, AttributeError):
            return json.dumps({
                'success': False,
                'message': 'Invalid token id'
            }).encode('utf-8')

        try:
            token_info = self.manager.tx_storage.tokens_index.get_token_info(
                token_uid)
        except KeyError:
            return json.dumps({
                'success': False,
                'message': 'Unknown token'
            }).encode('utf-8')

        mint = []
        melt = []

        transactions_count = self.manager.tx_storage.tokens_index.get_transactions_count(
            token_uid)

        for tx_hash, index in token_info.mint:
            mint.append({'tx_id': tx_hash.hex(), 'index': index})

        for tx_hash, index in token_info.melt:
            melt.append({'tx_id': tx_hash.hex(), 'index': index})

        data = {
            'name': token_info.name,
            'symbol': token_info.symbol,
            'success': True,
            'mint': mint,
            'melt': melt,
            'total': token_info.total,
            'transactions_count': transactions_count,
        }
        return json.dumps(data).encode('utf-8')
Пример #12
0
    def render_POST(self, request: Request) -> Any:
        """ POST request for /thin_wallet/send_tokens/
            We expect 'tx_hex' as request args
            'tx_hex': serialized tx in hexadecimal
            We return success (bool)

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'POST')

        # Validating if we still have unused threads to solve the pow
        if len(self.manager.pow_thread_pool.working
               ) == settings.MAX_POW_THREADS:
            return self.return_POST(
                False,
                'The server is currently fully loaded to send tokens. Wait a moment and try again, please.'
            )

        post_data = json.loads(request.content.read().decode('utf-8'))
        tx_hex = post_data['tx_hex']

        try:
            tx = tx_or_block_from_bytes(bytes.fromhex(tx_hex))
        except struct.error:
            return self.return_POST(
                False, 'Error parsing hexdump to create the transaction')

        assert isinstance(tx, Transaction)
        # Set tx storage
        tx.storage = self.manager.tx_storage

        # If this tx is a double spending, don't even try to propagate in the network
        is_double_spending = tx.is_double_spending()
        if is_double_spending:
            data = {
                'success':
                False,
                'message':
                'Invalid transaction. At least one of your inputs has already been spent.'
            }
            return json.dumps(data, indent=4).encode('utf-8')

        request.should_stop_mining_thread = False

        if settings.SEND_TOKENS_STRATUM and self.manager.stratum_factory:
            self._render_POST_stratum(tx, request)
        else:
            self._render_POST(tx, request)

        request.notifyFinish().addErrback(self._responseFailed, request)

        from twisted.web.server import NOT_DONE_YET
        return NOT_DONE_YET
Пример #13
0
    def make_response(request: TxRequest, extra_data: str):
        response = {
            'request-headers': {},
            'request-body': json.loads(request.content.read()),
            'extra-data': extra_data
        }
        for k, v in request.requestHeaders.getAllRawHeaders():
            response['request-headers'][str(k, 'utf-8')] = str(v[0], 'utf-8')

        response_bytes = bytes(json.dumps(response), 'utf-8')
        request.setHeader('Content-Type', 'application/json; charset=UTF-8')
        request.setHeader('Content-Encoding', 'UTF-8')
        return response_bytes
Пример #14
0
def render_options(request: Request, verbs: str = 'GET, POST, OPTIONS') -> int:
    """Function to return OPTIONS request.

    Most of the APIs only need it for GET, POST and OPTIONS, but verbs can be passed as parameter.

    :param verbs: verbs to reply on render options
    :type verbs: str
    """
    from twisted.web import server
    set_cors(request, verbs)
    request.setHeader(b'content-type', b'application/json; charset=utf-8')
    request.write(b'')
    request.finish()
    return server.NOT_DONE_YET
Пример #15
0
    def render(self, request: Request):
        request.setHeader(b'Content-Type', b'application/json')

        if self.config.get('set_cors'):
            request.setHeader(b'Access-Control-Allow-Origin', b'*')
            request.setHeader(b'Access-Control-Allow-Methods', b'GET,POST,PUT,DELETE')
            request.setHeader(b'Access-Control-Allow-Headers', b'x-prototype-version,x-requested-with')
            request.setHeader(b'Access-Control-Max-Age', b'2520')

        callback_name = 'response_%s_process' % request.method.lower().decode('ascii')
        callback = getattr(self, callback_name, None)
        if not callback:
            request.setResponseCode(NOT_ALLOWED)
            return b'Method not allowed'

        session_id = request.getSession().uid
        d = self.cache_interface.get(session_id)
        d.addCallback(callback, session_id, request)

        return NOT_DONE_YET
Пример #16
0
    async def _async_render_POST(self, request: Request) -> Tuple[int, bytes]:
        sid = parse_string(request, "sid", required=True)
        token = parse_string(request, "token", required=True)
        client_secret = parse_string(request, "client_secret", required=True)

        # Attempt to validate a 3PID session
        try:
            # Mark the session as valid
            next_link = await self.store.validate_threepid_session(
                sid, client_secret, token, self.clock.time_msec())

            # Perform a 302 redirect if next_link is set
            if next_link:
                if next_link.startswith("file:///"):
                    logger.warning(
                        "Not redirecting to next_link as it is a local file: address"
                    )
                else:
                    next_link_bytes = next_link.encode("utf-8")
                    request.setHeader("Location", next_link_bytes)
                    return (
                        302,
                        (b'You are being redirected to <a src="%s">%s</a>.' %
                         (next_link_bytes, next_link_bytes)),
                    )

            # Otherwise show the success template
            html_bytes = self._email_password_reset_template_success_html.encode(
                "utf-8")
            status_code = 200
        except ThreepidValidationError as e:
            status_code = e.code

            # Show a failure page with a reason
            template_vars = {"failure_reason": e.msg}
            html_bytes = self._failure_email_template.render(
                **template_vars).encode("utf-8")

        return status_code, html_bytes
Пример #17
0
    def render(self, request: Request) -> bytes:
        """We're adding some CORS headers for future use, also we're sending content-type

        """
        request.setHeader('content-type', 'application/json')
        acrm = request.getHeader('access-control-request-method')
        if acrm:
            request.setHeader('Access-Control-Allow-Methods', acrm)
        acrh = request.getHeader('access-control-request-headers')
        if acrh:
            request.setHeader('Access-Control-Allow-Headers', acrh)
        origin = request.getHeader('origin')
        if origin:
            request.setHeader('Access-Control-Allow-Origin', origin)
        return super(ChatResource, self).render(request)
Пример #18
0
    def render_POST(self, request: Request) -> bytes:
        """ POST request for /push_tx/
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'POST')

        error_ret = json_dumpb({
            'success': False,
            'message': 'Missing hexadecimal data',
            'can_force': False
        })
        body_content = request.content.read()
        if not body_content:
            return error_ret

        data = json_loadb(body_content)

        # Need to do that because json_loadb returns an object, which is not compatible with Dict[str, Any]
        data = cast(Dict[str, Any], data)

        if 'hex_tx' not in data:
            return error_ret

        return self.handle_push_tx(data)
Пример #19
0
    def render_GET(self, request: Request) -> bytes:
        """ GET request for /push_tx/
            Expects 'hex_tx' as args parameter that is the hex representation of the whole tx

            :rtype: string (json)

            This resource will be deprecated soon
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')
        parsed = parse_get_arguments(request.args, ARGS)
        if not parsed['success']:
            data = {
                'success': False,
                'message': 'Missing hexadecimal data',
                'can_force': False
            }
            return json_dumpb(data)

        data = parsed['args']
        data['force'] = b'force' in request.args and request.args[b'force'][
            0].decode('utf-8') == 'true'

        return self.handle_push_tx(data)
Пример #20
0
def set_cors(request: Request, method: str) -> None:
    request.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000')
    request.setHeader('Access-Control-Allow-Methods', method)
    request.setHeader('Access-Control-Allow-Headers',
                      'x-prototype-version,x-requested-with,content-type')
    request.setHeader('Access-Control-Max-Age', '604800')
Пример #21
0
 def server_handler(request: http.Request) -> None:
     request.setHeader("foo", "bar")
     request.write("Woo-hoo".encode())
     request.finish()
 def server_handler(request: http.Request):
     request.setHeader("foo", "bar")
     request.write("my content".encode())
     request.finish()
Пример #23
0
    def _render_GET_thread(self, request: Request) -> bytes:
        """ GET request /graphviz/
            Expects 'format' parameter in request to set the content-type of the graph
            Format options are 'pdf', 'png' and 'jpg'. Default format is 'pdf'
            Returns the file
        """
        set_cors(request, 'GET')

        contenttype = {
            'pdf': b'application/pdf',
            'png': b'image/png',
            'jpg': b'image/jpeg',
            'dot': b'application/dot',
        }

        dotformat = 'pdf'
        if b'format' in request.args:
            dotformat = request.args[b'format'][0].decode('utf-8')

        tx_storage = self.manager.tx_storage

        if b'tx' in request.args:
            # Getting tx neightborhood
            tx_hex = request.args[b'tx'][0].decode('utf-8')
            success, message = validate_tx_hash(tx_hex, tx_storage)
            if not success:
                return json.dumps({
                    'success': False,
                    'message': message
                },
                                  indent=4).encode('utf-8')
            else:
                graph_type = request.args[b'graph_type'][0].decode('utf-8')
                max_level = int(request.args[b'max_level'][0])
                if max_level > settings.MAX_GRAPH_LEVEL:
                    return json.dumps(
                        {
                            'success':
                            False,
                            'message':
                            'Graph max level is {}'.format(
                                settings.MAX_GRAPH_LEVEL)
                        },
                        indent=4).encode('utf-8')
                tx = tx_storage.get_transaction(bytes.fromhex(tx_hex))

                graphviz = GraphvizVisualizer(tx_storage)
                dot = graphviz.tx_neighborhood(tx,
                                               format=dotformat,
                                               max_level=max_level,
                                               graph_type=graph_type)

        else:
            weight = False
            if b'weight' in request.args:
                weight = self.parseBoolArg(
                    request.args[b'weight'][0].decode('utf-8'))

            acc_weight = False
            if b'acc_weight' in request.args:
                acc_weight = self.parseBoolArg(
                    request.args[b'acc_weight'][0].decode('utf-8'))

            include_verifications = True
            if b'verifications' in request.args:
                include_verifications = self.parseBoolArg(
                    request.args[b'verifications'][0].decode('utf-8'))

            include_funds = False
            if b'funds' in request.args:
                include_funds = self.parseBoolArg(
                    request.args[b'funds'][0].decode('utf-8'))

            only_blocks = False
            if b'only_blocks' in request.args:
                only_blocks = self.parseBoolArg(
                    request.args[b'only_blocks'][0].decode('utf-8'))

            graphviz = GraphvizVisualizer(tx_storage)
            graphviz.include_verifications = include_verifications
            graphviz.include_funds = include_funds
            graphviz.only_blocks = only_blocks
            graphviz.show_weight = weight
            graphviz.show_acc_weight = acc_weight
            dot = graphviz.dot(format=dotformat)

        if dotformat == 'dot':
            request.setHeader(b'content-type', contenttype[dotformat])
            return str(dot).encode('utf-8')

        request.setHeader(b'content-type', contenttype[dotformat])
        return dot.pipe()
Пример #24
0
 async def _async_render_OPTIONS(self, request: Request) -> None:
     request.setHeader(b"Allow", b"OPTIONS, GET")
     respond_with_json(request, 200, {}, send_cors=True)
Пример #25
0
 def handle_redirect(request: http.Request) -> None:
     request.setResponseCode(HTTPStatus.FOUND)
     request.setHeader("location", to)
     request.finish()
Пример #26
0
    def render_GET(self, request: Request) -> bytes:
        """ GET request for /thin_wallet/token_history/

            Expects as GET parameter of the queried token:
                - 'id': token uid the history is being requested
                - 'count': int, to indicate the quantity of elements we should return
                - 'hash': string, the hash reference we are in the pagination
                - 'timestamp': int, the timestamp reference we are in the pagination
                - 'page': 'previous' or 'next', to indicate if the user wants after or before the hash reference

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        if not self.manager.tx_storage.tokens_index:
            request.setResponseCode(503)
            return json.dumps({'success': False}).encode('utf-8')

        if b'id' not in request.args:
            return get_missing_params_msg('id')

        try:
            token_uid_str = request.args[b'id'][0].decode('utf-8')
            token_uid = bytes.fromhex(token_uid_str)
        except (ValueError, AttributeError):
            return json.dumps({
                'success': False,
                'message': 'Invalid token id'
            }).encode('utf-8')

        if b'count' not in request.args:
            return get_missing_params_msg('count')

        count = min(int(request.args[b'count'][0]), settings.MAX_TX_COUNT)

        if b'hash' in request.args:
            if b'timestamp' not in request.args:
                return get_missing_params_msg('timestamp')

            if b'page' not in request.args:
                return get_missing_params_msg('page')

            ref_hash = request.args[b'hash'][0].decode('utf-8')
            ref_timestamp = int(request.args[b'timestamp'][0].decode('utf-8'))
            page = request.args[b'page'][0].decode('utf-8')

            if page == 'previous':
                elements, has_more = self.manager.tx_storage.tokens_index.get_newer_transactions(
                    token_uid, ref_timestamp, bytes.fromhex(ref_hash), count)
            else:
                elements, has_more = self.manager.tx_storage.tokens_index.get_older_transactions(
                    token_uid, ref_timestamp, bytes.fromhex(ref_hash), count)
        else:
            elements, has_more = self.manager.tx_storage.tokens_index.get_newest_transactions(
                token_uid, count)

        transactions = [
            self.manager.tx_storage.get_transaction(element)
            for element in elements
        ]
        serialized = [tx.to_json_extended() for tx in transactions]

        data = {
            'success': True,
            'transactions': serialized,
            'has_more': has_more,
        }
        return json.dumps(data).encode('utf-8')
Пример #27
0
    def render_GET(self, request: Request) -> bytes:
        """ GET request for /thin_wallet/token_history/

            Expects as GET parameter of the queried token:
                - 'id': uid of token whose history is being requested
                - 'count': int, to indicate the quantity of elements we should return
                - 'hash': string, the hash reference we are in the pagination
                - 'timestamp': int, the timestamp reference we are in the pagination
                - 'page': 'previous' or 'next', to indicate if the user wants after or before the hash reference

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        if not self.manager.tx_storage.tokens_index:
            request.setResponseCode(503)
            return json.dumps({'success': False}).encode('utf-8')

        parsed = parse_get_arguments(request.args, ARGS)
        if not parsed['success']:
            return get_missing_params_msg(parsed['missing'])

        if b'id' not in request.args:
            return get_missing_params_msg('id')

        try:
            token_uid = bytes.fromhex(parsed['args']['id'])
        except (ValueError, AttributeError):
            return json.dumps({'success': False, 'message': 'Invalid token id'}).encode('utf-8')

        try:
            count = min(int(parsed['args']['count']), settings.MAX_TX_COUNT)
        except ValueError:
            return json.dumps({
                'success': False,
                'message': 'Invalid \'count\' parameter, expected an int'
            }).encode('utf-8')

        if b'hash' in request.args:
            parsed = parse_get_arguments(request.args, ['timestamp', 'page', 'hash'])
            if not parsed['success']:
                return get_missing_params_msg(parsed['missing'])

            try:
                hash_bytes = bytes.fromhex(parsed['args']['hash'])
            except ValueError:
                return json.dumps({
                    'success': False,
                    'message': 'Invalid \'hash\' parameter, could not decode hexadecimal'
                }).encode('utf-8')

            page = parsed['args']['page']
            if page != 'previous' and page != 'next':
                return json.dumps({
                    'success': False,
                    'message': 'Invalid \'page\' parameter, expected \'previous\' or \'next\''
                }).encode('utf-8')

            try:
                ref_timestamp = int(parsed['args']['timestamp'])
            except ValueError:
                return json.dumps({
                    'success': False,
                    'message': 'Invalid \'timestamp\' parameter, expected an int'
                }).encode('utf-8')

            if page == 'previous':
                elements, has_more = self.manager.tx_storage.tokens_index.get_newer_transactions(
                        token_uid, ref_timestamp, hash_bytes, count)
            else:
                elements, has_more = self.manager.tx_storage.tokens_index.get_older_transactions(
                        token_uid, ref_timestamp, hash_bytes, count)
        else:
            elements, has_more = self.manager.tx_storage.tokens_index.get_newest_transactions(token_uid, count)

        transactions = [self.manager.tx_storage.get_transaction(element) for element in elements]
        serialized = [tx.to_json_extended() for tx in transactions]

        data = {
            'success': True,
            'transactions': serialized,
            'has_more': has_more,
        }
        return json.dumps(data).encode('utf-8')
Пример #28
0
def add_file_headers(
    request: Request,
    media_type: str,
    file_size: Optional[int],
    upload_name: Optional[str],
) -> None:
    """Adds the correct response headers in preparation for responding with the
    media.

    Args:
        request
        media_type: The media/content type.
        file_size: Size in bytes of the media, if known.
        upload_name: The name of the requested file, if any.
    """
    def _quote(x):
        return urllib.parse.quote(x.encode("utf-8"))

    # Default to a UTF-8 charset for text content types.
    # ex, uses UTF-8 for 'text/css' but not 'text/css; charset=UTF-16'
    if media_type.lower() in TEXT_CONTENT_TYPES:
        content_type = media_type + "; charset=UTF-8"
    else:
        content_type = media_type

    request.setHeader(b"Content-Type", content_type.encode("UTF-8"))
    if upload_name:
        # RFC6266 section 4.1 [1] defines both `filename` and `filename*`.
        #
        # `filename` is defined to be a `value`, which is defined by RFC2616
        # section 3.6 [2] to be a `token` or a `quoted-string`, where a `token`
        # is (essentially) a single US-ASCII word, and a `quoted-string` is a
        # US-ASCII string surrounded by double-quotes, using backslash as an
        # escape charater. Note that %-encoding is *not* permitted.
        #
        # `filename*` is defined to be an `ext-value`, which is defined in
        # RFC5987 section 3.2.1 [3] to be `charset "'" [ language ] "'" value-chars`,
        # where `value-chars` is essentially a %-encoded string in the given charset.
        #
        # [1]: https://tools.ietf.org/html/rfc6266#section-4.1
        # [2]: https://tools.ietf.org/html/rfc2616#section-3.6
        # [3]: https://tools.ietf.org/html/rfc5987#section-3.2.1

        # We avoid the quoted-string version of `filename`, because (a) synapse didn't
        # correctly interpret those as of 0.99.2 and (b) they are a bit of a pain and we
        # may as well just do the filename* version.
        if _can_encode_filename_as_token(upload_name):
            disposition = "inline; filename=%s" % (upload_name, )
        else:
            disposition = "inline; filename*=utf-8''%s" % (
                _quote(upload_name), )

        request.setHeader(b"Content-Disposition", disposition.encode("ascii"))

    # cache for at least a day.
    # XXX: we might want to turn this off for data we don't want to
    # recommend caching as it's sensitive or private - or at least
    # select private. don't bother setting Expires as all our
    # clients are smart enough to be happy with Cache-Control
    request.setHeader(b"Cache-Control", b"public,max-age=86400,s-maxage=86400")
    if file_size is not None:
        request.setHeader(b"Content-Length", b"%d" % (file_size, ))

    # Tell web crawlers to not index, archive, or follow links in media. This
    # should help to prevent things in the media repo from showing up in web
    # search results.
    request.setHeader(b"X-Robots-Tag",
                      "noindex, nofollow, noarchive, noimageindex")
Пример #29
0
    def render_GET(self, request: Request) -> bytes:
        """ GET request for /thin_wallet/address_search/
            Expects 'address' and 'count' as required request args
            'token' is an optional parameter to define if the search wants an specific token only
            'hash' and 'page' are optional args to be used in pagination

            'address' is a base58 address string
            'count' is an integer indicating the quantity of elements to be returned
            'token' is the token uid in hex to be searched
            'hash' is the first address of the pagination to start the history
            'page' is either 'previous' or 'next' to indicate the page clicked

            Returns an array of WalletIndex until the count limit and the hash
            parameter for the next request, if has more
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'GET')

        wallet_index = self.manager.tx_storage.wallet_index

        if not wallet_index:
            request.setResponseCode(503)
            return json.dumps({'success': False}, indent=4).encode('utf-8')

        if b'address' not in request.args:
            return get_missing_params_msg('address')

        if b'count' not in request.args:
            return get_missing_params_msg('count')

        try:
            address = request.args[b'address'][0].decode('utf-8')
            # Check if address is valid
            decode_address(address)
        except InvalidAddress:
            return json.dumps({
                'success': False,
                'message': 'Invalid \'address\' parameter'
            }).encode('utf-8')

        try:
            count = min(int(request.args[b'count'][0]), settings.MAX_TX_COUNT)
        except ValueError:
            return json.dumps({
                'success':
                False,
                'message':
                'Invalid \'count\' parameter, expected an int'
            }).encode('utf-8')

        token_uid_bytes = None
        if b'token' in request.args:
            # It's an optional parameter, we just check if it's a valid hex
            token_uid = request.args[b'token'][0].decode('utf-8')

            try:
                token_uid_bytes = bytes.fromhex(token_uid)
            except ValueError:
                return json.dumps({
                    'success':
                    False,
                    'message':
                    'Token uid is not a valid hexadecimal value.'
                }).encode('utf-8')

        hashes = wallet_index.get_from_address(address)
        # XXX To do a timestamp sorting, so the pagination works better
        # we must get all transactions and sort them
        # This is not optimal for performance
        transactions = []
        for tx_hash in hashes:
            tx = self.manager.tx_storage.get_transaction(tx_hash)
            if token_uid_bytes and not self.has_token_and_address(
                    tx, address, token_uid_bytes):
                # Request wants to filter by token but tx does not have this token
                # so we don't add it to the transactions array
                continue
            transactions.append(tx.to_json_extended())

        sorted_transactions = sorted(transactions,
                                     key=lambda tx: tx['timestamp'],
                                     reverse=True)
        if b'hash' in request.args:
            # It's a paginated request, so 'page' must also be in request.args
            if b'page' not in request.args:
                return get_missing_params_msg('page')

            page = request.args[b'page'][0].decode('utf-8')
            if page != 'previous' and page != 'next':
                # Invalid value for page parameter
                return json.dumps(
                    {
                        'success': False,
                        'message': 'Invalid value for \'page\' parameter',
                    },
                    indent=4).encode('utf-8')

            ref_hash = request.args[b'hash'][0].decode('utf-8')
            # Index where the reference hash is
            for ref_index, tx in enumerate(sorted_transactions):
                if tx['tx_id'] == ref_hash:
                    break
            else:
                # ref_hash is not in the list
                return json.dumps(
                    {
                        'success': False,
                        'message': 'Invalid hash {}'.format(ref_hash)
                    },
                    indent=4).encode('utf-8')

            if page == 'next':
                # User clicked on 'Next' button, so the ref_hash is the last hash of the list
                # So I need to get the transactions after the ref
                start_index = ref_index + 1
                end_index = start_index + count
                ret_transactions = sorted_transactions[start_index:end_index]
                # If has more transactions after this
                has_more = len(sorted_transactions) > end_index
            else:
                # User clicked on 'Previous' button, so the ref_hash is the first hash of the list
                # So I need to get the transactions before the ref
                end_index = ref_index
                start_index = max(end_index - count, 0)
                ret_transactions = sorted_transactions[start_index:end_index]
                # If has more transactions before this
                has_more = start_index > 0
        else:
            ret_transactions = sorted_transactions[:count]
            has_more = len(sorted_transactions) > count

        data = {
            'success': True,
            'transactions': ret_transactions,
            'has_more': has_more,
            'total': len(sorted_transactions),
        }
        return json.dumps(data, indent=4).encode('utf-8')
Пример #30
0
    async def get(self, request: Request) -> None:
        try:
            if self.data is None:
                await asyncio.wait_for(self.read_task, timeout=30)
                if self.data is None:
                    request.setResponseCode(503)
                    request.write(b'Service temporarily unavailable')
                    return

            raw_query = urlparse(request.uri).query
            query = parse_qs(raw_query)

            # Check for etag matching
            if self.etag_base:
                hasher = hashlib.sha256()
                hasher.update(self.etag_base)
                hasher.update(raw_query)
                if request.setETag(hasher.hexdigest().encode('utf-8')):
                    # Etag matched; do not send a body
                    return

            # decode query parameter
            releases = None
            if b'releases' in query:
                releases = set(
                    self.release_aliases.get(release)
                    for release in sanitize_query_list(query[b'releases']))
                releases &= self.releases

            components = None
            if b'components' in query:
                components = sanitize_query_list(query[b'components'])
                components &= self.components

            architectures = None
            if b'architectures' in query:
                architectures = sanitize_query_list(query[b'architectures'])
                architectures.add('all')
                architectures &= self.architectures

            # generate filtered results
            def transform(item):
                result = item.copy()
                if releases is not None:
                    result['packages'] = [
                        package for package in result['packages']
                        if package['release'] in releases
                    ]
                if components is not None:
                    result['packages'] = [
                        package for package in result['packages']
                        if package['component'] in components
                    ]
                if architectures is not None:
                    result['packages'] = [
                        package for package in result['packages']
                        if package['architecture'] in architectures
                    ]
                return result, bool(result['packages'])

            result = [
                value for value, pred in map(transform, self.data) if pred
            ]

            # deliver results
            request.setHeader(b'content-type',
                              b'application/json; charset=utf-8')
            request.write(simplejson.dumps(result).encode('utf-8'))
        except Exception as e:
            log.err(
                "An exception occurred while handling request ({})".format(e))
            request.setResponseCode(400)
            request.write('Bad request'.encode('utf-8'))
        finally:
            request.finish()