3 /// Blackberry database record classes. Help translate data
4 /// from data packets to useful structurs, and back.
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.
25 #include "protostructs.h"
34 #define __DEBUG_MODE__
41 std::ostream
& operator<<(std::ostream
&os
, const Message::Address
&msga
) {
42 os
<< msga
.Name
.c_str() << " <" << msga
.Email
.c_str() << ">";
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
;
68 memcpy(field
->data
.raw
, str
.c_str(), strsize
);
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
;
82 field
->data
.link
= link
;
88 ///////////////////////////////////////////////////////////////////////////////
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
);
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
115 if( !field
->size
) // if field has no size, something's up
119 command
.Code
= field
->code
;
120 command
.Name
.assign((const char *)field
->name
, field
->size
);
121 Commands
.push_back(command
);
125 void CommandTable::Parse(const Data
&data
, int offset
)
127 if( offset
>= data
.GetSize() )
130 const unsigned char *begin
= data
.GetData() + offset
;
131 const unsigned char *end
= data
.GetData() + data
.GetSize();
134 begin
= ParseField(begin
, end
);
137 void CommandTable::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
)
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
);
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
197 if( !field
->nameSize
) // if field has no size, something's up
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
);
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) )
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
229 begin
= ParseField
<DBDBField
>(begin
, end
);
232 dout("Contact: not enough data for parsing");
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
244 begin
= ParseField
<OldDBDBField
>(begin
, end
);
247 dout("Contact: not enough data for parsing");
252 dout("Unknown protocol");
259 void DatabaseDatabase::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
)
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();
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());
301 ///////////////////////////////////////////////////////////////////////////////
304 // Contact field codes
308 #define CFC_WORK_PHONE 6
309 #define CFC_HOME_PHONE 7
310 #define CFC_MOBILE_PHONE 8
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
320 #define CFC_PROVINCE 39
321 #define CFC_POSTAL_CODE 40
322 #define CFC_COUNTRY 41
324 #define CFC_PUBLIC_KEY 43
325 #define CFC_GROUP_LINK 52
327 #define CFC_INVALID_FIELD 255
329 // Contact code to field table
330 template <class Record
>
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
);
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
394 if( !field
->size
) // if field has no size, something's up
397 // cycle through the type table
398 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
399 b
->type
!= CFC_INVALID_FIELD
;
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
)
413 // can be used multiple times, for first/last names
415 if( FirstName
.size() )
416 // first name already filled, use last name
421 name
->assign((const char*)field
->data
.raw
, field
->size
-1);
426 // just add the unique ID to the list
427 GroupLinks
.push_back(
428 GroupLink(field
->data
.link
.uniqueId
,
429 field
->data
.link
.unknown
));
433 // if still not handled, add to the Unknowns list
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
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;
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];
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];
465 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
471 /// Build a raw protocol packet based on data in the class.
473 void Contact::Build(Data
&data
, unsigned int databaseId
) const
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
;
496 // print only fields with data
497 const std::string
&field
= this->*(b
->strMember
);
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
);
526 /// Format a mailing address, handling missing fields.
528 std::string
Contact::GetPostalAddress() const
530 std::string address
= Address1
;
531 if( Address2
.size() ) {
536 if( Address3
.size() ) {
544 address
+= City
+ " ";
545 if( Province
.size() )
546 address
+= Province
+ " ";
551 if( PostalCode
.size() )
552 address
+= PostalCode
;
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
;
575 // print only fields with data
576 const std::string
&field
= this->*(b
->strMember
);
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();
587 os
<< " GroupLinks:\n";
588 for( ; gb
!= ge
; gb
++ ) {
589 os
<< " ID: 0x" << setbase(16) << gb
->Link
<< "\n";
592 // and finally print unknowns
595 // cleanup the stream
603 /// Output contact data to os in LDAP LDIF format.
604 /// This is hardcoded for now. Someday we should support mapping
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
;
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";
636 os
<< "objectClass: " << b
->objectClass
<< "\n";
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();
656 os << " GroupLinks:\n";
657 for( ; gb != ge; gb++ ) {
658 os << " ID: 0x" << setbase(16) << gb->Link << "\n";
662 // last line must be empty
665 // cleanup the stream
672 ///////////////////////////////////////////////////////////////////////////////
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
);
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
719 if( !field
->size
) // if field has no size, something's up
722 // cycle through the type table
723 for( FieldLink
<Message
> *b
= MessageFieldLinks
;
727 if( b
->type
== field
->type
) {
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;
753 void Message::Parse(const Data
&data
, unsigned int operation
)
755 MAKE_PACKET(pack
, data
);
756 const void *begin
= 0;
759 case SB_DBOP_GET_RECORDS
:
760 begin
= &pack
->data
.db
.data
.message
.field
[0];
763 case SB_DBOP_OLD_GET_RECORDS_REPLY
:
764 begin
= &pack
->data
.db
.data
.old_message
.field
[0];
768 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
771 void Message::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";
798 os
<< "Subject: " << Subject
<< "\n";
800 for( std::string::const_iterator i
= Body
.begin();
801 i
!= Body
.end() && *i
;
814 ///////////////////////////////////////////////////////////////////////////////
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
);
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
868 if( !field
->size
) // if field has no size, something's up
871 // cycle through the type table
872 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
873 b
->type
!= CALFC_END
;
876 if( b
->type
== field
->type
) {
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
);
890 // if still not handled, add to the Unknowns list
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
900 void Calendar::Parse(const Data
&data
, unsigned int operation
)
902 MAKE_PACKET(pack
, data
);
903 const void *begin
= 0;
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];
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];
922 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
925 void Calendar::Clear()
930 NotificationTime
= StartTime
= EndTime
= 0;
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
;
944 const std::string
&s
= this->*(b
->strMember
);
946 os
<< " " << b
->name
<< ": " << s
<< "\n";
948 else if( b
->timeMember
) {
949 time_t t
= this->*(b
->timeMember
);
951 os
<< " " << b
->name
<< ": " << ctime(&t
);
955 // print any unknowns
967 int main(int argc
, char *argv
[])
970 cerr
<< "Usage: test <datafile>" << endl
;
974 std::vector
<Data
> array
;
975 if( !LoadDataArray(argv
[1], array
) ) {
976 cerr
<< "Unable to load file: " << argv
[1] << endl
;
980 cout
<< "Loaded " << array
.size() << " items" << endl
;
982 for( std::vector
<Data
>::iterator b
= array
.begin(), e
= array
.end();
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 ) {
995 cal
.Parse(d
, d
.GetData()[6]);