- renamed Contact class's Title field to JobTitle for clarity
[barry.git] / src / r_message.cc
blob481060389e19d89bb9069cb95275c65b6f05f5e5
1 ///
2 /// \file r_message.cc
3 /// Blackberry database record parser class for email records.
4 ///
6 /*
7 Copyright (C) 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "r_message.h"
23 #include "record-internal.h"
24 #include "protocol.h"
25 #include "protostructs.h"
26 #include "data.h"
27 #include "time.h"
28 #include "error.h"
29 #include "endian.h"
30 #include <ostream>
31 #include <iomanip>
32 #include <time.h>
33 #include <stdexcept>
35 #define __DEBUG_MODE__
36 #include "debug.h"
38 using namespace std;
39 using namespace Barry::Protocol;
41 namespace Barry {
43 std::ostream& operator<<(std::ostream &os, const Address &msga) {
44 os << msga.Name.c_str() << " <" << msga.Email.c_str() << ">";
45 return os;
48 ///////////////////////////////////////////////////////////////////////////////
49 // Message class
52 // Email / message field codes
53 #define MFC_TO 0x01 // can occur multiple times
54 #define MFC_CC 0x02 // ditto
55 #define MFC_BCC 0x03 // ditto
56 #define MFC_SENDER 0x04
57 #define MFC_FROM 0x05
58 #define MFC_REPLY_TO 0x06
59 #define MFC_SUBJECT 0x0b
60 #define MFC_BODY 0x0c
61 #define MFC_REPLY_UNKNOWN 0x12 // This shows up as 0x00 on replies but we don't do much with it now
62 #define MFC_ATTACHMENT 0x16
63 #define MFC_RECORDID 0x4b
64 #define MFC_END 0xffff
66 #define PRIORITY_MASK 0x003f
67 #define PRIORITY_HIGH 0x0008
68 #define PRIORITY_LOW 0x0002
70 #define SENSITIVE_MASK 0xff80
71 #define SENSITIVE_CONFIDENTIAL 0x0100
72 #define SENSITIVE_PERSONAL 0x0080
73 #define SENSITIVE_PRIVATE 0x0040 // actual pattern is 0x00C0
75 #define MESSAGE_READ 0x0800
76 #define MESSAGE_REPLY 0x0001
77 #define MESSAGE_SAVED 0x0002
78 #define MESSAGE_FORWARD 0x0008
79 #define MESSAGE_TRUNCATED 0x0020
80 #define MESSAGE_SAVED_DELETED 0x0080
82 FieldLink<Message> MessageFieldLinks[] = {
83 { MFC_TO, "To", 0, 0, 0, &Message::To, 0 },
84 { MFC_CC, "Cc", 0, 0, 0, &Message::Cc, 0 },
85 { MFC_BCC, "Bcc", 0, 0, 0, &Message::Bcc, 0 },
86 { MFC_SENDER, "Sender", 0, 0, 0, &Message::Sender, 0 },
87 { MFC_FROM, "From", 0, 0, 0, &Message::From, 0 },
88 { MFC_REPLY_TO, "ReplyTo", 0, 0, 0, &Message::ReplyTo, 0 },
89 { MFC_SUBJECT, "Subject", 0, 0, &Message::Subject, 0, 0 },
90 { MFC_BODY, "Body", 0, 0, &Message::Body, 0, 0 },
91 { MFC_ATTACHMENT, "Attachment", 0, 0, &Message::Attachment, 0, 0 },
92 { MFC_END, "End of List", 0, 0, 0, 0, 0 }
95 Message::Message()
97 Clear();
100 Message::~Message()
104 const unsigned char* Message::ParseField(const unsigned char *begin,
105 const unsigned char *end)
107 const CommonField *field = (const CommonField *) begin;
109 // advance and check size
110 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
111 if( begin > end ) // if begin==end, we are ok
112 return begin;
114 if( !btohs(field->size) ) // if field has no size, something's up
115 return begin;
117 // cycle through the type table
118 for( FieldLink<Message> *b = MessageFieldLinks;
119 b->type != MFC_END;
120 b++ )
122 if( b->type == field->type ) {
123 if( b->strMember ) {
124 // parse regular string
125 std::string &s = this->*(b->strMember);
126 s = ParseFieldString(field);
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...letting std::string add it for us if it doesn't exist
136 Address &a = this->*(b->addrMember);
137 a.Name = dual.c_str();
139 // assign second string, using first size as starting point
140 a.Email = dual.c_str() + a.Name.size() + 1;
141 return begin;
145 // handle special cases
146 char swallow;
147 switch( field->type )
149 case MFC_RECORDID:
150 MessageRecordId = field->u.uint32;
151 return begin;
152 case MFC_REPLY_UNKNOWN:
153 swallow = field->u.raw[0];
154 return begin;
156 // if still not handled, add to the Unknowns list
157 UnknownField uf;
158 uf.type = field->type;
159 uf.data.assign((const char*)field->u.raw, btohs(field->size));
160 Unknowns.push_back(uf);
162 return begin;
165 uint8_t Message::GetRecType() const
167 throw std::logic_error("Message::GetRecType() called, and not supported by the USB protocol. Should never get called.");
170 // empty API, not required by protocol
171 uint32_t Message::GetUniqueId() const
173 throw std::logic_error("Message::GetUniqueId() called, and not supported by the USB protocol. Should never get called.");
176 void Message::ParseHeader(const Data &data, size_t &offset)
178 MAKE_RECORD(const Barry::Protocol::MessageRecord, mr, data, offset);
180 // Priority
181 MessagePriority = NormalPriority;
182 if( mr->priority & PRIORITY_MASK ) {
183 if( mr->priority & PRIORITY_HIGH ) {
184 MessagePriority = HighPriority;
186 else if( mr->priority & PRIORITY_LOW ) {
187 MessagePriority = LowPriority;
189 else
190 MessagePriority = UnknownPriority;
192 // Sensitivity
193 MessageSensitivity = NormalSensitivity;
194 if( mr->priority & SENSITIVE_MASK ) {
195 if(( mr->priority & SENSITIVE_CONFIDENTIAL ) == SENSITIVE_CONFIDENTIAL ) {
196 MessageSensitivity = Confidential;
198 else if(( mr->priority & SENSITIVE_PRIVATE ) == SENSITIVE_PRIVATE ) {
199 MessageSensitivity = Private;
201 else if(( mr->priority & SENSITIVE_PERSONAL ) == SENSITIVE_PERSONAL ) {
202 MessageSensitivity = Personal;
204 else
205 MessageSensitivity = UnknownSensitivity;
207 // X-rim-org-message-ref-id // NOTE: I'm cheating a bit here and using this as a reply-to
208 if( mr->inReplyTo ) // It's actually sent by BB with the actual UID in every message
209 MessageReplyTo = mr->inReplyTo;
210 // Status Flags
211 if( !( mr->flags & MESSAGE_READ ))
212 MessageRead = true; // NOTE: A lot of these flags are 'backwards' but this seemed
213 // like the most logical way to interpret them for now
214 if(( mr->flags & MESSAGE_REPLY ) == MESSAGE_REPLY )
215 MessageReply = true; // NOTE: This is a reply, the original message's flags are not changed
216 // the inReplyTo field is updated with the original messages's UID
217 if( !( mr->flags & MESSAGE_TRUNCATED ))
218 MessageTruncated = true; // NOTE: This bit is unset on truncation, around 4096 on my 7100g
219 // NOTE: bit 0x400 is set on REALLY huge messages, haven't tested
220 // the exact size yet
221 if( !( mr->flags & MESSAGE_SAVED ))
222 MessageSaved = true; // NOTE: Saved to 'saved' folder
223 if( !( mr->flags & MESSAGE_SAVED_DELETED ))
224 MessageSavedDeleted = true; // NOTE: Saved to 'saved' folder and then deleted from inbox
226 MessageDateSent = ( mr->dateSent & 0x01ff ) - 0x29;
227 MessageDateSent = DayToDate( MessageDateSent );
228 MessageDateSent += (time_t)( mr->timeSent*1.77 );
230 MessageDateReceived = ( mr->dateReceived & 0x01ff ) - 0x29;
231 MessageDateReceived = DayToDate( MessageDateReceived );
232 MessageDateReceived += (time_t)( mr->timeReceived*1.77 );
234 offset += MESSAGE_RECORD_HEADER_SIZE;
237 void Message::ParseFields(const Data &data, size_t &offset)
239 const unsigned char *finish = ParseCommonFields(*this,
240 data.GetData() + offset, data.GetData() + data.GetSize());
241 offset += finish - (data.GetData() + offset);
244 void Message::BuildHeader(Data &data, size_t &offset) const
246 throw std::logic_error("Message::BuildHeader not yet implemented");
249 void Message::BuildFields(Data &data, size_t &offset) const
251 throw std::logic_error("Message::BuildFields not yet implemented");
254 void Message::Clear()
256 From.clear();
257 To.clear();
258 Cc.clear();
259 Bcc.clear();
260 Sender.clear();
261 ReplyTo.clear();
262 Subject.clear();
263 Body.clear();
264 Attachment.clear();
266 MessageRecordId = 0;
267 MessageReplyTo = 0;
268 MessageDateSent = 0;
269 MessageDateReceived = 0;
270 MessageTruncated = false;
271 MessageRead = false;
272 MessageReply = false;
273 MessageSaved = false;
274 MessageSavedDeleted = false;
276 Unknowns.clear();
279 std::string Message::SimpleEmailAddress() const
281 if( From.Email.size() ) {
282 // remove all spaces from the email
283 std::string ret;
284 for( size_t i = 0; i < From.Email.size(); i++ )
285 if( From.Email[i] != ' ' )
286 ret += From.Email[i];
288 return ret;
290 else {
291 return "unknown";
295 // dump message in mbox format
296 void Message::Dump(std::ostream &os) const
298 static const char *MessageImportance[] =
299 { "Low", "Normal", "High", "Unknown Priority" };
300 static const char *MessageSensitivityString[] =
301 { "Normal", "Personal", "Private", "Confidential", "Unknown Sensivity" };
303 os << "From " << SimpleEmailAddress() << " " << ctime( &MessageDateReceived );
304 os << "X-Record-ID: (" << setw(8) << std::hex << MessageRecordId << ")\n";
305 if( MessageReplyTo )
306 os << "X-rim-org-msg-ref-id: " << std::dec << MessageReplyTo << "\n";
307 if( MessageSaved )
308 os << "X-Message-Status: Saved\n";
309 else if( MessageRead )
310 os << "Message Status: Opened\n";
311 if( MessagePriority != NormalPriority )
312 os << "Importance: " << MessageImportance[MessagePriority] << "\n";
313 if( MessageSensitivity != NormalSensitivity )
314 os << "Sensitivity: " << MessageSensitivityString[MessageSensitivity] << "\n";
315 os << "Date: " << ctime(&MessageDateSent);
316 os << "From: " << From << "\n";
317 if( To.Email.size() )
318 os << "To: " << To << "\n";
319 if( Cc.Email.size() )
320 os << "Cc: " << Cc << "\n";
321 if( Bcc.Email.size() )
322 os << "Bcc: " << Bcc << "\n";
323 if( Sender.Email.size() )
324 os << "Sender: " << Sender << "\n";
325 if( ReplyTo.Email.size())
326 os << "Reply To: " << ReplyTo << "\n";
327 if( Subject.size() )
328 os << "Subject: " << Subject << "\n";
329 os << "\n";
330 for( std::string::const_iterator i = Body.begin();
331 i != Body.end() && *i;
332 i++)
334 if( *i == '\r' )
335 os << '\n';
336 else
337 os << *i;
339 os << "\n";
340 if( Attachment.size() )
341 os << "Attachments: " << Attachment << "\n";
343 os << Unknowns;
344 os << "\n\n";
348 } // namespace Barry