r_message_base.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       r_message_base.cc
00003 ///             Base class for email-oriented Blackberry database records
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008     Copyright (C) 2007, Brian Edginton (edge@edginton.net)
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00018 
00019     See the GNU General Public License in the COPYING file at the
00020     root directory of this project for more details.
00021 */
00022 
00023 #include "r_message_base.h"
00024 #include "record-internal.h"
00025 #include "protostructs.h"
00026 #include "data.h"
00027 #include "time.h"
00028 #include "endian.h"
00029 #include "iconv.h"
00030 #include <ostream>
00031 #include <iomanip>
00032 #include <stdexcept>
00033 #include "ios_state.h"
00034 
00035 #define __DEBUG_MODE__
00036 #include "debug.h"
00037 
00038 using namespace std;
00039 using namespace Barry::Protocol;
00040 
00041 namespace Barry {
00042 
00043 ///////////////////////////////////////////////////////////////////////////////
00044 // Message class
00045 
00046 
00047 // Email / message field codes
00048 #define MBFC_TO                 0x01    // can occur multiple times
00049 #define MBFC_CC                 0x02    // ditto
00050 #define MBFC_BCC                0x03    // ditto
00051 #define MBFC_SENDER             0x04
00052 #define MBFC_FROM               0x05
00053 #define MBFC_REPLY_TO           0x06
00054 #define MBFC_SUBJECT            0x0b
00055 #define MBFC_BODY               0x0c
00056 #define MBFC_REPLY_UNKNOWN      0x12    // This shows up as 0x00 on replies
00057                                         // but we don't do much with it now
00058 #define MBFC_ATTACHMENT         0x16
00059 #define MBFC_RECORDID           0x4b    // Internal Message ID, mimics header RecNumber
00060 #define MBFC_END                0xffff
00061 
00062 #define PRIORITY_MASK           0x003f
00063 #define PRIORITY_HIGH           0x0008
00064 #define PRIORITY_LOW            0x0002
00065 
00066 #define SENSITIVE_MASK          0xff80
00067 #define SENSITIVE_CONFIDENTIAL  0x0100
00068 #define SENSITIVE_PERSONAL      0x0080
00069 #define SENSITIVE_PRIVATE       0x0040  // actual pattern is 0x00C0
00070 
00071 #define MESSAGE_READ            0x0800
00072 #define MESSAGE_REPLY           0x0001
00073 #define MESSAGE_SAVED           0x0002
00074 #define MESSAGE_FORWARD         0x0008
00075 #define MESSAGE_TRUNCATED       0x0020
00076 #define MESSAGE_SAVED_DELETED   0x0080
00077 
00078 static FieldLink<MessageBase> MessageBaseFieldLinks[] = {
00079    { MBFC_TO,         "To",           0, 0, 0, &MessageBase::To, 0, 0, 0, true },
00080    { MBFC_CC,         "Cc",           0, 0, 0, &MessageBase::Cc, 0, 0, 0, true },
00081    { MBFC_BCC,        "Bcc",          0, 0, 0, &MessageBase::Bcc, 0, 0, 0, true },
00082    { MBFC_SENDER,     "Sender",       0, 0, 0, &MessageBase::Sender, 0, 0, 0, true },
00083    { MBFC_FROM,       "From",         0, 0, 0, &MessageBase::From, 0, 0, 0, true },
00084    { MBFC_REPLY_TO,   "ReplyTo",      0, 0, 0, &MessageBase::ReplyTo, 0, 0, 0, true },
00085    { MBFC_SUBJECT,    "Subject",      0, 0, &MessageBase::Subject, 0, 0, 0, 0, true },
00086    { MBFC_BODY,       "Body",         0, 0, &MessageBase::Body, 0, 0, 0, 0, true },
00087    { MBFC_ATTACHMENT, "Attachment",   0, 0, &MessageBase::Attachment, 0, 0, 0, 0, false },
00088    { MBFC_END,        "End of List",  0, 0, 0, 0, 0, 0, 0, false }
00089 };
00090 
00091 MessageBase::MessageBase()
00092 {
00093         Clear();
00094 }
00095 
00096 MessageBase::~MessageBase()
00097 {
00098 }
00099 
00100 const unsigned char* MessageBase::ParseField(const unsigned char *begin,
00101                                          const unsigned char *end,
00102                                          const IConverter *ic)
00103 {
00104         const CommonField *field = (const CommonField *) begin;
00105 
00106         // advance and check size
00107         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00108         if( begin > end )               // if begin==end, we are ok
00109                 return begin;
00110 
00111         if( !btohs(field->size) )       // if field has no size, something's up
00112                 return begin;
00113 
00114         // cycle through the type table
00115         for(    FieldLink<MessageBase> *b = MessageBaseFieldLinks;
00116                 b->type != MBFC_END;
00117                 b++ )
00118         {
00119                 if( b->type == field->type ) {
00120                         if( b->strMember ) {
00121                                 // parse regular string
00122                                 std::string &s = this->*(b->strMember);
00123                                 s = ParseFieldString(field);
00124                                 if( b->iconvNeeded && ic )
00125                                         s = ic->FromBB(s);
00126                                 return begin;   // done!
00127                         }
00128                         else if( b->addrMember ) {
00129                                 // parse email address
00130                                 // get dual name+addr string first
00131                                 const char *fa = (const char*)field->u.addr.addr;
00132                                 std::string dual(fa, btohs(field->size) - sizeof(field->u.addr.unknown));
00133 
00134                                 // assign first string, using null terminator
00135                                 // letting std::string add it for us if it
00136                                 // doesn't exist
00137                                 EmailAddress a;
00138                                 a.Name = dual.c_str();
00139 
00140                                 // assign second string, using first size
00141                                 // as starting point
00142                                 a.Email = dual.c_str() + a.Name.size() + 1;
00143 
00144                                 // if the address is non-empty, add to list
00145                                 if( a.size() ) {
00146                                         // i18n convert if needed
00147                                         if( b->iconvNeeded && ic ) {
00148                                                 a.Name = ic->FromBB(a.Name);
00149                                                 a.Email = ic->FromBB(a.Email);
00150                                         }
00151 
00152                                         EmailAddressList &al = this->*(b->addrMember);
00153                                         al.push_back(a);
00154                                 }
00155 
00156                                 return begin;
00157                         }
00158                 }
00159         }
00160 
00161         // handle special cases
00162         switch( field->type )
00163         {
00164         case MBFC_RECORDID:
00165                 MessageRecordId = btohl(field->u.uint32);
00166                 return begin;
00167 
00168         case MBFC_REPLY_UNKNOWN:        // FIXME - not available in SavedMessage?
00169                 //char swallow = field->u.raw[0];
00170                 return begin;
00171         }
00172 
00173         // if still not handled, add to the Unknowns list
00174         UnknownField uf;
00175         uf.type = field->type;
00176         uf.data.assign((const char*)field->u.raw, btohs(field->size));
00177         Unknowns.push_back(uf);
00178 
00179         return begin;
00180 }
00181 
00182 void MessageBase::ParseHeader(const Data &data, size_t &offset)
00183 {
00184         const unsigned char *begin = data.GetData();
00185         const unsigned char *end = data.GetData() + data.GetSize();
00186 
00187         begin += offset + MESSAGE_RECORD_HEADER_SIZE;
00188         if( begin > end )
00189                 return;
00190 
00191         MAKE_RECORD(const Barry::Protocol::MessageRecord, mr, data, offset);
00192 
00193         // Priority
00194         Priority = NormalPriority;
00195 
00196         uint16_t priority = btohs(mr->priority);  // deal with endian swap once
00197         if( priority & PRIORITY_MASK ) {
00198                 if( priority & PRIORITY_HIGH ) {
00199                         Priority = HighPriority;
00200                 }
00201                 else if( priority & PRIORITY_LOW ) {
00202                         Priority = LowPriority;
00203                 }
00204                 else
00205                         Priority = UnknownPriority;
00206         }
00207 
00208         // Sensitivity
00209         Sensitivity = NormalSensitivity;
00210 
00211         if( priority & SENSITIVE_MASK ) {
00212                 if(( priority & SENSITIVE_CONFIDENTIAL ) == SENSITIVE_CONFIDENTIAL ) {
00213                         Sensitivity = Confidential;
00214                 }
00215                 else if(( priority & SENSITIVE_PRIVATE ) == SENSITIVE_PRIVATE ) {
00216                         Sensitivity = Private;
00217                 }
00218                 else if(( priority & SENSITIVE_PERSONAL ) == SENSITIVE_PERSONAL ) {
00219                         Sensitivity = Personal;
00220                 }
00221                 else
00222                         Sensitivity = UnknownSensitivity;
00223         }
00224 
00225         // X-rim-org-message-ref-id
00226         // NOTE: I'm cheating a bit here and using this as a reply-to
00227         // It's actually sent by BB with the actual UID in every message
00228         if( mr->inReplyTo )
00229                 MessageReplyTo = btohl(mr->inReplyTo);
00230 
00231         // Status Flags
00232         uint32_t flags = btohl(mr->flags);
00233 
00234         // NOTE: A lot of these flags are 'backwards' but this seemed
00235         // like the most logical way to interpret them for now
00236         if( !( flags & MESSAGE_READ ))
00237                 MessageRead = true;
00238 
00239         // NOTE: This is a reply, the original message's flags are not changed
00240         // the inReplyTo field is updated with the original messages's UID
00241         if(( flags & MESSAGE_REPLY ) == MESSAGE_REPLY )
00242                 MessageReply = true;
00243 
00244         // NOTE: This bit is unset on truncation, around 4096 on my 7100g
00245         // NOTE: bit 0x400 is set on REALLY huge messages, haven't tested
00246         //       the exact size yet
00247         if( !( flags & MESSAGE_TRUNCATED ))
00248                 MessageTruncated = true;
00249 
00250         // NOTE: Saved to 'saved' folder
00251         if( !( flags & MESSAGE_SAVED ))
00252                 MessageSaved = true;
00253 
00254         // NOTE: Saved to 'saved' folder and then deleted from inbox
00255         if( !( flags & MESSAGE_SAVED_DELETED ))
00256                 MessageSavedDeleted = true;
00257 
00258         MessageDateSent.Time = Message2Time(mr->dateSent, mr->timeSent);
00259         MessageDateReceived.Time = Message2Time(mr->dateReceived, mr->timeReceived);
00260 
00261         offset += MESSAGE_RECORD_HEADER_SIZE;
00262 }
00263 
00264 void MessageBase::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00265 {
00266         const unsigned char *finish = ParseCommonFields(*this,
00267                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00268         offset += finish - (data.GetData() + offset);
00269 }
00270 
00271 void MessageBase::Validate() const
00272 {
00273 }
00274 
00275 void MessageBase::BuildHeader(Data &data, size_t &offset) const
00276 {
00277         throw std::logic_error("MessageBase::BuildHeader not yet implemented");
00278 }
00279 
00280 void MessageBase::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00281 {
00282         throw std::logic_error("MessageBase::BuildFields not yet implemented");
00283 }
00284 
00285 void MessageBase::Clear()
00286 {
00287         // these must be overwritten by any derived classes
00288         RecType = 0;
00289         RecordId = 0;
00290 
00291         // clear base class variables
00292         From.clear();
00293         To.clear();
00294         Cc.clear();
00295         Bcc.clear();
00296         Sender.clear();
00297         ReplyTo.clear();
00298 
00299         Subject.clear();
00300         Body.clear();
00301         Attachment.clear();
00302 
00303         MessageRecordId = 0;
00304         MessageReplyTo = 0;
00305         MessageDateSent.clear();
00306         MessageDateReceived.clear();
00307 
00308         MessageTruncated = false;
00309         MessageRead = false;
00310         MessageReply = false;
00311         MessageSaved = false;
00312         MessageSavedDeleted = false;
00313 
00314         Priority = NormalPriority;
00315         Sensitivity = NormalSensitivity;
00316 
00317         Unknowns.clear();
00318 }
00319 
00320 template <class RecordT>
00321 void DoFillHandles(typename FieldHandle<RecordT>::ListT &handles)
00322 {
00323         // start fresh
00324         handles.clear();
00325 
00326 #undef CONTAINER_OBJECT_NAME
00327 #define CONTAINER_OBJECT_NAME handles
00328 
00329 #undef RECORD_CLASS_NAME
00330 #define RECORD_CLASS_NAME RecordT
00331 
00332         FHP(RecType, "Record Type Code");
00333         FHP(RecordId, "Unique Record ID");
00334 
00335         FHD(From, "From", MBFC_FROM, true);
00336         FHD(To, "To", MBFC_TO, true);
00337         FHD(Cc, "CC", MBFC_CC, true);
00338         FHD(Bcc, "BCC", MBFC_BCC, true);
00339         FHD(Sender, "Sender", MBFC_SENDER, true);
00340         FHD(ReplyTo, "Reply To", MBFC_REPLY_TO, true);
00341         FHD(Subject, "Subject", MBFC_SUBJECT, true);
00342         FHD(Body, "Body", MBFC_BODY, true);
00343         FHD(Attachment, "Attachment", MBFC_ATTACHMENT, false);
00344 
00345         FHD(MessageRecordId, "Message Record ID", MBFC_RECORDID, false);
00346         FHP(MessageReplyTo, "Message Reply To");
00347         FHP(MessageDateSent, "Date Sent");
00348         FHP(MessageDateReceived, "Date Received");
00349 
00350         FHP(MessageTruncated, "Truncated");
00351         FHP(MessageRead, "Read");
00352         FHP(MessageReply, "Reply");
00353         FHP(MessageSaved, "Saved");
00354         FHP(MessageSavedDeleted, "Saved Deleted");
00355 
00356         FHET(pt, PriorityType, Priority, "Priority");
00357         FHE_CONST(pt, LowPriority, "Low");
00358         FHE_CONST(pt, NormalPriority, "Normal");
00359         FHE_CONST(pt, HighPriority, "High");
00360         FHE_CONST(pt, UnknownPriority, "Unknown");
00361 
00362         FHET(st, SensitivityType, Sensitivity, "Sensitivity");
00363         FHE_CONST(st, NormalSensitivity, "Normal");
00364         FHE_CONST(st, Personal, "Personal");
00365         FHE_CONST(st, Private, "Private");
00366         FHE_CONST(st, Confidential, "Confidential");
00367         FHE_CONST(st, UnknownSensitivity, "Unknown");
00368 
00369         FHP(Unknowns, "Unknown Fields");
00370 }
00371 
00372 std::string MessageBase::GetDescription() const
00373 {
00374         // FIXME - ponder a better description...
00375         return Subject;
00376 }
00377 
00378 std::string MessageBase::SimpleFromAddress() const
00379 {
00380         if( From.size() ) {
00381                 // remove all spaces from the email
00382                 std::string ret;
00383                 for( size_t i = 0; i < From[0].Email.size(); i++ )
00384                         if( From[0].Email[i] != ' ' )
00385                                 ret += From[0].Email[i];
00386 
00387                 return ret;
00388         }
00389         else {
00390                 return "unknown";
00391         }
00392 }
00393 
00394 // dump message in mbox format
00395 void MessageBase::Dump(std::ostream &os) const
00396 {
00397         ios_format_state state(os);
00398 
00399         static const char *Importance[] =
00400                 { "Low", "Normal", "High", "Unknown Priority" };
00401         static const char *SensitivityString[] =
00402                 { "Normal", "Personal", "Private", "Confidential", "Unknown Sensivity" };
00403 
00404         os << "From " << SimpleFromAddress() << "  " << MessageDateReceived << "\n";
00405 
00406 /*
00407 FIXME
00408 // savedmessage prints like this:
00409 os << "From " << SimpleFromAddress() << "  " << ctime( &MessageDateSent );
00410 // pinmessage prints like this:
00411 os << "From " << SimpleFromAddress() << "  " << ctime( &MessageDateSent );
00412 */
00413 
00414         os << "X-Record-ID: (" << setw(8) << std::hex << MessageRecordId << ")\n";
00415 
00416         if( MessageReplyTo )
00417                 os << "X-rim-org-msg-ref-id: " << std::dec << MessageReplyTo << "\n";
00418         if( MessageSaved )
00419                 os << "X-Message-Status: Saved\n";
00420         else if( MessageRead )
00421                 os << "Message Status: Opened\n";
00422         if( Priority != NormalPriority )
00423                 os << "Importance: " << Importance[Priority] << "\n";
00424         if( Sensitivity != NormalSensitivity )
00425                 os << "Sensitivity: " << SensitivityString[Sensitivity] << "\n";
00426         os << "Date: " << MessageDateSent << "\n";
00427         if( From.size() )
00428                 os << "From: " << From[0] << "\n";
00429         if( To.size() )
00430                 os << "To: " << To << "\n";
00431         if( Cc.size() )
00432                 os << "Cc: " << Cc << "\n";
00433         if( Bcc.size() )
00434                 os << "Bcc: " << Bcc << "\n";
00435         if( Sender.size() )
00436                 os << "Sender: " << Sender << "\n";
00437         if( ReplyTo.size())
00438                 os << "Reply To: " << ReplyTo << "\n";
00439         if( Subject.size() )
00440                 os << "Subject: " << Subject << "\n";
00441         os << "\n";
00442         os << Cr2LfWrapper(Body);
00443         os << "\n";
00444 
00445         if( Attachment.size() )
00446                 os << "Attachments: " << Data(Attachment.data(), Attachment.size()) << "\n";
00447 
00448         os << Unknowns;
00449         os << "\n\n";
00450 }
00451 
00452 bool MessageBase::operator<(const MessageBase &other) const
00453 {
00454         // just in case either of these are set to '0', use the
00455         // one with the max value... this uses the latest date, which
00456         // is likely the most accurate
00457         time_t date = std::max(MessageDateSent.Time, MessageDateReceived.Time);
00458         time_t odate = std::max(other.MessageDateSent.Time, other.MessageDateReceived.Time);
00459 
00460         if( date != odate )
00461                 return date < odate;
00462 
00463         return Subject < other.Subject;
00464 }
00465 
00466 } // namespace Barry
00467 
00468 
00469 
00470 
00471 
00472 //////////////////////////////////////////////////////////////////////////////
00473 // Generic Field Handle support
00474 
00475 #include "r_message.h"
00476 #include "r_pin_message.h"
00477 #include "r_saved_message.h"
00478 
00479 
00480 namespace Barry {
00481 
00482 //////////////////////////////////////////////////////////////////////////////
00483 // Message class - statics
00484 
00485 const FieldHandle<Message>::ListT& Message::GetFieldHandles()
00486 {
00487         static FieldHandle<Message>::ListT fhv;
00488 
00489         if( fhv.size() )
00490                 return fhv;
00491 
00492         DoFillHandles<Message>(fhv);
00493         return fhv;
00494 }
00495 
00496 //////////////////////////////////////////////////////////////////////////////
00497 // PINMessage class - statics
00498 
00499 const FieldHandle<PINMessage>::ListT& PINMessage::GetFieldHandles()
00500 {
00501         static FieldHandle<PINMessage>::ListT fhv;
00502 
00503         if( fhv.size() )
00504                 return fhv;
00505 
00506         DoFillHandles<PINMessage>(fhv);
00507         return fhv;
00508 }
00509 
00510 //////////////////////////////////////////////////////////////////////////////
00511 // SavedMessage class - statics
00512 
00513 const FieldHandle<SavedMessage>::ListT& SavedMessage::GetFieldHandles()
00514 {
00515         static FieldHandle<SavedMessage>::ListT fhv;
00516 
00517         if( fhv.size() )
00518                 return fhv;
00519 
00520         DoFillHandles<SavedMessage>(fhv);
00521         return fhv;
00522 }
00523 
00524 } // namespace Barry
00525