3 /// Blackberry database record classes. Help translate data
4 /// from data packets to useful structurs, and back.
8 Copyright (C) 2005-2006, 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"
35 #define __DEBUG_MODE__
42 std::ostream
& operator<<(std::ostream
&os
, const Message::Address
&msga
) {
43 os
<< msga
.Name
.c_str() << " <" << msga
.Email
.c_str() << ">";
49 template <class Record
>
50 void ParseCommonFields(Record
&rec
, const void *begin
, const void *end
)
52 const unsigned char *b
= (const unsigned char*) begin
;
53 const unsigned char *e
= (const unsigned char*) end
;
55 while( (b
+ COMMON_FIELD_HEADER_SIZE
) < e
)
56 b
= rec
.ParseField(b
, e
);
59 void BuildField1900(Data
&data
, size_t &size
, uint8_t type
, time_t t
)
61 size_t timesize
= COMMON_FIELD_MIN1900_SIZE
;
62 size_t fieldsize
= COMMON_FIELD_HEADER_SIZE
+ timesize
;
63 unsigned char *pd
= data
.GetBuffer(size
+ fieldsize
) + size
;
64 CommonField
*field
= (CommonField
*) pd
;
66 field
->size
= timesize
;
68 field
->u
.min1900
= time2min(t
);
73 void BuildField(Data
&data
, size_t &size
, uint8_t type
, char c
)
76 size_t fieldsize
= COMMON_FIELD_HEADER_SIZE
+ strsize
;
77 unsigned char *pd
= data
.GetBuffer(size
+ fieldsize
) + size
;
78 CommonField
*field
= (CommonField
*) pd
;
80 field
->size
= strsize
;
82 memcpy(field
->u
.raw
, &c
, strsize
);
87 void BuildField(Data
&data
, size_t &size
, uint8_t type
, const std::string
&str
)
89 // include null terminator
90 size_t strsize
= str
.size() + 1;
91 size_t fieldsize
= COMMON_FIELD_HEADER_SIZE
+ strsize
;
92 unsigned char *pd
= data
.GetBuffer(size
+ fieldsize
) + size
;
93 CommonField
*field
= (CommonField
*) pd
;
95 field
->size
= strsize
;
97 memcpy(field
->u
.raw
, str
.c_str(), strsize
);
102 void BuildField(Data
&data
, size_t &size
, uint8_t type
, const Barry::GroupLink
&link
)
104 size_t linksize
= sizeof(Barry::GroupLink
);
105 size_t fieldsize
= COMMON_FIELD_HEADER_SIZE
+ linksize
;
106 unsigned char *pd
= data
.GetBuffer(size
+ fieldsize
) + size
;
107 CommonField
*field
= (CommonField
*) pd
;
109 field
->size
= linksize
;
111 field
->u
.link
= link
;
117 ///////////////////////////////////////////////////////////////////////////////
118 // CommandTable class
121 CommandTable::CommandTable()
125 CommandTable::~CommandTable()
129 const unsigned char* CommandTable::ParseField(const unsigned char *begin
,
130 const unsigned char *end
)
132 // check if there is enough data for a header
133 const unsigned char *headend
= begin
+ sizeof(CommandTableField
);
137 const CommandTableField
*field
= (const CommandTableField
*) begin
;
139 // advance and check size
140 begin
+= COMMAND_FIELD_HEADER_SIZE
+ field
->size
;
141 if( begin
> end
) // if begin==end, we are ok
144 if( !field
->size
) // if field has no size, something's up
148 command
.Code
= field
->code
;
149 command
.Name
.assign((const char *)field
->name
, field
->size
);
150 Commands
.push_back(command
);
154 void CommandTable::Parse(const Data
&data
, size_t offset
)
156 if( offset
>= data
.GetSize() )
159 const unsigned char *begin
= data
.GetData() + offset
;
160 const unsigned char *end
= data
.GetData() + data
.GetSize();
163 begin
= ParseField(begin
, end
);
166 void CommandTable::Clear()
171 unsigned int CommandTable::GetCommand(const std::string
&name
) const
173 CommandArrayType::const_iterator b
= Commands
.begin();
174 for( ; b
!= Commands
.end(); b
++ )
175 if( b
->Name
== name
)
180 void CommandTable::Dump(std::ostream
&os
) const
182 CommandArrayType::const_iterator b
= Commands
.begin();
183 os
<< "Command table:\n";
184 for( ; b
!= Commands
.end(); b
++ ) {
185 os
<< " Command: 0x" << setbase(16) << b
->Code
186 << " '" << b
->Name
<< "'\n";
193 ///////////////////////////////////////////////////////////////////////////////
194 // DatabaseDatabase class
196 DatabaseDatabase::DatabaseDatabase()
200 DatabaseDatabase::~DatabaseDatabase()
204 template <class RecordType
, class FieldType
>
205 void DatabaseDatabase::ParseRec(const RecordType
&rec
, const unsigned char *end
)
209 template <class FieldType
>
210 const unsigned char* DatabaseDatabase::ParseField(const unsigned char *begin
,
211 const unsigned char *end
)
213 // check if there is enough data for a header
214 const unsigned char *headend
= begin
+ sizeof(FieldType
);
219 const FieldType
*field
= (const FieldType
*) begin
;
221 // advance and check size
222 begin
+= sizeof(FieldType
) - sizeof(field
->name
) + field
->nameSize
;
223 if( begin
> end
) // if begin==end, we are ok
226 if( !field
->nameSize
) // if field has no size, something's up
230 db
.Number
= field
->dbNumber
;
231 db
.RecordCount
= field
->dbRecordCount
;
232 db
.Name
.assign((const char *)field
->name
, field
->nameSize
- 1);
233 Databases
.push_back(db
);
237 void DatabaseDatabase::Parse(const Data
&data
)
239 // check size to make sure we have up to the DBAccess operation byte
240 if( data
.GetSize() < (SB_PACKET_DBACCESS_HEADER_SIZE
+ 1) )
243 MAKE_PACKET(pack
, data
);
244 const unsigned char *begin
= 0;
245 const unsigned char *end
= data
.GetData() + data
.GetSize();
247 switch( pack
->u
.db
.u
.dbdb
.operation
)
249 case SB_DBOP_GET_DBDB
:
250 // using the new protocol
251 if( data
.GetSize() > SB_PACKET_DBDB_HEADER_SIZE
) {
252 begin
= (const unsigned char *)
253 &pack
->u
.db
.u
.dbdb
.field
[0];
255 // this while check is ok, since ParseField checks
258 begin
= ParseField
<DBDBField
>(begin
, end
);
261 dout("Contact: not enough data for parsing");
264 case SB_DBOP_OLD_GET_DBDB
:
265 // using the old protocol
266 if( data
.GetSize() > SB_PACKET_OLD_DBDB_HEADER_SIZE
) {
267 begin
= (const unsigned char *)
268 &pack
->u
.db
.u
.old_dbdb
.field
[0];
270 // this while check is ok, since ParseField checks
273 begin
= ParseField
<OldDBDBField
>(begin
, end
);
276 dout("Contact: not enough data for parsing");
281 dout("Unknown protocol");
288 void DatabaseDatabase::Clear()
293 bool DatabaseDatabase::GetDBNumber(const std::string
&name
,
294 unsigned int &number
) const
296 DatabaseArrayType::const_iterator b
= Databases
.begin();
297 for( ; b
!= Databases
.end(); b
++ )
298 if( b
->Name
== name
) {
305 bool DatabaseDatabase::GetDBName(unsigned int number
,
306 std::string
&name
) const
308 DatabaseArrayType::const_iterator b
= Databases
.begin();
309 for( ; b
!= Databases
.end(); b
++ )
310 if( b
->Number
== number
) {
317 void DatabaseDatabase::Dump(std::ostream
&os
) const
319 DatabaseArrayType::const_iterator b
= Databases
.begin();
320 os
<< "Database database:\n";
321 for( ; b
!= Databases
.end(); b
++ ) {
322 os
<< " Database: 0x" << setbase(16) << b
->Number
323 << " '" << b
->Name
<< "' (records: "
324 << setbase(10) << b
->RecordCount
<< ")\n";
329 std::ostream
& operator<< (std::ostream
&os
, const std::vector
<UnknownField
> &unknowns
)
331 std::vector
<UnknownField
>::const_iterator
332 ub
= unknowns
.begin(), ue
= unknowns
.end();
334 os
<< " Unknowns:\n";
335 for( ; ub
!= ue
; ub
++ ) {
336 os
<< " Type: 0x" << setbase(16)
337 << (unsigned int) ub
->type
338 << " Data:\n" << Data(ub
->data
.data(), ub
->data
.size());
345 ///////////////////////////////////////////////////////////////////////////////
348 // Contact field codes
352 #define CFC_WORK_PHONE 6
353 #define CFC_HOME_PHONE 7
354 #define CFC_MOBILE_PHONE 8
357 #define CFC_NAME 32 // used twice, in first/last name order
358 #define CFC_COMPANY 33
359 #define CFC_DEFAULT_COMM_METHOD 34
360 #define CFC_ADDRESS1 35
361 #define CFC_ADDRESS2 36
362 #define CFC_ADDRESS3 37
364 #define CFC_PROVINCE 39
365 #define CFC_POSTAL_CODE 40
366 #define CFC_COUNTRY 41
368 #define CFC_PUBLIC_KEY 43
369 #define CFC_GROUP_FLAG 44
370 #define CFC_GROUP_LINK 52
372 #define CFC_INVALID_FIELD 255
374 // Contact code to field table
375 template <class Record
>
382 std::string
Record::* strMember
; // FIXME - find a more general
383 Message::Address
Record::* addrMember
; // way to do this...
384 time_t Record::* timeMember
;
387 FieldLink
<Contact
> ContactFieldLinks
[] = {
388 { CFC_EMAIL
, "Email", "mail",0, &Contact::Email
, 0, 0 },
389 { CFC_PHONE
, "Phone", 0,0, &Contact::Phone
, 0, 0 },
390 { CFC_FAX
, "Fax", "facsimileTelephoneNumber",0, &Contact::Fax
, 0, 0 },
391 { CFC_WORK_PHONE
, "WorkPhone", "telephoneNumber",0, &Contact::WorkPhone
, 0, 0 },
392 { CFC_HOME_PHONE
, "HomePhone", "homePhone",0, &Contact::HomePhone
, 0, 0 },
393 { CFC_MOBILE_PHONE
, "MobilePhone","mobile",0, &Contact::MobilePhone
, 0, 0 },
394 { CFC_PAGER
, "Pager", "pager",0, &Contact::Pager
, 0, 0 },
395 { CFC_PIN
, "PIN", 0,0, &Contact::PIN
, 0, 0 },
396 { CFC_COMPANY
, "Company", "o",0, &Contact::Company
, 0, 0 },
397 { CFC_DEFAULT_COMM_METHOD
,"DefaultCommMethod",0,0, &Contact::DefaultCommunicationsMethod
, 0, 0 },
398 { CFC_ADDRESS1
, "Address1", 0,0, &Contact::Address1
, 0, 0 },
399 { CFC_ADDRESS2
, "Address2", 0,0, &Contact::Address2
, 0, 0 },
400 { CFC_ADDRESS3
, "Address3", 0,0, &Contact::Address3
, 0, 0 },
401 { CFC_CITY
, "City", "l",0, &Contact::City
, 0, 0 },
402 { CFC_PROVINCE
, "Province", "st",0, &Contact::Province
, 0, 0 },
403 { CFC_POSTAL_CODE
, "PostalCode", "postalCode",0, &Contact::PostalCode
, 0, 0 },
404 { CFC_COUNTRY
, "Country", "c", "country", &Contact::Country
, 0, 0 },
405 { CFC_TITLE
, "Title", "title",0, &Contact::Title
, 0, 0 },
406 { CFC_PUBLIC_KEY
, "PublicKey", 0,0, &Contact::PublicKey
, 0, 0 },
407 { CFC_NOTES
, "Notes", 0,0, &Contact::Notes
, 0, 0 },
408 { CFC_INVALID_FIELD
,"EndOfList", 0, 0, 0 }
411 size_t Contact::GetOldProtocolRecordSize()
413 return sizeof(Barry::OldContactRecord
);
416 size_t Contact::GetProtocolRecordSize()
418 return sizeof(Barry::ContactRecord
);
430 const unsigned char* Contact::ParseField(const unsigned char *begin
,
431 const unsigned char *end
)
433 const CommonField
*field
= (const CommonField
*) begin
;
435 // advance and check size
436 begin
+= COMMON_FIELD_HEADER_SIZE
+ field
->size
;
437 if( begin
> end
) // if begin==end, we are ok
440 if( !field
->size
) // if field has no size, something's up
443 // cycle through the type table
444 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
445 b
->type
!= CFC_INVALID_FIELD
;
448 if( b
->type
== field
->type
) {
449 std::string
&s
= this->*(b
->strMember
);
450 s
.assign((const char *)field
->u
.raw
, field
->size
-1);
451 return begin
; // done!
455 // if not found in the type table, check for special handling
456 switch( field
->type
)
459 // can be used multiple times, for first/last names
461 if( FirstName
.size() )
462 // first name already filled, use last name
467 name
->assign((const char*)field
->u
.raw
, field
->size
-1);
472 // just add the unique ID to the list
473 GroupLinks
.push_back(
474 GroupLink(field
->u
.link
.uniqueId
,
475 field
->u
.link
.unknown
));
479 // ignore the group flag... the presense of group link items
480 // behaves as the flag in this class
484 // if still not handled, add to the Unknowns list
486 uf
.type
= field
->type
;
487 uf
.data
.assign((const char*)field
->u
.raw
, field
->size
);
488 Unknowns
.push_back(uf
);
490 // return new pointer for next field
494 // this is called by the RecordParser<> class, which checks size for us
495 void Contact::Parse(const Data
&data
, size_t offset
, unsigned int operation
)
497 const void *begin
= 0;
500 case SB_DBOP_GET_RECORDS
:
502 // using the new protocol
503 // save the contact record ID
504 MAKE_RECORD(const Barry::ContactRecord
, contact
, data
, offset
);
505 RecordId
= contact
->uniqueId
;
506 begin
= &contact
->field
[0];
510 case SB_DBOP_OLD_GET_RECORDS_REPLY
:
512 // using the old protocol
513 // save the contact record ID
514 MAKE_RECORD(const Barry::OldContactRecord
, contact
, data
, offset
);
515 RecordId
= contact
->uniqueId
;
516 begin
= &contact
->field
[0];
521 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
527 /// Build a raw protocol packet based on data in the class.
529 void Contact::Build(Data
&data
, size_t offset
) const
533 // uploading always seems to use the old record
534 size_t size
= offset
+ OLD_CONTACT_RECORD_HEADER_SIZE
;
535 unsigned char *pd
= data
.GetBuffer(size
);
536 MAKE_RECORD_PTR(Barry::OldContactRecord
, contact
, pd
, offset
);
538 contact
->uniqueId
= RecordId
;
539 contact
->unknown
= 1;
541 // check if this is a group link record, and if so, output
543 if( GroupLinks
.size() )
544 BuildField(data
, size
, CFC_GROUP_FLAG
, 'G');
546 // special fields not in type table
547 if( FirstName
.size() )
548 BuildField(data
, size
, CFC_NAME
, FirstName
);
549 if( LastName
.size() )
550 BuildField(data
, size
, CFC_NAME
, LastName
);
552 // cycle through the type table
553 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
554 b
->type
!= CFC_INVALID_FIELD
;
557 // print only fields with data
558 const std::string
&field
= this->*(b
->strMember
);
560 BuildField(data
, size
, b
->type
, field
);
564 // save any group links
565 GroupLinksType::const_iterator
566 gb
= GroupLinks
.begin(), ge
= GroupLinks
.end();
567 for( ; gb
!= ge
; gb
++ ) {
568 Barry::GroupLink link
;
569 link
.uniqueId
= gb
->Link
;
570 link
.unknown
= gb
->Unknown
;
571 BuildField(data
, size
, CFC_GROUP_LINK
, link
);
574 // and finally save unknowns
575 UnknownsType::const_iterator
576 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
577 for( ; ub
!= ue
; ub
++ ) {
578 BuildField(data
, size
, ub
->type
, ub
->data
);
581 data
.ReleaseBuffer(size
);
584 void Contact::Clear()
597 DefaultCommunicationsMethod
.clear();
616 /// Format a mailing address, handling missing fields.
618 std::string
Contact::GetPostalAddress() const
620 std::string address
= Address1
;
621 if( Address2
.size() ) {
626 if( Address3
.size() ) {
634 address
+= City
+ " ";
635 if( Province
.size() )
636 address
+= Province
+ " ";
641 if( PostalCode
.size() )
642 address
+= PostalCode
;
647 void Contact::Dump(std::ostream
&os
) const
649 ios::fmtflags oldflags
= os
.setf(ios::left
);
650 char fill
= os
.fill(' ');
652 os
<< "Contact: 0x" << setbase(16) << GetID() << "\n";
654 // special fields not in type table
655 os
<< " " << setw(20) << "FirstName";
656 os
<< ": " << FirstName
<< "\n";
657 os
<< " " << setw(20) << "LastName";
658 os
<< ": " << LastName
<< "\n";
660 // cycle through the type table
661 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
662 b
->type
!= CFC_INVALID_FIELD
;
665 // print only fields with data
666 const std::string
&field
= this->*(b
->strMember
);
668 os
<< " " << setw(20) << b
->name
;
669 os
<< ": " << field
<< "\n";
673 // print any group links
674 GroupLinksType::const_iterator
675 gb
= GroupLinks
.begin(), ge
= GroupLinks
.end();
677 os
<< " GroupLinks:\n";
678 for( ; gb
!= ge
; gb
++ ) {
679 os
<< " ID: 0x" << setbase(16) << gb
->Link
<< "\n";
682 // and finally print unknowns
685 // cleanup the stream
693 /// Output contact data to os in LDAP LDIF format.
694 /// This is hardcoded for now. Someday we should support mapping
697 void Contact::DumpLdif(std::ostream
&os
, const std::string
&baseDN
) const
699 ios::fmtflags oldflags
= os
.setf(ios::left
);
700 char fill
= os
.fill(' ');
702 if( FirstName
.size() == 0 && LastName
.size() == 0 )
703 return; // nothing to do
705 std::string FullName
= FirstName
+ " " + LastName
;
706 os
<< "# Contact 0x" << setbase(16) << GetID() << FullName
<< "\n";
707 os
<< "dn: " << FullName
<< "," << baseDN
<< "\n";
708 os
<< "objectClass: inetOrgPerson\n";
709 os
<< "displayName: " << FullName
<< "\n";
710 os
<< "cn: " << FullName
<< "\n";
711 if( LastName
.size() )
712 os
<< "sn: " << LastName
<< "\n";
713 if( FirstName
.size() )
714 os
<< "givenName: " << FirstName
<< "\n";
716 // cycle through the type table
717 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
718 b
->type
!= CFC_INVALID_FIELD
;
721 // print only fields with data
722 const std::string
&field
= this->*(b
->strMember
);
723 if( b
->ldif
&& field
.size() ) {
724 os
<< b
->ldif
<< ": " << field
<< "\n";
726 os
<< "objectClass: " << b
->objectClass
<< "\n";
731 if( Address1
.size() && base64_encode(Address1
, b64
) )
732 os
<< "street:: " << b64
<< "\n";
734 std::string FullAddress
= GetPostalAddress();
735 if( FullAddress
.size() && base64_encode(FullAddress
, b64
) )
736 os
<< "postalAddress:: " << b64
<< "\n";
738 if( Notes
.size() && base64_encode(Notes
, b64
) )
739 os
<< "note:: " << b64
<< "\n";
742 // print any group links
743 GroupLinksType::const_iterator
744 gb = GroupLinks.begin(), ge = GroupLinks.end();
746 os << " GroupLinks:\n";
747 for( ; gb != ge; gb++ ) {
748 os << " ID: 0x" << setbase(16) << gb->Link << "\n";
752 // last line must be empty
755 // cleanup the stream
760 bool Contact::ReadLdif(std::istream
&is
)
767 // search for beginning dn: line
769 while( getline(is
, line
) ) {
770 if( strncmp(line
.c_str(), "dn: ", 4) == 0 ) {
778 // storage for various name styles
779 string cn
, displayName
, sn
, givenName
;
780 string coded
, decode
;
781 string
*b64field
= 0;
783 // read ldif lines until empty line is found
784 while( getline(is
, line
) && line
.size() ) {
787 // processing a base64 encoded field
788 if( line
[0] == ' ' ) {
794 // end of base64 block
795 base64_decode(coded
, decode
);
800 // fall through to process new line
803 if( strncmp(line
.c_str(), "cn: ", 4) == 0 ) {
804 cn
= line
.c_str() + 4; // full name
806 else if( strncmp(line
.c_str(), "displayName: ", 13) == 0 ) {
807 displayName
= line
.c_str() + 13; // full name
809 else if( strncmp(line
.c_str(), "sn: ", 4) == 0 ) {
810 sn
= line
.c_str() + 4; // last name
812 else if( strncmp(line
.c_str(), "givenName: ", 11) == 0 ) {
813 givenName
= line
.c_str() + 11; // first name
815 else if( strncmp(line
.c_str(), "street:: ", 9) == 0 ) {
816 coded
= line
.substr(9);
817 b64field
= &Address1
;
820 // else if( strncmp(line.c_str(), "postalAddress:: ", 16) == 0 ) {
821 // std::string FullAddress = GetPostalAddress();
822 // if( FullAddress.size() && base64_encode(FullAddress, b64) )
823 // os << "postalAddress:: " << b64 << "\n";
825 else if( strncmp(line
.c_str(), "note:: ", 7) == 0 ) {
826 coded
= line
.substr(7);
831 // cycle through the type table
832 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
833 b
->type
!= CFC_INVALID_FIELD
;
838 std::string
&field
= this->*(b
->strMember
);
839 std::string key
= b
->ldif
;
842 if( strncmp(line
.c_str(), key
.c_str(), key
.size()) == 0 ) {
843 field
= line
.c_str() + key
.size();
852 // clean up base64 decoding
853 base64_decode(coded
, decode
);
859 // find the best match for name... prefer sn/givenName if available
863 if( givenName
.size() ) {
864 FirstName
= givenName
;
867 if( !LastName
.size() || !FirstName
.size() ) {
870 // still don't have a complete name, check cn first
872 SplitName(cn
, first
, last
);
873 if( !LastName
.size() && last
.size() )
875 if( !FirstName
.size() && first
.size() )
879 // displayName is last chance
880 if( displayName
.size() ) {
881 SplitName(displayName
, first
, last
);
882 if( !LastName
.size() && last
.size() )
884 if( !FirstName
.size() && first
.size() )
889 return LastName
.size() && FirstName
.size();
892 void Contact::SplitName(const std::string
&full
, std::string
&first
, std::string
&last
)
897 string::size_type pos
= full
.find_last_of(' ');
898 if( pos
!= string::npos
) {
899 // has space, assume last word is last name
900 last
= full
.c_str() + pos
+ 1;
901 first
= full
.substr(0, pos
);
904 // no space, assume only first name
905 first
= full
.substr(0);
911 ///////////////////////////////////////////////////////////////////////////////
915 // Email / message field codes
916 #define MFC_TO 0x01 // can occur multiple times
917 #define MFC_FROM 0x05
918 #define MFC_SUBJECT 0x0b
919 #define MFC_BODY 0x0c
920 #define MFC_END 0xffff
922 FieldLink
<Message
> MessageFieldLinks
[] = {
923 { MFC_TO
, "To", 0, 0, 0, &Message::To
, 0 },
924 { MFC_FROM
, "From", 0, 0, 0, &Message::From
, 0 },
925 { MFC_SUBJECT
, "Subject", 0, 0, &Message::Subject
, 0, 0 },
926 { MFC_BODY
, "Body", 0, 0, &Message::Body
, 0, 0 },
927 { MFC_END
, "End of List",0, 0, 0, 0, 0 }
930 size_t Message::GetOldProtocolRecordSize()
932 return sizeof(Barry::OldMessageRecord
);
935 size_t Message::GetProtocolRecordSize()
937 return sizeof(Barry::MessageRecord
);
948 const unsigned char* Message::ParseField(const unsigned char *begin
,
949 const unsigned char *end
)
951 const CommonField
*field
= (const CommonField
*) begin
;
953 // advance and check size
954 begin
+= COMMON_FIELD_HEADER_SIZE
+ field
->size
;
955 if( begin
> end
) // if begin==end, we are ok
958 if( !field
->size
) // if field has no size, something's up
961 // cycle through the type table
962 for( FieldLink
<Message
> *b
= MessageFieldLinks
;
966 if( b
->type
== field
->type
) {
968 // parse regular string
969 std::string
&s
= this->*(b
->strMember
);
970 s
.assign((const char *)field
->u
.raw
, field
->size
-1);
971 return begin
; // done!
973 else if( b
->addrMember
) {
974 // parse email address
975 // get dual name+addr string first
976 const char *fa
= (const char*)field
->u
.addr
.addr
;
977 std::string
dual(fa
, field
->size
- sizeof(field
->u
.addr
.unknown
));
979 // assign first string, using null terminator...letting std::string add it for us if it doesn't exist
980 Address
&a
= this->*(b
->addrMember
);
981 a
.Name
= dual
.c_str();
983 // assign second string, using first size as starting point
984 a
.Email
= dual
.c_str() + a
.Name
.size() + 1;
992 void Message::Parse(const Data
&data
, size_t offset
, unsigned int operation
)
994 const void *begin
= 0;
997 case SB_DBOP_GET_RECORDS
:
999 MAKE_RECORD(const Barry::MessageRecord
, msg
, data
, offset
);
1000 begin
= &msg
->field
[0];
1004 case SB_DBOP_OLD_GET_RECORDS_REPLY
:
1006 MAKE_RECORD(const Barry::OldMessageRecord
, msg
, data
, offset
);
1007 begin
= &msg
->field
[0];
1012 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
1015 void Message::Clear()
1027 // dump message in mbox format
1028 void Message::Dump(std::ostream
&os
) const
1030 // FIXME - use current time until we figure out the date headers
1031 time_t fixme
= time(NULL
);
1033 os
<< "From " << (From
.Email
.size() ? From
.Email
.c_str() : "unknown")
1034 << " " << ctime(&fixme
);
1035 os
<< "Date: " << ctime(&fixme
);
1036 os
<< "From: " << From
<< "\n";
1037 if( To
.Email
.size() )
1038 os
<< "To: " << To
<< "\n";
1039 if( Cc
.Email
.size() )
1040 os
<< "Cc: " << Cc
<< "\n";
1041 if( Subject
.size() )
1042 os
<< "Subject: " << Subject
<< "\n";
1044 for( std::string::const_iterator i
= Body
.begin();
1045 i
!= Body
.end() && *i
;
1058 ///////////////////////////////////////////////////////////////////////////////
1061 // calendar field codes
1062 #define CALFC_APPT_TYPE_FLAG 0x01
1063 #define CALFC_SUBJECT 0x02
1064 #define CALFC_NOTES 0x03
1065 #define CALFC_LOCATION 0x04
1066 #define CALFC_NOTIFICATION_TIME 0x05
1067 #define CALFC_START_TIME 0x06
1068 #define CALFC_END_TIME 0x07
1069 #define CALFC_RECURRANCE_DATA 0x0c
1070 #define CALFC_VERSION_DATA 0x10
1071 #define CALFC_NOTIFICATION_DATA 0x1a
1072 #define CALFC_ALLDAYEVENT_FLAG 0xff
1073 #define CALFC_END 0xffff
1075 FieldLink
<Calendar
> CalendarFieldLinks
[] = {
1076 { CALFC_SUBJECT
, "Subject", 0, 0, &Calendar::Subject
, 0, 0 },
1077 { CALFC_NOTES
, "Notes", 0, 0, &Calendar::Notes
, 0, 0 },
1078 { CALFC_LOCATION
, "Location", 0, 0, &Calendar::Location
, 0, 0 },
1079 { CALFC_NOTIFICATION_TIME
,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime
},
1080 { CALFC_START_TIME
, "Start Time", 0, 0, 0, 0, &Calendar::StartTime
},
1081 { CALFC_END_TIME
, "End Time", 0, 0, 0, 0, &Calendar::EndTime
},
1082 { CALFC_END
, "End of List",0, 0, 0, 0, 0 }
1085 size_t Calendar::GetOldProtocolRecordSize()
1087 return sizeof(Barry::OldCalendarRecord
);
1090 size_t Calendar::GetProtocolRecordSize()
1092 return sizeof(Barry::CalendarRecord
);
1095 Calendar::Calendar()
1102 Calendar::~Calendar()
1106 const unsigned char* Calendar::ParseField(const unsigned char *begin
,
1107 const unsigned char *end
)
1109 const CommonField
*field
= (const CommonField
*) begin
;
1111 // advance and check size
1112 begin
+= COMMON_FIELD_HEADER_SIZE
+ field
->size
;
1113 if( begin
> end
) // if begin==end, we are ok
1116 if( !field
->size
) // if field has no size, something's up
1119 // cycle through the type table
1120 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
1121 b
->type
!= CALFC_END
;
1124 if( b
->type
== field
->type
) {
1125 if( b
->strMember
) {
1126 std::string
&s
= this->*(b
->strMember
);
1127 s
.assign((const char *)field
->u
.raw
, field
->size
-1);
1128 return begin
; // done!
1130 else if( b
->timeMember
) {
1131 time_t &t
= this->*(b
->timeMember
);
1132 t
= min2time(field
->u
.min1900
);
1138 // handle special cases
1139 switch( field
->type
)
1141 case CALFC_APPT_TYPE_FLAG
:
1142 switch( field
->u
.raw
[0] )
1144 case 'a': // regular non-recurring appointment
1148 case '*': // recurring appointment
1153 throw BError("Calendar::ParseField: unknown appointment type");
1157 case CALFC_ALLDAYEVENT_FLAG
:
1158 AllDayEvent
= field
->u
.raw
[0] == 1;
1162 // if still not handled, add to the Unknowns list
1164 uf
.type
= field
->type
;
1165 uf
.data
.assign((const char*)field
->u
.raw
, field
->size
);
1166 Unknowns
.push_back(uf
);
1168 // return new pointer for next field
1172 void Calendar::Parse(const Data
&data
, size_t offset
, unsigned int operation
)
1174 const void *begin
= 0;
1178 case SB_DBOP_GET_RECORDS
:
1179 // using the new protocol
1180 // save the contact record ID
1181 throw std::logic_error("New Calendar: Not yet implemented");
1182 // RecordId = pack->u.db.u.calendar.uniqueId;
1183 // begin = &pack->u.db.u.calendar.field[0];
1186 case SB_DBOP_OLD_GET_RECORDS_REPLY
:
1188 // using the old protocol
1189 // save the contact record ID
1190 MAKE_RECORD(const Barry::OldCalendarRecord
, cal
, data
, offset
);
1191 RecordId
= cal
->uniqueId
;
1192 begin
= &cal
->field
[0];
1197 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
1203 /// Build a raw protocol packet based on data in the class.
1205 void Calendar::Build(Data
&data
, size_t offset
) const
1209 // uploading always seems to use the old record
1210 size_t size
= offset
+ OLD_CALENDAR_RECORD_HEADER_SIZE
;
1211 unsigned char *pd
= data
.GetBuffer(size
);
1212 MAKE_RECORD_PTR(Barry::OldCalendarRecord
, contact
, pd
, offset
);
1214 contact
->uniqueId
= RecordId
;
1215 contact
->unknown
= 1;
1217 // output the type first
1218 BuildField(data
, size
, CALFC_APPT_TYPE_FLAG
, Recurring
? '*' : 'a');
1220 // output all day event flag only if set
1222 BuildField(data
, size
, CALFC_ALLDAYEVENT_FLAG
, (char)1);
1224 // cycle through the type table
1225 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
1226 b
->type
!= CALFC_END
;
1229 if( b
->strMember
) {
1230 const std::string
&s
= this->*(b
->strMember
);
1232 BuildField(data
, size
, b
->type
, s
);
1234 else if( b
->timeMember
) {
1235 time_t t
= this->*(b
->timeMember
);
1237 BuildField1900(data
, size
, b
->type
, t
);
1241 // and finally save unknowns
1242 UnknownsType::const_iterator
1243 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
1244 for( ; ub
!= ue
; ub
++ ) {
1245 BuildField(data
, size
, ub
->type
, ub
->data
);
1248 data
.ReleaseBuffer(size
);
1251 void Calendar::Clear()
1256 NotificationTime
= StartTime
= EndTime
= 0;
1260 void Calendar::Dump(std::ostream
&os
) const
1262 os
<< "Calendar entry: 0x" << setbase(16) << RecordId
<< "\n";
1263 os
<< " Recurring: " << (Recurring
? "yes" : "no") << "\n";
1264 os
<< " All Day Event: " << (AllDayEvent
? "yes" : "no") << "\n";
1266 // cycle through the type table
1267 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
1268 b
->type
!= CALFC_END
;
1271 if( b
->strMember
) {
1272 const std::string
&s
= this->*(b
->strMember
);
1274 os
<< " " << b
->name
<< ": " << s
<< "\n";
1276 else if( b
->timeMember
) {
1277 time_t t
= this->*(b
->timeMember
);
1279 os
<< " " << b
->name
<< ": " << ctime(&t
);
1283 // print any unknowns
1288 ///////////////////////////////////////////////////////////////////////////////
1289 // ServiceBookConfig class
1291 // service book packed field codes
1292 #define SBFCC_END 0xffff
1294 FieldLink
<ServiceBookConfig
> ServiceBookConfigFieldLinks
[] = {
1295 // { SBFC_DSID, "DSID", 0, 0, &ServiceBook::DSID, 0, 0 },
1296 { SBFCC_END
, "End of List",0, 0, 0, 0, 0 }
1299 ServiceBookConfig::ServiceBookConfig()
1305 ServiceBookConfig::~ServiceBookConfig()
1309 const unsigned char* ServiceBookConfig::ParseField(const unsigned char *begin
,
1310 const unsigned char *end
)
1313 uint16_t size
, type
;
1319 const PackedField_02
*field
= (const PackedField_02
*) begin
;
1323 begin
+= PACKED_FIELD_02_HEADER_SIZE
+ size
;
1329 const PackedField_10
*field
= (const PackedField_10
*) begin
;
1333 begin
+= PACKED_FIELD_10_HEADER_SIZE
+ size
;
1338 eout("Unknown packed field format" << Format
);
1344 if( begin
> end
) // if begin==end, we are ok
1347 if( !size
) // if field has no size, something's up
1350 // cycle through the type table
1351 for( FieldLink
<ServiceBookConfig
> *b
= ServiceBookConfigFieldLinks
;
1352 b
->type
!= SBFCC_END
;
1355 if( b
->type
== type
) {
1356 if( b
->strMember
) {
1357 std::string
&s
= this->*(b
->strMember
);
1358 s
.assign((const char *)raw
, size
-1);
1359 return begin
; // done!
1365 // handle special cases
1371 // if still not handled, add to the Unknowns list
1374 uf
.data
.assign((const char*)raw
, size
);
1375 Unknowns
.push_back(uf
);
1377 // return new pointer for next field
1381 void ServiceBookConfig::Parse(const Data
&data
, size_t offset
, unsigned int operation
)
1383 const void *begin
= 0;
1387 case SB_DBOP_GET_RECORDS
:
1388 case SB_DBOP_OLD_GET_RECORDS_REPLY
:
1389 default: // FIXME - any operation is ok here... we don't know yet if it makes a difference
1391 // using the old protocol
1392 // save the contact record ID
1393 MAKE_RECORD(const Barry::ServiceBookConfigField
, sbc
, data
, offset
);
1394 Format
= sbc
->format
;
1395 begin
= &sbc
->fields
[0];
1400 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
1406 /// Build a raw protocol packet based on data in the class.
1408 void ServiceBookConfig::Build(Data
&data
, size_t offset
) const
1410 throw std::logic_error("ServiceBookConfig::Build not yet implemented");
1413 void ServiceBookConfig::Clear()
1418 void ServiceBookConfig::Dump(std::ostream
&os
) const
1420 os
<< " ServiceBookConfig Format: " << setbase(16) << (uint16_t)Format
<< "\n";
1422 // cycle through the type table
1423 for( const FieldLink
<ServiceBookConfig
> *b
= ServiceBookConfigFieldLinks
;
1424 b
->type
!= SBFCC_END
;
1427 if( b
->strMember
) {
1428 const std::string
&s
= this->*(b
->strMember
);
1430 os
<< " " << b
->name
<< ": " << s
<< "\n";
1432 else if( b
->timeMember
) {
1433 time_t t
= this->*(b
->timeMember
);
1435 os
<< " " << b
->name
<< ": " << ctime(&t
);
1439 // print any unknowns
1441 os
<< " ------------------- End of Config Field\n";
1445 ///////////////////////////////////////////////////////////////////////////////
1446 // ServiceBook class
1448 // service book field codes
1449 #define SBFC_OLD_NAME 0x01
1450 #define SBFC_HIDDEN_NAME 0x02
1451 #define SBFC_NAME 0x03
1452 #define SBFC_OLD_UNIQUE_ID 0x06
1453 #define SBFC_UNIQUE_ID 0x07
1454 #define SBFC_CONTENT_ID 0x08
1455 #define SBFC_CONFIG 0x09
1456 #define SBFC_OLD_DESC 0x32
1457 #define SBFC_DESCRIPTION 0x0f
1458 #define SBFC_DSID 0xa1
1459 #define SBFC_BES_DOMAIN 0xa2
1460 #define SBFC_USER_ID 0xa3
1461 #define SBFC_END 0xffff
1463 FieldLink
<ServiceBook
> ServiceBookFieldLinks
[] = {
1464 { SBFC_HIDDEN_NAME
, "Hidden Name",0, 0, &ServiceBook::HiddenName
, 0, 0 },
1465 { SBFC_DSID
, "DSID", 0, 0, &ServiceBook::DSID
, 0, 0 },
1466 { SBFC_END
, "End of List",0, 0, 0, 0, 0 }
1469 size_t ServiceBook::GetOldProtocolRecordSize()
1471 return sizeof(Barry::OldServiceBookRecord
);
1474 size_t ServiceBook::GetProtocolRecordSize()
1476 throw std::logic_error("ServiceBook::GetProtocolRecordSize not yet implemented");
1477 // return sizeof(Barry::ServiceBookRecord);
1481 ServiceBook::ServiceBook()
1482 : NameType(SBFC_OLD_NAME
),
1483 DescType(SBFC_OLD_DESC
),
1484 UniqueIdType(SBFC_OLD_UNIQUE_ID
),
1490 ServiceBook::~ServiceBook()
1494 const unsigned char* ServiceBook::ParseField(const unsigned char *begin
,
1495 const unsigned char *end
)
1497 const CommonField
*field
= (const CommonField
*) begin
;
1499 // advance and check size
1500 begin
+= COMMON_FIELD_HEADER_SIZE
+ field
->size
;
1501 if( begin
> end
) // if begin==end, we are ok
1504 if( !field
->size
) // if field has no size, something's up
1507 // cycle through the type table
1508 for( FieldLink
<ServiceBook
> *b
= ServiceBookFieldLinks
;
1509 b
->type
!= SBFC_END
;
1512 if( b
->type
== field
->type
) {
1513 if( b
->strMember
) {
1514 std::string
&s
= this->*(b
->strMember
);
1515 s
.assign((const char *)field
->u
.raw
, field
->size
-1);
1516 return begin
; // done!
1518 else if( b
->timeMember
) {
1519 time_t &t
= this->*(b
->timeMember
);
1520 t
= min2time(field
->u
.min1900
);
1526 // handle special cases
1527 switch( field
->type
)
1529 case SBFC_OLD_NAME
: // strings with old/new type codes
1531 Name
.assign((const char *)field
->u
.raw
, field
->size
-1);
1532 NameType
= field
->type
;
1536 case SBFC_DESCRIPTION
:
1537 Description
.assign((const char *)field
->u
.raw
, field
->size
-1);
1538 DescType
= field
->type
;
1541 case SBFC_OLD_UNIQUE_ID
:
1542 case SBFC_UNIQUE_ID
:
1543 UniqueId
.assign((const char *)field
->u
.raw
, field
->size
);
1544 UniqueIdType
= field
->type
;
1547 case SBFC_CONTENT_ID
:
1548 ContentId
.assign((const char *)field
->u
.raw
, field
->size
);
1551 case SBFC_BES_DOMAIN
:
1552 BesDomain
.assign((const char *)field
->u
.raw
, field
->size
);
1557 Data
config((const void *)field
->u
.raw
, field
->size
);
1558 Config
.Parse(config
, 0, 0);
1560 break; // break here so raw packet is still visible in dump
1564 // if still not handled, add to the Unknowns list
1566 uf
.type
= field
->type
;
1567 uf
.data
.assign((const char*)field
->u
.raw
, field
->size
);
1568 Unknowns
.push_back(uf
);
1570 // return new pointer for next field
1574 void ServiceBook::Parse(const Data
&data
, size_t offset
, unsigned int operation
)
1576 const void *begin
= 0;
1580 case SB_DBOP_GET_RECORDS
:
1581 // using the new protocol
1582 // save the contact record ID
1583 throw std::logic_error("New ServiceBook: Not yet implemented");
1584 // RecordId = pack->u.db.u.calendar.uniqueId;
1585 // begin = &pack->u.db.u.calendar.field[0];
1588 case SB_DBOP_OLD_GET_RECORDS_REPLY
:
1590 // using the old protocol
1591 // save the contact record ID
1592 MAKE_RECORD(const Barry::OldServiceBookRecord
, sb
, data
, offset
);
1593 RecordId
= sb
->uniqueId
;
1594 begin
= &sb
->field
[0];
1599 ParseCommonFields(*this, begin
, data
.GetData() + data
.GetSize());
1605 /// Build a raw protocol packet based on data in the class.
1607 void ServiceBook::Build(Data
&data
, size_t offset
) const
1609 throw std::logic_error("ServiceBook::Build not yet implemented");
1613 // uploading always seems to use the old record
1614 size_t size = offset + OLD_SERVICE_BOOK_RECORD_HEADER_SIZE;
1615 unsigned char *pd = data.GetBuffer(size);
1616 MAKE_RECORD_PTR(Barry::OldServiceBookRecord, sbook, pd, offset);
1618 sbook->uniqueId = RecordId;
1621 // output the type first
1622 BuildField(data, size, SBFC_APPT_TYPE_FLAG, Recurring ? '*' : 'a');
1624 // output all day event flag only if set
1626 BuildField(data, size, SBFC_ALLDAYEVENT_FLAG, (char)1);
1628 // cycle through the type table
1629 for( const FieldLink<ServiceBook> *b = ServiceBookFieldLinks;
1630 b->type != SBFC_END;
1633 if( b->strMember ) {
1634 const std::string &s = this->*(b->strMember);
1636 BuildField(data, size, b->type, s);
1638 else if( b->timeMember ) {
1639 time_t t = this->*(b->timeMember);
1641 BuildField1900(data, size, b->type, t);
1645 // and finally save unknowns
1646 UnknownsType::const_iterator
1647 ub = Unknowns.begin(), ue = Unknowns.end();
1648 for( ; ub != ue; ub++ ) {
1649 BuildField(data, size, ub->type, ub->data);
1652 data.ReleaseBuffer(size);
1656 void ServiceBook::Clear()
1662 void ServiceBook::Dump(std::ostream
&os
) const
1664 os
<< "ServiceBook entry: 0x" << setbase(16) << RecordId
<< "\n";
1666 // cycle through the type table
1667 for( const FieldLink
<ServiceBook
> *b
= ServiceBookFieldLinks
;
1668 b
->type
!= SBFC_END
;
1671 if( b
->strMember
) {
1672 const std::string
&s
= this->*(b
->strMember
);
1674 os
<< " " << b
->name
<< ": " << s
<< "\n";
1676 else if( b
->timeMember
) {
1677 time_t t
= this->*(b
->timeMember
);
1679 os
<< " " << b
->name
<< ": " << ctime(&t
);
1684 if( UniqueId
.size() )
1685 os
<< " Unique ID: " << UniqueId
<< "\n";
1686 if( ContentId
.size() )
1687 os
<< " Content ID: " << ContentId
<< "\n";
1688 if( BesDomain
.size() )
1689 os
<< " (BES) Domain: " << BesDomain
<< "\n";
1693 // print any unknowns
1698 } // namespace Barry
1701 #ifdef __TEST_MODE__
1705 int main(int argc
, char *argv
[])
1708 cerr
<< "Usage: test <datafile>" << endl
;
1712 std::vector
<Data
> array
;
1713 if( !LoadDataArray(argv
[1], array
) ) {
1714 cerr
<< "Unable to load file: " << argv
[1] << endl
;
1718 cout
<< "Loaded " << array
.size() << " items" << endl
;
1720 for( std::vector
<Data
>::iterator b
= array
.begin(), e
= array
.end();
1724 // cout << d << endl;
1725 if( d
.GetSize() > 13 && d
.GetData()[6] == 0x4f ) {
1726 Barry::Contact contact
;
1727 contact
.Parse(d
, 13, d
.GetData()[6]);
1728 cout
<< contact
<< endl
;
1729 contact
.DumpLdif(cout
, "ou=People,dc=example,dc=com");
1731 else if( d
.GetSize() > 13 && d
.GetData()[6] == 0x44 ) {
1732 Barry::Calendar cal
;
1733 cal
.Parse(d
, 13, d
.GetData()[6]);
1734 cout
<< cal
<< endl
;