2 /// \file r_message_base.cc
3 /// Base class for email-oriented Blackberry database records
7 Copyright (C) 2005-2013, 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.
24 #include "r_message_base.h"
25 #include "record-internal.h"
26 #include "protostructs.h"
34 #include "ios_state.h"
36 #define __DEBUG_MODE__
40 using namespace Barry::Protocol
;
44 ///////////////////////////////////////////////////////////////////////////////
48 // Email / message field codes
49 #define MBFC_TO 0x01 // can occur multiple times
50 #define MBFC_CC 0x02 // ditto
51 #define MBFC_BCC 0x03 // ditto
52 #define MBFC_SENDER 0x04
53 #define MBFC_FROM 0x05
54 #define MBFC_REPLY_TO 0x06
55 #define MBFC_SUBJECT 0x0b
56 #define MBFC_BODY 0x0c
57 #define MBFC_REPLY_UNKNOWN 0x12 // This shows up as 0x00 on replies
58 // but we don't do much with it now
59 #define MBFC_ATTACHMENT 0x16
60 #define MBFC_RECORDID 0x4b // Internal Message ID, mimics header RecNumber
61 #define MBFC_END 0xffff
63 #define PRIORITY_MASK 0x003f
64 #define PRIORITY_HIGH 0x0008
65 #define PRIORITY_LOW 0x0002
67 #define SENSITIVE_MASK 0xff80
68 #define SENSITIVE_CONFIDENTIAL 0x0100
69 #define SENSITIVE_PERSONAL 0x0080
70 #define SENSITIVE_PRIVATE 0x0040 // actual pattern is 0x00C0
72 #define MESSAGE_READ 0x0800
73 #define MESSAGE_REPLY 0x0001
74 #define MESSAGE_SAVED 0x0002
75 #define MESSAGE_FORWARD 0x0008
76 #define MESSAGE_TRUNCATED 0x0020
77 #define MESSAGE_SAVED_DELETED 0x0080
79 static FieldLink
<MessageBase
> MessageBaseFieldLinks
[] = {
80 { MBFC_TO
, N_("To"), 0, 0, 0, &MessageBase::To
, 0, 0, 0, true },
81 { MBFC_CC
, N_("Cc"), 0, 0, 0, &MessageBase::Cc
, 0, 0, 0, true },
82 { MBFC_BCC
, N_("Bcc"), 0, 0, 0, &MessageBase::Bcc
, 0, 0, 0, true },
83 { MBFC_SENDER
, N_("Sender"), 0, 0, 0, &MessageBase::Sender
, 0, 0, 0, true },
84 { MBFC_FROM
, N_("From"), 0, 0, 0, &MessageBase::From
, 0, 0, 0, true },
85 { MBFC_REPLY_TO
, N_("ReplyTo"), 0, 0, 0, &MessageBase::ReplyTo
, 0, 0, 0, true },
86 { MBFC_SUBJECT
, N_("Subject"), 0, 0, &MessageBase::Subject
, 0, 0, 0, 0, true },
87 { MBFC_BODY
, N_("Body"), 0, 0, &MessageBase::Body
, 0, 0, 0, 0, true },
88 { MBFC_ATTACHMENT
,N_("Attachment"), 0, 0, &MessageBase::Attachment
, 0, 0, 0, 0, false },
89 { MBFC_END
, N_("End of List"), 0, 0, 0, 0, 0, 0, 0, false }
92 MessageBase::MessageBase()
97 MessageBase::~MessageBase()
101 const unsigned char* MessageBase::ParseField(const unsigned char *begin
,
102 const unsigned char *end
,
103 const IConverter
*ic
)
105 const CommonField
*field
= (const CommonField
*) begin
;
107 // advance and check size
108 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
109 if( begin
> end
) // if begin==end, we are ok
112 if( !btohs(field
->size
) ) // if field has no size, something's up
115 // cycle through the type table
116 for( FieldLink
<MessageBase
> *b
= MessageBaseFieldLinks
;
120 if( b
->type
== field
->type
) {
122 // parse regular string
123 std::string
&s
= this->*(b
->strMember
);
124 s
= ParseFieldString(field
);
125 if( b
->iconvNeeded
&& ic
)
127 return begin
; // done!
129 else if( b
->addrMember
) {
130 // parse email address
131 // get dual name+addr string first
132 const char *fa
= (const char*)field
->u
.addr
.addr
;
133 std::string
dual(fa
, btohs(field
->size
) - sizeof(field
->u
.addr
.unknown
));
135 // assign first string, using null terminator
136 // letting std::string add it for us if it
139 a
.Name
= dual
.c_str();
141 // assign second string, using first size
143 a
.Email
= dual
.c_str() + a
.Name
.size() + 1;
145 // if the address is non-empty, add to list
147 // i18n convert if needed
148 if( b
->iconvNeeded
&& ic
) {
149 a
.Name
= ic
->FromBB(a
.Name
);
150 a
.Email
= ic
->FromBB(a
.Email
);
153 EmailAddressList
&al
= this->*(b
->addrMember
);
162 // handle special cases
163 switch( field
->type
)
166 MessageRecordId
= btohl(field
->u
.uint32
);
169 case MBFC_REPLY_UNKNOWN
: // FIXME - not available in SavedMessage?
170 //char swallow = field->u.raw[0];
174 // if still not handled, add to the Unknowns list
176 uf
.type
= field
->type
;
177 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
178 Unknowns
.push_back(uf
);
183 void MessageBase::ParseHeader(const Data
&data
, size_t &offset
)
185 const unsigned char *begin
= data
.GetData();
186 const unsigned char *end
= data
.GetData() + data
.GetSize();
188 begin
+= offset
+ MESSAGE_RECORD_HEADER_SIZE
;
192 MAKE_RECORD(const Barry::Protocol::MessageRecord
, mr
, data
, offset
);
195 Priority
= NormalPriority
;
197 uint16_t priority
= btohs(mr
->priority
); // deal with endian swap once
198 if( priority
& PRIORITY_MASK
) {
199 if( priority
& PRIORITY_HIGH
) {
200 Priority
= HighPriority
;
202 else if( priority
& PRIORITY_LOW
) {
203 Priority
= LowPriority
;
206 Priority
= UnknownPriority
;
210 Sensitivity
= NormalSensitivity
;
212 if( priority
& SENSITIVE_MASK
) {
213 if(( priority
& SENSITIVE_CONFIDENTIAL
) == SENSITIVE_CONFIDENTIAL
) {
214 Sensitivity
= Confidential
;
216 else if(( priority
& SENSITIVE_PRIVATE
) == SENSITIVE_PRIVATE
) {
217 Sensitivity
= Private
;
219 else if(( priority
& SENSITIVE_PERSONAL
) == SENSITIVE_PERSONAL
) {
220 Sensitivity
= Personal
;
223 Sensitivity
= UnknownSensitivity
;
226 // X-rim-org-message-ref-id
227 // NOTE: I'm cheating a bit here and using this as a reply-to
228 // It's actually sent by BB with the actual UID in every message
230 MessageReplyTo
= btohl(mr
->inReplyTo
);
233 uint32_t flags
= btohl(mr
->flags
);
235 // NOTE: A lot of these flags are 'backwards' but this seemed
236 // like the most logical way to interpret them for now
237 if( !( flags
& MESSAGE_READ
))
240 // NOTE: This is a reply, the original message's flags are not changed
241 // the inReplyTo field is updated with the original messages's UID
242 if(( flags
& MESSAGE_REPLY
) == MESSAGE_REPLY
)
245 // NOTE: This bit is unset on truncation, around 4096 on my 7100g
246 // NOTE: bit 0x400 is set on REALLY huge messages, haven't tested
247 // the exact size yet
248 if( !( flags
& MESSAGE_TRUNCATED
))
249 MessageTruncated
= true;
251 // NOTE: Saved to 'saved' folder
252 if( !( flags
& MESSAGE_SAVED
))
255 // NOTE: Saved to 'saved' folder and then deleted from inbox
256 if( !( flags
& MESSAGE_SAVED_DELETED
))
257 MessageSavedDeleted
= true;
259 MessageDateSent
.Time
= Message2Time(mr
->dateSent
, mr
->timeSent
);
260 MessageDateReceived
.Time
= Message2Time(mr
->dateReceived
, mr
->timeReceived
);
262 offset
+= MESSAGE_RECORD_HEADER_SIZE
;
265 void MessageBase::ParseFields(const Data
&data
, size_t &offset
, const IConverter
*ic
)
267 const unsigned char *finish
= ParseCommonFields(*this,
268 data
.GetData() + offset
, data
.GetData() + data
.GetSize(), ic
);
269 offset
+= finish
- (data
.GetData() + offset
);
272 void MessageBase::Validate() const
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()
288 // these must be overwritten by any derived classes
292 // clear base class variables
306 MessageDateSent
.clear();
307 MessageDateReceived
.clear();
309 MessageTruncated
= false;
311 MessageReply
= false;
312 MessageSaved
= false;
313 MessageSavedDeleted
= false;
315 Priority
= NormalPriority
;
316 Sensitivity
= NormalSensitivity
;
321 template <class RecordT
>
322 void DoFillHandles(typename FieldHandle
<RecordT
>::ListT
&handles
)
327 #undef CONTAINER_OBJECT_NAME
328 #define CONTAINER_OBJECT_NAME handles
330 #undef RECORD_CLASS_NAME
331 #define RECORD_CLASS_NAME RecordT
333 FHP(RecType
, _("Record Type Code"));
334 FHP(RecordId
, _("Unique Record ID"));
336 FHD(From
, _("From"), MBFC_FROM
, true);
337 FHD(To
, _("To"), MBFC_TO
, true);
338 FHD(Cc
, _("CC"), MBFC_CC
, true);
339 FHD(Bcc
, _("BCC"), MBFC_BCC
, true);
340 FHD(Sender
, _("Sender"), MBFC_SENDER
, true);
341 FHD(ReplyTo
, _("Reply To"), MBFC_REPLY_TO
, true);
342 FHD(Subject
, _("Subject"), MBFC_SUBJECT
, true);
343 FHD(Body
, _("Body"), MBFC_BODY
, true);
344 FHD(Attachment
, _("Attachment"), MBFC_ATTACHMENT
, false);
346 FHD(MessageRecordId
, _("Message Record ID"), MBFC_RECORDID
, false);
347 FHP(MessageReplyTo
, _("Message Reply To"));
348 FHP(MessageDateSent
, _("Date Sent"));
349 FHP(MessageDateReceived
, _("Date Received"));
351 FHP(MessageTruncated
, _("Truncated"));
352 FHP(MessageRead
, _("Read"));
353 FHP(MessageReply
, _("Reply"));
354 FHP(MessageSaved
, _("Saved"));
355 FHP(MessageSavedDeleted
, _("Saved Deleted"));
357 FHET(pt
, PriorityType
, Priority
, _("Priority"));
358 FHE_CONST(pt
, LowPriority
, _("Low"));
359 FHE_CONST(pt
, NormalPriority
, _("Normal"));
360 FHE_CONST(pt
, HighPriority
, _("High"));
361 FHE_CONST(pt
, UnknownPriority
, _("Unknown"));
363 FHET(st
, SensitivityType
, Sensitivity
, _("Sensitivity"));
364 FHE_CONST(st
, NormalSensitivity
, _("Normal"));
365 FHE_CONST(st
, Personal
, _("Personal"));
366 FHE_CONST(st
, Private
, _("Private"));
367 FHE_CONST(st
, Confidential
, _("Confidential"));
368 FHE_CONST(st
, UnknownSensitivity
, _("Unknown"));
370 FHP(Unknowns
, _("Unknown Fields"));
373 std::string
MessageBase::GetDescription() const
375 // FIXME - ponder a better description...
379 std::string
MessageBase::SimpleFromAddress() const
382 // remove all spaces from the email
384 for( size_t i
= 0; i
< From
[0].Email
.size(); i
++ )
385 if( From
[0].Email
[i
] != ' ' )
386 ret
+= From
[0].Email
[i
];
395 // dump message in mbox format
396 void MessageBase::Dump(std::ostream
&os
) const
398 ios_format_state
state(os
);
400 static const char *Importance
[] =
401 { "Low", "Normal", "High", "Unknown Priority" };
402 static const char *SensitivityString
[] =
403 { "Normal", "Personal", "Private", "Confidential", "Unknown Sensivity" };
405 os
<< "From " << SimpleFromAddress() << " " << MessageDateReceived
<< "\n";
409 // savedmessage prints like this:
410 os << "From " << SimpleFromAddress() << " " << ctime( &MessageDateSent );
411 // pinmessage prints like this:
412 os << "From " << SimpleFromAddress() << " " << ctime( &MessageDateSent );
415 os
<< "X-Record-ID: (" << setw(8) << std::hex
<< MessageRecordId
<< ")\n";
418 os
<< "X-rim-org-msg-ref-id: " << std::dec
<< MessageReplyTo
<< "\n";
420 os
<< "X-Message-Status: Saved\n";
421 else if( MessageRead
)
422 os
<< "Message Status: Opened\n";
423 if( Priority
!= NormalPriority
)
424 os
<< "Importance: " << Importance
[Priority
] << "\n";
425 if( Sensitivity
!= NormalSensitivity
)
426 os
<< "Sensitivity: " << SensitivityString
[Sensitivity
] << "\n";
427 os
<< "Date: " << MessageDateSent
<< "\n";
429 os
<< "From: " << From
[0] << "\n";
431 os
<< "To: " << To
<< "\n";
433 os
<< "Cc: " << Cc
<< "\n";
435 os
<< "Bcc: " << Bcc
<< "\n";
437 os
<< "Sender: " << Sender
<< "\n";
439 os
<< "Reply To: " << ReplyTo
<< "\n";
441 os
<< "Subject: " << Subject
<< "\n";
443 os
<< Cr2LfWrapper(Body
);
446 if( Attachment
.size() )
447 os
<< "Attachments: " << Data(Attachment
.data(), Attachment
.size()) << "\n";
453 bool MessageBase::operator<(const MessageBase
&other
) const
455 // just in case either of these are set to '0', use the
456 // one with the max value... this uses the latest date, which
457 // is likely the most accurate
458 time_t date
= std::max(MessageDateSent
.Time
, MessageDateReceived
.Time
);
459 time_t odate
= std::max(other
.MessageDateSent
.Time
, other
.MessageDateReceived
.Time
);
464 return Subject
< other
.Subject
;
473 //////////////////////////////////////////////////////////////////////////////
474 // Generic Field Handle support
476 #include "r_message.h"
477 #include "r_pin_message.h"
478 #include "r_saved_message.h"
483 //////////////////////////////////////////////////////////////////////////////
484 // Message class - statics
486 const FieldHandle
<Message
>::ListT
& Message::GetFieldHandles()
488 static FieldHandle
<Message
>::ListT fhv
;
493 DoFillHandles
<Message
>(fhv
);
497 //////////////////////////////////////////////////////////////////////////////
498 // PINMessage class - statics
500 const FieldHandle
<PINMessage
>::ListT
& PINMessage::GetFieldHandles()
502 static FieldHandle
<PINMessage
>::ListT fhv
;
507 DoFillHandles
<PINMessage
>(fhv
);
511 //////////////////////////////////////////////////////////////////////////////
512 // SavedMessage class - statics
514 const FieldHandle
<SavedMessage
>::ListT
& SavedMessage::GetFieldHandles()
516 static FieldHandle
<SavedMessage
>::ListT fhv
;
521 DoFillHandles
<SavedMessage
>(fhv
);