Example #1
0
 def __init__(self, fromto=None):
     self.display_name = None
     self.uri = None
     self.params = HParams()
     if fromto is not None:
         self.display_name, self.uri, self.params = self.parse_fromto(
             fromto)
Example #2
0
def test_parse_param():
    uri_str = 'sip:[email protected]'
    uri = Uri(uri_str)
    fromto_hdr = FromToHeader('<sip:[email protected]>;Some=y')
    hp = HParams()
    hp.set_raw('Some', 'y')
    assert fromto_hdr.params == hp
    assert fromto_hdr.uri == uri
Example #3
0
 def __init__(self, contact_string=None):
     self.display_name = None
     self.uri = Uri()
     self.uri.parser_impl = SIPUriParserUnicode()
     self.params = HParams()
     self.rest = None
     if contact_string is not None:
         self.display_name, self.uri, self.params, self.rest = self.parse_hdr(
             contact_string)
Example #4
0
 def parse_params(params_str):
     params = HParams()
     if params_str.startswith(';'):
         params = HParams(params_str[1:])
         tag = params.find_raw(TAG)
         if not isinstance(tag, HParamNotFound):
             if not check_token(tag):
                 raise FromToError(
                     f'Cannot parse params {params_str}: tag value should be token.'
                 )
     return params
Example #5
0
 def __init__(self, via=None):
     self.sent_protocol = dict()
     self._sent_by = dict()
     self.hparams = HParams()
     if via is not None:
         if isinstance(via, str):
             via_str = via
         else:
             raise ViaHeaderError(
                 f'Cannot initialize ViaHeader: via should be type str, not {type(via)}'
             )
         protocol_name, protocol_version, transport, sent_by_host, sent_by_port, via_params = \
             self.parse_via(via_str)
         self.sent_protocol = SentProtocol(name=protocol_name,
                                           version=protocol_version,
                                           transport=transport)
         self._sent_by = SentBy(host=sent_by_host, port=sent_by_port)
         self.hparams = via_params
Example #6
0
 def do_parse_contact_params(string):
     try:
         hparams = HParams()
         params_list = parse_params(string, ';')
         for k, v in params_list:
             if k.lower() in (EXPIRES, Q):
                 hparams.set(k.lower(),
                             ContactHeader.parse_param(k.lower(), v), k, v)
             elif check_token(k.lower()):
                 hparams.set_raw(k, v)
             else:
                 raise ContactHeaderError(f'Key {k}={v} is not a token.')
         return hparams, ''
     except (ParserAUXError, HParamsError, ContactHeaderError) as e:
         raise ContactHeaderError(
             f'Cannot parse contact header {string}: {e}')
Example #7
0
    def parse_via_params(via_params_str):
        """Parses Via header parameters out of string.

        Args:
            via_params_str (str)

        Returns:
            :obj:HParams: header parameters container with parsed parameters.

        Raises:
            ViaHeaderError if parameters cannot be parsed.
        """
        if not via_params_str or not via_params_str.startswith(';'):
            return HParams(), via_params_str
        else:
            hparams = HParams()
            try:
                hparams.parse_raw(via_params_str[1:])
            except Exception as e:
                raise ViaHeaderError(
                    f'Cannot parse Via params from {via_params_str}: {e}')
            hparams.parse_known(known_function=ViaHeader.parse_known_param_fun)
            rest = via_params_str.lstrip(hparams.assemble())
            return hparams, rest
Example #8
0
 def parse_contact_params(self, contact_params):
     if contact_params.startswith(';'):
         return self.do_parse_contact_params(contact_params[1:])
     return HParams(), ''
Example #9
0
def test_find_raw_set():
    hparams = HParams()
    hparams.set('expires', 3, 'Expires', '3')
    hparams.set('q', QValue('0.1'), 'q', '0.1')
    hparams.set_raw('MyCustomParam', None)
    assert hparams.find_raw('expires') == '3'
    assert isinstance(hparams.find_raw('q1'), HParamNotFound)
    assert hparams.find_raw('q') == '0.1'
    assert hparams.find_raw('mycustoMParam') is None
Example #10
0
class FromToHeader(BaseSipHeader):
    DEFAULT_HOST = '127.0.0.1'

    def __init__(self, fromto=None):
        self.display_name = None
        self.uri = None
        self.params = HParams()
        if fromto is not None:
            self.display_name, self.uri, self.params = self.parse_fromto(
                fromto)

    def __eq__(self, other):
        if isinstance(other, FromToHeader):
            return self.display_name == other.display_name and self.uri == other.uri and self.params == other.params
        return NotImplemented

    def build(self, header_name):
        hdr = Header(header_name)
        hdr.add_value(self.assemble())
        return hdr

    @staticmethod
    def parse(fromto):
        ft = FromToHeader()
        ft.display_name, ft.uri, ft.params = FromToHeader.parse_fromto(fromto)
        return ft

    @staticmethod
    def parse_fromto(fromto):
        if isinstance(fromto, str):
            return FromToHeader.parse_string(fromto)
        elif isinstance(fromto, Header):
            return FromToHeader.parse_header(fromto)
        else:
            raise FromToError(
                f'Cannot parse fromto {fromto}: should be str or Header not {type(fromto)}'
            )

    @staticmethod
    def parse_header(fromto):
        if not fromto.values:
            raise FromToError(f'Cannot parse fromto {fromto}: no values')
        elif len(fromto.values) > 1:
            raise FromToError(f'Cannot parse fromto {fromto}: multiple values')
        else:
            return FromToHeader.parse_string(fromto.values[0])

    @staticmethod
    def parse_string(fromto):
        nameaddr = NameAddress(fromto)
        params = FromToHeader.parse_params(nameaddr.rest.strip())
        return nameaddr.display_name, nameaddr.uri, params

    @staticmethod
    def parse_params(params_str):
        params = HParams()
        if params_str.startswith(';'):
            params = HParams(params_str[1:])
            tag = params.find_raw(TAG)
            if not isinstance(tag, HParamNotFound):
                if not check_token(tag):
                    raise FromToError(
                        f'Cannot parse params {params_str}: tag value should be token.'
                    )
        return params

    @property
    def tag(self):
        tag = self.params.find_raw(TAG)
        if isinstance(tag, HParamNotFound):
            tag = None
        return tag

    @tag.setter
    def tag(self, value):
        self.params.set_raw(TAG, value)

    @property
    def tag_key(self):
        tag = self.tag
        if tag is None:
            return tag
        return tag.lower()

    def assemble(self):
        nameaddr = NameAddress.assemble(self.display_name, self.uri)
        params = self.params.assemble()
        if params:
            params = f';{params}'
        return f'{nameaddr}{params}'
Example #11
0
def test_parse_raw_two_params():
    hparams = HParams('a=b ; c=d')
    assert hparams.find_raw(param='c') == 'd'
Example #12
0
def test_assemble(value):
    hparams = HParams(value)
    assert hparams.assemble() == value
Example #13
0
def test_set_find():
    hparams = HParams()
    hparams.set('expires', 3, 'Expires', '3')
    assert hparams.assemble() == 'Expires=3'
    hparams.set('q', QValue('0.1'), 'q', '0.1')
    assert hparams.assemble() == 'Expires=3;q=0.1'
    hparams.set_raw('MyCustomParam', None)
    assert hparams.assemble() == 'Expires=3;q=0.1;MyCustomParam'
    assert hparams.find('expires') == 3
    assert hparams.find('Expires') == 3
    assert hparams.find('q') == QValue('0.1')
    assert isinstance(hparams.find('q1'), HParamNotFound)
    assert hparams.find_raw('expires') == '3'
Example #14
0
def test_parse_raw_single_param():
    hparams = HParams('a=b')
    assert hparams.find_raw(param='A') == 'b'
Example #15
0
def test_parse_raw_no_value_param():
    hparams = HParams('a=b ; c')
    assert hparams.find_raw('c') is None
Example #16
0
class ViaHeader(BaseSipHeader):
    """
    Args:
        via (:obj: Header || str): VIA header to be parsed.

    Attributes:
        sent_protocol (:obj:SentProtocol): parsed sent protocol section of VIA header.
        _sent_by (:obj:SentBy): parsed sent by section of VIA header.
        hparams (:obj:HParams): parsed via parameters.
    """
    SENT_BY_RX = re.compile(r'(.*?)(;.*)')
    IPV6_RX = re.compile(r'(\[.+?\])')

    def __init__(self, via=None):
        self.sent_protocol = dict()
        self._sent_by = dict()
        self.hparams = HParams()
        if via is not None:
            if isinstance(via, str):
                via_str = via
            else:
                raise ViaHeaderError(
                    f'Cannot initialize ViaHeader: via should be type str, not {type(via)}'
                )
            protocol_name, protocol_version, transport, sent_by_host, sent_by_port, via_params = \
                self.parse_via(via_str)
            self.sent_protocol = SentProtocol(name=protocol_name,
                                              version=protocol_version,
                                              transport=transport)
            self._sent_by = SentBy(host=sent_by_host, port=sent_by_port)
            self.hparams = via_params

    @staticmethod
    def parse(via_hdr):
        return ViaHeader.topmost_via(via_hdr)

    def get_raw_param(self, param_name):
        """Get raw parameter value.

        Args:
            param_name (str): parameter name.

        Returns:
            :obj: raw parameter value.
        """
        return self.hparams.find_raw(param_name)

    @property
    def ttl(self):
        """TTL value"""
        ttl = self.hparams.find(PARAM_TTL)
        if not isinstance(ttl, HParamNotFound):
            return ttl
        return None

    @ttl.setter
    def ttl(self, value):
        ttl, parsed_value = self.parse_known_param_fun(PARAM_TTL, value)
        self.hparams.set(PARAM_TTL, parsed_value, PARAM_TTL, value)

    @property
    def sent_by(self):
        """Sent by host and port values.

        Returns:
            :obj:SentBy: Parsed sent by section of VIA header. If port is not specified (is None), default port for transport is
            returned.
        """
        ret_val = SentBy(host=self._sent_by.host, port=self._sent_by.port)
        if ret_val.port is None:
            ret_val.port = Transport.default_port(self.sent_protocol.transport)
        return ret_val

    @sent_by.setter
    def sent_by(self, value):
        if not isinstance(value, SentBy):
            raise NotImplemented
        self._sent_by = value

    @property
    def branch(self):
        """Branch value.

        Returns:
            :obj:Branch: Branch parameter. If no branch is specified, None is returned.
        """
        branch = self.hparams.find(PARAM_BRANCH)
        if isinstance(branch, HParamNotFound):
            return None
        return branch

    @branch.setter
    def branch(self, value):
        self.hparams.set(PARAM_BRANCH, value, 'Branch',
                         assemble_branch(branch=value))

    @property
    def received(self):
        """Received value.

        Returns:
            :obj:ipaddress || str: Received parameter. If no received is specified, None is returned.
        """
        received = self.hparams.find(PARAM_RECEIVED)
        print(f'received: ', received)
        if not isinstance(received, HParamNotFound):
            return received
        return None

    @property
    def maddr(self):
        """maddr value."""
        maddr = self.hparams.find(PARAM_MADDR)
        if not isinstance(maddr, HParamNotFound):
            return maddr
        return None

    @staticmethod
    def assemble_param_value(name, value):
        """Get parameter's value string representation.

        Args:
            name (str): parameter name.
            value (:obj:): parameter value.

        Returns:
            str: Value of specified parameter.
        """
        if name == PARAM_RPORT and value is True:
            value = None
        elif name == PARAM_BRANCH:
            value = assemble_branch(value)
        return value

    def set_param(self, name, value):
        """Sets RECEIVED or RPORT parameter.

        Args:
            name (str): parameter name
            value (str || int): valid host str or rport int in range 1..65535

        Raises:
            ViaHeaderError if RECEIVED value is not a valid IPv4 or IPv6 host.
            ViaHeaderError if RPORT is not a integer in range 1..65535.
        """
        if name == PARAM_RECEIVED:
            host = value
            if isinstance(value, str):
                try:
                    host = PARSER.parse_host(value)
                except Exception as e:
                    raise ViaHeaderError(
                        f'Cannot set Via param {name}={value}: invalid host {e}'
                    )
            if isinstance(value, IPv6Address):
                value = f'[{value}]'
            if isinstance(host, IPv4Address) or isinstance(host, IPv6Address):
                self.hparams.set(
                    PARAM_RECEIVED, host, PARAM_RECEIVED,
                    ViaHeader.assemble_param_value(PARAM_RECEIVED, value))
            else:
                raise ViaHeaderError(
                    f'Cannot set Via RECEIVED param {name}={value}: invalid IPv4 or IPv6 host'
                )
        elif name == PARAM_RPORT:
            if (isinstance(value, bool) and value is False) or \
                    (not isinstance(value, int) or value < 1 or value > 65535):
                raise ViaHeaderError(
                    f'Cannot set Via RPORT param {name}={value}: invalid rport'
                )
            self.hparams.set(
                PARAM_RPORT, value, PARAM_RPORT,
                ViaHeader.assemble_param_value(PARAM_RPORT, value))

    @staticmethod
    def parse_transport(transport_str):
        """Parse transport from string that starts with transport description.

        Args:
            transport_str (str): tail of Via header value that contains transport information.

        Returns:
            :obj:tuple of :obj:Transport, :obj:str
        """
        transport, rest = parse_token(transport_str)
        try:
            transport = Transport(transport)
            return transport, rest
        except Exception as e:
            raise e

    @staticmethod
    def parse_sent_protocol(via_string):
        """Parse sent protocol from Via header string.

        Args:
            via_string (str): Via header value.

        Returns:
            :obj:tuple of str:protocol_name, str:protocol_version, :obj:Transport, str:rest: parsed protocol name,
            protocol version, protocol transport and unparsed rest.
        """
        try:
            protocol_name, rest = parse_token(via_string)
        except Exception as e:
            raise ViaHeaderError(
                f'Cannot parse protocol name from Via header "{via_string}": {e}'
            )
        try:
            slash, rest = parse_slash(rest)
            protocol_version, rest = parse_token(rest)
        except Exception as e:
            raise ViaHeaderError(
                f'Cannot parse protocol version from Via header "{via_string}": {e}'
            )
        try:
            slash, rest = parse_slash(rest)
            transport, rest = ViaHeader.parse_transport(rest)
        except Exception as e:
            raise ViaHeaderError(
                f'Cannot parse transport from Via header "{via_string}": {e}')
        return protocol_name, protocol_version, transport, rest

    @staticmethod
    def parse_via(via_str):
        """Parse Via header.

        Args:
            via_str (str): Via header value.

        Returns:
            str:protocol_name, str:protocol_version, Transport:transport, str:sent_by_host, str:sent_by_port, HParams:via_params

        """
        try:
            protocol_name, protocol_version, transport, rest = ViaHeader.parse_sent_protocol(
                via_str)
            sent_by_host, sent_by_port, rest = ViaHeader.parse_sent_by(
                rest.strip())
            via_params, rest = ViaHeader.parse_via_params(rest.strip())
            return protocol_name, protocol_version, transport, sent_by_host, sent_by_port, via_params
        except Exception as e:
            raise e

    @staticmethod
    def parse_sent_by(sentby_str):
        """Parses Via header sent by value out of string.

        Args:
            sentby_str (str): string that starts with sent_by host and port

        Returns:
            host, int:port and unparsed str:rest

        Raises:
            ViaHeaderError if sentby_str host and port are not rfc compliant.
        """
        try:
            rx_match = ViaHeader.SENT_BY_RX.match(sentby_str)
            if rx_match:
                host_port, rest = rx_match.group(1).strip(), rx_match.group(2)
            else:
                host_port, rest = sentby_str.strip(), ''
            print(
                f'ViaHeader.parse_sent_by({sentby_str}): hostport {host_port}')
            h, p = ViaHeader.parse_sent_by_host_port(host_port)
            return h, p, rest
        except Exception as e:
            raise ViaHeaderError(
                f'Cannot parse Via sent by parameter from {sentby_str}: {e}')

    @staticmethod
    def parse_sent_by_host_port(host_port):
        """Parses Via header sent by host and port values out of string.

        Args:
            host_port (str)

        Returns:
            :obj:str||ipaddress:host, int:port

        Raises:
            ViaHeaderError if sentby_str host and port are not rfc compliant.
        """
        print(f'ViaHeader.parse_sent_by_host_port({host_port})')
        try:
            return PARSER.parse_host(host_port), PARSER.parse_port(host_port)
        except Exception as e:
            raise ViaHeaderError(
                f'Cannot parse via header host and port {host_port}: {e}')

    @staticmethod
    def parse_via_params(via_params_str):
        """Parses Via header parameters out of string.

        Args:
            via_params_str (str)

        Returns:
            :obj:HParams: header parameters container with parsed parameters.

        Raises:
            ViaHeaderError if parameters cannot be parsed.
        """
        if not via_params_str or not via_params_str.startswith(';'):
            return HParams(), via_params_str
        else:
            hparams = HParams()
            try:
                hparams.parse_raw(via_params_str[1:])
            except Exception as e:
                raise ViaHeaderError(
                    f'Cannot parse Via params from {via_params_str}: {e}')
            hparams.parse_known(known_function=ViaHeader.parse_known_param_fun)
            rest = via_params_str.lstrip(hparams.assemble())
            return hparams, rest

    @staticmethod
    def parse_known_param_fun(param, value):
        """Filtering function that is passed as argument to HParams.parse_known method.

        Args:
            param (str): parameter name.
            value (str): parameter value.

        Returns:
            str:parsed_name, :obj:parsed_value
            None, None: if lowercase parameter name is not in (PARAM_TTL, PARAM_RECEIVED, PARAM_MADDR, PARAM_BRANCH, PARAM_RPORT)
        """
        if param == PARAM_TTL:
            ttl, rest = parse_non_negative_integer(value.strip())
            if rest or ttl < 0 or ttl > 255:
                raise ViaHeaderError(
                    f'Cannot parse via header TTL {param}={value}: value should be 0..255 integer'
                )
            return PARAM_TTL, ttl
        elif param == PARAM_RECEIVED:
            try:
                host = PARSER.parse_host(value)
            except Exception as e:
                raise ViaHeaderError(
                    f'Cannot parse Via RECEIVED {param}={value}: invalid host {e}'
                )
            if isinstance(host, IPv4Address) or isinstance(host, IPv6Address):
                return PARAM_RECEIVED, host
            else:
                raise ViaHeaderError(
                    f'Cannot set Via RECEIVED {param}={value}: invalid IPv4 or IPv6 host'
                )
        elif param == PARAM_MADDR:
            try:
                host = PARSER.parse_host(value)
            except Exception as e:
                raise ViaHeaderError(
                    f'Cannot parse Via MADDR {param}={value}: invalid host {e}'
                )
            return PARAM_MADDR, host
        elif param == PARAM_BRANCH:
            try:
                branch, rest = parse_token(value)
                if rest:
                    raise ViaHeaderError(
                        f'Cannot parse Via BRANCH {param}={value}: value should be token'
                    )
                return PARAM_BRANCH, Branch(branch)
            except Exception as e:
                raise ViaHeaderError(
                    f'Cannot parse Via BRANCH {param}={value}: {e}')
        elif param == PARAM_RPORT:
            if value is None:
                port, rest = True, ''
            else:
                port, rest = parse_non_negative_integer(value)
            if rest or (port is not None and (port <= 0 or port > 65535)):
                raise ViaHeaderError(
                    f'Cannot parse via header RPORT {param}={value}: value should be 1..65535 integer'
                )
            return PARAM_RPORT, port
        else:
            return None, None

    @staticmethod
    def topmost_via(via_hdr):
        """Makes a ViaHeader instance from Header object's topmost value.

        Args:
            via_hdr (obj:Header): header to be parsed.

        Returns:
            obj:ViaHeader: Via header parsed from first element of via_hdr object.

        Raises:
            ViaHeaderError if via_hdr parameter is not a Header object or if via_hdr object doesn't contain any values.

        """
        if not isinstance(via_hdr, Header):
            raise ViaHeaderError(
                f'Cannot parse topmost via: via_header should be of type Header not {type(via_hdr)}'
            )
        if not via_hdr.values:
            raise ViaHeaderError(f'Cannot parse topmost via: no via')
        return ViaHeader(via_hdr.values[0])

    @staticmethod
    def make_param_key(hparams):
        """Helper function that returns comparable object of reduced HParams.

        Args:
            hparams (obj:HParams): header parameters container

        Returns:
            :obj:list: list of param_name, param_value pairs that can be compared to similar list.

        """
        params_key = []
        for param_name, value in hparams.to_list():
            if param_name == PARAM_TTL:
                params_key.append((PARAM_TTL, value))
            elif param_name == PARAM_BRANCH:
                params_key.append((PARAM_BRANCH, value.make_key()))
            elif param_name == PARAM_MADDR:
                if isinstance(value,
                              (ipaddress.IPv4Address, ipaddress.IPv6Address)):
                    app_val = value
                else:
                    app_val = value.lower()
                params_key.append(app_val)
            elif param_name == PARAM_RPORT:
                if isinstance(value, bool):
                    params_key.append(f'{value}')
                else:
                    params_key.append(value)
            else:
                params_key.append((param_name.lower(), value))
        return params_key

    def __eq__(self, other):
        if isinstance(other, ViaHeader):
            return self.sent_protocol == other.sent_protocol and self.sent_by == other.sent_by and \
                   self.make_param_key(self.hparams) == self.make_param_key(other.hparams)
        return NotImplemented

    def assemble(self):
        """Makes a string that represents Via header.

        Returns:
            str: Via header string.
        """
        hparams_str = self.hparams.assemble()
        if hparams_str:
            hparams_str = f';{hparams_str}'
        ret_val = f'{self.sent_protocol} {self.sent_by}{hparams_str}'
        return ret_val

    def __repr__(self):
        return self.assemble()

    def has_rport(self):
        """Is RPORT defined in Via header.

        Returns:
            bool: True if rport defined in Via parameters, False otherwise.
        """
        rport = self.hparams.find(PARAM_RPORT)
        if not isinstance(rport, HParamNotFound):
            return True
        return False

    @property
    def rport(self):
        """RPORT value.

        Returns:
            :obj:ipaddress || str: Received parameter. If no received is specified, None is returned.
        """
        rport = self.hparams.find(PARAM_RPORT)
        if not isinstance(rport, HParamNotFound):
            return rport
        return None

    def build(self, header_name):
        raise NotImplementedError
Example #17
0
def test_find_raw():
    hparams = HParams('a=b')
    assert hparams.find_raw('A') == 'b'
    assert isinstance(hparams.find_raw('ab'), HParamNotFound)
Example #18
0
def test_parse_raw_error(value):
    with pytest.raises(HParamsError):
        HParams(value)
Example #19
0
def test_set_raw():
    hparams = HParams()
    hparams.set_raw('a', 'b')
    hparams.set_raw('a', 'c')
    assert hparams.assemble() == 'a=c'
    assert hparams.find_raw('A') == 'c'
Example #20
0
class ContactHeader(object):
    def __init__(self, contact_string=None):
        self.display_name = None
        self.uri = Uri()
        self.uri.parser_impl = SIPUriParserUnicode()
        self.params = HParams()
        self.rest = None
        if contact_string is not None:
            self.display_name, self.uri, self.params, self.rest = self.parse_hdr(
                contact_string)

    def parse_contact_params(self, contact_params):
        if contact_params.startswith(';'):
            return self.do_parse_contact_params(contact_params[1:])
        return HParams(), ''

    @staticmethod
    def do_parse_contact_params(string):
        try:
            hparams = HParams()
            params_list = parse_params(string, ';')
            for k, v in params_list:
                if k.lower() in (EXPIRES, Q):
                    hparams.set(k.lower(),
                                ContactHeader.parse_param(k.lower(), v), k, v)
                elif check_token(k.lower()):
                    hparams.set_raw(k, v)
                else:
                    raise ContactHeaderError(f'Key {k}={v} is not a token.')
            return hparams, ''
        except (ParserAUXError, HParamsError, ContactHeaderError) as e:
            raise ContactHeaderError(
                f'Cannot parse contact header {string}: {e}')

    def parse_hdr(self, string):
        try:
            nameaddr = NameAddress(string)
            display_name = nameaddr.display_name
            uri = nameaddr.uri
        except NameAddressError as e:
            raise ContactHeaderError(
                f'Cannot parse contact header {string} nameaddress part: {e}')
        contact_params, rest = self.parse_contact_params(nameaddr.rest)
        return display_name, uri, contact_params, rest

    def get_expires(self, default=None):
        expires = self.params.find(EXPIRES)
        if isinstance(expires, HParamNotFound):
            return default
        return expires

    def set_expires(self, expires_val):
        if is_integer(expires_val):
            self.params.set(EXPIRES, expires_val, EXPIRES, expires_val)
        else:
            raise ContactHeaderError(
                f'Cannot set expires parameter {expires_val}: not an integer')

    def get_qvalue(self, default=None):
        qval = self.params.find('q')
        if isinstance(qval, HParamNotFound):
            return default
        return qval

    def set_qvalue(self, qvalue):
        self.params.set(Q, qvalue, Q, qvalue)

    @staticmethod
    def parse_param(param_name, param_value):
        if param_name == EXPIRES:
            try:
                return int(param_value)
            except ValueError:
                raise ContactHeaderError(
                    f'Cannot parse expires param: value {param_value} is not int.'
                )
        if param_name == Q:
            try:
                return QValue(param_value)
            except QValueError as e:
                raise ContactHeaderError(f'Cannot parse q param: {e}')

    def __eq__(self, other):
        if isinstance(other, ContactHeader):
            return self.display_name == other.display_name and self.uri == other.uri and self.params == other.params
        return NotImplemented

    def assemble(self):
        assembled_hparams = self.params.assemble()
        if assembled_hparams:
            assembled_hparams = f';{assembled_hparams}'
        return f'{NameAddress.assemble(self.display_name, self.uri)}{assembled_hparams}'

    def set_param(self, name, value):
        if name.lower() in (EXPIRES, Q):
            try:
                parsed_value = self.parse_param(name, value)
                self.params.set(name.lower(), parsed_value, name, value)
            except ContactHeaderError as e:
                raise ContactHeaderError(
                    f'Cannot set param {name}={value}: {e}')
        elif check_token(name.lower()):
            self.params.set_raw(name, value)
        else:
            raise ContactHeaderError(
                f'Cannot set param {name}={value}: invalid param.')

    def get_param(self, name):
        return self.params.find_raw(name)

    def __repr__(self):
        return self.assemble()