// 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; }
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 ); } } } }
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 {