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
);
59 ///////////////////////////////////////////////////////////////////////////////
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
);
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
86 if( !field
->size
) // if field has no size, something's up
90 command
.Code
= field
->code
;
91 command
.Name
.assign((const char *)field
->name
, field
->size
);
92 Commands
.push_back(command
);
96 void CommandTable::Parse(const Data
&data
, int offset
)
98 if( offset
>= data
.GetSize() )
101 const unsigned char *begin
= data
.GetData() + offset
;
102 const unsigned char *end
= data
.GetData() + data
.GetSize();
105 begin
= ParseField(begin
, end
);
108 void CommandTable::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
)
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
);
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
168 if( !field
->nameSize
) // if field has no size, something's up
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
);
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) )
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
200 begin
= ParseField
<DBDBField
>(begin
, end
);
203 dout("Contact: not enough data for parsing");
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
215 begin
= ParseField
<OldDBDBField
>(begin
, end
);
218 dout("Contact: not enough data for parsing");
223 dout("Unknown protocol");
230 void DatabaseDatabase::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
)
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();
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());
272 ///////////////////////////////////////////////////////////////////////////////
275 // Contact field codes
279 #define CFC_WORK_PHONE 6
280 #define CFC_HOME_PHONE 7
281 #define CFC_MOBILE_PHONE 8
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
291 #define CFC_PROVINCE 39
292 #define CFC_POSTAL_CODE 40
293 #define CFC_COUNTRY 41
295 #define CFC_PUBLIC_KEY 43
296 #define CFC_GROUP_LINK 52
298 #define CFC_INVALID_FIELD 255
300 // Contact code to field table
301 template <class Record
>
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
);
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
365 if( !field
->size
) // if field has no size, something's up
368 // cycle through the type table
369 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
370 b
->type
!= CFC_INVALID_FIELD
;
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
)
384 // can be used multiple times, for first/last names
386 if( FirstName
.size() )
387 // first name already filled, use last name
392 name
->assign((const char*)field
->data
.raw
, field
->size
-1);
397 // just add the unique ID to the list
398 GroupLinks
.push_back(field
->data
.link
.uniqueId
);
402 // if still not handled, add to the Unknowns list
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
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;
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];
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];
434 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
440 /// Format a mailing address, handling missing fields.
442 std::string
Contact::GetPostalAddress() const
444 std::string address
= Address1
;
445 if( Address2
.size() ) {
450 if( Address3
.size() ) {
458 address
+= City
+ " ";
459 if( Province
.size() )
460 address
+= Province
+ " ";
465 if( PostalCode
.size() )
466 address
+= PostalCode
;
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
;
489 // print only fields with data
490 const std::string
&field
= this->*(b
->strMember
);
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();
501 os
<< " GroupLinks:\n";
502 for( ; gb
!= ge
; gb
++ ) {
503 os
<< " ID: 0x" << setbase(16) << *gb
<< "\n";
506 // and finally print unknowns
509 // cleanup the stream
517 /// Output contact data to os in LDAP LDIF format.
518 /// This is hardcoded for now. Someday we should support mapping
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
;
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";
550 os
<< "objectClass: " << b
->objectClass
<< "\n";
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();
570 os << " GroupLinks:\n";
571 for( ; gb != ge; gb++ ) {
572 os << " ID: 0x" << setbase(16) << *gb << "\n";
576 // last line must be empty
579 // cleanup the stream
586 ///////////////////////////////////////////////////////////////////////////////
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
);
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
633 if( !field
->size
) // if field has no size, something's up
636 // cycle through the type table
637 for( FieldLink
<Message
> *b
= MessageFieldLinks
;
641 if( b
->type
== field
->type
) {
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;
667 void Message::Parse(const Data
&data
, unsigned int operation
)
669 MAKE_PACKET(pack
, data
);
670 const void *begin
= 0;
673 case SB_DBOP_GET_RECORDS
:
674 begin
= &pack
->data
.db
.data
.message
.field
[0];
677 case SB_DBOP_OLD_GET_RECORDS_REPLY
:
678 begin
= &pack
->data
.db
.data
.old_message
.field
[0];
682 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
685 void Message::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";
712 os
<< "Subject: " << Subject
<< "\n";
714 for( std::string::const_iterator i
= Body
.begin();
715 i
!= Body
.end() && *i
;
728 ///////////////////////////////////////////////////////////////////////////////
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
);
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
782 if( !field
->size
) // if field has no size, something's up
785 // cycle through the type table
786 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
787 b
->type
!= CALFC_END
;
790 if( b
->type
== field
->type
) {
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
);
804 // if still not handled, add to the Unknowns list
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
814 void Calendar::Parse(const Data
&data
, unsigned int operation
)
816 MAKE_PACKET(pack
, data
);
817 const void *begin
= 0;
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];
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];
836 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
839 void Calendar::Clear()
844 NotificationTime
= StartTime
= EndTime
= 0;
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
;
858 const std::string
&s
= this->*(b
->strMember
);
860 os
<< " " << b
->name
<< ": " << s
<< "\n";
862 else if( b
->timeMember
) {
863 time_t t
= this->*(b
->timeMember
);
865 os
<< " " << b
->name
<< ": " << ctime(&t
);
869 // print any unknowns
881 int main(int argc
, char *argv
[])
884 cerr
<< "Usage: test <datafile>" << endl
;
888 std::vector
<Data
> array
;
889 if( !LoadDataArray(argv
[1], array
) ) {
890 cerr
<< "Unable to load file: " << argv
[1] << endl
;
894 cout
<< "Loaded " << array
.size() << " items" << endl
;
896 for( std::vector
<Data
>::iterator b
= array
.begin(), e
= array
.end();
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 ) {
909 cal
.Parse(d
, d
.GetData()[6]);