- fixing issues with Brian Edginton's MessageRecord patch (2007/06/29)
[barry.git] / src / r_saved_message.cc
blob6d759c176407592f43d9dbc5b6f74cd6bf6178ad
1 ///
2 /// \file r_saved_message.cc
3 /// Blackberry database record parser class for saved email
4 /// message records.
5 ///
7 /*
8 Copyright (C) 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)
9 Copyright (C) 2005-2007, Brian Edginton (edge@edginton.net)
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "r_saved_message.h"
25 #include "record-internal.h"
26 #include "protocol.h"
27 #include "protostructs.h"
28 #include "data.h"
29 #include "time.h"
30 #include "error.h"
31 #include "endian.h"
32 #include <ostream>
33 #include <iomanip>
34 #include <time.h>
35 #include <stdexcept>
37 #define __DEBUG_MODE__
38 #include "debug.h"
40 using namespace std;
41 using namespace Barry::Protocol;
43 namespace Barry {
45 ///////////////////////////////////////////////////////////////////////////////
46 // Message class
49 // Email / message field codes
50 #define SEMFC_TO 0x01 // can occur multiple times
51 #define SEMFC_CC 0x02 // ditto
52 #define SEMFC_BCC 0x03 // ditto
53 #define SEMFC_SENDER 0x04
54 #define SEMFC_FROM 0x05
55 #define SEMFC_REPLY_TO 0x06
56 #define SEMFC_SUBJECT 0x0b
57 #define SEMFC_BODY 0x0c
58 #define SEMFC_ATTACHMENT 0x16
59 #define SEMFC_RECORDID 0x4b
60 #define SEMFC_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 FieldLink<SavedMessage> SavedMessageFieldLinks[] = {
79 { SEMFC_TO, "To", 0, 0, 0, &SavedMessage::To, 0 },
80 { SEMFC_CC, "Cc", 0, 0, 0, &SavedMessage::Cc, 0 },
81 { SEMFC_BCC, "Bcc", 0, 0, 0, &SavedMessage::Bcc, 0 },
82 { SEMFC_SENDER, "Sender", 0, 0, 0, &SavedMessage::Sender, 0 },
83 { SEMFC_FROM, "From", 0, 0, 0, &SavedMessage::From, 0 },
84 { SEMFC_REPLY_TO, "ReplyTo", 0, 0, 0, &SavedMessage::ReplyTo, 0 },
85 { SEMFC_SUBJECT, "Subject", 0, 0, &SavedMessage::Subject, 0, 0 },
86 { SEMFC_BODY, "Body", 0, 0, &SavedMessage::Body, 0, 0 },
87 { SEMFC_ATTACHMENT, "Attachment", 0, 0, &SavedMessage::Attachment, 0, 0 },
88 { SEMFC_END, "End of List", 0, 0, 0, 0, 0 }
91 SavedMessage::SavedMessage()
93 Clear();
96 SavedMessage::~SavedMessage()
100 const unsigned char* SavedMessage::ParseField(const unsigned char *begin,
101 const unsigned char *end)
103 const CommonField *field = (const CommonField *) begin;
105 // advance and check size
106 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
107 if( begin > end ) // if begin==end, we are ok
108 return begin;
110 if( !btohs(field->size) ) // if field has no size, something's up
111 return begin;
113 // cycle through the type table
114 for( FieldLink<SavedMessage> *b = SavedMessageFieldLinks;
115 b->type != SEMFC_END;
116 b++ )
118 if( b->type == field->type ) {
119 if( b->strMember ) {
120 // parse regular string
121 std::string &s = this->*(b->strMember);
122 s = ParseFieldString(field);
123 return begin; // done!
125 else if( b->addrMember ) {
126 // parse email address
127 // get dual name+addr string first
128 const char *fa = (const char*)field->u.addr.addr;
129 std::string dual(fa, btohs(field->size) - sizeof(field->u.addr.unknown));
131 // assign first string, using null terminator...letting std::string add it for us if it doesn't exist
132 EmailAddress &a = this->*(b->addrMember);
133 a.Name = dual.c_str();
135 // assign second string, using first size as starting point
136 a.Email = dual.c_str() + a.Name.size() + 1;
137 return begin;
142 // handle special cases
143 switch( field->type )
145 case SEMFC_RECORDID:
146 MessageRecordId = field->u.uint32;
147 return begin;
150 // if still not handled, add to the Unknowns list
151 UnknownField uf;
152 uf.type = field->type;
153 uf.data.assign((const char*)field->u.raw, btohs(field->size));
154 Unknowns.push_back(uf);
156 return begin;
159 void SavedMessage::ParseHeader(const Data &data, size_t &offset)
161 Protocol::CheckSize(data, offset + MESSAGE_RECORD_HEADER_SIZE);
163 MAKE_RECORD(const Barry::Protocol::MessageRecord, mr, data, offset);
165 // Priority
166 MessagePriority = NormalPriority;
167 if( mr->priority & PRIORITY_MASK ) {
168 if( mr->priority & PRIORITY_HIGH ) {
169 MessagePriority = HighPriority;
171 else if( mr->priority & PRIORITY_LOW ) {
172 MessagePriority = LowPriority;
174 else
175 MessagePriority = UnknownPriority;
177 // Sensitivity
178 MessageSensitivity = NormalSensitivity;
179 if( mr->priority & SENSITIVE_MASK ) {
180 if(( mr->priority & SENSITIVE_CONFIDENTIAL ) == SENSITIVE_CONFIDENTIAL ) {
181 MessageSensitivity = Confidential;
183 else if(( mr->priority & SENSITIVE_PRIVATE ) == SENSITIVE_PRIVATE ) {
184 MessageSensitivity = Private;
186 else if(( mr->priority & SENSITIVE_PERSONAL ) == SENSITIVE_PERSONAL ) {
187 MessageSensitivity = Personal;
189 else
190 MessageSensitivity = UnknownSensitivity;
192 // X-rim-org-message-ref-id // NOTE: I'm cheating a bit here and using this as a reply-to
193 if( mr->inReplyTo ) // It's actually sent by BB with the actual UID in every message
194 MessageReplyTo = mr->inReplyTo;
195 // Status Flags
196 if( !( mr->flags & MESSAGE_READ ))
197 MessageRead = true; // NOTE: A lot of these flags are 'backwards' but this seemed
198 // like the most logical way to interpret them for now
199 if(( mr->flags & MESSAGE_REPLY ) == MESSAGE_REPLY )
200 MessageReply = true; // NOTE: This is a reply, the original message's flags are not changed
201 // the inReplyTo field is updated with the original messages's UID
202 if( !( mr->flags & MESSAGE_TRUNCATED ))
203 MessageTruncated = true; // NOTE: This bit is unset on truncation, around 4096 on my 7100g
204 // NOTE: bit 0x400 is set on REALLY huge messages, haven't tested
205 // the exact size yet
206 if( !( mr->flags & MESSAGE_SAVED ))
207 MessageSaved = true; // NOTE: Saved to 'saved' folder
208 if( !( mr->flags & MESSAGE_SAVED_DELETED ))
209 MessageSavedDeleted = true; // NOTE: Saved to 'saved' folder and then deleted from inbox
211 MessageDateSent = ( mr->dateSent & 0x01ff ) - 0x29;
212 MessageDateSent = DayToDate( MessageDateSent );
213 MessageDateSent += (time_t)( mr->timeSent*1.77 );
215 MessageDateReceived = ( mr->dateReceived & 0x01ff ) - 0x29;
216 MessageDateReceived = DayToDate( MessageDateReceived );
217 MessageDateReceived += (time_t)( mr->timeReceived*1.77 );
219 offset += MESSAGE_RECORD_HEADER_SIZE;
222 void SavedMessage::ParseFields(const Data &data, size_t &offset)
224 const unsigned char *finish = ParseCommonFields(*this,
225 data.GetData() + offset, data.GetData() + data.GetSize());
226 offset += finish - (data.GetData() + offset);
229 void SavedMessage::BuildHeader(Data &data, size_t &offset) const
231 throw std::logic_error("SavedMessage::BuildHeader not yet implemented");
234 void SavedMessage::BuildFields(Data &data, size_t &offset) const
236 throw std::logic_error("SavedMessage::BuildFields not yet implemented");
239 void SavedMessage::Clear()
241 From.clear();
242 To.clear();
243 Cc.clear();
244 Bcc.clear();
245 Sender.clear();
246 ReplyTo.clear();
247 Subject.clear();
248 Body.clear();
249 Attachment.clear();
251 MessageRecordId = 0;
252 MessageReplyTo = 0;
253 MessageDateSent = 0;
254 MessageDateReceived = 0;
255 MessageTruncated = false;
256 MessageRead = false;
257 MessageReply = false;
258 MessageSaved = false;
259 MessageSavedDeleted = false;
260 Unknowns.clear();
263 // dump message in mbox format
264 void SavedMessage::Dump(std::ostream &os) const
266 static const char *MessageImportance[] =
267 { "Low", "Normal", "High", "Unknown Priority" };
268 static const char *MessageSensitivityString[] =
269 { "Normal", "Personal", "Private", "Confidential", "Unknown Sensivity" };
271 os << "From " << (From.Email.size() ? From.Email.c_str() : "unknown")
272 << " " << ctime( &MessageDateSent );
273 os << "X-Record-ID: (" << setw(8) << std::hex << MessageRecordId << ")\n";
274 if( MessageReplyTo )
275 os << "X-rim-org-msg-ref-id: " << std::dec << MessageReplyTo << "\n";
276 if( MessageSaved )
277 os << "Message Status: Saved\n";
278 else if( MessageRead )
279 os << "Message Status: Opened\n";
280 if( MessagePriority != NormalPriority )
281 os << "Importance: " << MessageImportance[MessagePriority] << "\n";
282 if( MessageSensitivity != NormalSensitivity )
283 os << "Sensitivity: " << MessageSensitivityString[MessageSensitivity] << "\n";
284 os << "Date: " << ctime(&MessageDateSent);
285 os << "From: " << From << "\n";
286 if( To.Email.size() )
287 os << "To: " << To << "\n";
288 if( Cc.Email.size() )
289 os << "Cc: " << Cc << "\n";
290 if( Bcc.Email.size() )
291 os << "Bcc: " << Bcc << "\n";
292 if( Sender.Email.size() )
293 os << "Sender: " << Sender << "\n";
294 if( ReplyTo.Email.size())
295 os << "Reply To: " << ReplyTo << "\n";
296 if( Subject.size() )
297 os << "Subject: " << Subject << "\n";
298 os << "\n";
299 for( std::string::const_iterator i = Body.begin();
300 i != Body.end() && *i;
301 i++)
303 if( *i == '\r' )
304 os << '\n';
305 else
306 os << *i;
308 os << "\n";
309 if( Attachment.size() )
310 os << "Attachments: " << Attachment << "\n";
312 os << Unknowns;
313 os << "\n\n";
317 } // namespace Barry