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::Clear()
00227 {
00228         RecType = GetDefaultRecType();
00229         RecordId = 0;
00230 
00231         MessageStatus = Unknown;
00232         DeliveryStatus = NoReport;
00233 
00234         IsNew = NewConversation = Saved = Deleted = Opened = false;
00235 
00236         Timestamp = ServiceCenterTimestamp = 0;
00237 
00238         DataCodingScheme = SevenBit;
00239 
00240         ErrorId = 0;
00241 
00242         Addresses.clear();
00243         Body.clear();
00244 
00245         Unknowns.clear();
00246 }
00247 
00248 void Sms::Dump(std::ostream &os) const
00249 {
00250 
00251         os << "SMS record: 0x" << setbase(16) << RecordId
00252                 << " (" << (unsigned int)RecType << ")\n";
00253         time_t t = GetTime();
00254         os << "\tTimestamp: " << ctime(&t);
00255 
00256         if (MessageStatus == Received) {
00257                 t = GetServiceCenterTime();
00258                 os << "\tService Center Timestamp: " << ctime(&t);
00259         }
00260 
00261         if (ErrorId)
00262                 os << "\tSend Error: 0x" << setbase(16) << ErrorId << "\n";
00263 
00264         switch (MessageStatus)
00265         {
00266                 case Received:
00267                         os << "\tReceived From:\n";
00268                         break;
00269                 case Sent:
00270                         os << "\tSent to:\n";
00271                         break;
00272                 case Draft:
00273                         os << "\tDraft for:\n";
00274                         break;
00275                 case Unknown:
00276                         os << "\tUnknown status for:\n";
00277                         break;
00278         }
00279 
00280         os << "\t";
00281         for (std::vector<std::string>::const_iterator i = Addresses.begin(); i < Addresses.end(); ++i) {
00282                 if (i != Addresses.begin())
00283                         os << ", ";
00284                 os << *i;
00285         }
00286         os << "\n";
00287 
00288         if (IsNew || Opened || Saved || Deleted || NewConversation) {
00289                 os << "\t";
00290                 if (IsNew)
00291                         os << "New ";
00292                 if (Opened)
00293                         os << "Opened ";
00294                 if (Saved)
00295                         os << "Saved ";
00296                 if (Deleted)
00297                         os << "Deleted ";
00298                 os << "Message" << (NewConversation ? " that starts a new conversation" : "") << "\n";
00299         }
00300         os << "\tContent: " << Body << "\n";
00301         os << "\n";
00302 }
00303 
00304 //
00305 // This function helps to convert GSM 03.38 defined 7-bit
00306 // SMS to UTF-8.
00307 // Detailed information can be found in:
00308 // ftp://ftp.3gpp.org/Specs/html-info/0338.htm (Official)
00309 // http://en.wikipedia.org/wiki/SMS#GSM
00310 //
00311 
00312 std::string Sms::ConvertGsmToUtf8(const std::string &s)
00313 {
00314         //
00315         // This array stores the GSM 03.38 defined encoding's
00316         // corresponding UTF-8 values.
00317         // For example: GsmTable[0] = "\x40", which refers to
00318         // a "@" in UTF-8 encoding.
00319         // The 0x1b item, leads to the extension table, using
00320         // the char right next to it as the index.
00321         // According to the official specification, when not
00322         // able to handle it, it should be treated simply as
00323         // a space, which is denoted in UTF-8 as "\x20".
00324         //
00325         static const std::string GsmTable[0x80] = {
00326         //  0x0,        0x1,        0x2,        0x3,        0x4,        0x5,        0x6,        0x7
00327                 "\x40",     "\xc2\xa3", "\x24",     "\xc2\xa5", "\xc3\xa8", "\xc3\xa9", "\xc3\xb9", "\xc3\xac", // 0x00
00328                 "\xc3\xb2", "\xc3\x87", "\x0a",     "\xc3\x98", "\xc3\xb8", "\x0d",     "\xc3\x85", "\xc3\xa5", // 0x08
00329                 "\xce\x94", "\x5f",     "\xce\xa6", "\xce\x93", "\xce\x9b", "\xce\xa9", "\xce\xa0", "\xce\xa8", // 0x10
00330                 "\xce\xa3", "\xce\x98", "\xce\x9e", "\x20",     "\xc3\x86", "\xc3\xa6", "\xc3\x9f", "\xc3\x89", // 0x18
00331                 "\x20",     "\x21",     "\x22",     "\x23",     "\xc2\xa4", "\x25",     "\x26",     "\x27",     // 0x20
00332                 "\x28",     "\x29",     "\x2a",     "\x2b",     "\x2c",     "\x2d",     "\x2e",     "\x2f",     // 0x28
00333                 "\x30",     "\x31",     "\x32",     "\x33",     "\x34",     "\x35",     "\x36",     "\x37",     // 0x30
00334                 "\x38",     "\x39",     "\x3a",     "\x3b",     "\x3c",     "\x3d",     "\x3e",     "\x3f",     // 0x38
00335                 "\xc2\xa1", "\x41",     "\x42",     "\x43",     "\x44",     "\x45",     "\x46",     "\x47",     // 0x40
00336                 "\x48",     "\x49",     "\x4a",     "\x4b",     "\x4c",     "\x4d",     "\x4e",     "\x4f",     // 0x48
00337                 "\x50",     "\x51",     "\x52",     "\x53",     "\x54",     "\x55",     "\x56",     "\x57",     // 0x50
00338                 "\x58",     "\x59",     "\x5a",     "\xc3\x84", "\xc3\x96", "\xc3\x91", "\xc3\x9c", "\xc2\xa7", // 0x58
00339                 "\xc2\xbf", "\x61",     "\x62",     "\x63",     "\x64",     "\x65",     "\x66",     "\x67",     // 0x60
00340                 "\x68",     "\x69",     "\x6a",     "\x6b",     "\x6c",     "\x6d",     "\x6e",     "\x6f",     // 0x68
00341                 "\x70",     "\x71",     "\x72",     "\x73",     "\x74",     "\x75",     "\x76",     "\x77",     // 0x70
00342                 "\x78",     "\x79",     "\x7a",     "\xc3\xa4", "\xc3\xb6", "\xc3\xb1", "\xc3\xbc", "\xc3\xa0"  // 0x78
00343         };
00344         //
00345         // This sparse array stores the GSM 03.38 defined
00346         // encoding extension table.
00347         // The \x1b item is also preserved, for possibly
00348         // another extension table.
00349         //
00350         static const std::string GsmExtensionTable[0x80] = {
00351         //  0x0,            0x1,            0x2,            0x3,            0x4,            0x5,            0x6,            0x7
00352                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x00
00353                 "",             "",             "\x0c",         "",             "",             "",             "",             "",     // 0x08
00354                 "",             "",             "",             "",             "\x5e",         "",             "",             "",     // 0x10
00355                 "",             "",             "",             " ",            "",             "",             "",             "",     // 0x18
00356                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x20
00357                 "\x7b",         "\x7d",         "",             "",             "",             "",             "",             "\x5c", // 0x28
00358                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x30
00359                 "",             "",             "",             "",             "\x5b",         "\x7e",         "\x5d",         "",     // 0x38
00360                 "\x7c",         "",             "",             "",             "",             "",             "",             "",     // 0x40
00361                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x48
00362                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x50
00363                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x58
00364                 "",             "",             "",             "",             "",             "\xe2\x82\xac", "",             "",     // 0x60
00365                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x68
00366                 "",             "",             "",             "",             "",             "",             "",             "",     // 0x70
00367                 "",             "",             "",             "",             "",             "",             "",             ""      // 0x78
00368         };
00369         std::string ret;
00370         unsigned len = s.length();
00371         for (unsigned i = 0; i < len; ++i) {
00372                 unsigned char c = (unsigned char) s[i];
00373                 if (c > 0x7f) // prevent from illegal index
00374                         continue;
00375                 else if (c == 0x1b) { // go to extension table
00376                         if (i < len - 1) {
00377                                 c = (unsigned char) s[++i];
00378                                 if (c <= 0x7f) // prevent from illegal index
00379                                         ret += GsmExtensionTable[c];
00380                         }
00381                 }
00382                 else
00383                         ret += GsmTable[c];
00384         }
00385         return ret;
00386 }
00387 
00388 } // namespace Barry
00389