lib: added DBDataBuilder class
[barry.git] / src / r_sms.cc
blob939ff23db7636ff78b03cdc900ea137fb02e518c
1 ///
2 /// \file r_sms.cc
3 /// Record parsing class for the SMS database.
4 ///
6 /*
7 Copyright (C) 2005-2010, Net Direct Inc. (http://www.netdirect.ca/)
8 Copyright (C) 2009, Ryan Li(ryan@ryanium.com)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "r_sms.h"
24 #include "record-internal.h"
25 #include "protostructs.h"
26 #include "data.h"
27 #include "time.h"
28 #include "debug.h"
29 #include "iconv.h"
30 #include "strnlen.h"
31 #include <ostream>
32 #include <iomanip>
33 #include <string.h>
35 using namespace std;
36 using namespace Barry::Protocol;
38 namespace Barry {
40 ///////////////////////////////////////////////////////////////////////////////
41 // Sms Class
43 // SMS Field Codes
44 #define SMSFC_METADATA 0x01
45 #define SMSFC_ADDRESS 0x02
46 #define SMSFC_BODY 0x04
48 // SMS Field Sizes and Header Sizes
49 #define SMS_ADDRESS_HEADER_SIZE 0x04
51 #define MILLISECONDS_IN_A_SECOND 1000
53 time_t Sms::GetTime() const
55 return (time_t)(Timestamp / MILLISECONDS_IN_A_SECOND);
58 time_t Sms::GetServiceCenterTime() const
60 return (time_t)(ServiceCenterTimestamp / MILLISECONDS_IN_A_SECOND);
63 void Sms::SetTime(const time_t timestamp, const unsigned milliseconds)
65 Timestamp = (uint64_t)timestamp * MILLISECONDS_IN_A_SECOND + milliseconds;
68 void Sms::SetServiceCenterTime(const time_t timestamp, const unsigned milliseconds)
70 ServiceCenterTimestamp = (uint64_t)timestamp * MILLISECONDS_IN_A_SECOND + milliseconds;
73 Sms::Sms()
75 Clear();
78 Sms::~Sms()
82 const unsigned char* Sms::ParseField(const unsigned char *begin,
83 const unsigned char *end,
84 const IConverter *ic)
86 const CommonField *field = (const CommonField *)begin;
88 // advance and check size
89 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
90 if (begin > end) // if begin==end, we are ok
91 return begin;
93 if (!btohs(field->size)) // if field has no size, something's up
94 return begin;
96 switch (field->type)
98 case SMSFC_METADATA:
100 if (btohs(field->size) < SMS_METADATA_SIZE)
101 break; // size not match
103 const SMSMetaData &metadata = field->u.sms_metadata;
104 NewConversation = metadata.flags & SMS_FLG_NEW_CONVERSATION;
105 Saved = metadata.flags & SMS_FLG_SAVED;
106 Deleted = metadata.flags & SMS_FLG_DELETED;
107 Opened = metadata.flags & SMS_FLG_OPENED;
109 IsNew = metadata.new_flag;
111 uint32_t status = btohl(metadata.status);
113 switch (status)
115 case SMS_STA_RECEIVED:
116 MessageStatus = Received;
117 break;
118 case SMS_STA_DRAFT:
119 MessageStatus = Draft;
120 break;
121 default:
122 MessageStatus = Sent; // consider all others as sent
125 ErrorId = btohl(metadata.error_id);
127 Timestamp = btohll(metadata.timestamp);
128 ServiceCenterTimestamp = btohll(metadata.service_center_timestamp);
130 switch (metadata.dcs)
132 case SMS_DCS_7BIT:
133 DataCodingScheme = SevenBit;
134 break;
135 case SMS_DCS_8BIT:
136 DataCodingScheme = EightBit;
137 break;
138 case SMS_DCS_UCS2:
139 DataCodingScheme = UCS2;
140 break;
141 default:
142 DataCodingScheme = SevenBit; // consider all unknowns as 7bit
145 return begin;
148 case SMSFC_ADDRESS:
150 uint16_t length = btohs(field->size);
151 if (length < SMS_ADDRESS_HEADER_SIZE + 1) // trailing '\0'
152 break; // too short
154 length -= SMS_ADDRESS_HEADER_SIZE;
155 const char *address = (const char *)field->u.raw + SMS_ADDRESS_HEADER_SIZE;
156 Addresses.push_back(std::string(address, strnlen(address, length)));
157 return begin;
160 case SMSFC_BODY:
163 // Some SMS bodies contain a null terminator
164 // in the middle, and it is unknown at the moment
165 // why this is. For regular 8bit char strings,
166 // we just strip out the nulls. For UCS2
167 // 16bit char strings, we strip out the
168 // 16bit nulls.
170 // Any further information on why these null
171 // terminators appear is welcome.
173 const char *str = (const char *)field->u.raw;
174 uint16_t maxlen = btohs(field->size);
175 if (DataCodingScheme != UCS2) {
176 for (uint16_t i = 0; i < maxlen; ++i) {
177 if (str[i]) // if not null, push it
178 Body += str[i];
181 else {
182 for (uint16_t i = 0; maxlen && i < (maxlen-1); i += 2) {
183 if (str[i] || str[i + 1]) // if not null, push it
184 Body += std::string(str + i, 2);
187 if (ic) {
188 if (DataCodingScheme == SevenBit) {
189 // SevenBit -> UTF-8 -> ic's tocode
190 IConvHandle utf8("UTF-8", *ic);
191 Body = ic->Convert(utf8, ConvertGsmToUtf8(Body)); // convert the Body string from GSM 03.38 defined to UTF-8
193 else if (DataCodingScheme == EightBit)
194 Body = ic->FromBB(Body);
195 else {
196 IConvHandle ucs2("UCS-2BE", *ic);
197 Body = ic->Convert(ucs2, Body);
200 return begin;
204 // if still not handled, add to the Unknowns list
205 UnknownField uf;
206 uf.type = field->type;
207 uf.data.assign((const char*)field->u.raw, btohs(field->size));
208 Unknowns.push_back(uf);
210 // return new pointer for next field
211 return begin;
214 void Sms::ParseHeader(const Data &data, size_t &offset)
216 // no header in SMS records
219 void Sms::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
221 const unsigned char *finish = ParseCommonFields(*this,
222 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
223 offset += finish - (data.GetData() + offset);
226 void Sms::Clear()
228 MessageStatus = Unknown;
229 DeliveryStatus = NoReport;
230 DataCodingScheme = SevenBit;
232 IsNew = NewConversation = Saved = Deleted = Opened = false;
234 Timestamp = ServiceCenterTimestamp = 0;
235 ErrorId = 0;
237 Addresses.clear();
238 Body.clear();
240 Unknowns.clear();
243 void Sms::Dump(std::ostream &os) const
246 os << "SMS record: 0x" << setbase(16) << RecordId
247 << " (" << (unsigned int)RecType << ")\n";
248 time_t t = GetTime();
249 os << "\tTimestamp: " << ctime(&t);
251 if (MessageStatus == Received) {
252 t = GetServiceCenterTime();
253 os << "\tService Center Timestamp: " << ctime(&t);
256 if (ErrorId)
257 os << "\tSend Error: 0x" << setbase(16) << ErrorId << "\n";
259 switch (MessageStatus)
261 case Received:
262 os << "\tReceived From:\n";
263 break;
264 case Sent:
265 os << "\tSent to:\n";
266 break;
267 case Draft:
268 os << "\tDraft for:\n";
269 break;
270 case Unknown:
271 os << "\tUnknown status for:\n";
272 break;
275 os << "\t";
276 for (std::vector<std::string>::const_iterator i = Addresses.begin(); i < Addresses.end(); ++i) {
277 if (i != Addresses.begin())
278 os << ", ";
279 os << *i;
281 os << "\n";
283 if (IsNew || Opened || Saved || Deleted || NewConversation) {
284 os << "\t";
285 if (IsNew)
286 os << "New ";
287 if (Opened)
288 os << "Opened ";
289 if (Saved)
290 os << "Saved ";
291 if (Deleted)
292 os << "Deleted ";
293 os << "Message" << (NewConversation ? " that starts a new conversation" : "") << "\n";
295 os << "\tContent: " << Body << "\n";
296 os << "\n";
300 // This function helps to convert GSM 03.38 defined 7-bit
301 // SMS to UTF-8.
302 // Detailed information can be found in:
303 // ftp://ftp.3gpp.org/Specs/html-info/0338.htm (Official)
304 // http://en.wikipedia.org/wiki/SMS#GSM
307 std::string Sms::ConvertGsmToUtf8(const std::string &s)
310 // This array stores the GSM 03.38 defined encoding's
311 // corresponding UTF-8 values.
312 // For example: GsmTable[0] = "\x40", which refers to
313 // a "@" in UTF-8 encoding.
314 // The 0x1b item, leads to the extension table, using
315 // the char right next to it as the index.
316 // According to the official specification, when not
317 // able to handle it, it should be treated simply as
318 // a space, which is denoted in UTF-8 as "\x20".
320 static const std::string GsmTable[0x80] = {
321 // 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
322 "\x40", "\xc2\xa3", "\x24", "\xc2\xa5", "\xc3\xa8", "\xc3\xa9", "\xc3\xb9", "\xc3\xac", // 0x00
323 "\xc3\xb2", "\xc3\x87", "\x0a", "\xc3\x98", "\xc3\xb8", "\x0d", "\xc3\x85", "\xc3\xa5", // 0x08
324 "\xce\x94", "\x5f", "\xce\xa6", "\xce\x93", "\xce\x9b", "\xce\xa9", "\xce\xa0", "\xce\xa8", // 0x10
325 "\xce\xa3", "\xce\x98", "\xce\x9e", "\x20", "\xc3\x86", "\xc3\xa6", "\xc3\x9f", "\xc3\x89", // 0x18
326 "\x20", "\x21", "\x22", "\x23", "\xc2\xa4", "\x25", "\x26", "\x27", // 0x20
327 "\x28", "\x29", "\x2a", "\x2b", "\x2c", "\x2d", "\x2e", "\x2f", // 0x28
328 "\x30", "\x31", "\x32", "\x33", "\x34", "\x35", "\x36", "\x37", // 0x30
329 "\x38", "\x39", "\x3a", "\x3b", "\x3c", "\x3d", "\x3e", "\x3f", // 0x38
330 "\xc2\xa1", "\x41", "\x42", "\x43", "\x44", "\x45", "\x46", "\x47", // 0x40
331 "\x48", "\x49", "\x4a", "\x4b", "\x4c", "\x4d", "\x4e", "\x4f", // 0x48
332 "\x50", "\x51", "\x52", "\x53", "\x54", "\x55", "\x56", "\x57", // 0x50
333 "\x58", "\x59", "\x5a", "\xc3\x84", "\xc3\x96", "\xc3\x91", "\xc3\x9c", "\xc2\xa7", // 0x58
334 "\xc2\xbf", "\x61", "\x62", "\x63", "\x64", "\x65", "\x66", "\x67", // 0x60
335 "\x68", "\x69", "\x6a", "\x6b", "\x6c", "\x6d", "\x6e", "\x6f", // 0x68
336 "\x70", "\x71", "\x72", "\x73", "\x74", "\x75", "\x76", "\x77", // 0x70
337 "\x78", "\x79", "\x7a", "\xc3\xa4", "\xc3\xb6", "\xc3\xb1", "\xc3\xbc", "\xc3\xa0" // 0x78
340 // This sparse array stores the GSM 03.38 defined
341 // encoding extension table.
342 // The \x1b item is also preserved, for possibly
343 // another extension table.
345 static const std::string GsmExtensionTable[0x80] = {
346 // 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
347 "", "", "", "", "", "", "", "", // 0x00
348 "", "", "\x0c", "", "", "", "", "", // 0x08
349 "", "", "", "", "\x5e", "", "", "", // 0x10
350 "", "", "", " ", "", "", "", "", // 0x18
351 "", "", "", "", "", "", "", "", // 0x20
352 "\x7b", "\x7d", "", "", "", "", "", "\x5c", // 0x28
353 "", "", "", "", "", "", "", "", // 0x30
354 "", "", "", "", "\x5b", "\x7e", "\x5d", "", // 0x38
355 "\x7c", "", "", "", "", "", "", "", // 0x40
356 "", "", "", "", "", "", "", "", // 0x48
357 "", "", "", "", "", "", "", "", // 0x50
358 "", "", "", "", "", "", "", "", // 0x58
359 "", "", "", "", "", "\xe2\x82\xac", "", "", // 0x60
360 "", "", "", "", "", "", "", "", // 0x68
361 "", "", "", "", "", "", "", "", // 0x70
362 "", "", "", "", "", "", "", "" // 0x78
364 std::string ret;
365 unsigned len = s.length();
366 for (unsigned i = 0; i < len; ++i) {
367 unsigned char c = (unsigned char) s[i];
368 if (c > 0x7f) // prevent from illegal index
369 continue;
370 else if (c == 0x1b) { // go to extension table
371 if (i < len - 1) {
372 c = (unsigned char) s[++i];
373 if (c <= 0x7f) // prevent from illegal index
374 ret += GsmExtensionTable[c];
377 else
378 ret += GsmTable[c];
380 return ret;
383 } // namespace Barry