/*
* 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