maint: added proper sample of Barry yum repo file for Fedora
[barry.git] / src / r_message_base.cc
blobf8608b6bc40a2c4ed3d220db35abab1a8aa7f4ac
1 ///
2 /// \file r_message_base.cc
3 /// Base class for email-oriented Blackberry database records
4 ///
6 /*
7 Copyright (C) 2005-2012, 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>
33 #include "ios_state.h"
35 #define __DEBUG_MODE__
36 #include "debug.h"
38 using namespace std;
39 using namespace Barry::Protocol;
41 namespace Barry {
43 ///////////////////////////////////////////////////////////////////////////////
44 // Message class
47 // Email / message field codes
48 #define MBFC_TO 0x01 // can occur multiple times
49 #define MBFC_CC 0x02 // ditto
50 #define MBFC_BCC 0x03 // ditto
51 #define MBFC_SENDER 0x04
52 #define MBFC_FROM 0x05
53 #define MBFC_REPLY_TO 0x06
54 #define MBFC_SUBJECT 0x0b
55 #define MBFC_BODY 0x0c
56 #define MBFC_REPLY_UNKNOWN 0x12 // This shows up as 0x00 on replies
57 // but we don't do much with it now
58 #define MBFC_ATTACHMENT 0x16
59 #define MBFC_RECORDID 0x4b // Internal Message ID, mimics header RecNumber
60 #define MBFC_END 0xffff
62 #define PRIORITY_MASK 0x003f
63 #define PRIORITY_HIGH 0x0008
64 #define PRIORITY_LOW 0x0002
66 #define SENSITIVE_MASK 0xff80
67 #define SENSITIVE_CONFIDENTIAL 0x0100
68 #define SENSITIVE_PERSONAL 0x0080
69 #define SENSITIVE_PRIVATE 0x0040 // actual pattern is 0x00C0
71 #define MESSAGE_READ 0x0800
72 #define MESSAGE_REPLY 0x0001
73 #define MESSAGE_SAVED 0x0002
74 #define MESSAGE_FORWARD 0x0008
75 #define MESSAGE_TRUNCATED 0x0020
76 #define MESSAGE_SAVED_DELETED 0x0080
78 static FieldLink<MessageBase> MessageBaseFieldLinks[] = {
79 { MBFC_TO, "To", 0, 0, 0, &MessageBase::To, 0, 0, 0, true },
80 { MBFC_CC, "Cc", 0, 0, 0, &MessageBase::Cc, 0, 0, 0, true },
81 { MBFC_BCC, "Bcc", 0, 0, 0, &MessageBase::Bcc, 0, 0, 0, true },
82 { MBFC_SENDER, "Sender", 0, 0, 0, &MessageBase::Sender, 0, 0, 0, true },
83 { MBFC_FROM, "From", 0, 0, 0, &MessageBase::From, 0, 0, 0, true },
84 { MBFC_REPLY_TO, "ReplyTo", 0, 0, 0, &MessageBase::ReplyTo, 0, 0, 0, true },
85 { MBFC_SUBJECT, "Subject", 0, 0, &MessageBase::Subject, 0, 0, 0, 0, true },
86 { MBFC_BODY, "Body", 0, 0, &MessageBase::Body, 0, 0, 0, 0, true },
87 { MBFC_ATTACHMENT, "Attachment", 0, 0, &MessageBase::Attachment, 0, 0, 0, 0, false },
88 { MBFC_END, "End of List", 0, 0, 0, 0, 0, 0, 0, false }
91 MessageBase::MessageBase()
93 Clear();
96 MessageBase::~MessageBase()
100 const unsigned char* MessageBase::ParseField(const unsigned char *begin,
101 const unsigned char *end,
102 const IConverter *ic)
104 const CommonField *field = (const CommonField *) begin;
106 // advance and check size
107 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
108 if( begin > end ) // if begin==end, we are ok
109 return begin;
111 if( !btohs(field->size) ) // if field has no size, something's up
112 return begin;
114 // cycle through the type table
115 for( FieldLink<MessageBase> *b = MessageBaseFieldLinks;
116 b->type != MBFC_END;
117 b++ )
119 if( b->type == field->type ) {
120 if( b->strMember ) {
121 // parse regular string
122 std::string &s = this->*(b->strMember);
123 s = ParseFieldString(field);
124 if( b->iconvNeeded && ic )
125 s = ic->FromBB(s);
126 return begin; // done!
128 else if( b->addrMember ) {
129 // parse email address
130 // get dual name+addr string first
131 const char *fa = (const char*)field->u.addr.addr;
132 std::string dual(fa, btohs(field->size) - sizeof(field->u.addr.unknown));
134 // assign first string, using null terminator
135 // letting std::string add it for us if it
136 // doesn't exist
137 EmailAddress a;
138 a.Name = dual.c_str();
140 // assign second string, using first size
141 // as starting point
142 a.Email = dual.c_str() + a.Name.size() + 1;
144 // if the address is non-empty, add to list
145 if( a.size() ) {
146 // i18n convert if needed
147 if( b->iconvNeeded && ic ) {
148 a.Name = ic->FromBB(a.Name);
149 a.Email = ic->FromBB(a.Email);
152 EmailAddressList &al = this->*(b->addrMember);
153 al.push_back(a);
156 return begin;
161 // handle special cases
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 //char 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 void MessageBase::ParseHeader(const Data &data, size_t &offset)
184 const unsigned char *begin = data.GetData();
185 const unsigned char *end = data.GetData() + data.GetSize();
187 begin += offset + MESSAGE_RECORD_HEADER_SIZE;
188 if( begin > end )
189 return;
191 MAKE_RECORD(const Barry::Protocol::MessageRecord, mr, data, offset);
193 // Priority
194 Priority = NormalPriority;
196 uint16_t priority = btohs(mr->priority); // deal with endian swap once
197 if( priority & PRIORITY_MASK ) {
198 if( priority & PRIORITY_HIGH ) {
199 Priority = HighPriority;
201 else if( priority & PRIORITY_LOW ) {
202 Priority = LowPriority;
204 else
205 Priority = UnknownPriority;
208 // Sensitivity
209 Sensitivity = NormalSensitivity;
211 if( priority & SENSITIVE_MASK ) {
212 if(( priority & SENSITIVE_CONFIDENTIAL ) == SENSITIVE_CONFIDENTIAL ) {
213 Sensitivity = Confidential;
215 else if(( priority & SENSITIVE_PRIVATE ) == SENSITIVE_PRIVATE ) {
216 Sensitivity = Private;
218 else if(( priority & SENSITIVE_PERSONAL ) == SENSITIVE_PERSONAL ) {
219 Sensitivity = Personal;
221 else
222 Sensitivity = UnknownSensitivity;
225 // X-rim-org-message-ref-id
226 // NOTE: I'm cheating a bit here and using this as a reply-to
227 // It's actually sent by BB with the actual UID in every message
228 if( mr->inReplyTo )
229 MessageReplyTo = btohl(mr->inReplyTo);
231 // Status Flags
232 uint32_t flags = btohl(mr->flags);
234 // NOTE: A lot of these flags are 'backwards' but this seemed
235 // like the most logical way to interpret them for now
236 if( !( flags & MESSAGE_READ ))
237 MessageRead = true;
239 // NOTE: This is a reply, the original message's flags are not changed
240 // the inReplyTo field is updated with the original messages's UID
241 if(( flags & MESSAGE_REPLY ) == MESSAGE_REPLY )
242 MessageReply = true;
244 // NOTE: This bit is unset on truncation, around 4096 on my 7100g
245 // NOTE: bit 0x400 is set on REALLY huge messages, haven't tested
246 // the exact size yet
247 if( !( flags & MESSAGE_TRUNCATED ))
248 MessageTruncated = true;
250 // NOTE: Saved to 'saved' folder
251 if( !( flags & MESSAGE_SAVED ))
252 MessageSaved = true;
254 // NOTE: Saved to 'saved' folder and then deleted from inbox
255 if( !( flags & MESSAGE_SAVED_DELETED ))
256 MessageSavedDeleted = true;
258 MessageDateSent.Time = Message2Time(mr->dateSent, mr->timeSent);
259 MessageDateReceived.Time = Message2Time(mr->dateReceived, mr->timeReceived);
261 offset += MESSAGE_RECORD_HEADER_SIZE;
264 void MessageBase::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
266 const unsigned char *finish = ParseCommonFields(*this,
267 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
268 offset += finish - (data.GetData() + offset);
271 void MessageBase::Validate() const
275 void MessageBase::BuildHeader(Data &data, size_t &offset) const
277 throw std::logic_error("MessageBase::BuildHeader not yet implemented");
280 void MessageBase::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
282 throw std::logic_error("MessageBase::BuildFields not yet implemented");
285 void MessageBase::Clear()
287 // these must be overwritten by any derived classes
288 RecType = 0;
289 RecordId = 0;
291 // clear base class variables
292 From.clear();
293 To.clear();
294 Cc.clear();
295 Bcc.clear();
296 Sender.clear();
297 ReplyTo.clear();
299 Subject.clear();
300 Body.clear();
301 Attachment.clear();
303 MessageRecordId = 0;
304 MessageReplyTo = 0;
305 MessageDateSent.clear();
306 MessageDateReceived.clear();
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 template <class RecordT>
321 void DoFillHandles(typename FieldHandle<RecordT>::ListT &handles)
323 // start fresh
324 handles.clear();
326 #undef CONTAINER_OBJECT_NAME
327 #define CONTAINER_OBJECT_NAME handles
329 #undef RECORD_CLASS_NAME
330 #define RECORD_CLASS_NAME RecordT
332 FHP(RecType, "Record Type Code");
333 FHP(RecordId, "Unique Record ID");
335 FHD(From, "From", MBFC_FROM, true);
336 FHD(To, "To", MBFC_TO, true);
337 FHD(Cc, "CC", MBFC_CC, true);
338 FHD(Bcc, "BCC", MBFC_BCC, true);
339 FHD(Sender, "Sender", MBFC_SENDER, true);
340 FHD(ReplyTo, "Reply To", MBFC_REPLY_TO, true);
341 FHD(Subject, "Subject", MBFC_SUBJECT, true);
342 FHD(Body, "Body", MBFC_BODY, true);
343 FHD(Attachment, "Attachment", MBFC_ATTACHMENT, false);
345 FHD(MessageRecordId, "Message Record ID", MBFC_RECORDID, false);
346 FHP(MessageReplyTo, "Message Reply To");
347 FHP(MessageDateSent, "Date Sent");
348 FHP(MessageDateReceived, "Date Received");
350 FHP(MessageTruncated, "Truncated");
351 FHP(MessageRead, "Read");
352 FHP(MessageReply, "Reply");
353 FHP(MessageSaved, "Saved");
354 FHP(MessageSavedDeleted, "Saved Deleted");
356 FHET(pt, PriorityType, Priority, "Priority");
357 FHE_CONST(pt, LowPriority, "Low");
358 FHE_CONST(pt, NormalPriority, "Normal");
359 FHE_CONST(pt, HighPriority, "High");
360 FHE_CONST(pt, UnknownPriority, "Unknown");
362 FHET(st, SensitivityType, Sensitivity, "Sensitivity");
363 FHE_CONST(st, NormalSensitivity, "Normal");
364 FHE_CONST(st, Personal, "Personal");
365 FHE_CONST(st, Private, "Private");
366 FHE_CONST(st, Confidential, "Confidential");
367 FHE_CONST(st, UnknownSensitivity, "Unknown");
369 FHP(Unknowns, "Unknown Fields");
372 std::string MessageBase::GetDescription() const
374 // FIXME - ponder a better description...
375 return Subject;
378 std::string MessageBase::SimpleFromAddress() const
380 if( From.size() ) {
381 // remove all spaces from the email
382 std::string ret;
383 for( size_t i = 0; i < From[0].Email.size(); i++ )
384 if( From[0].Email[i] != ' ' )
385 ret += From[0].Email[i];
387 return ret;
389 else {
390 return "unknown";
394 // dump message in mbox format
395 void MessageBase::Dump(std::ostream &os) const
397 ios_format_state state(os);
399 static const char *Importance[] =
400 { "Low", "Normal", "High", "Unknown Priority" };
401 static const char *SensitivityString[] =
402 { "Normal", "Personal", "Private", "Confidential", "Unknown Sensivity" };
404 os << "From " << SimpleFromAddress() << " " << MessageDateReceived << "\n";
407 FIXME
408 // savedmessage prints like this:
409 os << "From " << SimpleFromAddress() << " " << ctime( &MessageDateSent );
410 // pinmessage prints like this:
411 os << "From " << SimpleFromAddress() << " " << ctime( &MessageDateSent );
414 os << "X-Record-ID: (" << setw(8) << std::hex << MessageRecordId << ")\n";
416 if( MessageReplyTo )
417 os << "X-rim-org-msg-ref-id: " << std::dec << MessageReplyTo << "\n";
418 if( MessageSaved )
419 os << "X-Message-Status: Saved\n";
420 else if( MessageRead )
421 os << "Message Status: Opened\n";
422 if( Priority != NormalPriority )
423 os << "Importance: " << Importance[Priority] << "\n";
424 if( Sensitivity != NormalSensitivity )
425 os << "Sensitivity: " << SensitivityString[Sensitivity] << "\n";
426 os << "Date: " << MessageDateSent << "\n";
427 if( From.size() )
428 os << "From: " << From[0] << "\n";
429 if( To.size() )
430 os << "To: " << To << "\n";
431 if( Cc.size() )
432 os << "Cc: " << Cc << "\n";
433 if( Bcc.size() )
434 os << "Bcc: " << Bcc << "\n";
435 if( Sender.size() )
436 os << "Sender: " << Sender << "\n";
437 if( ReplyTo.size())
438 os << "Reply To: " << ReplyTo << "\n";
439 if( Subject.size() )
440 os << "Subject: " << Subject << "\n";
441 os << "\n";
442 os << Cr2LfWrapper(Body);
443 os << "\n";
445 if( Attachment.size() )
446 os << "Attachments: " << Data(Attachment.data(), Attachment.size()) << "\n";
448 os << Unknowns;
449 os << "\n\n";
452 bool MessageBase::operator<(const MessageBase &other) const
454 // just in case either of these are set to '0', use the
455 // one with the max value... this uses the latest date, which
456 // is likely the most accurate
457 time_t date = std::max(MessageDateSent.Time, MessageDateReceived.Time);
458 time_t odate = std::max(other.MessageDateSent.Time, other.MessageDateReceived.Time);
460 if( date != odate )
461 return date < odate;
463 return Subject < other.Subject;
466 } // namespace Barry
472 //////////////////////////////////////////////////////////////////////////////
473 // Generic Field Handle support
475 #include "r_message.h"
476 #include "r_pin_message.h"
477 #include "r_saved_message.h"
480 namespace Barry {
482 //////////////////////////////////////////////////////////////////////////////
483 // Message class - statics
485 const FieldHandle<Message>::ListT& Message::GetFieldHandles()
487 static FieldHandle<Message>::ListT fhv;
489 if( fhv.size() )
490 return fhv;
492 DoFillHandles<Message>(fhv);
493 return fhv;
496 //////////////////////////////////////////////////////////////////////////////
497 // PINMessage class - statics
499 const FieldHandle<PINMessage>::ListT& PINMessage::GetFieldHandles()
501 static FieldHandle<PINMessage>::ListT fhv;
503 if( fhv.size() )
504 return fhv;
506 DoFillHandles<PINMessage>(fhv);
507 return fhv;
510 //////////////////////////////////////////////////////////////////////////////
511 // SavedMessage class - statics
513 const FieldHandle<SavedMessage>::ListT& SavedMessage::GetFieldHandles()
515 static FieldHandle<SavedMessage>::ListT fhv;
517 if( fhv.size() )
518 return fhv;
520 DoFillHandles<SavedMessage>(fhv);
521 return fhv;
524 } // namespace Barry