# r_sms.cc

Go to the documentation of this file.

    00001 ///
    00002 /// \file       r_sms.cc
    00003 ///             Record parsing class for the SMS database.
    00004 ///
    00005
    00006 /*
    00007     Copyright (C) 2005-2011, Net Direct Inc. (http://www.netdirect.ca/)
    00008     Copyright (C) 2009, Ryan Li(ryan@ryanium.com)
    00009
    00010     This program is free software; you can redistribute it and/or modify
    00011     it under the terms of the GNU General Public License as published by
    00012     the Free Software Foundation; either version 2 of the License, or
    00013     (at your option) any later version.
    00014
    00015     This program is distributed in the hope that it will be useful,
    00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00018
    00019     See the GNU General Public License in the COPYING file at the
    00020     root directory of this project for more details.
    00021 */
    00022
    00023 #include "r_sms.h"
    00024 #include "record-internal.h"
    00025 #include "protostructs.h"
    00026 #include "data.h"
    00027 #include "time.h"
    00028 #include "debug.h"
    00029 #include "iconv.h"
    00030 #include "strnlen.h"
    00031 #include <ostream>
    00032 #include <iomanip>
    00033 #include <string.h>
    00034
    00035 using namespace std;
    00036 using namespace Barry::Protocol;
    00037
    00038 namespace Barry {
    00039
    00040 ///////////////////////////////////////////////////////////////////////////////
    00041 // Sms Class
    00042
    00043 // SMS Field Codes
    00044 #define SMSFC_METADATA 0x01
    00045 #define SMSFC_ADDRESS 0x02
    00046 #define SMSFC_BODY 0x04
    00047
    00048 // SMS Field Sizes and Header Sizes
    00049 #define SMS_ADDRESS_HEADER_SIZE 0x04
    00050
    00051 #define MILLISECONDS_IN_A_SECOND 1000
    00052
    00053 time_t Sms::GetTime() const
    00054 {
    00055         return (time_t)(Timestamp / MILLISECONDS_IN_A_SECOND);
    00056 }
    00057
    00058 time_t Sms::GetServiceCenterTime() const
    00059 {
    00060         return (time_t)(ServiceCenterTimestamp / MILLISECONDS_IN_A_SECOND);
    00061 }
    00062
    00063 void Sms::SetTime(const time_t timestamp, const unsigned milliseconds)
    00064 {
    00065         Timestamp = (uint64_t)timestamp * MILLISECONDS_IN_A_SECOND + milliseconds;
    00066 }
    00067
    00068 void Sms::SetServiceCenterTime(const time_t timestamp, const unsigned milliseconds)
    00069 {
    00070         ServiceCenterTimestamp = (uint64_t)timestamp * MILLISECONDS_IN_A_SECOND + milliseconds;
    00071 }
    00072
    00073 Sms::Sms()
    00074 {
    00075         Clear();
    00076 }
    00077
    00078 Sms::~Sms()
    00079 {
    00080 }
    00081
    00082 const unsigned char* Sms::ParseField(const unsigned char *begin,
    00083                                      const unsigned char *end,
    00084                                      const IConverter *ic)
    00085 {
    00086         const CommonField *field = (const CommonField *)begin;
    00087
    00088         // advance and check size
    00089         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
    00090         if (begin > end) // if begin==end, we are ok
    00091                 return begin;
    00092
    00093         if (!btohs(field->size)) // if field has no size, something's up
    00094                 return begin;
    00095
    00096         switch (field->type)
    00097         {
    00098                 case SMSFC_METADATA:
    00099                 {
    00100                         if (btohs(field->size) < SMS_METADATA_SIZE)
    00101                                 break; // size not match
    00102
    00103                         const SMSMetaData &metadata = field->u.sms_metadata;
    00104                         NewConversation = metadata.flags & SMS_FLG_NEW_CONVERSATION;
    00105                         Saved = metadata.flags & SMS_FLG_SAVED;
    00106                         Deleted = metadata.flags & SMS_FLG_DELETED;
    00107                         Opened = metadata.flags & SMS_FLG_OPENED;
    00108
    00109                         IsNew = metadata.new_flag;
    00110
    00111                         uint32_t status = btohl(metadata.status);
    00112
    00113                         switch (status)
    00114                         {
    00115                                 case SMS_STA_RECEIVED:
    00116                                         MessageStatus = Received;
    00117                                         break;
    00118                                 case SMS_STA_DRAFT:
    00119                                         MessageStatus = Draft;
    00120                                         break;
    00121                                 default:
    00122                                         MessageStatus = Sent; // consider all others as sent
    00123                         }
    00124
    00125                         ErrorId = btohl(metadata.error_id);
    00126
    00127                         Timestamp = btohll(metadata.timestamp);
    00128                         ServiceCenterTimestamp = btohll(metadata.service_center_timestamp);
    00129
    00130                         switch (metadata.dcs)
    00131                         {
    00132                                 case SMS_DCS_7BIT:
    00133                                         DataCodingScheme = SevenBit;
    00134                                         break;
    00135                                 case SMS_DCS_8BIT:
    00136                                         DataCodingScheme = EightBit;
    00137                                         break;
    00138                                 case SMS_DCS_UCS2:
    00139                                         DataCodingScheme = UCS2;
    00140                                         break;
    00141                                 default:
    00142                                         DataCodingScheme = SevenBit; // consider all unknowns as 7bit
    00143                         }
    00144
    00145                         return begin;
    00146                 }
    00147
    00148                 case SMSFC_ADDRESS:
    00149                 {
    00150                         uint16_t length = btohs(field->size);
    00151                         if (length < SMS_ADDRESS_HEADER_SIZE + 1) // trailing '\0'
    00152                                 break; // too short
    00153
    00154                         length -= SMS_ADDRESS_HEADER_SIZE;
    00155                         const char *address = (const char *)field->u.raw + SMS_ADDRESS_HEADER_SIZE;
    00156                         Addresses.push_back(std::string(address, strnlen(address, length)));
    00157                         return begin;
    00158                 }
    00159
    00160                 case SMSFC_BODY:
    00161                 {
    00162                         //
    00163                         // Some SMS bodies contain a null terminator
    00164                         // in the middle, and it is unknown at the moment
    00165                         // why this is.  For regular 8bit char strings,
    00166                         // we just strip out the nulls.  For UCS2
    00167                         // 16bit char strings, we strip out the
    00168                         // 16bit nulls.
    00169                         //
    00170                         // Any further information on why these null
    00171                         // terminators appear is welcome.
    00172                         //
    00173                         const char *str = (const char *)field->u.raw;
    00174                         uint16_t maxlen = btohs(field->size);
    00175                         if (DataCodingScheme != UCS2) {
    00176                                 for (uint16_t i = 0; i < maxlen; ++i) {
    00177                                         if (str[i]) // if not null, push it
    00178                                                 Body += str[i];
    00179                                 }
    00180                         }
    00181                         else {
    00182                                 for (uint16_t i = 0; maxlen && i < (maxlen-1); i += 2) {
    00183                                         if (str[i] || str[i + 1]) // if not null, push it
    00184                                                 Body += std::string(str + i, 2);
    00185                                 }
    00186                         }
    00187                         if (ic) {
    00188                                 if (DataCodingScheme == SevenBit) {
    00189                                         // SevenBit -> UTF-8 -> ic's tocode
    00190                                         IConvHandle utf8("UTF-8", *ic);
    00191                                         Body = ic->Convert(utf8, ConvertGsmToUtf8(Body)); // convert the Body string from GSM 03.38 defined to UTF-8
    00192                                 }
    00193                                 else if (DataCodingScheme == EightBit)
    00194                                         Body = ic->FromBB(Body);
    00195                                 else {
    00196                                         IConvHandle ucs2("UCS-2BE", *ic);
    00197                                         Body = ic->Convert(ucs2, Body);
    00198                                 }
    00199                         }
    00200                         return begin;
    00201                 }
    00202         }
    00203
    00204         // if still not handled, add to the Unknowns list
    00205         UnknownField uf;
    00206         uf.type = field->type;
    00207         uf.data.assign((const char*)field->u.raw, btohs(field->size));
    00208         Unknowns.push_back(uf);
    00209
    00210         // return new pointer for next field
    00211         return begin;
    00212 }
    00213
    00214 void Sms::ParseHeader(const Data &data, size_t &offset)
    00215 {
    00216         // no header in SMS records
    00217 }
    00218
    00219 void Sms::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
    00220 {
    00221         const unsigned char *finish = ParseCommonFields(*this,
    00222                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
    00223         offset += finish - (data.GetData() + offset);
    00224 }
    00225
    00226 void Sms::BuildHeader(Data &data, size_t &offset) const
    00227 {
    00228         // not yet implemented
    00229 }
    00230
    00231 void Sms::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
    00232 {
    00233         // not yet implemented
    00234 }
    00235
    00236 void Sms::Clear()
    00237 {
    00238         RecType = GetDefaultRecType();
    00239         RecordId = 0;
    00240
    00241         MessageStatus = Unknown;
    00242         DeliveryStatus = NoReport;
    00243
    00244         IsNew = NewConversation = Saved = Deleted = Opened = false;
    00245
    00246         Timestamp = ServiceCenterTimestamp = 0;
    00247
    00248         DataCodingScheme = SevenBit;
    00249
    00250         ErrorId = 0;
    00251
    00252         Addresses.clear();
    00253         Body.clear();
    00254
    00255         Unknowns.clear();
    00256 }
    00257
    00258 std::string Sms::GetDescription() const
    00259 {
    00260         if( Addresses.size() )
    00261                 return Addresses[0];
    00262         else
    00263                 return "Unknown destination";
    00264 }
    00265
    00266 void Sms::Dump(std::ostream &os) const
    00267 {
    00268
    00269         os << "SMS record: 0x" << setbase(16) << RecordId
    00270                 << " (" << (unsigned int)RecType << ")\n";
    00271         time_t t = GetTime();
    00272         os << "\tTimestamp: " << ctime(&t);
    00273
    00274         if (MessageStatus == Received) {
    00275                 t = GetServiceCenterTime();
    00276                 os << "\tService Center Timestamp: " << ctime(&t);
    00277         }
    00278
    00279         if (ErrorId)
    00280                 os << "\tSend Error: 0x" << setbase(16) << ErrorId << "\n";
    00281
    00282         switch (MessageStatus)
    00283         {
    00284                 case Received:
    00285                         os << "\tReceived From:\n";
    00286                         break;
    00287                 case Sent:
    00288                         os << "\tSent to:\n";
    00289                         break;
    00290                 case Draft:
    00291                         os << "\tDraft for:\n";
    00292                         break;
    00293                 case Unknown:
    00294                         os << "\tUnknown status for:\n";
    00295                         break;
    00296         }
    00297
    00298         os << "\t";
    00299         for (std::vector<std::string>::const_iterator i = Addresses.begin(); i < Addresses.end(); ++i) {
    00300                 if (i != Addresses.begin())
    00301                         os << ", ";
    00302                 os << *i;
    00303         }
    00304         os << "\n";
    00305
    00306         if (IsNew || Opened || Saved || Deleted || NewConversation) {
    00307                 os << "\t";
    00308                 if (IsNew)
    00309                         os << "New ";
    00310                 if (Opened)
    00311                         os << "Opened ";
    00312                 if (Saved)
    00313                         os << "Saved ";
    00314                 if (Deleted)
    00315                         os << "Deleted ";
    00316                 os << "Message" << (NewConversation ? " that starts a new conversation" : "") << "\n";
    00317         }
    00318         os << "\tContent: " << Body << "\n";
    00319         os << "\n";
    00320 }
    00321
    00322 //
    00323 // This function helps to convert GSM 03.38 defined 7-bit
    00324 // SMS to UTF-8.
    00325 // Detailed information can be found in:
    00326 // ftp://ftp.3gpp.org/Specs/html-info/0338.htm (Official)
    00327 // http://en.wikipedia.org/wiki/SMS#GSM
    00328 //
    00329
    00330 std::string Sms::ConvertGsmToUtf8(const std::string &s)
    00331 {
    00332         //
    00333         // This array stores the GSM 03.38 defined encoding's
    00334         // corresponding UTF-8 values.
    00335         // For example: GsmTable[0] = "\x40", which refers to
    00336         // a "@" in UTF-8 encoding.
    00337         // The 0x1b item, leads to the extension table, using
    00338         // the char right next to it as the index.
    00339         // According to the official specification, when not
    00340         // able to handle it, it should be treated simply as
    00341         // a space, which is denoted in UTF-8 as "\x20".
    00342         //
    00343         static const std::string GsmTable[0x80] = {
    00344         //  0x0,        0x1,        0x2,        0x3,        0x4,        0x5,        0x6,        0x7
    00345                 "\x40",     "\xc2\xa3", "\x24",     "\xc2\xa5", "\xc3\xa8", "\xc3\xa9", "\xc3\xb9", "\xc3\xac", // 0x00
    00346                 "\xc3\xb2", "\xc3\x87", "\x0a",     "\xc3\x98", "\xc3\xb8", "\x0d",     "\xc3\x85", "\xc3\xa5", // 0x08
    00347                 "\xce\x94", "\x5f",     "\xce\xa6", "\xce\x93", "\xce\x9b", "\xce\xa9", "\xce\xa0", "\xce\xa8", // 0x10
    00348                 "\xce\xa3", "\xce\x98", "\xce\x9e", "\x20",     "\xc3\x86", "\xc3\xa6", "\xc3\x9f", "\xc3\x89", // 0x18
    00349                 "\x20",     "\x21",     "\x22",     "\x23",     "\xc2\xa4", "\x25",     "\x26",     "\x27",     // 0x20
    00350                 "\x28",     "\x29",     "\x2a",     "\x2b",     "\x2c",     "\x2d",     "\x2e",     "\x2f",     // 0x28
    00351                 "\x30",     "\x31",     "\x32",     "\x33",     "\x34",     "\x35",     "\x36",     "\x37",     // 0x30
    00352                 "\x38",     "\x39",     "\x3a",     "\x3b",     "\x3c",     "\x3d",     "\x3e",     "\x3f",     // 0x38
    00353                 "\xc2\xa1", "\x41",     "\x42",     "\x43",     "\x44",     "\x45",     "\x46",     "\x47",     // 0x40
    00354                 "\x48",     "\x49",     "\x4a",     "\x4b",     "\x4c",     "\x4d",     "\x4e",     "\x4f",     // 0x48
    00355                 "\x50",     "\x51",     "\x52",     "\x53",     "\x54",     "\x55",     "\x56",     "\x57",     // 0x50
    00356                 "\x58",     "\x59",     "\x5a",     "\xc3\x84", "\xc3\x96", "\xc3\x91", "\xc3\x9c", "\xc2\xa7", // 0x58
    00357                 "\xc2\xbf", "\x61",     "\x62",     "\x63",     "\x64",     "\x65",     "\x66",     "\x67",     // 0x60
    00358                 "\x68",     "\x69",     "\x6a",     "\x6b",     "\x6c",     "\x6d",     "\x6e",     "\x6f",     // 0x68
    00359                 "\x70",     "\x71",     "\x72",     "\x73",     "\x74",     "\x75",     "\x76",     "\x77",     // 0x70
    00360                 "\x78",     "\x79",     "\x7a",     "\xc3\xa4", "\xc3\xb6", "\xc3\xb1", "\xc3\xbc", "\xc3\xa0"  // 0x78
    00361         };
    00362         //
    00363         // This sparse array stores the GSM 03.38 defined
    00364         // encoding extension table.
    00365         // The \x1b item is also preserved, for possibly
    00366         // another extension table.
    00367         //
    00368         static const std::string GsmExtensionTable[0x80] = {
    00369         //  0x0,            0x1,            0x2,            0x3,            0x4,            0x5,            0x6,            0x7
    00370                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x00
    00371                 "",             "",             "\x0c",         "",             "",             "",             "",             "",     // 0x08
    00372                 "",             "",             "",             "",             "\x5e",         "",             "",             "",     // 0x10
    00373                 "",             "",             "",             " ",            "",             "",             "",             "",     // 0x18
    00374                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x20
    00375                 "\x7b",         "\x7d",         "",             "",             "",             "",             "",             "\x5c", // 0x28
    00376                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x30
    00377                 "",             "",             "",             "",             "\x5b",         "\x7e",         "\x5d",         "",     // 0x38
    00378                 "\x7c",         "",             "",             "",             "",             "",             "",             "",     // 0x40
    00379                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x48
    00380                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x50
    00381                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x58
    00382                 "",             "",             "",             "",             "",             "\xe2\x82\xac", "",             "",     // 0x60
    00383                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x68
    00384                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x70
    00385                 "",             "",             "",             "",             "",             "",             "",             ""      // 0x78
    00386         };
    00387         std::string ret;
    00388         unsigned len = s.length();
    00389         for (unsigned i = 0; i < len; ++i) {
    00390                 unsigned char c = (unsigned char) s[i];
    00391                 if (c > 0x7f) // prevent from illegal index
    00392                         continue;
    00393                 else if (c == 0x1b) { // go to extension table
    00394                         if (i < len - 1) {
    00395                                 c = (unsigned char) s[++i];
    00396                                 if (c <= 0x7f) // prevent from illegal index
    00397                                         ret += GsmExtensionTable[c];
    00398                         }
    00399                 }
    00400                 else
    00401                         ret += GsmTable[c];
    00402         }
    00403         return ret;
    00404 }
    00405
    00406 } // namespace Barry
    00407