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-2009, 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         Protocol::CheckSize(data, offset + MESSAGE_RECORD_HEADER_SIZE);
00195 
00196         MAKE_RECORD(const Barry::Protocol::MessageRecord, mr, data, offset);
00197 
00198         // Priority
00199         Priority = NormalPriority;
00200 
00201         uint16_t priority = btohs(mr->priority);  // deal with endian swap once
00202         if( priority & PRIORITY_MASK ) {
00203                 if( priority & PRIORITY_HIGH ) {
00204                         Priority = HighPriority;
00205                 }
00206                 else if( priority & PRIORITY_LOW ) {
00207                         Priority = LowPriority;
00208                 }
00209                 else
00210                         Priority = UnknownPriority;
00211         }
00212 
00213         // Sensitivity
00214         Sensitivity = NormalSensitivity;
00215 
00216         if( priority & SENSITIVE_MASK ) {
00217                 if(( priority & SENSITIVE_CONFIDENTIAL ) == SENSITIVE_CONFIDENTIAL ) {
00218                         Sensitivity = Confidential;
00219                 }
00220                 else if(( priority & SENSITIVE_PRIVATE ) == SENSITIVE_PRIVATE ) {
00221                         Sensitivity = Private;
00222                 }
00223                 else if(( priority & SENSITIVE_PERSONAL ) == SENSITIVE_PERSONAL ) {
00224                         Sensitivity = Personal;
00225                 }
00226                 else
00227                         Sensitivity = UnknownSensitivity;
00228         }
00229 
00230         // X-rim-org-message-ref-id
00231         // NOTE: I'm cheating a bit here and using this as a reply-to
00232         // It's actually sent by BB with the actual UID in every message
00233         if( mr->inReplyTo )
00234                 MessageReplyTo = btohl(mr->inReplyTo);
00235 
00236         // Status Flags
00237         uint32_t flags = btohl(mr->flags);
00238 
00239         // NOTE: A lot of these flags are 'backwards' but this seemed
00240         // like the most logical way to interpret them for now
00241         if( !( flags & MESSAGE_READ ))
00242                 MessageRead = true;
00243 
00244         // NOTE: This is a reply, the original message's flags are not changed
00245         // the inReplyTo field is updated with the original messages's UID
00246         if(( flags & MESSAGE_REPLY ) == MESSAGE_REPLY )
00247                 MessageReply = true;
00248 
00249         // NOTE: This bit is unset on truncation, around 4096 on my 7100g
00250         // NOTE: bit 0x400 is set on REALLY huge messages, haven't tested
00251         //       the exact size yet
00252         if( !( flags & MESSAGE_TRUNCATED ))
00253                 MessageTruncated = true;
00254 
00255         // NOTE: Saved to 'saved' folder
00256         if( !( flags & MESSAGE_SAVED ))
00257                 MessageSaved = true;
00258 
00259         // NOTE: Saved to 'saved' folder and then deleted from inbox
00260         if( !( flags & MESSAGE_SAVED_DELETED ))
00261                 MessageSavedDeleted = true;
00262 
00263         MessageDateSent = Message2Time(mr->dateSent, mr->timeSent);
00264         MessageDateReceived = Message2Time(mr->dateReceived, mr->timeReceived);
00265 
00266         offset += MESSAGE_RECORD_HEADER_SIZE;
00267 }
00268 
00269 void MessageBase::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00270 {
00271         const unsigned char *finish = ParseCommonFields(*this,
00272                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00273         offset += finish - (data.GetData() + offset);
00274 }
00275 
00276 void MessageBase::BuildHeader(Data &data, size_t &offset) const
00277 {
00278         throw std::logic_error("MessageBase::BuildHeader not yet implemented");
00279 }
00280 
00281 void MessageBase::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00282 {
00283         throw std::logic_error("MessageBase::BuildFields not yet implemented");
00284 }
00285 
00286 void MessageBase::Clear()
00287 {
00288         From.clear();
00289         To.clear();
00290         Cc.clear();
00291         Bcc.clear();
00292         Sender.clear();
00293         ReplyTo.clear();
00294 
00295         Subject.clear();
00296         Body.clear();
00297         Attachment.clear();
00298 
00299         MessageRecordId = 0;
00300         MessageReplyTo = 0;
00301         MessageDateSent = 0;
00302         MessageDateReceived = 0;
00303         MessageTruncated = false;
00304         MessageRead = false;
00305         MessageReply = false;
00306         MessageSaved = false;
00307         MessageSavedDeleted = false;
00308 
00309         Priority = NormalPriority;
00310         Sensitivity = NormalSensitivity;
00311 
00312         Unknowns.clear();
00313 }
00314 
00315 std::string MessageBase::SimpleFromAddress() const
00316 {
00317         if( From.size() ) {
00318                 // remove all spaces from the email
00319                 std::string ret;
00320                 for( size_t i = 0; i < From[0].Email.size(); i++ )
00321                         if( From[0].Email[i] != ' ' )
00322                                 ret += From[0].Email[i];
00323 
00324                 return ret;
00325         }
00326         else {
00327                 return "unknown";
00328         }
00329 }
00330 
00331 // dump message in mbox format
00332 void MessageBase::Dump(std::ostream &os) const
00333 {
00334         static const char *Importance[] =
00335                 { "Low", "Normal", "High", "Unknown Priority" };
00336         static const char *SensitivityString[] =
00337                 { "Normal", "Personal", "Private", "Confidential", "Unknown Sensivity" };
00338 
00339         os << "From " << SimpleFromAddress() << "  " << ctime( &MessageDateReceived );
00340 
00341 /*
00342 FIXME
00343 // savedmessage prints like this:
00344 os << "From " << SimpleFromAddress() << "  " << ctime( &MessageDateSent );
00345 // pinmessage prints like this:
00346 os << "From " << SimpleFromAddress() << "  " << ctime( &MessageDateSent );
00347 */
00348 
00349         os << "X-Record-ID: (" << setw(8) << std::hex << MessageRecordId << ")\n";
00350 
00351         if( MessageReplyTo )
00352                 os << "X-rim-org-msg-ref-id: " << std::dec << MessageReplyTo << "\n";
00353         if( MessageSaved )
00354                 os << "X-Message-Status: Saved\n";
00355         else if( MessageRead )
00356                 os << "Message Status: Opened\n";
00357         if( Priority != NormalPriority )
00358                 os << "Importance: " << Importance[Priority] << "\n";
00359         if( Sensitivity != NormalSensitivity )
00360                 os << "Sensitivity: " << SensitivityString[Sensitivity] << "\n";
00361         os << "Date: " << ctime(&MessageDateSent);
00362         if( From.size() )
00363                 os << "From: " << From[0] << "\n";
00364         if( To.size() )
00365                 os << "To: " << To << "\n";
00366         if( Cc.size() )
00367                 os << "Cc: " << Cc << "\n";
00368         if( Bcc.size() )
00369                 os << "Bcc: " << Bcc << "\n";
00370         if( Sender.size() )
00371                 os << "Sender: " << Sender << "\n";
00372         if( ReplyTo.size())
00373                 os << "Reply To: " << ReplyTo << "\n";
00374         if( Subject.size() )
00375                 os << "Subject: " << Subject << "\n";
00376         os << "\n";
00377         for(    std::string::const_iterator i = Body.begin();
00378                 i != Body.end() && *i;
00379                 i++)
00380         {
00381                 if( *i == '\r' )
00382                         os << '\n';
00383                 else
00384                         os << *i;
00385         }
00386         os << "\n";
00387 
00388         if( Attachment.size() )
00389                 os << "Attachments: " << Data(Attachment.data(), Attachment.size()) << "\n";
00390 
00391         os << Unknowns;
00392         os << "\n\n";
00393 }
00394 
00395 } // namespace Barry
00396 

Generated on Tue Jun 30 16:08:14 2009 for Barry by  doxygen 1.5.8