- changed headers so that any low level protocol-specific sizes and
[barry.git] / src / record.cc
blobf03b95aa3f90f6ea796f4f7ad81cc29b1b2f4997
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);
59 ///////////////////////////////////////////////////////////////////////////////
60 // CommandTable class
63 CommandTable::CommandTable()
67 CommandTable::~CommandTable()
71 const unsigned char* CommandTable::ParseField(const unsigned char *begin,
72 const unsigned char *end)
74 // check if there is enough data for a header
75 const unsigned char *headend = begin + sizeof(CommandTableField);
76 if( headend > end )
77 return headend;
79 const CommandTableField *field = (const CommandTableField *) begin;
81 // advance and check size
82 begin += COMMAND_FIELD_HEADER_SIZE + field->size;
83 if( begin > end ) // if begin==end, we are ok
84 return begin;
86 if( !field->size ) // if field has no size, something's up
87 return begin;
89 Command command;
90 command.Code = field->code;
91 command.Name.assign((const char *)field->name, field->size);
92 Commands.push_back(command);
93 return begin;
96 void CommandTable::Parse(const Data &data, int offset)
98 if( offset >= data.GetSize() )
99 return;
101 const unsigned char *begin = data.GetData() + offset;
102 const unsigned char *end = data.GetData() + data.GetSize();
104 while( begin < end )
105 begin = ParseField(begin, end);
108 void CommandTable::Clear()
110 Commands.clear();
113 unsigned int CommandTable::GetCommand(const std::string &name) const
115 CommandArrayType::const_iterator b = Commands.begin();
116 for( ; b != Commands.end(); b++ )
117 if( b->Name == name )
118 return b->Code;
119 return 0;
122 void CommandTable::Dump(std::ostream &os) const
124 CommandArrayType::const_iterator b = Commands.begin();
125 os << "Command table:\n";
126 for( ; b != Commands.end(); b++ ) {
127 os << " Command: 0x" << setbase(16) << b->Code
128 << " '" << b->Name << "'\n";
135 ///////////////////////////////////////////////////////////////////////////////
136 // DatabaseDatabase class
138 DatabaseDatabase::DatabaseDatabase()
142 DatabaseDatabase::~DatabaseDatabase()
146 template <class RecordType, class FieldType>
147 void DatabaseDatabase::ParseRec(const RecordType &rec, const unsigned char *end)
151 template <class FieldType>
152 const unsigned char* DatabaseDatabase::ParseField(const unsigned char *begin,
153 const unsigned char *end)
155 // check if there is enough data for a header
156 const unsigned char *headend = begin + sizeof(FieldType);
157 if( headend > end )
158 return headend;
160 // get our header
161 const FieldType *field = (const FieldType *) begin;
163 // advance and check size
164 begin += sizeof(FieldType) - sizeof(field->name) + field->nameSize;
165 if( begin > end ) // if begin==end, we are ok
166 return begin;
168 if( !field->nameSize ) // if field has no size, something's up
169 return begin;
171 Database db;
172 db.Number = field->dbNumber;
173 db.RecordCount = field->dbRecordCount;
174 db.Name.assign((const char *)field->name, field->nameSize - 1);
175 Databases.push_back(db);
176 return begin;
179 void DatabaseDatabase::Parse(const Data &data)
181 // check size to make sure we have up to the DBAccess operation byte
182 if( (unsigned int)data.GetSize() < (SB_PACKET_DBACCESS_HEADER_SIZE + 1) )
183 return;
185 MAKE_PACKET(pack, data);
186 const unsigned char *begin = 0;
187 const unsigned char *end = data.GetData() + data.GetSize();
189 switch( pack->data.db.data.dbdb.operation )
191 case SB_DBOP_GET_DBDB:
192 // using the new protocol
193 if( (unsigned int)data.GetSize() > SB_PACKET_DBDB_HEADER_SIZE ) {
194 begin = (const unsigned char *)
195 &pack->data.db.data.dbdb.field[0];
197 // this while check is ok, since ParseField checks
198 // for header size
199 while( begin < end )
200 begin = ParseField<DBDBField>(begin, end);
202 else
203 dout("Contact: not enough data for parsing");
204 break;
206 case SB_DBOP_OLD_GET_DBDB:
207 // using the old protocol
208 if( (unsigned int)data.GetSize() > SB_PACKET_OLD_DBDB_HEADER_SIZE ) {
209 begin = (const unsigned char *)
210 &pack->data.db.data.old_dbdb.field[0];
212 // this while check is ok, since ParseField checks
213 // for header size
214 while( begin < end )
215 begin = ParseField<OldDBDBField>(begin, end);
217 else
218 dout("Contact: not enough data for parsing");
219 break;
221 default:
222 // unknown protocol
223 dout("Unknown protocol");
224 break;
230 void DatabaseDatabase::Clear()
232 Databases.clear();
235 unsigned int DatabaseDatabase::GetDBNumber(const std::string &name) const
237 DatabaseArrayType::const_iterator b = Databases.begin();
238 for( ; b != Databases.end(); b++ )
239 if( b->Name == name )
240 return b->Number;
241 return 0;
244 void DatabaseDatabase::Dump(std::ostream &os) const
246 DatabaseArrayType::const_iterator b = Databases.begin();
247 os << "Database database:\n";
248 for( ; b != Databases.end(); b++ ) {
249 os << " Database: 0x" << setbase(16) << b->Number
250 << " '" << b->Name << "' (records: "
251 << setbase(10) << b->RecordCount << ")\n";
256 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
258 std::vector<UnknownField>::const_iterator
259 ub = unknowns.begin(), ue = unknowns.end();
260 if( ub != ue )
261 os << " Unknowns:\n";
262 for( ; ub != ue; ub++ ) {
263 os << " Type: 0x" << setbase(16)
264 << (unsigned int) ub->type
265 << " Data:\n" << Data(ub->data.data(), ub->data.size());
267 return os;
272 ///////////////////////////////////////////////////////////////////////////////
273 // Contact class
275 // Contact field codes
276 #define CFC_EMAIL 1
277 #define CFC_PHONE 2
278 #define CFC_FAX 3
279 #define CFC_WORK_PHONE 6
280 #define CFC_HOME_PHONE 7
281 #define CFC_MOBILE_PHONE 8
282 #define CFC_PAGER 9
283 #define CFC_PIN 10
284 #define CFC_NAME 32 // used twice, in first/last name order
285 #define CFC_COMPANY 33
286 #define CFC_DEFAULT_COMM_METHOD 34
287 #define CFC_ADDRESS1 35
288 #define CFC_ADDRESS2 36
289 #define CFC_ADDRESS3 37
290 #define CFC_CITY 38
291 #define CFC_PROVINCE 39
292 #define CFC_POSTAL_CODE 40
293 #define CFC_COUNTRY 41
294 #define CFC_TITLE 42
295 #define CFC_PUBLIC_KEY 43
296 #define CFC_GROUP_LINK 52
297 #define CFC_NOTES 64
298 #define CFC_INVALID_FIELD 255
300 // Contact code to field table
301 template <class Record>
302 struct FieldLink
304 int type;
305 char *name;
306 char *ldif;
307 char *objectClass;
308 std::string Record::* strMember; // FIXME - find a more general
309 Message::Address Record::* addrMember; // way to do this...
310 time_t Record::* timeMember;
313 FieldLink<Contact> ContactFieldLinks[] = {
314 { CFC_EMAIL, "Email", "mail",0, &Contact::Email, 0, 0 },
315 { CFC_PHONE, "Phone", 0,0, &Contact::Phone, 0, 0 },
316 { CFC_FAX, "Fax", "facsimileTelephoneNumber",0, &Contact::Fax, 0, 0 },
317 { CFC_WORK_PHONE, "WorkPhone", "telephoneNumber",0, &Contact::WorkPhone, 0, 0 },
318 { CFC_HOME_PHONE, "HomePhone", "homePhone",0, &Contact::HomePhone, 0, 0 },
319 { CFC_MOBILE_PHONE, "MobilePhone","mobile",0, &Contact::MobilePhone, 0, 0 },
320 { CFC_PAGER, "Pager", "pager",0, &Contact::Pager, 0, 0 },
321 { CFC_PIN, "PIN", 0,0, &Contact::PIN, 0, 0 },
322 { CFC_COMPANY, "Company", "o",0, &Contact::Company, 0, 0 },
323 { CFC_DEFAULT_COMM_METHOD,"DefaultCommMethod",0,0, &Contact::DefaultCommunicationsMethod, 0, 0 },
324 { CFC_ADDRESS1, "Address1", 0,0, &Contact::Address1, 0, 0 },
325 { CFC_ADDRESS2, "Address2", 0,0, &Contact::Address2, 0, 0 },
326 { CFC_ADDRESS3, "Address3", 0,0, &Contact::Address3, 0, 0 },
327 { CFC_CITY, "City", "l",0, &Contact::City, 0, 0 },
328 { CFC_PROVINCE, "Province", "st",0, &Contact::Province, 0, 0 },
329 { CFC_POSTAL_CODE, "PostalCode", "postalCode",0, &Contact::PostalCode, 0, 0 },
330 { CFC_COUNTRY, "Country", "c", "country", &Contact::Country, 0, 0 },
331 { CFC_TITLE, "Title", "title",0, &Contact::Title, 0, 0 },
332 { CFC_PUBLIC_KEY, "PublicKey", 0,0, &Contact::PublicKey, 0, 0 },
333 { CFC_NOTES, "Notes", 0,0, &Contact::Notes, 0, 0 },
334 { CFC_INVALID_FIELD,"EndOfList", 0, 0, 0 }
337 size_t Contact::GetOldProtocolRecordSize()
339 return sizeof(Barry::OldContactRecord);
342 size_t Contact::GetProtocolRecordSize()
344 return sizeof(Barry::ContactRecord);
347 Contact::Contact()
351 Contact::~Contact()
355 const unsigned char* Contact::ParseField(const unsigned char *begin,
356 const unsigned char *end)
358 const CommonField *field = (const CommonField *) begin;
360 // advance and check size
361 begin += COMMON_FIELD_HEADER_SIZE + field->size;
362 if( begin > end ) // if begin==end, we are ok
363 return begin;
365 if( !field->size ) // if field has no size, something's up
366 return begin;
368 // cycle through the type table
369 for( FieldLink<Contact> *b = ContactFieldLinks;
370 b->type != CFC_INVALID_FIELD;
371 b++ )
373 if( b->type == field->type ) {
374 std::string &s = this->*(b->strMember);
375 s.assign((const char *)field->data.raw, field->size-1);
376 return begin; // done!
380 // if not found in the type table, check for special handling
381 switch( field->type )
383 case CFC_NAME: {
384 // can be used multiple times, for first/last names
385 std::string *name;
386 if( FirstName.size() )
387 // first name already filled, use last name
388 name = &LastName;
389 else
390 name = &FirstName;
392 name->assign((const char*)field->data.raw, field->size-1);
394 return begin;
396 case CFC_GROUP_LINK:
397 // just add the unique ID to the list
398 GroupLinks.push_back(field->data.link.uniqueId);
399 return begin;
402 // if still not handled, add to the Unknowns list
403 UnknownField uf;
404 uf.type = field->type;
405 uf.data.assign((const char*)field->data.raw, field->size);
406 Unknowns.push_back(uf);
408 // return new pointer for next field
409 return begin;
412 // this is called by the RecordParser<> class, which checks size for us
413 void Contact::Parse(const Data &data, unsigned int operation)
415 MAKE_PACKET(pack, data);
416 const void *begin = 0;
417 switch( operation )
419 case SB_DBOP_GET_RECORDS:
420 // using the new protocol
421 // save the contact record ID
422 m_recordId = pack->data.db.data.contact.uniqueId;
423 begin = &pack->data.db.data.contact.field[0];
424 break;
426 case SB_DBOP_OLD_GET_RECORDS_REPLY:
427 // using the old protocol
428 // save the contact record ID
429 m_recordId = pack->data.db.data.old_contact.uniqueId;
430 begin = &pack->data.db.data.old_contact.field[0];
431 break;
434 ParseCommonFields(*this, begin, data.GetData() + data.GetSize());
438 // GetPostalAddress
440 /// Format a mailing address, handling missing fields.
442 std::string Contact::GetPostalAddress() const
444 std::string address = Address1;
445 if( Address2.size() ) {
446 if( address.size() )
447 address += "\n";
448 address += Address2;
450 if( Address3.size() ) {
451 if( address.size() )
452 address += "\n";
453 address += Address3;
455 if( address.size() )
456 address += "\n";
457 if( City.size() )
458 address += City + " ";
459 if( Province.size() )
460 address += Province + " ";
461 if( Country.size() )
462 address += Country;
463 if( address.size() )
464 address += "\n";
465 if( PostalCode.size() )
466 address += PostalCode;
468 return address;
471 void Contact::Dump(std::ostream &os) const
473 ios::fmtflags oldflags = os.setf(ios::left);
474 char fill = os.fill(' ');
476 os << "Contact: 0x" << setbase(16) << GetID() << "\n";
478 // special fields not in type table
479 os << " " << setw(20) << "FirstName";
480 os << ": " << FirstName << "\n";
481 os << " " << setw(20) << "LastName";
482 os << ": " << LastName << "\n";
484 // cycle through the type table
485 for( FieldLink<Contact> *b = ContactFieldLinks;
486 b->type != CFC_INVALID_FIELD;
487 b++ )
489 // print only fields with data
490 const std::string &field = this->*(b->strMember);
491 if( field.size() ) {
492 os << " " << setw(20) << b->name;
493 os << ": " << field << "\n";
497 // print any group links
498 std::vector<uint64_t>::const_iterator
499 gb = GroupLinks.begin(), ge = GroupLinks.end();
500 if( gb != ge )
501 os << " GroupLinks:\n";
502 for( ; gb != ge; gb++ ) {
503 os << " ID: 0x" << setbase(16) << *gb << "\n";
506 // and finally print unknowns
507 os << Unknowns;
509 // cleanup the stream
510 os.flags(oldflags);
511 os.fill(fill);
515 // DumpLdif
517 /// Output contact data to os in LDAP LDIF format.
518 /// This is hardcoded for now. Someday we should support mapping
519 /// of fields.
521 void Contact::DumpLdif(std::ostream &os, const std::string &baseDN) const
523 ios::fmtflags oldflags = os.setf(ios::left);
524 char fill = os.fill(' ');
526 if( FirstName.size() == 0 && LastName.size() == 0 )
527 return; // nothing to do
529 std::string FullName = FirstName + " " + LastName;
530 os << "# Contact 0x" << setbase(16) << GetID() << FullName << "\n";
531 os << "dn: " << FullName << "," << baseDN << "\n";
532 os << "objectClass: inetOrgPerson\n";
533 os << "displayName: " << FullName << "\n";
534 os << "cn: " << FullName << "\n";
535 if( LastName.size() )
536 os << "sn: " << LastName << "\n";
537 if( FirstName.size() )
538 os << "givenName: " << FirstName << "\n";
540 // cycle through the type table
541 for( FieldLink<Contact> *b = ContactFieldLinks;
542 b->type != CFC_INVALID_FIELD;
543 b++ )
545 // print only fields with data
546 const std::string &field = this->*(b->strMember);
547 if( b->ldif && field.size() ) {
548 os << b->ldif << ": " << field << "\n";
549 if( b->objectClass )
550 os << "objectClass: " << b->objectClass << "\n";
554 std::string b64;
555 if( Address1.size() && base64_encode(Address1, b64) )
556 os << "street:: " << b64 << "\n";
558 std::string FullAddress = GetPostalAddress();
559 if( FullAddress.size() && base64_encode(FullAddress, b64) )
560 os << "postalAddress:: " << b64 << "\n";
562 if( Notes.size() && base64_encode(Notes, b64) )
563 os << "note:: " << b64 << "\n";
566 // print any group links
567 std::vector<uint64_t>::const_iterator
568 gb = GroupLinks.begin(), ge = GroupLinks.end();
569 if( gb != ge )
570 os << " GroupLinks:\n";
571 for( ; gb != ge; gb++ ) {
572 os << " ID: 0x" << setbase(16) << *gb << "\n";
576 // last line must be empty
577 os << "\n";
579 // cleanup the stream
580 os.flags(oldflags);
581 os.fill(fill);
586 ///////////////////////////////////////////////////////////////////////////////
587 // Message class
590 // Email / message field codes
591 #define MFC_TO 0x01 // can occur multiple times
592 #define MFC_FROM 0x05
593 #define MFC_SUBJECT 0x0b
594 #define MFC_BODY 0x0c
595 #define MFC_END 0xffff
597 FieldLink<Message> MessageFieldLinks[] = {
598 { MFC_TO, "To", 0, 0, 0, &Message::To, 0 },
599 { MFC_FROM, "From", 0, 0, 0, &Message::From, 0 },
600 { MFC_SUBJECT, "Subject", 0, 0, &Message::Subject, 0, 0 },
601 { MFC_BODY, "Body", 0, 0, &Message::Body, 0, 0 },
602 { MFC_END, "End of List",0, 0, 0, 0, 0 }
605 size_t Message::GetOldProtocolRecordSize()
607 return sizeof(Barry::OldMessageRecord);
610 size_t Message::GetProtocolRecordSize()
612 return sizeof(Barry::MessageRecord);
615 Message::Message()
619 Message::~Message()
623 const unsigned char* Message::ParseField(const unsigned char *begin,
624 const unsigned char *end)
626 const CommonField *field = (const CommonField *) begin;
628 // advance and check size
629 begin += COMMON_FIELD_HEADER_SIZE + field->size;
630 if( begin > end ) // if begin==end, we are ok
631 return begin;
633 if( !field->size ) // if field has no size, something's up
634 return begin;
636 // cycle through the type table
637 for( FieldLink<Message> *b = MessageFieldLinks;
638 b->type != MFC_END;
639 b++ )
641 if( b->type == field->type ) {
642 if( b->strMember ) {
643 // parse regular string
644 std::string &s = this->*(b->strMember);
645 s.assign((const char *)field->data.raw, field->size-1);
646 return begin; // done!
648 else if( b->addrMember ) {
649 // parse email address
650 // get dual name+addr string first
651 const char *fa = (const char*)field->data.addr.addr;
652 std::string dual(fa, field->size - sizeof(field->data.addr.unknown));
654 // assign first string, using null terminator...letting std::string add it for us if it doesn't exist
655 Address &a = this->*(b->addrMember);
656 a.Name = dual.c_str();
658 // assign second string, using first size as starting point
659 a.Email = dual.c_str() + a.Name.size() + 1;
664 return begin;
667 void Message::Parse(const Data &data, unsigned int operation)
669 MAKE_PACKET(pack, data);
670 const void *begin = 0;
671 switch( operation )
673 case SB_DBOP_GET_RECORDS:
674 begin = &pack->data.db.data.message.field[0];
675 break;
677 case SB_DBOP_OLD_GET_RECORDS_REPLY:
678 begin = &pack->data.db.data.old_message.field[0];
679 break;
682 ParseCommonFields(*this, begin, data.GetData() + data.GetSize());
685 void Message::Clear()
687 From.Name.clear();
688 From.Email.clear();
689 To.Name.clear();
690 To.Email.clear();
691 Cc.Name.clear();
692 Cc.Email.clear();
693 Subject.clear();
694 Body.clear();
697 // dump message in mbox format
698 void Message::Dump(std::ostream &os) const
700 // FIXME - use current time until we figure out the date headers
701 time_t fixme = time(NULL);
703 os << "From " << (From.Email.size() ? From.Email.c_str() : "unknown")
704 << " " << ctime(&fixme);
705 os << "Date: " << ctime(&fixme);
706 os << "From: " << From << "\n";
707 if( To.Email.size() )
708 os << "To: " << To << "\n";
709 if( Cc.Email.size() )
710 os << "Cc: " << Cc << "\n";
711 if( Subject.size() )
712 os << "Subject: " << Subject << "\n";
713 os << "\n";
714 for( std::string::const_iterator i = Body.begin();
715 i != Body.end() && *i;
716 i++)
718 if( *i == '\r' )
719 os << '\n';
720 else
721 os << *i;
723 os << "\n\n";
728 ///////////////////////////////////////////////////////////////////////////////
729 // Calendar class
731 // calendar field codes
732 #define CALFC_SUBJECT 0x02
733 #define CALFC_NOTES 0x03
734 #define CALFC_LOCATION 0x04
735 #define CALFC_NOTIFICATION_TIME 0x05
736 #define CALFC_START_TIME 0x06
737 #define CALFC_END_TIME 0x07
738 #define CALFC_RECURRANCE_DATA 0x0c
739 #define CALFC_VERSION_DATA 0x10
740 #define CALFC_NOTIFICATION_DATA 0x1a
741 #define CALFC_END 0xffff
743 FieldLink<Calendar> CalendarFieldLinks[] = {
744 { CALFC_SUBJECT, "Subject", 0, 0, &Calendar::Subject, 0, 0 },
745 { CALFC_NOTES, "Notes", 0, 0, &Calendar::Notes, 0, 0 },
746 { CALFC_LOCATION, "Location", 0, 0, &Calendar::Location, 0, 0 },
747 { CALFC_NOTIFICATION_TIME,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime },
748 { CALFC_START_TIME, "Start Time", 0, 0, 0, 0, &Calendar::StartTime },
749 { CALFC_END_TIME, "End Time", 0, 0, 0, 0, &Calendar::EndTime },
750 { CALFC_END, "End of List",0, 0, 0, 0, 0 }
753 size_t Calendar::GetOldProtocolRecordSize()
755 return sizeof(Barry::OldCalendarRecord);
758 size_t Calendar::GetProtocolRecordSize()
760 return sizeof(Barry::CalendarRecord);
763 Calendar::Calendar()
765 Clear();
768 Calendar::~Calendar()
772 const unsigned char* Calendar::ParseField(const unsigned char *begin,
773 const unsigned char *end)
775 const CommonField *field = (const CommonField *) begin;
777 // advance and check size
778 begin += COMMON_FIELD_HEADER_SIZE + field->size;
779 if( begin > end ) // if begin==end, we are ok
780 return begin;
782 if( !field->size ) // if field has no size, something's up
783 return begin;
785 // cycle through the type table
786 for( FieldLink<Calendar> *b = CalendarFieldLinks;
787 b->type != CALFC_END;
788 b++ )
790 if( b->type == field->type ) {
791 if( b->strMember ) {
792 std::string &s = this->*(b->strMember);
793 s.assign((const char *)field->data.raw, field->size-1);
794 return begin; // done!
796 else if( b->timeMember ) {
797 time_t &t = this->*(b->timeMember);
798 t = min2time(field->data.min1900);
799 return begin;
804 // if still not handled, add to the Unknowns list
805 UnknownField uf;
806 uf.type = field->type;
807 uf.data.assign((const char*)field->data.raw, field->size);
808 Unknowns.push_back(uf);
810 // return new pointer for next field
811 return begin;
814 void Calendar::Parse(const Data &data, unsigned int operation)
816 MAKE_PACKET(pack, data);
817 const void *begin = 0;
818 switch( operation )
820 case SB_DBOP_GET_RECORDS:
821 // using the new protocol
822 // save the contact record ID
823 throw std::logic_error("New Calendar: Not yet implemented");
824 // m_recordId = pack->data.db.data.calendar.uniqueId;
825 // begin = &pack->data.db.data.calendar.field[0];
826 break;
828 case SB_DBOP_OLD_GET_RECORDS_REPLY:
829 // using the old protocol
830 // save the contact record ID
831 m_recordId = pack->data.db.data.old_calendar.uniqueId;
832 begin = &pack->data.db.data.old_calendar.field[0];
833 break;
836 ParseCommonFields(*this, begin, data.GetData() + data.GetSize());
839 void Calendar::Clear()
841 Subject.clear();
842 Notes.clear();
843 Location.clear();
844 NotificationTime = StartTime = EndTime = 0;
845 Unknowns.clear();
848 void Calendar::Dump(std::ostream &os) const
850 os << "Calendar entry: 0x" << setbase(16) << m_recordId << "\n";
852 // cycle through the type table
853 for( const FieldLink<Calendar> *b = CalendarFieldLinks;
854 b->type != CALFC_END;
855 b++ )
857 if( b->strMember ) {
858 const std::string &s = this->*(b->strMember);
859 if( s.size() )
860 os << " " << b->name << ": " << s << "\n";
862 else if( b->timeMember ) {
863 time_t t = this->*(b->timeMember);
864 if( t > 0 )
865 os << " " << b->name << ": " << ctime(&t);
869 // print any unknowns
870 os << Unknowns;
874 } // namespace Barry
877 #ifdef __TEST_MODE__
879 #include <iostream>
881 int main(int argc, char *argv[])
883 if( argc < 2 ) {
884 cerr << "Usage: test <datafile>" << endl;
885 return 1;
888 std::vector<Data> array;
889 if( !LoadDataArray(argv[1], array) ) {
890 cerr << "Unable to load file: " << argv[1] << endl;
891 return 1;
894 cout << "Loaded " << array.size() << " items" << endl;
896 for( std::vector<Data>::iterator b = array.begin(), e = array.end();
897 b != e; b++ )
899 Data &d = *b;
900 // cout << d << endl;
901 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
902 Barry::Contact contact;
903 contact.Parse(d, d.GetData()[6]);
904 cout << contact << endl;
905 contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
907 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
908 Barry::Calendar cal;
909 cal.Parse(d, d.GetData()[6]);
910 cout << cal << endl;
915 #endif