Example #1
0
/*!
    Dispatch the SMS datagram \a msg according to its SMS port number
    or WAP Push MIME type.  This is used by telephony services that
    accept incoming SMS messages to dispatch them according to the
    installed services.  Returns true if the message was dispatched,
    or false if no service exists that can handle the message.

    See the documentation for QSMSMessage::destinationPort() for more
    information on how WAP push messages and SMS datagrams are dispatched.

    \sa QSMSMessage::destinationPort()
*/
bool QTelephonyService::dispatchDatagram( const QSMSMessage& msg )
{
    QString chan, type;
    QDSServiceInfo info;
    QByteArray payload;
    QByteArray raw;

    // If the message does not have a port number, then it isn't a datagram.
    int port = msg.destinationPort();
    if ( port == -1 )
        return dispatchFlashMessage( msg );

    // Recognise port numbers that may contain WAP push datagrams.
    // We want to check the content type to see if we have a
    // specialised handler for it before dispatching by port number.
    if ( port == 2948 || port == 49999 ) {
        type = QWspPush::quickContentType( msg.applicationData() );
        qLog(Modem) << "WAP push message of type " << type;
        QDSServices services( type, QString(), QStringList() << "push" );
        if ( !services.isEmpty() ) {
            info = services.first();
            QByteArray a = msg.applicationData();
            QBuffer pushpdu(&a);
            pushpdu.open(QIODevice::ReadOnly);
            QWspPduDecoder decoder(&pushpdu);
            QWspPush push = decoder.decodePush();
            payload = push.data();
        }
    }

    // See if we have a registered service for this port number.
    if ( !info.isValid() ) {
        qLog(Modem) << "SMS datagram on port " << port;
        type = "application/x-smsapp-" + QString::number(port);
        QDSServices services( type, QString(), QStringList() << "push" );
        if ( !services.isEmpty() ) {
            info = services.first();
            payload = msg.applicationData();
        } else {
            return dispatchFlashMessage( msg );
        }
    }

    // Pack the entire SMS message into a raw datastream, to be sent
    // along with the message as auxillary data.  This allows programs
    // that need the full SMS/WAP headers to access them.
    {
        QDataStream stream
            ( &raw, QIODevice::WriteOnly | QIODevice::Append );
        stream << msg;
    }

    // Send the datagram to the specified QDS service for processing.
    QDSAction action( info );
    action.invoke( QDSData( payload, QMimeType( type ) ), raw );

    // The datagram has been dispatched.
    return true;
}
Example #2
0
void SmsReader::deleteSms(const int number)
{
    if (number >= count()) {
        return;
    }
    QSMSMessage msg = getSmsMessage(number);
    for (const int numSmsInMemory : msg.messageIds()) {
        SimpleAtCommand *cmd = new SimpleAtCommand(QString("AT+CMGD=%0").arg(numSmsInMemory));
        connect(cmd, SIGNAL(isProcessed()), this, SLOT(onDeleteCommand()));
        m_atChat->addCommand(cmd);
    }
    m_incomingMessages.removeAt(number);
}
Example #3
0
void SmsClient::newConnection()
{
    // XXX need better way to handle this.
    QSettings c("PhoneProfile"); // no tr
    c.beginGroup("Profiles"); // no tr
    bool planeMode = c.value("PlaneMode", false).toBool();
    smsSending = true;
    if (planeMode) {
        errorHandling(0, tr("Messages cannot be sent in Airplane Mode."));
        return;
    }

    QList<RawSms>::iterator rawMsg;
    for ( rawMsg = smsList.begin(); rawMsg != smsList.end(); rawMsg++) {
        QSMSMessage msg;

        //check for vcard over sms
        if (rawMsg->mimetype == QLatin1String("text/x-vCard")) {
            QString vcardData = rawMsg->body;
            //restore CR's stripped by composer
            vcardData.replace("\n","\r\n");
            msg.setApplicationData(vcardData.toLatin1());
            msg.setDestinationPort(9204);
        } else {
            msg.setText( rawMsg->body );
        }

        msg.setRecipient( rawMsg->number );

        QString smsKey = sender->send( msg );
        sentMessages.insert( smsKey, *rawMsg );
        ++total;
    }
    success = true;
    smsSending = false;
    smsList.clear();
}
Example #4
0
// Get the multipart identifier information for a message.
static bool getMultipartInfo( const QSMSMessage& msg, QString& multiId,
                              uint& part, uint& numParts )
{
    QByteArray headers = msg.headers();
    int posn = 0;
    uint tag;
    int len;
    while ( ( posn + 2 ) <= headers.size() ) {
        tag = (unsigned char)(headers[posn]);
        len = (unsigned char)(headers[posn + 1]);
        if ( ( posn + len + 2 ) > headers.size() )
            break;
        if ( tag == 0 && len >= 3 ) {
            // Concatenated message with 8-bit identifiers.
            multiId = msg.sender() + "&" +
                      QString::number
                        ( (uint)(unsigned char)(headers[posn + 2]) );
            numParts = (unsigned char)(headers[posn + 3]);
            part     = (unsigned char)(headers[posn + 4]);
            if ( numParts && part && part <= numParts )
                return true;
        } else if ( tag == 8 && len >= 4 ) {
            // Concatenated message with 16-bit identifiers.
            multiId = msg.sender() + "&" +
                      QString::number
                        ( ( (uint)(unsigned char)(headers[posn + 2]) << 8 ) +
                            (uint)(unsigned char)(headers[posn + 3]) );
            numParts = (unsigned char)(headers[posn + 4]);
            part     = (unsigned char)(headers[posn + 5]);
            if ( numParts && part && part <= numParts )
                return true;
        }
        posn += 2 + len;
    }
    return false;
}
Example #5
0
// Check for and dispatch flash sms messages.
static bool dispatchFlashMessage( const QSMSMessage& msg )
{
    if ( msg.messageClass() != 0 )
        return false;
    qLog(Modem) << "SMS flash message";
    QString type = "application/x-sms-flash";
    QDSServices services( type, QString(), QStringList() << "push" );
    if ( !services.isEmpty() ) {
        QDSAction action( services.first() );
        QByteArray payload;
        {
            QDataStream stream
                ( &payload, QIODevice::WriteOnly | QIODevice::Append );
            stream << msg;
        }
        action.invoke( QDSData( payload, QMimeType( type ) ) );
        return true;
    } else {
        return false;
    }
}
void HardwareManipulator::constructSMSDatagram(int port, const QString &sender, const QByteArray &data,
                                               const QByteArray &contentType)
{
    QWspPush pdu;
    pdu.setIdentifier(0);
    pdu.setPduType(6);

    pdu.setData(data.data(),data.length());

    QBuffer buffer;
    buffer.open(QBuffer::ReadWrite);
    QWspPduEncoder encoder(&buffer);

    if ( contentType.length() != 0 ) {
        pdu.addHeader("Content-Type", contentType);
        encoder.encodePush(pdu);
    } else {
        pdu.writeData(&buffer);
    }
    QByteArray appData = buffer.buffer();
    buffer.close();

    QSMSMessage m;
    m.setDestinationPort(port);
    m.setSender(sender);
    m.setApplicationData(appData);

    if( m.shouldSplit() ) {
        QList<QSMSMessage> list = m.split();

        for( int i =0; i < list.count(); i++ ) {
           SMSList.appendSMS( list[i].toPdu() );
        }
    } else {
        SMSList.appendSMS( m.toPdu() );
    }
}
void HardwareManipulator::constructSMSMessage( const QString &sender, const QString &serviceCenter, const QString &text )
{
    QSMSMessage m;
    if ( sender.isEmpty() ) {
        warning(tr("Invalid Sender"),
                tr("Sender must not be empty"));
        return;
    }
    m.setSender(sender);

    if ( serviceCenter.contains(QRegExp("\\D")) ) {
        warning(tr("Invalid Service Center"),
                tr("Service Center must not be empty and contain "
                   "only digits"));
        return;
    }
    m.setServiceCenter(serviceCenter);

    m.setText(text);
    m.setTimestamp(QDateTime::currentDateTime());

    SMSList.appendSMS( m.toPdu() );
}
Example #8
0
void SmsClient::fetched( const QString& id, const QSMSMessage& message )
{
    if (!id.isEmpty()) {
        QMailMessage mail;
        QString subject;
        QString body;
        int part;

        // Construct a full identity for the message.  This should be
        // unique enough to identify messages on different SIM's.
        QDateTime dt = message.timestamp();
        QString identity = QString("sms:%1:%2%3%4%5%6%7:>%8")
                                .arg( simIdentity )
                                .arg( dt.date().year(), 4 )
                                .arg( dt.date().month(), 2 )
                                .arg( dt.date().day(), 2 )
                                .arg( dt.time().hour(), 2 )
                                .arg( dt.time().minute(), 2 )
                                .arg( dt.time().second(), 2 )
                                .arg( id );

        // Add it to the active list, so that we know what's on the
        // inserted SIM right now, as opposed to the cached copy in
        // the mailbox folder.
        activeIds += identity;
        timeStamps += dt;

        // If we already have this message in the mailbox, then ignore it.
        if ( account ) {
            QStringList list = account->getUidlList();
            if ( list.contains( identity ) ) {
                if ( account->deleteMail() )
                    deleteImmediately( id );
                req->nextMessage();
                return;
            }
            list += identity;
            account->setUidlList( list );
        }
        mail.setServerUid( identity );

        // If the sender is not set, but the recipient is, then this
        // is probably an outgoing message that was reflected back
        // by the phone simulator.
        if( !message.sender().isEmpty() )
            mail.setHeaderField( "From", message.sender() );
        else if( !message.recipient().isEmpty() )
            mail.setHeaderField( "From", message.recipient() );

        // Extract the subject and body.
        extractSubjectAndBody( message.text(), subject, body );

        // Set the subject from the first few words of the text.
        mail.setSubject( subject );

        // Determine if the entire body is text, or if it contains attachments.
        bool hasAttachments = false;
        QList<QSMSMessagePart> parts = message.parts();
        for ( part = 0; part < parts.count(); ++part ) {
            if ( !(parts[part].isText()) ) {
                hasAttachments = true;
                break;
            }
        }
        if( !hasAttachments ) {
            QMailMessageContentType type("text/plain; charset=UTF-8");
            mail.setBody( QMailMessageBody::fromData( body, type, QMailMessageBody::Base64 ) );
        } else {
            SMSDecoder::formatMessage( mail, message );
        }

        // Set the reception date.
        QDateTime date = message.timestamp();
        if (!date.isValid())
            date = QDateTime::currentDateTime();
        mail.setDate( QMailTimeStamp( date ) );

        // Synthesize some other headers
        QString smsType;
        switch(message.messageType()) {
            case QSMSMessage::Normal:
                smsType = "normal"; break;
            case QSMSMessage::CellBroadCast:
                smsType = "broadcast"; break;
            case QSMSMessage::StatusReport:
                smsType = "status-report"; break;
            default:
                smsType = "unknown"; break;
        }
        mail.setHeaderField( "X-Sms-Type", smsType );

        QMailId id;
        mail.setId( id );

        mail.setStatus( QMailMessage::Incoming, true);
        mail.setStatus( QMailMessage::Downloaded, true);
        mail.setFromAccount( account->id() );
        mail.setFromMailbox( "" );
        mail.setMessageType( QMailMessage::Sms );

        // Is this necessary?
        QByteArray mailStr = mail.toRfc2822();
        mail.setSize( mailStr.length() );

        emit newMessage(mail);
        
        // If the "deleteMail" flag is set, then delete the message
        // from the SIM immediately, rather than waiting for later.
        if ( account && account->deleteMail() )
            deleteImmediately( QString::number(id.toULongLong()) );

        req->nextMessage();

        sawNewMessage = true;
    } else {
        smsFetching = false;
        if ( sawNewMessage ) {
            // Check again, just in case another new message arrived
            // while we were performing the fetch.
            req->check();
            return;
        }

        emit allMessagesReceived();

        // Make sure there are always 5 free slots on the SIM card
        // so there is enough space for the reception of new messages.
        if ( account && !account->deleteMail()
             && (req->totalMessages() - req->usedMessages()) < 5
             && req->totalMessages() >= 5
             && !timeStamps.isEmpty() ) {
            int toBeDeleted = 5 - (req->totalMessages() - req->usedMessages());
            while ( toBeDeleted-- > 0 && activeIds.size() > 0 ) {
                QDateTime dt = timeStamps[0];
                int index = 0;
                for (int i = 1; i < timeStamps.size(); ++i) {
                    if (timeStamps[i] < dt) {
                        dt = timeStamps[i];
                        index = i;
                    }
                }
                deleteImmediately( activeIds[index] );
                activeIds.removeAt( index );
                timeStamps.removeAt( index );
            }
        }
    }
}
Example #9
0
void SmsReader::onRequestSmsCommand()
{
    AtCommand *cmd = qobject_cast<AtCommand*>(sender());
    if (cmd) {
        QHash<QString, QList<QSMSMessage> > multiSms;
        AtResult res = cmd->getCommandResult();
        if (res.resultCode() == AtResult::OK) {
            m_incomingMessages.clear();
            QStringList lines = res.content().split("\n");
            int messageId = -1;
            QRegExp messageIdExtractor("^\\+CMGL:\\s*(\\d+)");
            Q_FOREACH(const QString &line, lines) {
                if (messageId < 0) {
                    // expected "CMGL: 0,1,,123", where first number is messageId
                    if (messageIdExtractor.indexIn(line) >= 0) {
                        bool ok;
                        messageId = messageIdExtractor.cap(1).toInt(&ok);
                        if (!ok) {
                            messageId = -1;
                        }
                    }
                } else {
                    // expected pdu
                    QSMSMessage smsMsg = QSMSMessage::fromPdu(QByteArray::fromHex(line.toLatin1()));
                    if (!smsMsg.sender().isEmpty()) {
                        QString multiId;
                        uint part;
                        uint partCount;
                        smsMsg.setMessageIds({messageId});
                        if (getMultipartInfo(smsMsg, multiId, part, partCount)) {
                            smsMsg.setMessagePartNumber(part);
                            multiSms[multiId].append(smsMsg);
                        } else {
                            m_incomingMessages.append(smsMsg);
                        }
                    }
                    messageId = -1;
                }
            }
            QHashIterator<QString, QList<QSMSMessage> > it(multiSms);
            while (it.hasNext()) {
                it.next();
                QList<QSMSMessage> mess = it.value();
                if (mess.count() > 1) {
                    QString multiId;
                    uint part;
                    uint partCount;
                    if (getMultipartInfo(mess.at(0), multiId, part, partCount)) {
                        if (partCount == (uint)mess.count()) {
                            // we have all part of sms message
                            QList<int> smsIds;
                            qSort(mess.begin(), mess.end(), [](const QSMSMessage &m1, const QSMSMessage &m2) {
                                return m1.messagePartNumber() < m2.messagePartNumber();
                            });
                            QString smsText;
                            QSMSMessage newMes;
                            for (const QSMSMessage &smsMess : mess) {
                                smsIds.append(smsMess.messageIds().at(0));
                                smsText.append(smsMess.text());
                                newMes.setTimestamp(smsMess.timestamp());
                                newMes.setSender(smsMess.sender());
                            }
                            newMes.setText(smsText);
                            newMes.setMessageIds(smsIds);
                            m_incomingMessages.append(newMes);
                        }
                    }
                }
            }
            logMsg(QString("Request sms command: we have %0 sms").arg(m_incomingMessages.count()));
            if (count() > 0) {
                emit incomingSms();
            }
        } else {
Example #10
0
void tst_QSimToolkit::testEncodeSendSMS()
{
    QFETCH( QByteArray, data );
    QFETCH( QByteArray, resp );
    QFETCH( QByteArray, tpdu );
    QFETCH( int, resptype );
    QFETCH( QString, text );
    QFETCH( QString, number );
    QFETCH( bool, smsPacking );
    QFETCH( int, iconId );
    QFETCH( bool, iconSelfExplanatory );
    QFETCH( QByteArray, textAttribute );
    QFETCH( QString, html );
    QFETCH( int, options );

    // Output a dummy line to give some indication of which test we are currently running.
    qDebug() << "";

    // Check that the command PDU can be parsed correctly.
    QSimCommand decoded = QSimCommand::fromPdu(data);
    QVERIFY( decoded.type() == QSimCommand::SendSMS );
    QVERIFY( decoded.destinationDevice() == QSimCommand::Network );
    QCOMPARE( decoded.text(), text );
    if ( text.isEmpty() ) {
        if ( ( options & QSimCommand::EncodeEmptyStrings ) != 0 )
            QVERIFY( decoded.suppressUserFeedback() );
        else
            QVERIFY( !decoded.suppressUserFeedback() );
    } else {
        QVERIFY( !decoded.suppressUserFeedback() );
    }
    QCOMPARE( decoded.number(), number );
    QCOMPARE( decoded.smsPacking(), smsPacking );
    QCOMPARE( (int)decoded.iconId(), iconId );
    QCOMPARE( decoded.iconSelfExplanatory(), iconSelfExplanatory );
    QCOMPARE( decoded.textAttribute(), textAttribute );
    if ( !textAttribute.isEmpty() )
        QCOMPARE( decoded.textHtml(), html );

    // Check the final TPDU.  If packing is specified, we have to parse the SMS
    // and then re-encode it with the 7-bit alphabet.
    QByteArray newtpdu = decoded.extensionField(0x8B);
    if ( smsPacking ) {
        // Add dummy service center address that isn't in the TPDU before decoding.
        QSMSMessage msg = QSMSMessage::fromPdu( QByteArray( 1, 0 ) + newtpdu );
        msg.setDataCodingScheme( msg.dataCodingScheme() & 0xF3 );   // Convert to 7-bit

        // Convert back into a pdu and strip off the dummy service center address.
        newtpdu = msg.toPdu().mid(1);
    }

    // The TPDU in the command will have a message reference of 0.
    // We need to change it to the transmission message reference of 1
    // before we do the comparison.
    newtpdu[1] = (char)1;
    QCOMPARE( newtpdu, tpdu );

    // Check that the original command PDU can be reconstructed correctly.
    QByteArray encoded = decoded.toPdu( (QSimCommand::ToPduOptions)options );
    QCOMPARE( encoded, data );

    // Check that the terminal response PDU can be parsed correctly.
    QSimTerminalResponse decodedResp = QSimTerminalResponse::fromPdu(resp);
    QVERIFY( data.contains( decodedResp.commandPdu() ) );
    if ( resptype < 0x0100 ) {
        QVERIFY( decodedResp.result() == (QSimTerminalResponse::Result)resptype );
        QVERIFY( decodedResp.causeData().isEmpty() );
        QVERIFY( decodedResp.cause() == QSimTerminalResponse::NoSpecificCause );
    } else {
        QVERIFY( decodedResp.result() == (QSimTerminalResponse::Result)(resptype >> 8) );
        QVERIFY( decodedResp.causeData().size() == 1 );
        QVERIFY( decodedResp.cause() == (QSimTerminalResponse::Cause)(resptype & 0xFF) );
    }

    // Check that the original terminal response PDU can be reconstructed correctly.
    QCOMPARE( decodedResp.toPdu(), resp );
}