Esempio n. 1
0
    def encode_header(header_field_name, header_value):
        """
        Encodes a header entry for an MMS message

        The return type of the "header value" depends on the header
        itself; it is thus up to the function calling this to determine
        what that type is (or at least compensate for possibly different
        return value types).

        From [4], section 7.1::

            Header = MMS-header | Application-header
            MMS-header = MMS-field-name MMS-value
            MMS-field-name = Short-integer
            MMS-value = Bcc-value | Cc-value | Content-location-value | Content-type-value | etc

        :raise DecodeError: This uses :func:`decode_mms_header` and
                            :func:`decode_application_header`, and will raise this
                            exception under the same circumstances as
                            :func:`decode_application_header`. ``byte_iter`` will
                            not be modified in this case.

        :return: The decoded header entry from the MMS, in the format:
                 (<str:header name>, <str/int/float:header value>)
        :rtype: tuple
        """
        encoded_header = []
        # First try encoding the header as a "MMS-header"...
        for assigned_number in mms_field_names:
            header = mms_field_names[assigned_number][0]
            if header == header_field_name:
                encoded_header.extend(
                    wsp_pdu.Encoder.encode_short_integer(assigned_number))
                # Now encode the value
                expected_type = mms_field_names[assigned_number][1]
                try:
                    ret = getattr(MMSEncoder,
                                  'encode_%s' % expected_type)(header_value)
                    encoded_header.extend(ret)
                except wsp_pdu.EncodeError as msg:
                    raise wsp_pdu.EncodeError('Error encoding parameter '
                                              'value: %s' % msg)
                except:
                    debug('A fatal error occurred, probably due to an '
                          'unimplemented encoding operation')
                    raise

                break

        # See if the "MMS-header" encoding worked
        if not len(encoded_header):
            # ...it didn't. Use "Application-header" encoding
            header_name = wsp_pdu.Encoder.encode_token_text(header_field_name)
            encoded_header.extend(header_name)
            # Now add the value
            encoded_header.extend(
                    wsp_pdu.Encoder.encode_text_string(header_value))

        return encoded_header
Esempio n. 2
0
    def to_pdu(self):
        """Returns a list of :class:`~messaging.pdu.Pdu` objects"""
        smsc_pdu = self._get_smsc_pdu()
        sms_submit_pdu = self._get_sms_submit_pdu()
        tpmessref_pdu = self._get_tpmessref_pdu()
        sms_phone_pdu = self._get_phone_pdu()
        tppid_pdu = self._get_tppid_pdu()
        sms_msg_pdu = self._get_msg_pdu()

        if len(sms_msg_pdu) == 1:
            pdu = smsc_pdu
            len_smsc = len(smsc_pdu) / 2
            pdu += sms_submit_pdu
            pdu += tpmessref_pdu
            pdu += sms_phone_pdu
            pdu += tppid_pdu
            pdu += sms_msg_pdu[0]
            debug("smsc_pdu: %s" % smsc_pdu)
            debug("sms_submit_pdu: %s" % sms_submit_pdu)
            debug("tpmessref_pdu: %s" % tpmessref_pdu)
            debug("sms_phone_pdu: %s" % sms_phone_pdu)
            debug("tppid_pdu: %s" % tppid_pdu)
            debug("sms_msg_pdu: %s" % sms_msg_pdu)
            debug("-" * 20)
            debug("full_pdu: %s" % pdu)
            debug("full_text: %s" % self.text)
            debug("-" * 20)
            return [Pdu(pdu, len_smsc)]

        # multipart SMS
        sms_submit_pdu = self._get_sms_submit_pdu(udh=True)
        pdu_list = []
        cnt = len(sms_msg_pdu)
        for i, sms_msg_pdu_item in enumerate(sms_msg_pdu):
            pdu = smsc_pdu
            len_smsc = len(smsc_pdu) / 2
            pdu += sms_submit_pdu
            pdu += tpmessref_pdu
            pdu += sms_phone_pdu
            pdu += tppid_pdu
            pdu += sms_msg_pdu_item
            debug("smsc_pdu: %s" % smsc_pdu)
            debug("sms_submit_pdu: %s" % sms_submit_pdu)
            debug("tpmessref_pdu: %s" % tpmessref_pdu)
            debug("sms_phone_pdu: %s" % sms_phone_pdu)
            debug("tppid_pdu: %s" % tppid_pdu)
            debug("sms_msg_pdu: %s" % sms_msg_pdu_item)
            debug("-" * 20)
            debug("full_pdu: %s" % pdu)
            debug("full_text: %s" % self.text)
            debug("-" * 20)

            pdu_list.append(Pdu(pdu, len_smsc, cnt=cnt, seq=i + 1))

        return pdu_list
Esempio n. 3
0
    def to_pdu(self):
        """Returns a list of :class:`~messaging.pdu.Pdu` objects"""
        smsc_pdu = self._get_smsc_pdu()
        sms_submit_pdu = self._get_sms_submit_pdu()
        tpmessref_pdu = self._get_tpmessref_pdu()
        sms_phone_pdu = self._get_phone_pdu()
        tppid_pdu = self._get_tppid_pdu()
        sms_msg_pdu = self._get_msg_pdu()

        if len(sms_msg_pdu) == 1:
            pdu = smsc_pdu
            len_smsc = len(smsc_pdu) / 2
            pdu += sms_submit_pdu
            pdu += tpmessref_pdu
            pdu += sms_phone_pdu
            pdu += tppid_pdu
            pdu += sms_msg_pdu[0]
            debug("smsc_pdu: %s" % smsc_pdu)
            debug("sms_submit_pdu: %s" % sms_submit_pdu)
            debug("tpmessref_pdu: %s" % tpmessref_pdu)
            debug("sms_phone_pdu: %s" % sms_phone_pdu)
            debug("tppid_pdu: %s" % tppid_pdu)
            debug("sms_msg_pdu: %s" % sms_msg_pdu)
            debug("-" * 20)
            debug("full_pdu: %s" % pdu)
            debug("full_text: %s" % self.text)
            debug("-" * 20)
            return [Pdu(pdu, len_smsc)]

        # multipart SMS
        sms_submit_pdu = self._get_sms_submit_pdu(udh=True)
        pdu_list = []
        cnt = len(sms_msg_pdu)
        for i, sms_msg_pdu_item in enumerate(sms_msg_pdu):
            pdu = smsc_pdu
            len_smsc = len(smsc_pdu) / 2
            pdu += sms_submit_pdu
            pdu += tpmessref_pdu
            pdu += sms_phone_pdu
            pdu += tppid_pdu
            pdu += sms_msg_pdu_item
            debug("smsc_pdu: %s" % smsc_pdu)
            debug("sms_submit_pdu: %s" % sms_submit_pdu)
            debug("tpmessref_pdu: %s" % tpmessref_pdu)
            debug("sms_phone_pdu: %s" % sms_phone_pdu)
            debug("tppid_pdu: %s" % tppid_pdu)
            debug("sms_msg_pdu: %s" % sms_msg_pdu_item)
            debug("-" * 20)
            debug("full_pdu: %s" % pdu)
            debug("full_text: %s" % self.text)
            debug("-" * 20)

            pdu_list.append(Pdu(pdu, len_smsc, cnt=cnt, seq=i + 1))

        return pdu_list
Esempio n. 4
0
    def _decode_status_report_pdu(self, data):
        self.udh = UserDataHeader.from_status_report_ref(data.pop(0))

        sndlen = data.pop(0)
        if sndlen % 2:
            sndlen += 1
        sndlen = int(sndlen / 2.0)

        sndtype = data.pop(0)
        recipient = swap_number(encode_bytes(data[:sndlen]))
        if (sndtype >> 4) & 0x07 == consts.INTERNATIONAL:
            recipient = '+%s' % recipient

        data = data[sndlen:]

        date = swap(list(encode_bytes(data[:7])))
        try:
            scts_str = "%s%s/%s%s/%s%s %s%s:%s%s:%s%s" % tuple(date[0:12])
            self.date = datetime.strptime(scts_str, "%y/%m/%d %H:%M:%S")
        except (ValueError, TypeError):
            scts_str = ''
            debug('Could not decode scts: %s' % date)

        data = data[7:]

        date = swap(list(encode_bytes(data[:7])))
        try:
            dt_str = "%s%s/%s%s/%s%s %s%s:%s%s:%s%s" % tuple(date[0:12])
            dt = datetime.strptime(dt_str, "%y/%m/%d %H:%M:%S")
        except (ValueError, TypeError):
            dt_str = ''
            dt = None
            debug('Could not decode date: %s' % date)

        data = data[7:]

        msg_l = [recipient, scts_str]
        try:
            status = data.pop(0)
        except IndexError:
            # Yes it is entirely possible that a status report comes
            # with no status at all! I'm faking for now the values and
            # set it to SR-UNKNOWN as that's all we can do
            _status = None
            status = 0x1
            sender = 'SR-UNKNOWN'
            msg_l.append(dt_str)
        else:
            _status = status
            if status == 0x00:
                msg_l.append(dt_str)
            else:
                msg_l.append('')
            if status == 0x00:
                sender = "SR-OK"
            elif status == 0x1:
                sender = "SR-UNKNOWN"
            elif status == 0x30:
                sender = "SR-STORED"
            else:
                sender = "SR-UNKNOWN"

        self.number = sender
        self.text = "|".join(msg_l)
        self.fmt = 0x08   # UCS2
        self.type = 0x03  # status report

        self.sr = {
            'recipient': recipient,
            'scts': self.date,
            'dt': dt,
            'status': _status
        }
Esempio n. 5
0
    def _decode_status_report_pdu(self, data):
        self.udh = UserDataHeader.from_status_report_ref(data.pop(0))

        sndlen = data.pop(0)
        if sndlen % 2:
            sndlen += 1
        sndlen = int(sndlen / 2.0)

        sndtype = data.pop(0)
        recipient = swap_number(encode_bytes(data[:sndlen]))
        if (sndtype >> 4) & 0x07 == consts.INTERNATIONAL:
            recipient = '+%s' % recipient

        data = data[sndlen:]

        date = swap(list(encode_bytes(data[:7])))
        try:
            scts_str = "%s%s/%s%s/%s%s %s%s:%s%s:%s%s" % tuple(date[0:12])
            self.date = datetime.strptime(scts_str, "%y/%m/%d %H:%M:%S")
        except (ValueError, TypeError):
            scts_str = ''
            debug('Could not decode scts: %s' % date)

        data = data[7:]

        date = swap(list(encode_bytes(data[:7])))
        try:
            dt_str = "%s%s/%s%s/%s%s %s%s:%s%s:%s%s" % tuple(date[0:12])
            dt = datetime.strptime(dt_str, "%y/%m/%d %H:%M:%S")
        except (ValueError, TypeError):
            dt_str = ''
            dt = None
            debug('Could not decode date: %s' % date)

        data = data[7:]

        msg_l = [recipient, scts_str]
        try:
            status = data.pop(0)
        except IndexError:
            # Yes it is entirely possible that a status report comes
            # with no status at all! I'm faking for now the values and
            # set it to SR-UNKNOWN as that's all we can do
            _status = None
            status = 0x1
            sender = 'SR-UNKNOWN'
            msg_l.append(dt_str)
        else:
            _status = status
            if status == 0x00:
                msg_l.append(dt_str)
            else:
                msg_l.append('')
            if status == 0x00:
                sender = "SR-OK"
            elif status == 0x1:
                sender = "SR-UNKNOWN"
            elif status == 0x30:
                sender = "SR-STORED"
            else:
                sender = "SR-UNKNOWN"

        self.number = sender
        self.text = "|".join(msg_l)
        self.fmt = 0x08  # UCS2
        self.type = 0x03  # status report

        self.sr = {
            'recipient': recipient,
            'scts': self.date,
            'dt': dt,
            'status': _status
        }
Esempio n. 6
0
class MMSEncoder(wsp_pdu.Encoder):
    """MMS Encoder"""
    def __init__(self):
        self._mms_message = message.MMSMessage()

    def encode(self, mms_message):
        """
        Encodes the specified MMS message ``mms_message``

        :param mms_message: The MMS message to encode
        :type mms_message: MMSMessage

        :return: The binary-encoded MMS data, as a sequence of bytes
        :rtype: array.array('B')
        """
        self._mms_message = mms_message
        msg_data = self.encode_message_header()
        msg_data.extend(self.encode_message_body())
        return msg_data

    def encode_message_header(self):
        """
        Binary-encodes the MMS header data.

        The encoding used for the MMS header is specified in [4].
        All "constant" encoded values found/used in this method
        are also defined in [4]. For a good example, see [2].

        :return: the MMS PDU header, as an array of bytes
        :rtype: array.array('B')
        """
        # See [4], chapter 8 for info on how to use these
        # from_types = {'Address-present-token': 0x80,
        #               'Insert-address-token': 0x81}

        # content_types = {'application/vnd.wap.multipart.related': 0xb3}

        # Create an array of 8-bit values
        message_header = array.array('B')

        headers_to_encode = self._mms_message.headers

        # If the user added any of these to the message manually
        # (X- prefix) use those instead
        for hdr in ('X-Mms-Message-Type', 'X-Mms-Transaction-Id',
                    'X-Mms-Version'):
            if hdr in headers_to_encode:
                if hdr == 'X-Mms-Version':
                    clean_header = 'MMS-Version'
                else:
                    clean_header = hdr.replace('X-Mms-', '', 1)

                headers_to_encode[clean_header] = headers_to_encode[hdr]
                del headers_to_encode[hdr]

        # First 3  headers (in order), according to [4]:
        ################################################
        # - X-Mms-Message-Type
        # - X-Mms-Transaction-ID
        # - X-Mms-Version

        ### Start of Message-Type verification
        if 'Message-Type' not in headers_to_encode:
            # Default to 'm-retrieve-conf'; we don't need a To/CC field for
            # this (see WAP-209, section 6.3, table 5)
            headers_to_encode['Message-Type'] = 'm-retrieve-conf'

        # See if the chosen message type is valid, given the message's
        # other headers. NOTE: we only distinguish between 'm-send-req'
        # (requires a destination number) and 'm-retrieve-conf'
        # (requires no destination number) - if "Message-Type" is
        # something else, we assume the message creator knows
        # what she is doing
        if headers_to_encode['Message-Type'] == 'm-send-req':
            found_dest_address = False
            for address_type in ('To', 'Cc', 'Bc'):
                if address_type in headers_to_encode:
                    found_dest_address = True
                    break

            if not found_dest_address:
                headers_to_encode['Message-Type'] = 'm-retrieve-conf'
        ### End of Message-Type verification

        ### Start of Transaction-Id verification
        if 'Transaction-Id' not in headers_to_encode:
            trans_id = str(random.randint(1000, 9999))
            headers_to_encode['Transaction-Id'] = trans_id
        ### End of Transaction-Id verification

        ### Start of MMS-Version verification
        if 'MMS-Version' not in headers_to_encode:
            headers_to_encode['MMS-Version'] = '1.0'

        # Encode the first three headers, in correct order
        for hdr in ('Message-Type', 'Transaction-Id', 'MMS-Version'):
            message_header.extend(
                MMSEncoder.encode_header(hdr, headers_to_encode[hdr]))
            del headers_to_encode[hdr]

        # Encode all remaining MMS message headers, except "Content-Type"
        # -- this needs to be added last, according [2] and [4]
        for hdr in headers_to_encode:
            if hdr != 'Content-Type':
                message_header.extend(
                    MMSEncoder.encode_header(hdr, headers_to_encode[hdr]))

        # Ok, now only "Content-type" should be left
        content_type, ct_parameters = headers_to_encode['Content-Type']
        message_header.extend(MMSEncoder.encode_mms_field_name('Content-Type'))
        ret = MMSEncoder.encode_content_type_value(content_type, ct_parameters)
        message_header.extend(flatten_list(ret))

        return message_header

    def encode_message_body(self):
        """
        Binary-encodes the MMS body data

        The MMS body's header should not be confused with the actual
        MMS header, as returned by :func:`encode_header`.

        The encoding used for the MMS body is specified in [5],
        section 8.5. It is only referenced in [4], however [2]
        provides a good example of how this ties in with the MMS
        header encoding.

        The MMS body is of type `application/vnd.wap.multipart` ``mixed``
        or ``related``. As such, its structure is divided into a header, and
        the data entries/parts::

            [ header ][ entries ]
            ^^^^^^^^^^^^^^^^^^^^^
                  MMS Body

        The MMS Body header consists of one entry[5]::

            name             type           purpose
            -------          -------        -----------
            num_entries      uint_var        num of entries in the multipart entity

        The MMS body's multipart entries structure::

            name             type                   purpose
            -------          -----                  -----------
            HeadersLen       uint_var                length of the ContentType and
                                                    Headers fields combined
            DataLen          uint_var                length of the Data field
            ContentType      Multiple octets        the content type of the data
            Headers          (<HeadersLen>
                              - length of
                             <ContentType>) octets  the part's headers
            Data             <DataLen> octets       the part's data

        :return: The binary-encoded MMS PDU body, as an array of bytes
        :rtype: array.array('B')
        """
        message_body = array.array('B')

        #TODO: enable encoding of MMSs without SMIL file
        ########## MMS body: header ##########
        # Parts: SMIL file + <number of data elements in each slide>
        num_entries = 1
        for page in self._mms_message._pages:
            num_entries += page.number_of_parts()

        for data_part in self._mms_message._data_parts:
            num_entries += 1

        message_body.extend(self.encode_uint_var(num_entries))

        ########## MMS body: entries ##########
        # For every data "part", we have to add the following sequence:
        # <length of content-type + other possible headers>,
        # <length of data>,
        # <content-type + other possible headers>,
        # <data>.

        # Gather the data parts, adding the MMS message's SMIL file
        smil_part = message.DataPart()
        smil = self._mms_message.smil()
        smil_part.set_data(smil, 'application/smil')
        #TODO: make this dynamic....
        smil_part.headers['Content-ID'] = '<0000>'
        parts = [smil_part]
        for slide in self._mms_message._pages:
            for part_tuple in (slide.image, slide.audio, slide.text):
                if part_tuple is not None:
                    parts.append(part_tuple[0])

        for part in parts:
            name, val_type = part.headers['Content-Type']
            part_content_type = self.encode_content_type_value(name, val_type)

            encoded_part_headers = []
            for hdr in part.headers:
                if hdr == 'Content-Type':
                    continue
                encoded_part_headers.extend(
                    wsp_pdu.Encoder.encode_header(hdr, part.headers[hdr]))

            # HeadersLen entry (length of the ContentType and
            #  Headers fields combined)
            headers_len = len(part_content_type) + len(encoded_part_headers)
            message_body.extend(self.encode_uint_var(headers_len))
            # DataLen entry (length of the Data field)
            message_body.extend(self.encode_uint_var(len(part)))
            # ContentType entry
            message_body.extend(part_content_type)
            # Headers
            message_body.extend(encoded_part_headers)
            # Data (note: we do not null-terminate this)
            for char in part.data:
                message_body.append(ord(char))

        return message_body

    @staticmethod
    def encode_header(header_field_name, header_value):
        """
        Encodes a header entry for an MMS message

        The return type of the "header value" depends on the header
        itself; it is thus up to the function calling this to determine
        what that type is (or at least compensate for possibly different
        return value types).

        From [4], section 7.1::

            Header = MMS-header | Application-header
            MMS-header = MMS-field-name MMS-value
            MMS-field-name = Short-integer
            MMS-value = Bcc-value | Cc-value | Content-location-value | Content-type-value | etc

        :raise DecodeError: This uses :func:`decode_mms_header` and
                            :func:`decode_application_header`, and will raise this
                            exception under the same circumstances as
                            :func:`decode_application_header`. ``byte_iter`` will
                            not be modified in this case.

        :return: The decoded header entry from the MMS, in the format:
                 (<str:header name>, <str/int/float:header value>)
        :rtype: tuple
        """
        encoded_header = []
        # First try encoding the header as a "MMS-header"...
        for assigned_number in mms_field_names:
            header = mms_field_names[assigned_number][0]
            if header == header_field_name:
                encoded_header.extend(
                    wsp_pdu.Encoder.encode_short_integer(assigned_number))
                # Now encode the value
                expected_type = mms_field_names[assigned_number][1]
                try:
                    ret = getattr(MMSEncoder,
                                  'encode_%s' % expected_type)(header_value)
                    encoded_header.extend(ret)
                except wsp_pdu.EncodeError, msg:
                    raise wsp_pdu.EncodeError('Error encoding parameter '
                                              'value: %s' % msg)
                except:
                    debug('A fatal error occurred, probably due to an '
                          'unimplemented encoding operation')
                    raise