2 /// \file r_message_base.cc
3 /// Base class for email-oriented Blackberry database records
7 Copyright (C) 2005-2009, 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"
34 #define __DEBUG_MODE__
38 using namespace Barry::Protocol
;
42 ///////////////////////////////////////////////////////////////////////////////
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()
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
110 if( !btohs(field
->size
) ) // if field has no size, something's up
113 // cycle through the type table
114 for( FieldLink
<MessageBase
> *b
= MessageBaseFieldLinks
;
118 if( b
->type
== field
->type
) {
120 // parse regular string
121 std::string
&s
= this->*(b
->strMember
);
122 s
= ParseFieldString(field
);
123 if( b
->iconvNeeded
&& ic
)
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
137 a
.Name
= dual
.c_str();
139 // assign second string, using first size
141 a
.Email
= dual
.c_str() + a
.Name
.size() + 1;
143 // if the address is non-empty, add to list
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
);
160 // handle special cases
162 switch( field
->type
)
165 MessageRecordId
= btohl(field
->u
.uint32
);
168 case MBFC_REPLY_UNKNOWN
: // FIXME - not available in SavedMessage?
169 swallow
= field
->u
.raw
[0];
173 // if still not handled, add to the Unknowns list
175 uf
.type
= field
->type
;
176 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
177 Unknowns
.push_back(uf
);
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 Protocol::CheckSize(data
, offset
+ MESSAGE_RECORD_HEADER_SIZE
);
196 MAKE_RECORD(const Barry::Protocol::MessageRecord
, mr
, data
, offset
);
199 MessagePriority
= NormalPriority
;
201 uint16_t priority
= btohs(mr
->priority
); // deal with endian swap once
202 if( priority
& PRIORITY_MASK
) {
203 if( priority
& PRIORITY_HIGH
) {
204 MessagePriority
= HighPriority
;
206 else if( priority
& PRIORITY_LOW
) {
207 MessagePriority
= LowPriority
;
210 MessagePriority
= UnknownPriority
;
214 MessageSensitivity
= NormalSensitivity
;
216 if( priority
& SENSITIVE_MASK
) {
217 if(( priority
& SENSITIVE_CONFIDENTIAL
) == SENSITIVE_CONFIDENTIAL
) {
218 MessageSensitivity
= Confidential
;
220 else if(( priority
& SENSITIVE_PRIVATE
) == SENSITIVE_PRIVATE
) {
221 MessageSensitivity
= Private
;
223 else if(( priority
& SENSITIVE_PERSONAL
) == SENSITIVE_PERSONAL
) {
224 MessageSensitivity
= Personal
;
227 MessageSensitivity
= UnknownSensitivity
;
230 // X-rim-org-message-ref-id
231 // NOTE: I'm cheating a bit here and using this as a reply-to
232 // It's actually sent by BB with the actual UID in every message
234 MessageReplyTo
= btohl(mr
->inReplyTo
);
237 uint32_t flags
= btohl(mr
->flags
);
239 // NOTE: A lot of these flags are 'backwards' but this seemed
240 // like the most logical way to interpret them for now
241 if( !( flags
& MESSAGE_READ
))
244 // NOTE: This is a reply, the original message's flags are not changed
245 // the inReplyTo field is updated with the original messages's UID
246 if(( flags
& MESSAGE_REPLY
) == MESSAGE_REPLY
)
249 // NOTE: This bit is unset on truncation, around 4096 on my 7100g
250 // NOTE: bit 0x400 is set on REALLY huge messages, haven't tested
251 // the exact size yet
252 if( !( flags
& MESSAGE_TRUNCATED
))
253 MessageTruncated
= true;
255 // NOTE: Saved to 'saved' folder
256 if( !( flags
& MESSAGE_SAVED
))
259 // NOTE: Saved to 'saved' folder and then deleted from inbox
260 if( !( flags
& MESSAGE_SAVED_DELETED
))
261 MessageSavedDeleted
= true;
263 MessageDateSent
= Message2Time(mr
->dateSent
, mr
->timeSent
);
264 MessageDateReceived
= Message2Time(mr
->dateReceived
, mr
->timeReceived
);
266 offset
+= MESSAGE_RECORD_HEADER_SIZE
;
269 void MessageBase::ParseFields(const Data
&data
, size_t &offset
, const IConverter
*ic
)
271 const unsigned char *finish
= ParseCommonFields(*this,
272 data
.GetData() + offset
, data
.GetData() + data
.GetSize(), ic
);
273 offset
+= finish
- (data
.GetData() + offset
);
276 void MessageBase::BuildHeader(Data
&data
, size_t &offset
) const
278 throw std::logic_error("MessageBase::BuildHeader not yet implemented");
281 void MessageBase::BuildFields(Data
&data
, size_t &offset
, const IConverter
*ic
) const
283 throw std::logic_error("MessageBase::BuildFields not yet implemented");
286 void MessageBase::Clear()
302 MessageDateReceived
= 0;
303 MessageTruncated
= false;
305 MessageReply
= false;
306 MessageSaved
= false;
307 MessageSavedDeleted
= false;
309 MessagePriority
= NormalPriority
;
310 MessageSensitivity
= NormalSensitivity
;
315 std::string
MessageBase::SimpleFromAddress() const
318 // remove all spaces from the email
320 for( size_t i
= 0; i
< From
[0].Email
.size(); i
++ )
321 if( From
[0].Email
[i
] != ' ' )
322 ret
+= From
[0].Email
[i
];
331 // dump message in mbox format
332 void MessageBase::Dump(std::ostream
&os
) const
334 static const char *MessageImportance
[] =
335 { "Low", "Normal", "High", "Unknown Priority" };
336 static const char *MessageSensitivityString
[] =
337 { "Normal", "Personal", "Private", "Confidential", "Unknown Sensivity" };
339 os
<< "From " << SimpleFromAddress() << " " << ctime( &MessageDateReceived
);
343 // savedmessage prints like this:
344 os << "From " << SimpleFromAddress() << " " << ctime( &MessageDateSent );
345 // pinmessage prints like this:
346 os << "From " << SimpleFromAddress() << " " << ctime( &MessageDateSent );
349 os
<< "X-Record-ID: (" << setw(8) << std::hex
<< MessageRecordId
<< ")\n";
352 os
<< "X-rim-org-msg-ref-id: " << std::dec
<< MessageReplyTo
<< "\n";
354 os
<< "X-Message-Status: Saved\n";
355 else if( MessageRead
)
356 os
<< "Message Status: Opened\n";
357 if( MessagePriority
!= NormalPriority
)
358 os
<< "Importance: " << MessageImportance
[MessagePriority
] << "\n";
359 if( MessageSensitivity
!= NormalSensitivity
)
360 os
<< "Sensitivity: " << MessageSensitivityString
[MessageSensitivity
] << "\n";
361 os
<< "Date: " << ctime(&MessageDateSent
);
363 os
<< "From: " << From
[0] << "\n";
365 os
<< "To: " << To
<< "\n";
367 os
<< "Cc: " << Cc
<< "\n";
369 os
<< "Bcc: " << Bcc
<< "\n";
371 os
<< "Sender: " << Sender
<< "\n";
373 os
<< "Reply To: " << ReplyTo
<< "\n";
375 os
<< "Subject: " << Subject
<< "\n";
377 for( std::string::const_iterator i
= Body
.begin();
378 i
!= Body
.end() && *i
;
388 if( Attachment
.size() )
389 os
<< "Attachments: " << Data(Attachment
.data(), Attachment
.size()) << "\n";