- moved some record class variables to public: so they can be
[barry.git] / src / record.cc
blob3c111ee1bbcde86733eb090843273d5ef7a0918b
1 ///
2 /// \file record.cc
3 /// Blackberry database record classes. Help translate data
4 /// from data packets to useful structurs, and back.
5 ///
7 /*
8 Copyright (C) 2005, Net Direct Inc. (http://www.netdirect.ca/)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "record.h"
24 #include "protocol.h"
25 #include "protostructs.h"
26 #include "data.h"
27 #include "base64.h"
28 #include "time.h"
29 #include <ostream>
30 #include <iomanip>
31 #include <time.h>
32 #include <stdexcept>
34 #define __DEBUG_MODE__
35 #include "debug.h"
37 using namespace std;
39 namespace Barry {
41 std::ostream& operator<<(std::ostream &os, const Message::Address &msga) {
42 os << msga.Name.c_str() << " <" << msga.Email.c_str() << ">";
43 return os;
48 template <class Record>
49 void ParseCommonFields(Record &rec, const void *begin, const void *end)
51 const unsigned char *b = (const unsigned char*) begin;
52 const unsigned char *e = (const unsigned char*) end;
54 while( (b + COMMON_FIELD_HEADER_SIZE) < e )
55 b = rec.ParseField(b, e);
58 void BuildField(Data &data, int &size, uint8_t type, const std::string &str)
60 // include null terminator
61 size_t strsize = str.size() + 1;
62 int fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
63 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
64 CommonField *field = (CommonField *) pd;
66 field->size = strsize;
67 field->type = type;
68 memcpy(field->data.raw, str.c_str(), strsize);
70 size += fieldsize;
73 void BuildField(Data &data, int &size, uint8_t type, const Barry::GroupLink &link)
75 size_t linksize = sizeof(Barry::GroupLink);
76 int fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
77 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
78 CommonField *field = (CommonField *) pd;
80 field->size = linksize;
81 field->type = type;
82 field->data.link = link;
84 size += fieldsize;
88 ///////////////////////////////////////////////////////////////////////////////
89 // CommandTable class
92 CommandTable::CommandTable()
96 CommandTable::~CommandTable()
100 const unsigned char* CommandTable::ParseField(const unsigned char *begin,
101 const unsigned char *end)
103 // check if there is enough data for a header
104 const unsigned char *headend = begin + sizeof(CommandTableField);
105 if( headend > end )
106 return headend;
108 const CommandTableField *field = (const CommandTableField *) begin;
110 // advance and check size
111 begin += COMMAND_FIELD_HEADER_SIZE + field->size;
112 if( begin > end ) // if begin==end, we are ok
113 return begin;
115 if( !field->size ) // if field has no size, something's up
116 return begin;
118 Command command;
119 command.Code = field->code;
120 command.Name.assign((const char *)field->name, field->size);
121 Commands.push_back(command);
122 return begin;
125 void CommandTable::Parse(const Data &data, int offset)
127 if( offset >= data.GetSize() )
128 return;
130 const unsigned char *begin = data.GetData() + offset;
131 const unsigned char *end = data.GetData() + data.GetSize();
133 while( begin < end )
134 begin = ParseField(begin, end);
137 void CommandTable::Clear()
139 Commands.clear();
142 unsigned int CommandTable::GetCommand(const std::string &name) const
144 CommandArrayType::const_iterator b = Commands.begin();
145 for( ; b != Commands.end(); b++ )
146 if( b->Name == name )
147 return b->Code;
148 return 0;
151 void CommandTable::Dump(std::ostream &os) const
153 CommandArrayType::const_iterator b = Commands.begin();
154 os << "Command table:\n";
155 for( ; b != Commands.end(); b++ ) {
156 os << " Command: 0x" << setbase(16) << b->Code
157 << " '" << b->Name << "'\n";
164 ///////////////////////////////////////////////////////////////////////////////
165 // DatabaseDatabase class
167 DatabaseDatabase::DatabaseDatabase()
171 DatabaseDatabase::~DatabaseDatabase()
175 template <class RecordType, class FieldType>
176 void DatabaseDatabase::ParseRec(const RecordType &rec, const unsigned char *end)
180 template <class FieldType>
181 const unsigned char* DatabaseDatabase::ParseField(const unsigned char *begin,
182 const unsigned char *end)
184 // check if there is enough data for a header
185 const unsigned char *headend = begin + sizeof(FieldType);
186 if( headend > end )
187 return headend;
189 // get our header
190 const FieldType *field = (const FieldType *) begin;
192 // advance and check size
193 begin += sizeof(FieldType) - sizeof(field->name) + field->nameSize;
194 if( begin > end ) // if begin==end, we are ok
195 return begin;
197 if( !field->nameSize ) // if field has no size, something's up
198 return begin;
200 Database db;
201 db.Number = field->dbNumber;
202 db.RecordCount = field->dbRecordCount;
203 db.Name.assign((const char *)field->name, field->nameSize - 1);
204 Databases.push_back(db);
205 return begin;
208 void DatabaseDatabase::Parse(const Data &data)
210 // check size to make sure we have up to the DBAccess operation byte
211 if( (unsigned int)data.GetSize() < (SB_PACKET_DBACCESS_HEADER_SIZE + 1) )
212 return;
214 MAKE_PACKET(pack, data);
215 const unsigned char *begin = 0;
216 const unsigned char *end = data.GetData() + data.GetSize();
218 switch( pack->data.db.data.dbdb.operation )
220 case SB_DBOP_GET_DBDB:
221 // using the new protocol
222 if( (unsigned int)data.GetSize() > SB_PACKET_DBDB_HEADER_SIZE ) {
223 begin = (const unsigned char *)
224 &pack->data.db.data.dbdb.field[0];
226 // this while check is ok, since ParseField checks
227 // for header size
228 while( begin < end )
229 begin = ParseField<DBDBField>(begin, end);
231 else
232 dout("Contact: not enough data for parsing");
233 break;
235 case SB_DBOP_OLD_GET_DBDB:
236 // using the old protocol
237 if( (unsigned int)data.GetSize() > SB_PACKET_OLD_DBDB_HEADER_SIZE ) {
238 begin = (const unsigned char *)
239 &pack->data.db.data.old_dbdb.field[0];
241 // this while check is ok, since ParseField checks
242 // for header size
243 while( begin < end )
244 begin = ParseField<OldDBDBField>(begin, end);
246 else
247 dout("Contact: not enough data for parsing");
248 break;
250 default:
251 // unknown protocol
252 dout("Unknown protocol");
253 break;
259 void DatabaseDatabase::Clear()
261 Databases.clear();
264 unsigned int DatabaseDatabase::GetDBNumber(const std::string &name) const
266 DatabaseArrayType::const_iterator b = Databases.begin();
267 for( ; b != Databases.end(); b++ )
268 if( b->Name == name )
269 return b->Number;
270 return 0;
273 void DatabaseDatabase::Dump(std::ostream &os) const
275 DatabaseArrayType::const_iterator b = Databases.begin();
276 os << "Database database:\n";
277 for( ; b != Databases.end(); b++ ) {
278 os << " Database: 0x" << setbase(16) << b->Number
279 << " '" << b->Name << "' (records: "
280 << setbase(10) << b->RecordCount << ")\n";
285 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
287 std::vector<UnknownField>::const_iterator
288 ub = unknowns.begin(), ue = unknowns.end();
289 if( ub != ue )
290 os << " Unknowns:\n";
291 for( ; ub != ue; ub++ ) {
292 os << " Type: 0x" << setbase(16)
293 << (unsigned int) ub->type
294 << " Data:\n" << Data(ub->data.data(), ub->data.size());
296 return os;
301 ///////////////////////////////////////////////////////////////////////////////
302 // Contact class
304 // Contact field codes
305 #define CFC_EMAIL 1
306 #define CFC_PHONE 2
307 #define CFC_FAX 3
308 #define CFC_WORK_PHONE 6
309 #define CFC_HOME_PHONE 7
310 #define CFC_MOBILE_PHONE 8
311 #define CFC_PAGER 9
312 #define CFC_PIN 10
313 #define CFC_NAME 32 // used twice, in first/last name order
314 #define CFC_COMPANY 33
315 #define CFC_DEFAULT_COMM_METHOD 34
316 #define CFC_ADDRESS1 35
317 #define CFC_ADDRESS2 36
318 #define CFC_ADDRESS3 37
319 #define CFC_CITY 38
320 #define CFC_PROVINCE 39
321 #define CFC_POSTAL_CODE 40
322 #define CFC_COUNTRY 41
323 #define CFC_TITLE 42
324 #define CFC_PUBLIC_KEY 43
325 #define CFC_GROUP_LINK 52
326 #define CFC_NOTES 64
327 #define CFC_INVALID_FIELD 255
329 // Contact code to field table
330 template <class Record>
331 struct FieldLink
333 int type;
334 char *name;
335 char *ldif;
336 char *objectClass;
337 std::string Record::* strMember; // FIXME - find a more general
338 Message::Address Record::* addrMember; // way to do this...
339 time_t Record::* timeMember;
342 FieldLink<Contact> ContactFieldLinks[] = {
343 { CFC_EMAIL, "Email", "mail",0, &Contact::Email, 0, 0 },
344 { CFC_PHONE, "Phone", 0,0, &Contact::Phone, 0, 0 },
345 { CFC_FAX, "Fax", "facsimileTelephoneNumber",0, &Contact::Fax, 0, 0 },
346 { CFC_WORK_PHONE, "WorkPhone", "telephoneNumber",0, &Contact::WorkPhone, 0, 0 },
347 { CFC_HOME_PHONE, "HomePhone", "homePhone",0, &Contact::HomePhone, 0, 0 },
348 { CFC_MOBILE_PHONE, "MobilePhone","mobile",0, &Contact::MobilePhone, 0, 0 },
349 { CFC_PAGER, "Pager", "pager",0, &Contact::Pager, 0, 0 },
350 { CFC_PIN, "PIN", 0,0, &Contact::PIN, 0, 0 },
351 { CFC_COMPANY, "Company", "o",0, &Contact::Company, 0, 0 },
352 { CFC_DEFAULT_COMM_METHOD,"DefaultCommMethod",0,0, &Contact::DefaultCommunicationsMethod, 0, 0 },
353 { CFC_ADDRESS1, "Address1", 0,0, &Contact::Address1, 0, 0 },
354 { CFC_ADDRESS2, "Address2", 0,0, &Contact::Address2, 0, 0 },
355 { CFC_ADDRESS3, "Address3", 0,0, &Contact::Address3, 0, 0 },
356 { CFC_CITY, "City", "l",0, &Contact::City, 0, 0 },
357 { CFC_PROVINCE, "Province", "st",0, &Contact::Province, 0, 0 },
358 { CFC_POSTAL_CODE, "PostalCode", "postalCode",0, &Contact::PostalCode, 0, 0 },
359 { CFC_COUNTRY, "Country", "c", "country", &Contact::Country, 0, 0 },
360 { CFC_TITLE, "Title", "title",0, &Contact::Title, 0, 0 },
361 { CFC_PUBLIC_KEY, "PublicKey", 0,0, &Contact::PublicKey, 0, 0 },
362 { CFC_NOTES, "Notes", 0,0, &Contact::Notes, 0, 0 },
363 { CFC_INVALID_FIELD,"EndOfList", 0, 0, 0 }
366 size_t Contact::GetOldProtocolRecordSize()
368 return sizeof(Barry::OldContactRecord);
371 size_t Contact::GetProtocolRecordSize()
373 return sizeof(Barry::ContactRecord);
376 Contact::Contact()
380 Contact::~Contact()
384 const unsigned char* Contact::ParseField(const unsigned char *begin,
385 const unsigned char *end)
387 const CommonField *field = (const CommonField *) begin;
389 // advance and check size
390 begin += COMMON_FIELD_HEADER_SIZE + field->size;
391 if( begin > end ) // if begin==end, we are ok
392 return begin;
394 if( !field->size ) // if field has no size, something's up
395 return begin;
397 // cycle through the type table
398 for( FieldLink<Contact> *b = ContactFieldLinks;
399 b->type != CFC_INVALID_FIELD;
400 b++ )
402 if( b->type == field->type ) {
403 std::string &s = this->*(b->strMember);
404 s.assign((const char *)field->data.raw, field->size-1);
405 return begin; // done!
409 // if not found in the type table, check for special handling
410 switch( field->type )
412 case CFC_NAME: {
413 // can be used multiple times, for first/last names
414 std::string *name;
415 if( FirstName.size() )
416 // first name already filled, use last name
417 name = &LastName;
418 else
419 name = &FirstName;
421 name->assign((const char*)field->data.raw, field->size-1);
423 return begin;
425 case CFC_GROUP_LINK:
426 // just add the unique ID to the list
427 GroupLinks.push_back(
428 GroupLink(field->data.link.uniqueId,
429 field->data.link.unknown));
430 return begin;
433 // if still not handled, add to the Unknowns list
434 UnknownField uf;
435 uf.type = field->type;
436 uf.data.assign((const char*)field->data.raw, field->size);
437 Unknowns.push_back(uf);
439 // return new pointer for next field
440 return begin;
443 // this is called by the RecordParser<> class, which checks size for us
444 void Contact::Parse(const Data &data, unsigned int operation)
446 MAKE_PACKET(pack, data);
447 const void *begin = 0;
448 switch( operation )
450 case SB_DBOP_GET_RECORDS:
451 // using the new protocol
452 // save the contact record ID
453 RecordId = pack->data.db.data.contact.uniqueId;
454 begin = &pack->data.db.data.contact.field[0];
455 break;
457 case SB_DBOP_OLD_GET_RECORDS_REPLY:
458 // using the old protocol
459 // save the contact record ID
460 RecordId = pack->data.db.data.old_contact.uniqueId;
461 begin = &pack->data.db.data.old_contact.field[0];
462 break;
465 ParseCommonFields(*this, begin, data.GetData() + data.GetSize());
469 // Build
471 /// Build a raw protocol packet based on data in the class.
473 void Contact::Build(Data &data, unsigned int databaseId) const
475 data.Zap();
477 int size = SB_PACKET_CONTACT_UPLOAD_HEADER_SIZE;
478 unsigned char *pd = data.GetBuffer(size);
479 MAKE_PACKETPTR_BUF(spack, pd);
481 spack->data.db.data.contact_up.operation = SB_DBOP_SET_RECORD;
482 spack->data.db.data.contact_up.databaseId = databaseId;
483 spack->data.db.data.contact_up.unknown = 0;
484 spack->data.db.data.contact_up.uniqueId = RecordId;
485 spack->data.db.data.contact_up.unknown2 = 1;
487 // special fields not in type table
488 BuildField(data, size, CFC_NAME, FirstName);
489 BuildField(data, size, CFC_NAME, LastName);
491 // cycle through the type table
492 for( FieldLink<Contact> *b = ContactFieldLinks;
493 b->type != CFC_INVALID_FIELD;
494 b++ )
496 // print only fields with data
497 const std::string &field = this->*(b->strMember);
498 if( field.size() ) {
499 BuildField(data, size, b->type, field);
503 // save any group links
504 GroupLinksType::const_iterator
505 gb = GroupLinks.begin(), ge = GroupLinks.end();
506 for( ; gb != ge; gb++ ) {
507 Barry::GroupLink link;
508 link.uniqueId = gb->Link;
509 link.unknown = gb->Unknown;
510 BuildField(data, size, CFC_GROUP_LINK, link);
513 // and finally save unknowns
514 UnknownsType::const_iterator
515 ub = Unknowns.begin(), ue = Unknowns.end();
516 for( ; ub != ue; ub++ ) {
517 BuildField(data, size, ub->type, ub->data);
520 data.ReleaseBuffer(size);
524 // GetPostalAddress
526 /// Format a mailing address, handling missing fields.
528 std::string Contact::GetPostalAddress() const
530 std::string address = Address1;
531 if( Address2.size() ) {
532 if( address.size() )
533 address += "\n";
534 address += Address2;
536 if( Address3.size() ) {
537 if( address.size() )
538 address += "\n";
539 address += Address3;
541 if( address.size() )
542 address += "\n";
543 if( City.size() )
544 address += City + " ";
545 if( Province.size() )
546 address += Province + " ";
547 if( Country.size() )
548 address += Country;
549 if( address.size() )
550 address += "\n";
551 if( PostalCode.size() )
552 address += PostalCode;
554 return address;
557 void Contact::Dump(std::ostream &os) const
559 ios::fmtflags oldflags = os.setf(ios::left);
560 char fill = os.fill(' ');
562 os << "Contact: 0x" << setbase(16) << GetID() << "\n";
564 // special fields not in type table
565 os << " " << setw(20) << "FirstName";
566 os << ": " << FirstName << "\n";
567 os << " " << setw(20) << "LastName";
568 os << ": " << LastName << "\n";
570 // cycle through the type table
571 for( FieldLink<Contact> *b = ContactFieldLinks;
572 b->type != CFC_INVALID_FIELD;
573 b++ )
575 // print only fields with data
576 const std::string &field = this->*(b->strMember);
577 if( field.size() ) {
578 os << " " << setw(20) << b->name;
579 os << ": " << field << "\n";
583 // print any group links
584 GroupLinksType::const_iterator
585 gb = GroupLinks.begin(), ge = GroupLinks.end();
586 if( gb != ge )
587 os << " GroupLinks:\n";
588 for( ; gb != ge; gb++ ) {
589 os << " ID: 0x" << setbase(16) << gb->Link << "\n";
592 // and finally print unknowns
593 os << Unknowns;
595 // cleanup the stream
596 os.flags(oldflags);
597 os.fill(fill);
601 // DumpLdif
603 /// Output contact data to os in LDAP LDIF format.
604 /// This is hardcoded for now. Someday we should support mapping
605 /// of fields.
607 void Contact::DumpLdif(std::ostream &os, const std::string &baseDN) const
609 ios::fmtflags oldflags = os.setf(ios::left);
610 char fill = os.fill(' ');
612 if( FirstName.size() == 0 && LastName.size() == 0 )
613 return; // nothing to do
615 std::string FullName = FirstName + " " + LastName;
616 os << "# Contact 0x" << setbase(16) << GetID() << FullName << "\n";
617 os << "dn: " << FullName << "," << baseDN << "\n";
618 os << "objectClass: inetOrgPerson\n";
619 os << "displayName: " << FullName << "\n";
620 os << "cn: " << FullName << "\n";
621 if( LastName.size() )
622 os << "sn: " << LastName << "\n";
623 if( FirstName.size() )
624 os << "givenName: " << FirstName << "\n";
626 // cycle through the type table
627 for( FieldLink<Contact> *b = ContactFieldLinks;
628 b->type != CFC_INVALID_FIELD;
629 b++ )
631 // print only fields with data
632 const std::string &field = this->*(b->strMember);
633 if( b->ldif && field.size() ) {
634 os << b->ldif << ": " << field << "\n";
635 if( b->objectClass )
636 os << "objectClass: " << b->objectClass << "\n";
640 std::string b64;
641 if( Address1.size() && base64_encode(Address1, b64) )
642 os << "street:: " << b64 << "\n";
644 std::string FullAddress = GetPostalAddress();
645 if( FullAddress.size() && base64_encode(FullAddress, b64) )
646 os << "postalAddress:: " << b64 << "\n";
648 if( Notes.size() && base64_encode(Notes, b64) )
649 os << "note:: " << b64 << "\n";
652 // print any group links
653 GroupLinksType::const_iterator
654 gb = GroupLinks.begin(), ge = GroupLinks.end();
655 if( gb != ge )
656 os << " GroupLinks:\n";
657 for( ; gb != ge; gb++ ) {
658 os << " ID: 0x" << setbase(16) << gb->Link << "\n";
662 // last line must be empty
663 os << "\n";
665 // cleanup the stream
666 os.flags(oldflags);
667 os.fill(fill);
672 ///////////////////////////////////////////////////////////////////////////////
673 // Message class
676 // Email / message field codes
677 #define MFC_TO 0x01 // can occur multiple times
678 #define MFC_FROM 0x05
679 #define MFC_SUBJECT 0x0b
680 #define MFC_BODY 0x0c
681 #define MFC_END 0xffff
683 FieldLink<Message> MessageFieldLinks[] = {
684 { MFC_TO, "To", 0, 0, 0, &Message::To, 0 },
685 { MFC_FROM, "From", 0, 0, 0, &Message::From, 0 },
686 { MFC_SUBJECT, "Subject", 0, 0, &Message::Subject, 0, 0 },
687 { MFC_BODY, "Body", 0, 0, &Message::Body, 0, 0 },
688 { MFC_END, "End of List",0, 0, 0, 0, 0 }
691 size_t Message::GetOldProtocolRecordSize()
693 return sizeof(Barry::OldMessageRecord);
696 size_t Message::GetProtocolRecordSize()
698 return sizeof(Barry::MessageRecord);
701 Message::Message()
705 Message::~Message()
709 const unsigned char* Message::ParseField(const unsigned char *begin,
710 const unsigned char *end)
712 const CommonField *field = (const CommonField *) begin;
714 // advance and check size
715 begin += COMMON_FIELD_HEADER_SIZE + field->size;
716 if( begin > end ) // if begin==end, we are ok
717 return begin;
719 if( !field->size ) // if field has no size, something's up
720 return begin;
722 // cycle through the type table
723 for( FieldLink<Message> *b = MessageFieldLinks;
724 b->type != MFC_END;
725 b++ )
727 if( b->type == field->type ) {
728 if( b->strMember ) {
729 // parse regular string
730 std::string &s = this->*(b->strMember);
731 s.assign((const char *)field->data.raw, field->size-1);
732 return begin; // done!
734 else if( b->addrMember ) {
735 // parse email address
736 // get dual name+addr string first
737 const char *fa = (const char*)field->data.addr.addr;
738 std::string dual(fa, field->size - sizeof(field->data.addr.unknown));
740 // assign first string, using null terminator...letting std::string add it for us if it doesn't exist
741 Address &a = this->*(b->addrMember);
742 a.Name = dual.c_str();
744 // assign second string, using first size as starting point
745 a.Email = dual.c_str() + a.Name.size() + 1;
750 return begin;
753 void Message::Parse(const Data &data, unsigned int operation)
755 MAKE_PACKET(pack, data);
756 const void *begin = 0;
757 switch( operation )
759 case SB_DBOP_GET_RECORDS:
760 begin = &pack->data.db.data.message.field[0];
761 break;
763 case SB_DBOP_OLD_GET_RECORDS_REPLY:
764 begin = &pack->data.db.data.old_message.field[0];
765 break;
768 ParseCommonFields(*this, begin, data.GetData() + data.GetSize());
771 void Message::Clear()
773 From.Name.clear();
774 From.Email.clear();
775 To.Name.clear();
776 To.Email.clear();
777 Cc.Name.clear();
778 Cc.Email.clear();
779 Subject.clear();
780 Body.clear();
783 // dump message in mbox format
784 void Message::Dump(std::ostream &os) const
786 // FIXME - use current time until we figure out the date headers
787 time_t fixme = time(NULL);
789 os << "From " << (From.Email.size() ? From.Email.c_str() : "unknown")
790 << " " << ctime(&fixme);
791 os << "Date: " << ctime(&fixme);
792 os << "From: " << From << "\n";
793 if( To.Email.size() )
794 os << "To: " << To << "\n";
795 if( Cc.Email.size() )
796 os << "Cc: " << Cc << "\n";
797 if( Subject.size() )
798 os << "Subject: " << Subject << "\n";
799 os << "\n";
800 for( std::string::const_iterator i = Body.begin();
801 i != Body.end() && *i;
802 i++)
804 if( *i == '\r' )
805 os << '\n';
806 else
807 os << *i;
809 os << "\n\n";
814 ///////////////////////////////////////////////////////////////////////////////
815 // Calendar class
817 // calendar field codes
818 #define CALFC_SUBJECT 0x02
819 #define CALFC_NOTES 0x03
820 #define CALFC_LOCATION 0x04
821 #define CALFC_NOTIFICATION_TIME 0x05
822 #define CALFC_START_TIME 0x06
823 #define CALFC_END_TIME 0x07
824 #define CALFC_RECURRANCE_DATA 0x0c
825 #define CALFC_VERSION_DATA 0x10
826 #define CALFC_NOTIFICATION_DATA 0x1a
827 #define CALFC_END 0xffff
829 FieldLink<Calendar> CalendarFieldLinks[] = {
830 { CALFC_SUBJECT, "Subject", 0, 0, &Calendar::Subject, 0, 0 },
831 { CALFC_NOTES, "Notes", 0, 0, &Calendar::Notes, 0, 0 },
832 { CALFC_LOCATION, "Location", 0, 0, &Calendar::Location, 0, 0 },
833 { CALFC_NOTIFICATION_TIME,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime },
834 { CALFC_START_TIME, "Start Time", 0, 0, 0, 0, &Calendar::StartTime },
835 { CALFC_END_TIME, "End Time", 0, 0, 0, 0, &Calendar::EndTime },
836 { CALFC_END, "End of List",0, 0, 0, 0, 0 }
839 size_t Calendar::GetOldProtocolRecordSize()
841 return sizeof(Barry::OldCalendarRecord);
844 size_t Calendar::GetProtocolRecordSize()
846 return sizeof(Barry::CalendarRecord);
849 Calendar::Calendar()
851 Clear();
854 Calendar::~Calendar()
858 const unsigned char* Calendar::ParseField(const unsigned char *begin,
859 const unsigned char *end)
861 const CommonField *field = (const CommonField *) begin;
863 // advance and check size
864 begin += COMMON_FIELD_HEADER_SIZE + field->size;
865 if( begin > end ) // if begin==end, we are ok
866 return begin;
868 if( !field->size ) // if field has no size, something's up
869 return begin;
871 // cycle through the type table
872 for( FieldLink<Calendar> *b = CalendarFieldLinks;
873 b->type != CALFC_END;
874 b++ )
876 if( b->type == field->type ) {
877 if( b->strMember ) {
878 std::string &s = this->*(b->strMember);
879 s.assign((const char *)field->data.raw, field->size-1);
880 return begin; // done!
882 else if( b->timeMember ) {
883 time_t &t = this->*(b->timeMember);
884 t = min2time(field->data.min1900);
885 return begin;
890 // if still not handled, add to the Unknowns list
891 UnknownField uf;
892 uf.type = field->type;
893 uf.data.assign((const char*)field->data.raw, field->size);
894 Unknowns.push_back(uf);
896 // return new pointer for next field
897 return begin;
900 void Calendar::Parse(const Data &data, unsigned int operation)
902 MAKE_PACKET(pack, data);
903 const void *begin = 0;
904 switch( operation )
906 case SB_DBOP_GET_RECORDS:
907 // using the new protocol
908 // save the contact record ID
909 throw std::logic_error("New Calendar: Not yet implemented");
910 // RecordId = pack->data.db.data.calendar.uniqueId;
911 // begin = &pack->data.db.data.calendar.field[0];
912 break;
914 case SB_DBOP_OLD_GET_RECORDS_REPLY:
915 // using the old protocol
916 // save the contact record ID
917 RecordId = pack->data.db.data.old_calendar.uniqueId;
918 begin = &pack->data.db.data.old_calendar.field[0];
919 break;
922 ParseCommonFields(*this, begin, data.GetData() + data.GetSize());
925 void Calendar::Clear()
927 Subject.clear();
928 Notes.clear();
929 Location.clear();
930 NotificationTime = StartTime = EndTime = 0;
931 Unknowns.clear();
934 void Calendar::Dump(std::ostream &os) const
936 os << "Calendar entry: 0x" << setbase(16) << RecordId << "\n";
938 // cycle through the type table
939 for( const FieldLink<Calendar> *b = CalendarFieldLinks;
940 b->type != CALFC_END;
941 b++ )
943 if( b->strMember ) {
944 const std::string &s = this->*(b->strMember);
945 if( s.size() )
946 os << " " << b->name << ": " << s << "\n";
948 else if( b->timeMember ) {
949 time_t t = this->*(b->timeMember);
950 if( t > 0 )
951 os << " " << b->name << ": " << ctime(&t);
955 // print any unknowns
956 os << Unknowns;
960 } // namespace Barry
963 #ifdef __TEST_MODE__
965 #include <iostream>
967 int main(int argc, char *argv[])
969 if( argc < 2 ) {
970 cerr << "Usage: test <datafile>" << endl;
971 return 1;
974 std::vector<Data> array;
975 if( !LoadDataArray(argv[1], array) ) {
976 cerr << "Unable to load file: " << argv[1] << endl;
977 return 1;
980 cout << "Loaded " << array.size() << " items" << endl;
982 for( std::vector<Data>::iterator b = array.begin(), e = array.end();
983 b != e; b++ )
985 Data &d = *b;
986 // cout << d << endl;
987 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
988 Barry::Contact contact;
989 contact.Parse(d, d.GetData()[6]);
990 cout << contact << endl;
991 contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
993 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
994 Barry::Calendar cal;
995 cal.Parse(d, d.GetData()[6]);
996 cout << cal << endl;
1001 #endif