# 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-2011, 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
    00034 #define __DEBUG_MODE__
    00035 #include "debug.h"
    00036
    00037 using namespace std;
    00038 using namespace Barry::Protocol;
    00039
    00040 namespace Barry {
    00041
    00042 ///////////////////////////////////////////////////////////////////////////////
    00043 // Message class
    00044
    00045
    00046 // Email / message field codes
    00047 #define MBFC_TO                 0x01    // can occur multiple times
    00048 #define MBFC_CC                 0x02    // ditto
    00049 #define MBFC_BCC                0x03    // ditto
    00050 #define MBFC_SENDER             0x04
    00051 #define MBFC_FROM               0x05
    00052 #define MBFC_REPLY_TO           0x06
    00053 #define MBFC_SUBJECT            0x0b
    00054 #define MBFC_BODY               0x0c
    00055 #define MBFC_REPLY_UNKNOWN      0x12    // This shows up as 0x00 on replies
    00056                                         // but we don't do much with it now
    00057 #define MBFC_ATTACHMENT         0x16
    00058 #define MBFC_RECORDID           0x4b    // Internal Message ID, mimics header RecNumber
    00059 #define MBFC_END                0xffff
    00060
    00061 #define PRIORITY_MASK           0x003f
    00062 #define PRIORITY_HIGH           0x0008
    00063 #define PRIORITY_LOW            0x0002
    00064
    00065 #define SENSITIVE_MASK          0xff80
    00066 #define SENSITIVE_CONFIDENTIAL  0x0100
    00067 #define SENSITIVE_PERSONAL      0x0080
    00068 #define SENSITIVE_PRIVATE       0x0040  // actual pattern is 0x00C0
    00069
    00070 #define MESSAGE_READ            0x0800
    00071 #define MESSAGE_REPLY           0x0001
    00072 #define MESSAGE_SAVED           0x0002
    00073 #define MESSAGE_FORWARD         0x0008
    00074 #define MESSAGE_TRUNCATED       0x0020
    00075 #define MESSAGE_SAVED_DELETED   0x0080
    00076
    00077 static FieldLink<MessageBase> MessageBaseFieldLinks[] = {
    00078    { MBFC_TO,         "To",           0, 0, 0, &MessageBase::To, 0, 0, 0, true },
    00079    { MBFC_CC,         "Cc",           0, 0, 0, &MessageBase::Cc, 0, 0, 0, true },
    00080    { MBFC_BCC,        "Bcc",          0, 0, 0, &MessageBase::Bcc, 0, 0, 0, true },
    00081    { MBFC_SENDER,     "Sender",       0, 0, 0, &MessageBase::Sender, 0, 0, 0, true },
    00082    { MBFC_FROM,       "From",         0, 0, 0, &MessageBase::From, 0, 0, 0, true },
    00083    { MBFC_REPLY_TO,   "ReplyTo",      0, 0, 0, &MessageBase::ReplyTo, 0, 0, 0, true },
    00084    { MBFC_SUBJECT,    "Subject",      0, 0, &MessageBase::Subject, 0, 0, 0, 0, true },
    00085    { MBFC_BODY,       "Body",         0, 0, &MessageBase::Body, 0, 0, 0, 0, true },
    00086    { MBFC_ATTACHMENT, "Attachment",   0, 0, &MessageBase::Attachment, 0, 0, 0, 0, false },
    00087    { MBFC_END,        "End of List",  0, 0, 0, 0, 0, 0, 0, false }
    00088 };
    00089
    00090 MessageBase::MessageBase()
    00091 {
    00092         Clear();
    00093 }
    00094
    00095 MessageBase::~MessageBase()
    00096 {
    00097 }
    00098
    00099 const unsigned char* MessageBase::ParseField(const unsigned char *begin,
    00100                                          const unsigned char *end,
    00101                                          const IConverter *ic)
    00102 {
    00103         const CommonField *field = (const CommonField *) begin;
    00104
    00105         // advance and check size
    00106         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
    00107         if( begin > end )               // if begin==end, we are ok
    00108                 return begin;
    00109
    00110         if( !btohs(field->size) )       // if field has no size, something's up
    00111                 return begin;
    00112
    00113         // cycle through the type table
    00114         for(    FieldLink<MessageBase> *b = MessageBaseFieldLinks;
    00115                 b->type != MBFC_END;
    00116                 b++ )
    00117         {
    00118                 if( b->type == field->type ) {
    00119                         if( b->strMember ) {
    00120                                 // parse regular string
    00121                                 std::string &s = this->*(b->strMember);
    00122                                 s = ParseFieldString(field);
    00123                                 if( b->iconvNeeded && ic )
    00124                                         s = ic->FromBB(s);
    00125                                 return begin;   // done!
    00126                         }
    00127                         else if( b->addrMember ) {
    00128                                 // parse email address
    00129                                 // get dual name+addr string first
    00130                                 const char *fa = (const char*)field->u.addr.addr;
    00131                                 std::string dual(fa, btohs(field->size) - sizeof(field->u.addr.unknown));
    00132
    00133                                 // assign first string, using null terminator
    00134                                 // letting std::string add it for us if it
    00135                                 // doesn't exist
    00136                                 EmailAddress a;
    00137                                 a.Name = dual.c_str();
    00138
    00139                                 // assign second string, using first size
    00140                                 // as starting point
    00141                                 a.Email = dual.c_str() + a.Name.size() + 1;
    00142
    00143                                 // if the address is non-empty, add to list
    00144                                 if( a.size() ) {
    00145                                         // i18n convert if needed
    00146                                         if( b->iconvNeeded && ic ) {
    00147                                                 a.Name = ic->FromBB(a.Name);
    00148                                                 a.Email = ic->FromBB(a.Email);
    00149                                         }
    00150
    00151                                         EmailAddressList &al = this->*(b->addrMember);
    00152                                         al.push_back(a);
    00153                                 }
    00154
    00155                                 return begin;
    00156                         }
    00157                 }
    00158         }
    00159
    00160         // handle special cases
    00161         char swallow;
    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                 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 uint8_t MessageBase::GetRecType() const
    00183 {
    00184         throw std::logic_error("MessageBase::GetRecType() called, and not supported by the USB protocol.  Should never get called.");
    00185 }
    00186
    00187 uint32_t MessageBase::GetUniqueId() const
    00188 {
    00189         throw std::logic_error("MessageBase::GetUniqueId() called, and not supported by the USB protocol.  Should never get called.");
    00190 }
    00191
    00192 void MessageBase::ParseHeader(const Data &data, size_t &offset)
    00193 {
    00194         const unsigned char *begin = data.GetData();
    00195         const unsigned char *end = data.GetData() + data.GetSize();
    00196
    00197         begin += offset + MESSAGE_RECORD_HEADER_SIZE;
    00198         if( begin > end )
    00199                 return;
    00200
    00201         MAKE_RECORD(const Barry::Protocol::MessageRecord, mr, data, offset);
    00202
    00203         // Priority
    00204         Priority = NormalPriority;
    00205
    00206         uint16_t priority = btohs(mr->priority);  // deal with endian swap once
    00207         if( priority & PRIORITY_MASK ) {
    00208                 if( priority & PRIORITY_HIGH ) {
    00209                         Priority = HighPriority;
    00210                 }
    00211                 else if( priority & PRIORITY_LOW ) {
    00212                         Priority = LowPriority;
    00213                 }
    00214                 else
    00215                         Priority = UnknownPriority;
    00216         }
    00217
    00218         // Sensitivity
    00219         Sensitivity = NormalSensitivity;
    00220
    00221         if( priority & SENSITIVE_MASK ) {
    00222                 if(( priority & SENSITIVE_CONFIDENTIAL ) == SENSITIVE_CONFIDENTIAL ) {
    00223                         Sensitivity = Confidential;
    00224                 }
    00225                 else if(( priority & SENSITIVE_PRIVATE ) == SENSITIVE_PRIVATE ) {
    00226                         Sensitivity = Private;
    00227                 }
    00228                 else if(( priority & SENSITIVE_PERSONAL ) == SENSITIVE_PERSONAL ) {
    00229                         Sensitivity = Personal;
    00230                 }
    00231                 else
    00232                         Sensitivity = UnknownSensitivity;
    00233         }
    00234
    00235         // X-rim-org-message-ref-id
    00236         // NOTE: I'm cheating a bit here and using this as a reply-to
    00237         // It's actually sent by BB with the actual UID in every message
    00238         if( mr->inReplyTo )
    00239                 MessageReplyTo = btohl(mr->inReplyTo);
    00240
    00241         // Status Flags
    00242         uint32_t flags = btohl(mr->flags);
    00243
    00244         // NOTE: A lot of these flags are 'backwards' but this seemed
    00245         // like the most logical way to interpret them for now
    00246         if( !( flags & MESSAGE_READ ))
    00247                 MessageRead = true;
    00248
    00249         // NOTE: This is a reply, the original message's flags are not changed
    00250         // the inReplyTo field is updated with the original messages's UID
    00251         if(( flags & MESSAGE_REPLY ) == MESSAGE_REPLY )
    00252                 MessageReply = true;
    00253
    00254         // NOTE: This bit is unset on truncation, around 4096 on my 7100g
    00255         // NOTE: bit 0x400 is set on REALLY huge messages, haven't tested
    00256         //       the exact size yet
    00257         if( !( flags & MESSAGE_TRUNCATED ))
    00258                 MessageTruncated = true;
    00259
    00260         // NOTE: Saved to 'saved' folder
    00261         if( !( flags & MESSAGE_SAVED ))
    00262                 MessageSaved = true;
    00263
    00264         // NOTE: Saved to 'saved' folder and then deleted from inbox
    00265         if( !( flags & MESSAGE_SAVED_DELETED ))
    00266                 MessageSavedDeleted = true;
    00267
    00268         MessageDateSent = Message2Time(mr->dateSent, mr->timeSent);
    00269         MessageDateReceived = Message2Time(mr->dateReceived, mr->timeReceived);
    00270
    00271         offset += MESSAGE_RECORD_HEADER_SIZE;
    00272 }
    00273
    00274 void MessageBase::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
    00275 {
    00276         const unsigned char *finish = ParseCommonFields(*this,
    00277                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
    00278         offset += finish - (data.GetData() + offset);
    00279 }
    00280
    00281 void MessageBase::BuildHeader(Data &data, size_t &offset) const
    00282 {
    00283         throw std::logic_error("MessageBase::BuildHeader not yet implemented");
    00284 }
    00285
    00286 void MessageBase::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
    00287 {
    00288         throw std::logic_error("MessageBase::BuildFields not yet implemented");
    00289 }
    00290
    00291 void MessageBase::Clear()
    00292 {
    00293         // these must be overwritten by any derived classes
    00294         RecType = 0;
    00295         RecordId = 0;
    00296
    00297         // clear base class variables
    00298         From.clear();
    00299         To.clear();
    00300         Cc.clear();
    00301         Bcc.clear();
    00302         Sender.clear();
    00303         ReplyTo.clear();
    00304
    00305         Subject.clear();
    00306         Body.clear();
    00307         Attachment.clear();
    00308
    00309         MessageRecordId = 0;
    00310         MessageReplyTo = 0;
    00311         MessageDateSent = 0;
    00312         MessageDateReceived = 0;
    00313
    00314         MessageTruncated = false;
    00315         MessageRead = false;
    00316         MessageReply = false;
    00317         MessageSaved = false;
    00318         MessageSavedDeleted = false;
    00319
    00320         Priority = NormalPriority;
    00321         Sensitivity = NormalSensitivity;
    00322
    00323         Unknowns.clear();
    00324 }
    00325
    00326 std::string MessageBase::GetDescription() const
    00327 {
    00328         // FIXME - ponder a better description...
    00329         return Subject;
    00330 }
    00331
    00332 std::string MessageBase::SimpleFromAddress() const
    00333 {
    00334         if( From.size() ) {
    00335                 // remove all spaces from the email
    00336                 std::string ret;
    00337                 for( size_t i = 0; i < From[0].Email.size(); i++ )
    00338                         if( From[0].Email[i] != ' ' )
    00339                                 ret += From[0].Email[i];
    00340
    00341                 return ret;
    00342         }
    00343         else {
    00344                 return "unknown";
    00345         }
    00346 }
    00347
    00348 // dump message in mbox format
    00349 void MessageBase::Dump(std::ostream &os) const
    00350 {
    00351         static const char *Importance[] =
    00352                 { "Low", "Normal", "High", "Unknown Priority" };
    00353         static const char *SensitivityString[] =
    00354                 { "Normal", "Personal", "Private", "Confidential", "Unknown Sensivity" };
    00355
    00356         os << "From " << SimpleFromAddress() << "  " << ctime( &MessageDateReceived );
    00357
    00358 /*
    00359 FIXME
    00360 // savedmessage prints like this:
    00361 os << "From " << SimpleFromAddress() << "  " << ctime( &MessageDateSent );
    00362 // pinmessage prints like this:
    00363 os << "From " << SimpleFromAddress() << "  " << ctime( &MessageDateSent );
    00364 */
    00365
    00366         os << "X-Record-ID: (" << setw(8) << std::hex << MessageRecordId << ")\n";
    00367
    00368         if( MessageReplyTo )
    00369                 os << "X-rim-org-msg-ref-id: " << std::dec << MessageReplyTo << "\n";
    00370         if( MessageSaved )
    00371                 os << "X-Message-Status: Saved\n";
    00372         else if( MessageRead )
    00373                 os << "Message Status: Opened\n";
    00374         if( Priority != NormalPriority )
    00375                 os << "Importance: " << Importance[Priority] << "\n";
    00376         if( Sensitivity != NormalSensitivity )
    00377                 os << "Sensitivity: " << SensitivityString[Sensitivity] << "\n";
    00378         os << "Date: " << ctime(&MessageDateSent);
    00379         if( From.size() )
    00380                 os << "From: " << From[0] << "\n";
    00381         if( To.size() )
    00382                 os << "To: " << To << "\n";
    00383         if( Cc.size() )
    00384                 os << "Cc: " << Cc << "\n";
    00385         if( Bcc.size() )
    00386                 os << "Bcc: " << Bcc << "\n";
    00387         if( Sender.size() )
    00388                 os << "Sender: " << Sender << "\n";
    00389         if( ReplyTo.size())
    00390                 os << "Reply To: " << ReplyTo << "\n";
    00391         if( Subject.size() )
    00392                 os << "Subject: " << Subject << "\n";
    00393         os << "\n";
    00394         os << Cr2LfWrapper(Body);
    00395         os << "\n";
    00396
    00397         if( Attachment.size() )
    00398                 os << "Attachments: " << Data(Attachment.data(), Attachment.size()) << "\n";
    00399
    00400         os << Unknowns;
    00401         os << "\n\n";
    00402 }
    00403
    00404 bool MessageBase::operator<(const MessageBase &other) const
    00405 {
    00406         // just in case either of these are set to '0', use the
    00407         // one with the max value... this uses the latest date, which
    00408         // is likely the most accurate
    00409         time_t date = std::max(MessageDateSent, MessageDateReceived);
    00410         time_t odate = std::max(other.MessageDateSent, other.MessageDateReceived);
    00411
    00412         if( date != odate )
    00413                 return date < odate;
    00414
    00415         return Subject < other.Subject;
    00416 }
    00417
    00418 } // namespace Barry
    00419