/* * Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include #include #include #include "SMSMessages.h" #include using namespace std; using namespace GSM; using namespace SMS; ostream& SMS::operator<<(ostream& os, CPMessage::MessageType val) { switch(val) { case CPMessage::DATA: os<<"CP-DATA"; break; case CPMessage::ACK: os<<"CP-ACK"; break; case CPMessage::ERROR: os<<"CP-ERROR"; break; default : os<parse(RPDU); LOG(DEBUG) << "SMS RP-DATA " << *rp_data; } catch (SMSReadError) { LOG(WARNING) << "SMS parsing failed (above L3)"; // TODO:: send error back to the phone delete rp_data; rp_data = NULL; } catch (L3ReadError) { LOG(WARNING) << "SMS parsing failed (in L3)"; // TODO:: send error back to the phone delete rp_data; rp_data = NULL; } return rp_data; } TLMessage *SMS::parseTPDU(const TLFrame& TPDU) { LOG(DEBUG) << "SMS: parseTPDU MTI=" << TPDU.MTI(); // Handle just the uplink cases. switch ((TLMessage::MessageType)TPDU.MTI()) { case TLMessage::DELIVER_REPORT: case TLMessage::STATUS_REPORT: // FIXME -- Not implemented yet. LOG(WARNING) << "Unsupported TPDU type: " << (TLMessage::MessageType)TPDU.MTI(); return NULL; case TLMessage::SUBMIT: { TLSubmit *submit = new TLSubmit; submit->parse(TPDU); LOG(INFO) << "SMS SMS-SUBMIT " << *submit; return submit; } default: return NULL; } } void CPMessage::text(ostream& os) const { os << (CPMessage::MessageType)MTI(); os <<" TI=" << mTI; } void CPMessage::write(L3Frame& dest) const { // We override L3Message::write for the transaction identifier. dest.resize(bitsNeeded()); size_t wp = 0; // Note that 1/2-octet fields are reversed relative to Table 7.1. dest.writeField(wp,mTI,4); dest.writeField(wp,PD(),4); dest.writeField(wp,MTI(),8); writeBody(dest, wp); } void CPData::parseBody( const L3Frame& src, size_t &rp ) { mData.parseLV(src,rp); } void CPData::writeBody( L3Frame& dest, size_t &wp ) const { mData.writeLV(dest,wp); } void CPData::text(ostream& os) const { CPMessage::text(os); os << " RPDU=(" << mData << ")"; } void CPError::writeBody( L3Frame& dest, size_t &wp ) const { mCause.writeV(dest,wp); } void CPUserData::parseV(const L3Frame& src, size_t &rp, size_t expectedLength) { unsigned numBits = expectedLength*8; mRPDU.resize(numBits); src.segmentCopyTo(mRPDU,rp,numBits); rp += numBits; } void CPUserData::writeV(L3Frame& dest, size_t &wp) const { unsigned numBits = mRPDU.size(); mRPDU.copyToSegment(dest,wp,numBits); wp += numBits; } ostream& SMS::operator<<(ostream& os, RPMessage::MessageType val) { switch(val) { case RPMessage::Data: os<<"RP-DATA"; break; case RPMessage::Ack: os<<"RP-ACK"; break; case RPMessage::Error: os<<"RP-ERROR"; break; case RPMessage::SMMA: os<<"RP-SMMA"; break; default : os< 0) { // skip fill bits rp += fill_bits; } } break; case UnknownTypeOfNumber: case InternationalNumber: case NationalNumber: case NetworkSpecificNumber: case ShortCodeNumber: case AbbreviatedNumber: { GSM::L3BCDDigits digitNumber; digitNumber.parse(src,rp,length); mAddressValue = digitNumber.digits(); } break; default: SMS_READ_ERROR; break; } } void TLAddress::text(ostream& os) const { os << "type=" << mType; os << " plan=" << mPlan; os << " digits=" << mAddressValue; } void TLAddress::write(TLFrame& dest, size_t& wp) const { switch (mType) { case AlphanumericNumber: { unsigned length = strlen(mAddressValue); int bytes = (length*7+7)/8; int filler_bits = bytes*8-length*7; int addressLength = bytes*2; if (filler_bits > 3) { addressLength--; } dest.writeField(wp,addressLength,8); dest.writeField(wp, 0x01, 1); dest.writeField(wp, mType, 3); dest.writeField(wp, mPlan, 4); BitVector textNumber(bytes*8); size_t np = 0; for (unsigned i=0; i0) { textNumber.writeField(np,0,filler_bits); } textNumber.LSB8MSB(); np = 0; for (int i = 0; i < bytes; i++) { dest.writeField(wp, textNumber.readField(np,8), 8); } } break; case UnknownTypeOfNumber: case InternationalNumber: case NationalNumber: case NetworkSpecificNumber: case ShortCodeNumber: case AbbreviatedNumber: { GSM::L3BCDDigits digitNumber(mAddressValue); dest.writeField(wp,digitNumber.size(),8); dest.writeField(wp, 0x01, 1); dest.writeField(wp, mType, 3); dest.writeField(wp, mPlan, 4); digitNumber.write(dest,wp); } break; default: SMS_READ_ERROR; break; } } size_t TLAddress::length() const { size_t length = 2; switch (mType) { case AlphanumericNumber: length += (strlen(mAddressValue)*7+7)/8; break; case UnknownTypeOfNumber: case InternationalNumber: case NationalNumber: case NetworkSpecificNumber: case ShortCodeNumber: case AbbreviatedNumber: { GSM::L3BCDDigits digitNumber(mAddressValue); length += digitNumber.lengthV(); } break; } return length; } size_t TLValidityPeriod::length() const { // GSM 03.40 9.2.3.3 switch (mVPF) { case 0: return 0; // not present case 1: return 1; // relative format, 9.2.3.12.1 case 2: return 7; // enhanced format, 9.2.3.12.2 case 3: return 7; // absolute format, 9.2.3.12.3 default: assert(0); // someone forgot to initialize the VPF } } void TLValidityPeriod::parse(const TLFrame& src, size_t& rp) { // FIXME -- Check remaining message length before reading!! LOG(DEBUG) << "SMS: TLValidityPeriod::parse VPF=" << mVPF; switch (mVPF) { case 2: { // Relative format. // GSM 03.40 9.2.3.12.1 unsigned vp = src.readField(rp,8); unsigned minutes = 0; if (vp<144) minutes = (vp+1)*5; else if (vp<168) minutes = 12*60 + (vp-143)*30; else if (vp<197) minutes = 24*60*(vp-166); else minutes = 7*24*60*(vp-192); mExpiration = Timeval(); mExpiration.addMinutes(minutes); return; } case 3: { // Absolute format, borrowed from GSM 04.08 MM // GSM 03.40 9.2.3.12.2 L3TimeZoneAndTime decoder; decoder.parseV((TLFrame)(BitVector)src,rp); mExpiration = decoder.time(); return; } case 1: // Enhanced format. // GSM 03.40 9.2.3.12.3 LOG(NOTICE) << "SMS: ignoring grossly complex \"enhanced\" TP-VP and assuming 1 week."; rp += 7; // fall through... case 0: // No validity period field. LOG(DEBUG) << "SMS: no validity period, assuming 1 week"; mExpiration = Timeval(7*24*60*60*1000); return; default: assert(0); // someone forgot to initialize the VPF } } void TLValidityPeriod::write(TLFrame& dest, size_t& wp) const { if (mVPF==0) return; // We only support VPF==1. assert(mVPF==1); int seconds = mExpiration.seconds() - time(NULL); int minutes = seconds/60; if (minutes<1) minutes=1; unsigned vp; if (minutes<=720) vp = (minutes-1)/5; else if (minutes<1440) vp = 143 + (minutes-720)/30; else if (minutes<43200) vp = 166 + minutes/(24*60); else vp = 192 + minutes/(7*24*60); if (vp>255) vp=255; dest.writeField(wp,vp,8); } void TLValidityPeriod::text(ostream& os) const { char str[27]; time_t seconds = mExpiration.sec(); ctime_r(&seconds,str); str[24]='\0'; os << "expiration=(" << str << ")"; } void TLUserData::encode7bit(const char *text) { size_t wp = 0; // 1. Prepare. // Default alphabet (7-bit) mDCS = 0; // With 7-bit encoding TP-User-Data-Length count septets, i.e. just number // of characters. mLength = strlen(text); int bytes = (mLength*7+7)/8; int filler_bits = bytes*8-mLength*7; mRawData.resize(bytes*8); // 2. Write TP-UD // This tail() works because UD is always the last field in the PDU. BitVector chars = mRawData.tail(wp); for (unsigned i=0; i (mRawData.size())) { LOG(NOTICE) << "badly formatted TL-UD"; SMS_READ_ERROR; } size_t crp = 0; unsigned text_length = mLength; // Skip User-Data-Header. We don't decode it here. // User-Data-Header handling is described in GSM 03.40 9.2.3.24 // and is pictured in GSM 03.40 Figure 9.2.3.24 (a) if (mUDHI) { // Length-of-User-Data-Header unsigned udhl = mRawData.peekFieldReversed(crp,8); // Calculate UDH length in septets, including fill bits. unsigned udh_septets = (udhl*8 + 8 + 6) / 7; // Adjust actual text position and length. crp += udh_septets * 7; text_length -= udh_septets; LOG(DEBUG) << "UDHL(octets)=" << udhl << " UDHL(septets)=" << udh_septets << " pointer(bits)=" << crp << " text_length(septets)=" << text_length; } // Do decoding text.resize(text_length); for (unsigned i=0; i (src.size()-rp)) { LOG(NOTICE) << "badly formatted TL-UD"; SMS_READ_ERROR; } BitVector chars(src.tail(rp)); chars.LSB8MSB(); size_t crp=0; for (unsigned i=0; i