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