lib: show offset and rectype in HexDumpParser
[barry.git] / src / r_message_base.cc
bloba262f696c4dbde181582734de782c236808b15d5
1 ///
2 /// \file r_message_base.cc
3 /// Base class for email-oriented Blackberry database records
4 ///
6 /*
7 Copyright (C) 2005-2010, Net Direct Inc. (http://www.netdirect.ca/)
8 Copyright (C) 2007, Brian Edginton (edge@edginton.net)
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_message_base.h"
24 #include "record-internal.h"
25 #include "protostructs.h"
26 #include "data.h"
27 #include "time.h"
28 #include "endian.h"
29 #include "iconv.h"
30 #include <ostream>
31 #include <iomanip>
32 #include <stdexcept>
34 #define __DEBUG_MODE__
35 #include "debug.h"
37 using namespace std;
38 using namespace Barry::Protocol;
40 namespace Barry {
42 ///////////////////////////////////////////////////////////////////////////////
43 // Message class
46 // Email / message field codes
47 #define MBFC_TO 0x01 // can occur multiple times
48 #define MBFC_CC 0x02 // ditto
49 #define MBFC_BCC 0x03 // ditto
50 #define MBFC_SENDER 0x04
51 #define MBFC_FROM 0x05
52 #define MBFC_REPLY_TO 0x06
53 #define MBFC_SUBJECT 0x0b
54 #define MBFC_BODY 0x0c
55 #define MBFC_REPLY_UNKNOWN 0x12 // This shows up as 0x00 on replies
56 // but we don't do much with it now
57 #define MBFC_ATTACHMENT 0x16
58 #define MBFC_RECORDID 0x4b // Internal Message ID, mimics header RecNumber
59 #define MBFC_END 0xffff
61 #define PRIORITY_MASK 0x003f
62 #define PRIORITY_HIGH 0x0008
63 #define PRIORITY_LOW 0x0002
65 #define SENSITIVE_MASK 0xff80
66 #define SENSITIVE_CONFIDENTIAL 0x0100
67 #define SENSITIVE_PERSONAL 0x0080
68 #define SENSITIVE_PRIVATE 0x0040 // actual pattern is 0x00C0
70 #define MESSAGE_READ 0x0800
71 #define MESSAGE_REPLY 0x0001
72 #define MESSAGE_SAVED 0x0002
73 #define MESSAGE_FORWARD 0x0008
74 #define MESSAGE_TRUNCATED 0x0020
75 #define MESSAGE_SAVED_DELETED 0x0080
77 static FieldLink<MessageBase> MessageBaseFieldLinks[] = {
78 { MBFC_TO, "To", 0, 0, 0, &MessageBase::To, 0, 0, 0, true },
79 { MBFC_CC, "Cc", 0, 0, 0, &MessageBase::Cc, 0, 0, 0, true },
80 { MBFC_BCC, "Bcc", 0, 0, 0, &MessageBase::Bcc, 0, 0, 0, true },
81 { MBFC_SENDER, "Sender", 0, 0, 0, &MessageBase::Sender, 0, 0, 0, true },
82 { MBFC_FROM, "From", 0, 0, 0, &MessageBase::From, 0, 0, 0, true },
83 { MBFC_REPLY_TO, "ReplyTo", 0, 0, 0, &MessageBase::ReplyTo, 0, 0, 0, true },
84 { MBFC_SUBJECT, "Subject", 0, 0, &MessageBase::Subject, 0, 0, 0, 0, true },
85 { MBFC_BODY, "Body", 0, 0, &MessageBase::Body, 0, 0, 0, 0, true },
86 { MBFC_ATTACHMENT, "Attachment", 0, 0, &MessageBase::Attachment, 0, 0, 0, 0, false },
87 { MBFC_END, "End of List", 0, 0, 0, 0, 0, 0, 0, false }
90 MessageBase::MessageBase()
92 Clear();
95 MessageBase::~MessageBase()
99 const unsigned char* MessageBase::ParseField(const unsigned char *begin,
100 const unsigned char *end,
101 const IConverter *ic)
103 const CommonField *field = (const CommonField *) begin;
105 // advance and check size
106 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
107 if( begin > end ) // if begin==end, we are ok
108 return begin;
110 if( !btohs(field->size) ) // if field has no size, something's up
111 return begin;
113 // cycle through the type table
114 for( FieldLink<MessageBase> *b = MessageBaseFieldLinks;
115 b->type != MBFC_END;
116 b++ )
118 if( b->type == field->type ) {
119 if( b->strMember ) {
120 // parse regular string
121 std::string &s = this->*(b->strMember);
122 s = ParseFieldString(field);
123 if( b->iconvNeeded && ic )
124 s = ic->FromBB(s);
125 return begin; // done!
127 else if( b->addrMember ) {
128 // parse email address
129 // get dual name+addr string first
130 const char *fa = (const char*)field->u.addr.addr;
131 std::string dual(fa, btohs(field->size) - sizeof(field->u.addr.unknown));
133 // assign first string, using null terminator
134 // letting std::string add it for us if it
135 // doesn't exist
136 EmailAddress a;
137 a.Name = dual.c_str();
139 // assign second string, using first size
140 // as starting point
141 a.Email = dual.c_str() + a.Name.size() + 1;
143 // if the address is non-empty, add to list
144 if( a.size() ) {
145 // i18n convert if needed
146 if( b->iconvNeeded && ic ) {
147 a.Name = ic->FromBB(a.Name);
148 a.Email = ic->FromBB(a.Email);
151 EmailAddressList &al = this->*(b->addrMember);
152 al.push_back(a);
155 return begin;
160 // handle special cases
161 char swallow;
162 switch( field->type )
164 case MBFC_RECORDID:
165 MessageRecordId = btohl(field->u.uint32);
166 return begin;
168 case MBFC_REPLY_UNKNOWN: // FIXME - not available in SavedMessage?
169 swallow = field->u.raw[0];
170 return begin;
173 // if still not handled, add to the Unknowns list
174 UnknownField uf;
175 uf.type = field->type;
176 uf.data.assign((const char*)field->u.raw, btohs(field->size));
177 Unknowns.push_back(uf);
179 return begin;
182 uint8_t MessageBase::GetRecType() const
184 throw std::logic_error("MessageBase::GetRecType() called, and not supported by the USB protocol. Should never get called.");
187 uint32_t MessageBase::GetUniqueId() const
189 throw std::logic_error("MessageBase::GetUniqueId() called, and not supported by the USB protocol. Should never get called.");
192 void MessageBase::ParseHeader(const Data &data, size_t &offset)
194 const unsigned char *begin = data.GetData();
195 const unsigned char *end = data.GetData() + data.GetSize();
197 begin += offset + MESSAGE_RECORD_HEADER_SIZE;
198 if( begin > end )
199 return;
201 MAKE_RECORD(const Barry::Protocol::MessageRecord, mr, data, offset);
203 // Priority
204 Priority = NormalPriority;
206 uint16_t priority = btohs(mr->priority); // deal with endian swap once
207 if( priority & PRIORITY_MASK ) {
208 if( priority & PRIORITY_HIGH ) {
209 Priority = HighPriority;
211 else if( priority & PRIORITY_LOW ) {
212 Priority = LowPriority;
214 else
215 Priority = UnknownPriority;
218 // Sensitivity
219 Sensitivity = NormalSensitivity;
221 if( priority & SENSITIVE_MASK ) {
222 if(( priority & SENSITIVE_CONFIDENTIAL ) == SENSITIVE_CONFIDENTIAL ) {
223 Sensitivity = Confidential;
225 else if(( priority & SENSITIVE_PRIVATE ) == SENSITIVE_PRIVATE ) {
226 Sensitivity = Private;
228 else if(( priority & SENSITIVE_PERSONAL ) == SENSITIVE_PERSONAL ) {
229 Sensitivity = Personal;
231 else
232 Sensitivity = UnknownSensitivity;
235 // X-rim-org-message-ref-id
236 // NOTE: I'm cheating a bit here and using this as a reply-to
237 // It's actually sent by BB with the actual UID in every message
238 if( mr->inReplyTo )
239 MessageReplyTo = btohl(mr->inReplyTo);
241 // Status Flags
242 uint32_t flags = btohl(mr->flags);
244 // NOTE: A lot of these flags are 'backwards' but this seemed
245 // like the most logical way to interpret them for now
246 if( !( flags & MESSAGE_READ ))
247 MessageRead = true;
249 // NOTE: This is a reply, the original message's flags are not changed
250 // the inReplyTo field is updated with the original messages's UID
251 if(( flags & MESSAGE_REPLY ) == MESSAGE_REPLY )
252 MessageReply = true;
254 // NOTE: This bit is unset on truncation, around 4096 on my 7100g
255 // NOTE: bit 0x400 is set on REALLY huge messages, haven't tested
256 // the exact size yet
257 if( !( flags & MESSAGE_TRUNCATED ))
258 MessageTruncated = true;
260 // NOTE: Saved to 'saved' folder
261 if( !( flags & MESSAGE_SAVED ))
262 MessageSaved = true;
264 // NOTE: Saved to 'saved' folder and then deleted from inbox
265 if( !( flags & MESSAGE_SAVED_DELETED ))
266 MessageSavedDeleted = true;
268 MessageDateSent = Message2Time(mr->dateSent, mr->timeSent);
269 MessageDateReceived = Message2Time(mr->dateReceived, mr->timeReceived);
271 offset += MESSAGE_RECORD_HEADER_SIZE;
274 void MessageBase::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
276 const unsigned char *finish = ParseCommonFields(*this,
277 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
278 offset += finish - (data.GetData() + offset);
281 void MessageBase::BuildHeader(Data &data, size_t &offset) const
283 throw std::logic_error("MessageBase::BuildHeader not yet implemented");
286 void MessageBase::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
288 throw std::logic_error("MessageBase::BuildFields not yet implemented");
291 void MessageBase::Clear()
293 From.clear();
294 To.clear();
295 Cc.clear();
296 Bcc.clear();
297 Sender.clear();
298 ReplyTo.clear();
300 Subject.clear();
301 Body.clear();
302 Attachment.clear();
304 MessageRecordId = 0;
305 MessageReplyTo = 0;
306 MessageDateSent = 0;
307 MessageDateReceived = 0;
308 MessageTruncated = false;
309 MessageRead = false;
310 MessageReply = false;
311 MessageSaved = false;
312 MessageSavedDeleted = false;
314 Priority = NormalPriority;
315 Sensitivity = NormalSensitivity;
317 Unknowns.clear();
320 std::string MessageBase::SimpleFromAddress() const
322 if( From.size() ) {
323 // remove all spaces from the email
324 std::string ret;
325 for( size_t i = 0; i < From[0].Email.size(); i++ )
326 if( From[0].Email[i] != ' ' )
327 ret += From[0].Email[i];
329 return ret;
331 else {
332 return "unknown";
336 // dump message in mbox format
337 void MessageBase::Dump(std::ostream &os) const
339 static const char *Importance[] =
340 { "Low", "Normal", "High", "Unknown Priority" };
341 static const char *SensitivityString[] =
342 { "Normal", "Personal", "Private", "Confidential", "Unknown Sensivity" };
344 os << "From " << SimpleFromAddress() << " " << ctime( &MessageDateReceived );
347 FIXME
348 // savedmessage prints like this:
349 os << "From " << SimpleFromAddress() << " " << ctime( &MessageDateSent );
350 // pinmessage prints like this:
351 os << "From " << SimpleFromAddress() << " " << ctime( &MessageDateSent );
354 os << "X-Record-ID: (" << setw(8) << std::hex << MessageRecordId << ")\n";
356 if( MessageReplyTo )
357 os << "X-rim-org-msg-ref-id: " << std::dec << MessageReplyTo << "\n";
358 if( MessageSaved )
359 os << "X-Message-Status: Saved\n";
360 else if( MessageRead )
361 os << "Message Status: Opened\n";
362 if( Priority != NormalPriority )
363 os << "Importance: " << Importance[Priority] << "\n";
364 if( Sensitivity != NormalSensitivity )
365 os << "Sensitivity: " << SensitivityString[Sensitivity] << "\n";
366 os << "Date: " << ctime(&MessageDateSent);
367 if( From.size() )
368 os << "From: " << From[0] << "\n";
369 if( To.size() )
370 os << "To: " << To << "\n";
371 if( Cc.size() )
372 os << "Cc: " << Cc << "\n";
373 if( Bcc.size() )
374 os << "Bcc: " << Bcc << "\n";
375 if( Sender.size() )
376 os << "Sender: " << Sender << "\n";
377 if( ReplyTo.size())
378 os << "Reply To: " << ReplyTo << "\n";
379 if( Subject.size() )
380 os << "Subject: " << Subject << "\n";
381 os << "\n";
382 for( std::string::const_iterator i = Body.begin();
383 i != Body.end() && *i;
384 i++)
386 if( *i == '\r' )
387 os << '\n';
388 else
389 os << *i;
391 os << "\n";
393 if( Attachment.size() )
394 os << "Attachments: " << Data(Attachment.data(), Attachment.size()) << "\n";
396 os << Unknowns;
397 os << "\n\n";
400 bool MessageBase::operator<(const MessageBase &other) const
402 // just in case either of these are set to '0', use the
403 // one with the max value... this uses the latest date, which
404 // is likely the most accurate
405 time_t date = std::max(MessageDateSent, MessageDateReceived);
406 time_t odate = std::max(other.MessageDateSent, other.MessageDateReceived);
408 if( date != odate )
409 return date < odate;
411 return Subject < other.Subject;
414 } // namespace Barry