tzwrapper.cc: fixed use of iterator after erase
[barry.git] / src / r_message_base.cc
blobe8b3b9bb59668bd786a582489d6724112f7ac3f9
1 ///
2 /// \file r_message_base.cc
3 /// Base class for email-oriented Blackberry database records
4 ///
6 /*
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.
23 #include "i18n.h"
24 #include "r_message_base.h"
25 #include "record-internal.h"
26 #include "protostructs.h"
27 #include "data.h"
28 #include "time.h"
29 #include "endian.h"
30 #include "iconv.h"
31 #include <ostream>
32 #include <iomanip>
33 #include <stdexcept>
34 #include "ios_state.h"
36 #define __DEBUG_MODE__
37 #include "debug.h"
39 using namespace std;
40 using namespace Barry::Protocol;
42 namespace Barry {
44 ///////////////////////////////////////////////////////////////////////////////
45 // Message class
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()
94 Clear();
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
110 return begin;
112 if( !btohs(field->size) ) // if field has no size, something's up
113 return begin;
115 // cycle through the type table
116 for( FieldLink<MessageBase> *b = MessageBaseFieldLinks;
117 b->type != MBFC_END;
118 b++ )
120 if( b->type == field->type ) {
121 if( b->strMember ) {
122 // parse regular string
123 std::string &s = this->*(b->strMember);
124 s = ParseFieldString(field);
125 if( b->iconvNeeded && ic )
126 s = ic->FromBB(s);
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
137 // doesn't exist
138 EmailAddress a;
139 a.Name = dual.c_str();
141 // assign second string, using first size
142 // as starting point
143 a.Email = dual.c_str() + a.Name.size() + 1;
145 // if the address is non-empty, add to list
146 if( a.size() ) {
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);
154 al.push_back(a);
157 return begin;
162 // handle special cases
163 switch( field->type )
165 case MBFC_RECORDID:
166 MessageRecordId = btohl(field->u.uint32);
167 return begin;
169 case MBFC_REPLY_UNKNOWN: // FIXME - not available in SavedMessage?
170 //char swallow = field->u.raw[0];
171 return begin;
174 // if still not handled, add to the Unknowns list
175 UnknownField uf;
176 uf.type = field->type;
177 uf.data.assign((const char*)field->u.raw, btohs(field->size));
178 Unknowns.push_back(uf);
180 return begin;
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;
189 if( begin > end )
190 return;
192 MAKE_RECORD(const Barry::Protocol::MessageRecord, mr, data, offset);
194 // Priority
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;
205 else
206 Priority = UnknownPriority;
209 // Sensitivity
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;
222 else
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
229 if( mr->inReplyTo )
230 MessageReplyTo = btohl(mr->inReplyTo);
232 // Status Flags
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 ))
238 MessageRead = true;
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 )
243 MessageReply = true;
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 ))
253 MessageSaved = true;
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
289 RecType = 0;
290 RecordId = 0;
292 // clear base class variables
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.clear();
307 MessageDateReceived.clear();
309 MessageTruncated = false;
310 MessageRead = false;
311 MessageReply = false;
312 MessageSaved = false;
313 MessageSavedDeleted = false;
315 Priority = NormalPriority;
316 Sensitivity = NormalSensitivity;
318 Unknowns.clear();
321 template <class RecordT>
322 void DoFillHandles(typename FieldHandle<RecordT>::ListT &handles)
324 // start fresh
325 handles.clear();
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...
376 return Subject;
379 std::string MessageBase::SimpleFromAddress() const
381 if( From.size() ) {
382 // remove all spaces from the email
383 std::string ret;
384 for( size_t i = 0; i < From[0].Email.size(); i++ )
385 if( From[0].Email[i] != ' ' )
386 ret += From[0].Email[i];
388 return ret;
390 else {
391 return _("unknown");
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";
408 FIXME
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";
417 if( MessageReplyTo )
418 os << "X-rim-org-msg-ref-id: " << std::dec << MessageReplyTo << "\n";
419 if( MessageSaved )
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";
428 if( From.size() )
429 os << "From: " << From[0] << "\n";
430 if( To.size() )
431 os << "To: " << To << "\n";
432 if( Cc.size() )
433 os << "Cc: " << Cc << "\n";
434 if( Bcc.size() )
435 os << "Bcc: " << Bcc << "\n";
436 if( Sender.size() )
437 os << "Sender: " << Sender << "\n";
438 if( ReplyTo.size())
439 os << "Reply To: " << ReplyTo << "\n";
440 if( Subject.size() )
441 os << "Subject: " << Subject << "\n";
442 os << "\n";
443 os << Cr2LfWrapper(Body);
444 os << "\n";
446 if( Attachment.size() )
447 os << "Attachments: " << Data(Attachment.data(), Attachment.size()) << "\n";
449 os << Unknowns;
450 os << "\n\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);
461 if( date != odate )
462 return date < odate;
464 return Subject < other.Subject;
467 } // namespace Barry
473 //////////////////////////////////////////////////////////////////////////////
474 // Generic Field Handle support
476 #include "r_message.h"
477 #include "r_pin_message.h"
478 #include "r_saved_message.h"
481 namespace Barry {
483 //////////////////////////////////////////////////////////////////////////////
484 // Message class - statics
486 const FieldHandle<Message>::ListT& Message::GetFieldHandles()
488 static FieldHandle<Message>::ListT fhv;
490 if( fhv.size() )
491 return fhv;
493 DoFillHandles<Message>(fhv);
494 return fhv;
497 //////////////////////////////////////////////////////////////////////////////
498 // PINMessage class - statics
500 const FieldHandle<PINMessage>::ListT& PINMessage::GetFieldHandles()
502 static FieldHandle<PINMessage>::ListT fhv;
504 if( fhv.size() )
505 return fhv;
507 DoFillHandles<PINMessage>(fhv);
508 return fhv;
511 //////////////////////////////////////////////////////////////////////////////
512 // SavedMessage class - statics
514 const FieldHandle<SavedMessage>::ListT& SavedMessage::GetFieldHandles()
516 static FieldHandle<SavedMessage>::ListT fhv;
518 if( fhv.size() )
519 return fhv;
521 DoFillHandles<SavedMessage>(fhv);
522 return fhv;
525 } // namespace Barry