3 /// Blackberry database record classes. Help translate data
4 /// from data packets to useful structurs, and back.
8 Copyright (C) 2005-2007, 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__
39 using namespace Barry::Protocol
;
43 std::ostream
& operator<<(std::ostream
&os
, const Message::Address
&msga
) {
44 os
<< msga
.Name
.c_str() << " <" << msga
.Email
.c_str() << ">";
50 template <class Record
>
51 const unsigned char* ParseCommonFields(Record
&rec
, const void *begin
, const void *end
)
53 const unsigned char *b
= (const unsigned char*) begin
;
54 const unsigned char *e
= (const unsigned char*) end
;
56 while( (b
+ COMMON_FIELD_HEADER_SIZE
) < e
)
57 b
= rec
.ParseField(b
, e
);
61 void BuildField1900(Data
&data
, size_t &size
, uint8_t type
, time_t t
)
63 size_t timesize
= COMMON_FIELD_MIN1900_SIZE
;
64 size_t fieldsize
= COMMON_FIELD_HEADER_SIZE
+ timesize
;
65 unsigned char *pd
= data
.GetBuffer(size
+ fieldsize
) + size
;
66 CommonField
*field
= (CommonField
*) pd
;
68 field
->size
= htobs(timesize
);
70 field
->u
.min1900
= time2min(t
);
75 void BuildField(Data
&data
, size_t &size
, uint8_t type
, char c
)
78 size_t fieldsize
= COMMON_FIELD_HEADER_SIZE
+ strsize
;
79 unsigned char *pd
= data
.GetBuffer(size
+ fieldsize
) + size
;
80 CommonField
*field
= (CommonField
*) pd
;
82 field
->size
= htobs(strsize
);
84 memcpy(field
->u
.raw
, &c
, strsize
);
89 void BuildField(Data
&data
, size_t &size
, uint8_t type
, const std::string
&str
)
91 // include null terminator
92 size_t strsize
= str
.size() + 1;
93 size_t fieldsize
= COMMON_FIELD_HEADER_SIZE
+ strsize
;
94 unsigned char *pd
= data
.GetBuffer(size
+ fieldsize
) + size
;
95 CommonField
*field
= (CommonField
*) pd
;
97 field
->size
= htobs(strsize
);
99 memcpy(field
->u
.raw
, str
.c_str(), strsize
);
104 void BuildField(Data
&data
, size_t &size
, uint8_t type
, const Barry::Protocol::GroupLink
&link
)
106 size_t linksize
= sizeof(Barry::Protocol::GroupLink
);
107 size_t fieldsize
= COMMON_FIELD_HEADER_SIZE
+ linksize
;
108 unsigned char *pd
= data
.GetBuffer(size
+ fieldsize
) + size
;
109 CommonField
*field
= (CommonField
*) pd
;
111 field
->size
= htobs(linksize
);
113 field
->u
.link
= link
;
119 ///////////////////////////////////////////////////////////////////////////////
120 // CommandTable class
123 CommandTable::CommandTable()
127 CommandTable::~CommandTable()
131 const unsigned char* CommandTable::ParseField(const unsigned char *begin
,
132 const unsigned char *end
)
134 // check if there is enough data for a header
135 const unsigned char *headend
= begin
+ sizeof(CommandTableField
);
139 const CommandTableField
*field
= (const CommandTableField
*) begin
;
141 // advance and check size
142 begin
+= COMMAND_FIELD_HEADER_SIZE
+ field
->size
; // size is byte
143 if( begin
> end
) // if begin==end, we are ok
146 if( !field
->size
) // if field has no size, something's up
150 command
.Code
= field
->code
;
151 command
.Name
.assign((const char *)field
->name
, field
->size
);
152 Commands
.push_back(command
);
156 void CommandTable::Parse(const Data
&data
, size_t offset
)
158 if( offset
>= data
.GetSize() )
161 const unsigned char *begin
= data
.GetData() + offset
;
162 const unsigned char *end
= data
.GetData() + data
.GetSize();
165 begin
= ParseField(begin
, end
);
168 void CommandTable::Clear()
173 unsigned int CommandTable::GetCommand(const std::string
&name
) const
175 CommandArrayType::const_iterator b
= Commands
.begin();
176 for( ; b
!= Commands
.end(); b
++ )
177 if( b
->Name
== name
)
182 void CommandTable::Dump(std::ostream
&os
) const
184 CommandArrayType::const_iterator b
= Commands
.begin();
185 os
<< "Command table:\n";
186 for( ; b
!= Commands
.end(); b
++ ) {
187 os
<< " Command: 0x" << setbase(16) << b
->Code
188 << " '" << b
->Name
<< "'\n";
195 ///////////////////////////////////////////////////////////////////////////////
196 // RecordStateTable class
198 RecordStateTable::RecordStateTable()
199 : m_LastNewRecordId(1)
203 RecordStateTable::~RecordStateTable()
207 const unsigned char* RecordStateTable::ParseField(const unsigned char *begin
,
208 const unsigned char *end
)
210 const RecordStateTableField
*field
= (const RecordStateTableField
*) begin
;
212 // advance and check size
213 begin
+= sizeof(RecordStateTableField
);
214 if( begin
> end
) // if begin==end, we are ok
218 state
.Index
= btohs(field
->index
);
219 state
.RecordId
= btohl(field
->uniqueId
);
220 state
.Dirty
= (field
->flags
& BARRY_RSTF_DIRTY
) != 0;
221 state
.RecType
= field
->rectype
;
222 state
.Unknown2
.assign((const char*)field
->unknown2
, sizeof(field
->unknown2
));
223 StateMap
[state
.Index
] = state
;
228 void RecordStateTable::Parse(const Data
&data
)
230 size_t offset
= 12; // skipping the unknown 2 bytes at start
232 if( offset
>= data
.GetSize() )
235 const unsigned char *begin
= data
.GetData() + offset
;
236 const unsigned char *end
= data
.GetData() + data
.GetSize();
239 begin
= ParseField(begin
, end
);
242 void RecordStateTable::Clear()
245 m_LastNewRecordId
= 1;
248 // Searches the StateMap table for RecordId, and returns the "index"
249 // in the map if found. Returns true if found, false if not.
250 // pFoundIndex can be null if only the existence of the index is desired
251 bool RecordStateTable::GetIndex(uint32_t RecordId
, IndexType
*pFoundIndex
) const
253 StateMapType::const_iterator i
= StateMap
.begin();
254 for( ; i
!= StateMap
.end(); ++i
) {
255 if( i
->second
.RecordId
== RecordId
) {
257 *pFoundIndex
= i
->first
;
264 // Generate a new RecordId that is not in the state table.
265 // Starts at 1 and keeps incrementing until a free one is found.
266 uint32_t RecordStateTable::MakeNewRecordId() const
268 // start with next Id
271 // make sure it doesn't already exist
272 StateMapType::const_iterator i
= StateMap
.begin();
273 while( i
!= StateMap
.end() ) {
274 if( m_LastNewRecordId
== i
->second
.RecordId
) {
275 m_LastNewRecordId
++; // try again
276 i
= StateMap
.begin(); // start over
282 return m_LastNewRecordId
;
285 void RecordStateTable::Dump(std::ostream
&os
) const
287 ios::fmtflags oldflags
= os
.setf(ios::right
);
288 char fill
= os
.fill(' ');
289 bool bPrintAscii
= Data::PrintAscii();
290 Data::PrintAscii(false);
292 os
<< " Index RecordId Dirty RecType" << endl
;
293 os
<< "------- ---------- ----- -------" << endl
;
295 StateMapType::const_iterator b
, e
= StateMap
.end();
296 for( b
= StateMap
.begin(); b
!= e
; ++b
) {
297 const State
&state
= b
->second
;
300 os
<< setbase(10) << setw(7) << state
.Index
;
301 os
<< " 0x" << setbase(16) << setfill('0') << setw(8) << state
.RecordId
;
302 os
<< " " << setfill(' ') << setw(5) << (state
.Dirty
? "yes" : "no");
303 os
<< " 0x" << setbase(16) << setfill('0') << setw(2) << state
.RecType
;
304 os
<< " " << Data(state
.Unknown2
.data(), state
.Unknown2
.size());
307 // cleanup the stream
310 Data::PrintAscii(bPrintAscii
);
314 ///////////////////////////////////////////////////////////////////////////////
315 // DatabaseDatabase class
317 DatabaseDatabase::DatabaseDatabase()
321 DatabaseDatabase::~DatabaseDatabase()
325 template <class RecordType
, class FieldType
>
326 void DatabaseDatabase::ParseRec(const RecordType
&rec
, const unsigned char *end
)
330 template <class SizeType
>
331 SizeType
ConvertHtoB(SizeType s
)
333 throw Error("Not implemented.");
336 // specializations for specific sizes
337 template <> uint8_t ConvertHtoB
<uint8_t>(uint8_t s
) { return s
; }
338 template <> uint16_t ConvertHtoB
<uint16_t>(uint16_t s
) { return htobs(s
); }
339 template <> uint32_t ConvertHtoB
<uint32_t>(uint32_t s
) { return htobl(s
); }
340 template <> uint64_t ConvertHtoB
<uint64_t>(uint64_t s
) { return htobll(s
); }
343 template <class FieldType
>
344 const unsigned char* DatabaseDatabase::ParseField(const unsigned char *begin
,
345 const unsigned char *end
)
347 // check if there is enough data for a header
348 const unsigned char *headend
= begin
+ sizeof(FieldType
);
353 const FieldType
*field
= (const FieldType
*) begin
;
355 // advance and check size
356 begin
+= sizeof(FieldType
) - sizeof(field
->name
) + ConvertHtoB(field
->nameSize
);
357 if( begin
> end
) // if begin==end, we are ok
360 if( !ConvertHtoB(field
->nameSize
) ) // if field has no size, something's up
364 db
.Number
= ConvertHtoB(field
->dbNumber
);
365 db
.RecordCount
= ConvertHtoB(field
->dbRecordCount
);
366 db
.Name
.assign((const char *)field
->name
, ConvertHtoB(field
->nameSize
) - 1);
367 Databases
.push_back(db
);
371 void DatabaseDatabase::Parse(const Data
&data
)
373 // check size to make sure we have up to the DBAccess operation byte
374 if( data
.GetSize() < (SB_PACKET_DBACCESS_HEADER_SIZE
+ 1) )
377 MAKE_PACKET(pack
, data
);
378 const unsigned char *begin
= 0;
379 const unsigned char *end
= data
.GetData() + data
.GetSize();
381 switch( pack
->u
.db
.u
.response
.operation
)
383 case SB_DBOP_GET_DBDB
:
384 // using the new protocol
385 if( data
.GetSize() > SB_PACKET_DBDB_HEADER_SIZE
) {
386 begin
= (const unsigned char *)
387 &pack
->u
.db
.u
.response
.u
.dbdb
.field
[0];
389 // this while check is ok, since ParseField checks
392 begin
= ParseField
<DBDBField
>(begin
, end
);
395 dout("DatabaseDatabase: not enough data for parsing");
398 case SB_DBOP_OLD_GET_DBDB
:
399 // using the old protocol
400 if( data
.GetSize() > SB_PACKET_OLD_DBDB_HEADER_SIZE
) {
401 begin
= (const unsigned char *)
402 &pack
->u
.db
.u
.response
.u
.old_dbdb
.field
[0];
404 // this while check is ok, since ParseField checks
407 begin
= ParseField
<OldDBDBField
>(begin
, end
);
410 dout("DatabaseDatabase: not enough data for parsing");
415 dout("Unknown protocol");
422 void DatabaseDatabase::Clear()
427 bool DatabaseDatabase::GetDBNumber(const std::string
&name
,
428 unsigned int &number
) const
430 DatabaseArrayType::const_iterator b
= Databases
.begin();
431 for( ; b
!= Databases
.end(); b
++ )
432 if( b
->Name
== name
) {
439 bool DatabaseDatabase::GetDBName(unsigned int number
,
440 std::string
&name
) const
442 DatabaseArrayType::const_iterator b
= Databases
.begin();
443 for( ; b
!= Databases
.end(); b
++ )
444 if( b
->Number
== number
) {
451 void DatabaseDatabase::Dump(std::ostream
&os
) const
453 DatabaseArrayType::const_iterator b
= Databases
.begin();
454 os
<< "Database database:\n";
455 for( ; b
!= Databases
.end(); b
++ ) {
456 os
<< " Database: 0x" << setbase(16) << b
->Number
457 << " '" << b
->Name
<< "' (records: "
458 << setbase(10) << b
->RecordCount
<< ")\n";
463 std::ostream
& operator<< (std::ostream
&os
, const std::vector
<UnknownField
> &unknowns
)
465 std::vector
<UnknownField
>::const_iterator
466 ub
= unknowns
.begin(), ue
= unknowns
.end();
468 os
<< " Unknowns:\n";
469 for( ; ub
!= ue
; ub
++ ) {
470 os
<< " Type: 0x" << setbase(16)
471 << (unsigned int) ub
->type
472 << " Data:\n" << Data(ub
->data
.data(), ub
->data
.size());
479 ///////////////////////////////////////////////////////////////////////////////
482 // Contact field codes
486 #define CFC_WORK_PHONE 6
487 #define CFC_HOME_PHONE 7
488 #define CFC_MOBILE_PHONE 8
491 #define CFC_NAME 32 // used twice, in first/last name order
492 #define CFC_COMPANY 33
493 #define CFC_DEFAULT_COMM_METHOD 34
494 #define CFC_ADDRESS1 35
495 #define CFC_ADDRESS2 36
496 #define CFC_ADDRESS3 37
498 #define CFC_PROVINCE 39
499 #define CFC_POSTAL_CODE 40
500 #define CFC_COUNTRY 41
502 #define CFC_PUBLIC_KEY 43
503 #define CFC_GROUP_FLAG 44
504 #define CFC_GROUP_LINK 52
506 #define CFC_INVALID_FIELD 255
508 // Contact code to field table
509 template <class Record
>
516 std::string
Record::* strMember
; // FIXME - find a more general
517 Message::Address
Record::* addrMember
; // way to do this...
518 time_t Record::* timeMember
;
521 FieldLink
<Contact
> ContactFieldLinks
[] = {
522 { CFC_EMAIL
, "Email", "mail",0, &Contact::Email
, 0, 0 },
523 { CFC_PHONE
, "Phone", 0,0, &Contact::Phone
, 0, 0 },
524 { CFC_FAX
, "Fax", "facsimileTelephoneNumber",0, &Contact::Fax
, 0, 0 },
525 { CFC_WORK_PHONE
, "WorkPhone", "telephoneNumber",0, &Contact::WorkPhone
, 0, 0 },
526 { CFC_HOME_PHONE
, "HomePhone", "homePhone",0, &Contact::HomePhone
, 0, 0 },
527 { CFC_MOBILE_PHONE
, "MobilePhone","mobile",0, &Contact::MobilePhone
, 0, 0 },
528 { CFC_PAGER
, "Pager", "pager",0, &Contact::Pager
, 0, 0 },
529 { CFC_PIN
, "PIN", 0,0, &Contact::PIN
, 0, 0 },
530 { CFC_COMPANY
, "Company", "o",0, &Contact::Company
, 0, 0 },
531 { CFC_DEFAULT_COMM_METHOD
,"DefaultCommMethod",0,0, &Contact::DefaultCommunicationsMethod
, 0, 0 },
532 { CFC_ADDRESS1
, "Address1", 0,0, &Contact::Address1
, 0, 0 },
533 { CFC_ADDRESS2
, "Address2", 0,0, &Contact::Address2
, 0, 0 },
534 { CFC_ADDRESS3
, "Address3", 0,0, &Contact::Address3
, 0, 0 },
535 { CFC_CITY
, "City", "l",0, &Contact::City
, 0, 0 },
536 { CFC_PROVINCE
, "Province", "st",0, &Contact::Province
, 0, 0 },
537 { CFC_POSTAL_CODE
, "PostalCode", "postalCode",0, &Contact::PostalCode
, 0, 0 },
538 { CFC_COUNTRY
, "Country", "c", "country", &Contact::Country
, 0, 0 },
539 { CFC_TITLE
, "Title", "title",0, &Contact::Title
, 0, 0 },
540 { CFC_PUBLIC_KEY
, "PublicKey", 0,0, &Contact::PublicKey
, 0, 0 },
541 { CFC_NOTES
, "Notes", 0,0, &Contact::Notes
, 0, 0 },
542 { CFC_INVALID_FIELD
,"EndOfList", 0, 0, 0 }
548 m_FirstNameSeen(false)
556 const unsigned char* Contact::ParseField(const unsigned char *begin
,
557 const unsigned char *end
)
559 const CommonField
*field
= (const CommonField
*) begin
;
561 // advance and check size
562 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
563 if( begin
> end
) // if begin==end, we are ok
566 if( !btohs(field
->size
) ) // if field has no size, something's up
569 // cycle through the type table
570 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
571 b
->type
!= CFC_INVALID_FIELD
;
574 if( b
->type
== field
->type
) {
575 std::string
&s
= this->*(b
->strMember
);
576 s
.assign((const char *)field
->u
.raw
, btohs(field
->size
)-1);
577 return begin
; // done!
581 // if not found in the type table, check for special handling
582 switch( field
->type
)
585 // can be used multiple times, for first/last names
587 if( FirstName
.size() || m_FirstNameSeen
) {
588 // first name already filled, use last name
590 m_FirstNameSeen
= false;
594 m_FirstNameSeen
= true;
597 name
->assign((const char*)field
->u
.raw
, btohs(field
->size
)-1);
602 // just add the unique ID to the list
603 GroupLinks
.push_back(
604 GroupLink(field
->u
.link
.uniqueId
,
605 field
->u
.link
.unknown
));
609 // ignore the group flag... the presense of group link items
610 // behaves as the flag in this class
614 // if still not handled, add to the Unknowns list
616 uf
.type
= field
->type
;
617 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
618 Unknowns
.push_back(uf
);
620 // return new pointer for next field
624 void Contact::ParseHeader(const Data
&data
, size_t &offset
)
626 // no header to parse in Contact records
629 // this is called by the RecordParser<> class, which checks size for us
630 void Contact::ParseFields(const Data
&data
, size_t &offset
)
632 const unsigned char *finish
= ParseCommonFields(*this,
633 data
.GetData() + offset
, data
.GetData() + data
.GetSize());
634 offset
+= finish
- (data
.GetData() + offset
);
637 void Contact::BuildHeader(Data
&data
, size_t &offset
) const
639 // no header in Contact records
645 /// Build fields part of record
647 void Contact::BuildFields(Data
&data
, size_t &offset
) const
651 // check if this is a group link record, and if so, output
653 if( GroupLinks
.size() )
654 BuildField(data
, offset
, CFC_GROUP_FLAG
, 'G');
656 // special fields not in type table
657 if( FirstName
.size() )
658 BuildField(data
, offset
, CFC_NAME
, FirstName
);
659 if( LastName
.size() )
660 BuildField(data
, offset
, CFC_NAME
, LastName
);
662 // cycle through the type table
663 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
664 b
->type
!= CFC_INVALID_FIELD
;
667 // print only fields with data
668 const std::string
&field
= this->*(b
->strMember
);
670 BuildField(data
, offset
, b
->type
, field
);
674 // save any group links
675 GroupLinksType::const_iterator
676 gb
= GroupLinks
.begin(), ge
= GroupLinks
.end();
677 for( ; gb
!= ge
; gb
++ ) {
678 Barry::Protocol::GroupLink link
;
679 link
.uniqueId
= htobl(gb
->Link
);
680 link
.unknown
= htobs(gb
->Unknown
);
681 BuildField(data
, offset
, CFC_GROUP_LINK
, link
);
684 // and finally save unknowns
685 UnknownsType::const_iterator
686 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
687 for( ; ub
!= ue
; ub
++ ) {
688 BuildField(data
, offset
, ub
->type
, ub
->data
);
691 data
.ReleaseBuffer(offset
);
694 void Contact::Clear()
707 DefaultCommunicationsMethod
.clear();
722 m_FirstNameSeen
= false;
728 /// Format a mailing address, handling missing fields.
730 std::string
Contact::GetPostalAddress() const
732 std::string address
= Address1
;
733 if( Address2
.size() ) {
738 if( Address3
.size() ) {
746 address
+= City
+ " ";
747 if( Province
.size() )
748 address
+= Province
+ " ";
753 if( PostalCode
.size() )
754 address
+= PostalCode
;
759 void Contact::Dump(std::ostream
&os
) const
761 ios::fmtflags oldflags
= os
.setf(ios::left
);
762 char fill
= os
.fill(' ');
764 os
<< "Contact: 0x" << setbase(16) << GetID()
765 << " (" << (unsigned int)RecType
<< ")\n";
767 // special fields not in type table
768 os
<< " " << setw(20) << "FirstName";
769 os
<< ": " << FirstName
<< "\n";
770 os
<< " " << setw(20) << "LastName";
771 os
<< ": " << LastName
<< "\n";
773 // cycle through the type table
774 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
775 b
->type
!= CFC_INVALID_FIELD
;
778 // print only fields with data
779 const std::string
&field
= this->*(b
->strMember
);
781 os
<< " " << setw(20) << b
->name
;
782 os
<< ": " << field
<< "\n";
786 // print any group links
787 GroupLinksType::const_iterator
788 gb
= GroupLinks
.begin(), ge
= GroupLinks
.end();
790 os
<< " GroupLinks:\n";
791 for( ; gb
!= ge
; gb
++ ) {
792 os
<< " ID: 0x" << setbase(16) << gb
->Link
<< "\n";
795 // and finally print unknowns
798 // cleanup the stream
803 void Contact::SplitName(const std::string
&full
, std::string
&first
, std::string
&last
)
808 string::size_type pos
= full
.find_last_of(' ');
809 if( pos
!= string::npos
) {
810 // has space, assume last word is last name
811 last
= full
.c_str() + pos
+ 1;
812 first
= full
.substr(0, pos
);
815 // no space, assume only first name
816 first
= full
.substr(0);
822 ///////////////////////////////////////////////////////////////////////////////
826 // Email / message field codes
827 #define MFC_TO 0x01 // can occur multiple times
828 #define MFC_FROM 0x05
829 #define MFC_SUBJECT 0x0b
830 #define MFC_BODY 0x0c
831 #define MFC_END 0xffff
833 FieldLink
<Message
> MessageFieldLinks
[] = {
834 { MFC_TO
, "To", 0, 0, 0, &Message::To
, 0 },
835 { MFC_FROM
, "From", 0, 0, 0, &Message::From
, 0 },
836 { MFC_SUBJECT
, "Subject", 0, 0, &Message::Subject
, 0, 0 },
837 { MFC_BODY
, "Body", 0, 0, &Message::Body
, 0, 0 },
838 { MFC_END
, "End of List",0, 0, 0, 0, 0 }
849 const unsigned char* Message::ParseField(const unsigned char *begin
,
850 const unsigned char *end
)
852 const CommonField
*field
= (const CommonField
*) begin
;
854 // advance and check size
855 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
856 if( begin
> end
) // if begin==end, we are ok
859 if( !btohs(field
->size
) ) // if field has no size, something's up
862 // cycle through the type table
863 for( FieldLink
<Message
> *b
= MessageFieldLinks
;
867 if( b
->type
== field
->type
) {
869 // parse regular string
870 std::string
&s
= this->*(b
->strMember
);
871 s
.assign((const char *)field
->u
.raw
, btohs(field
->size
)-1);
872 return begin
; // done!
874 else if( b
->addrMember
) {
875 // parse email address
876 // get dual name+addr string first
877 const char *fa
= (const char*)field
->u
.addr
.addr
;
878 std::string
dual(fa
, btohs(field
->size
) - sizeof(field
->u
.addr
.unknown
));
880 // assign first string, using null terminator...letting std::string add it for us if it doesn't exist
881 Address
&a
= this->*(b
->addrMember
);
882 a
.Name
= dual
.c_str();
884 // assign second string, using first size as starting point
885 a
.Email
= dual
.c_str() + a
.Name
.size() + 1;
893 uint8_t Message::GetRecType() const
895 throw std::logic_error("Message::GetRecType() called, and not supported by the USB protocol. Should never get called.");
898 // empty API, not required by protocol
899 uint32_t Message::GetUniqueId() const
901 throw std::logic_error("Message::GetUniqueId() called, and not supported by the USB protocol. Should never get called.");
904 // empty API, not required by protocol
905 void Message::SetIds(uint8_t Type
, uint32_t Id
)
907 // accept it without complaining, just do nothing
910 void Message::ParseHeader(const Data
&data
, size_t &offset
)
912 // we skip the "header" since we don't know what to do with it yet
913 offset
+= MESSAGE_RECORD_HEADER_SIZE
;
916 void Message::ParseFields(const Data
&data
, size_t &offset
)
918 const unsigned char *finish
= ParseCommonFields(*this,
919 data
.GetData() + offset
, data
.GetData() + data
.GetSize());
920 offset
+= finish
- (data
.GetData() + offset
);
923 void Message::BuildHeader(Data
&data
, size_t &offset
) const
925 throw std::logic_error("Message::BuildHeader not yet implemented");
928 void Message::BuildFields(Data
&data
, size_t &offset
) const
930 throw std::logic_error("Message::BuildFields not yet implemented");
933 void Message::Clear()
945 // dump message in mbox format
946 void Message::Dump(std::ostream
&os
) const
948 // FIXME - use current time until we figure out the date headers
949 time_t fixme
= time(NULL
);
951 os
<< "From " << (From
.Email
.size() ? From
.Email
.c_str() : "unknown")
952 << " " << ctime(&fixme
);
953 os
<< "Date: " << ctime(&fixme
);
954 os
<< "From: " << From
<< "\n";
955 if( To
.Email
.size() )
956 os
<< "To: " << To
<< "\n";
957 if( Cc
.Email
.size() )
958 os
<< "Cc: " << Cc
<< "\n";
960 os
<< "Subject: " << Subject
<< "\n";
962 for( std::string::const_iterator i
= Body
.begin();
963 i
!= Body
.end() && *i
;
976 ///////////////////////////////////////////////////////////////////////////////
979 // calendar field codes
980 #define CALFC_APPT_TYPE_FLAG 0x01
981 #define CALFC_SUBJECT 0x02
982 #define CALFC_NOTES 0x03
983 #define CALFC_LOCATION 0x04
984 #define CALFC_NOTIFICATION_TIME 0x05
985 #define CALFC_START_TIME 0x06
986 #define CALFC_END_TIME 0x07
987 #define CALFC_RECURRENCE_DATA 0x0c
988 #define CALFC_VERSION_DATA 0x10
989 #define CALFC_NOTIFICATION_DATA 0x1a
990 #define CALFC_TIMEZONE_CODE 0x1e // only seems to show up if recurring
991 #define CALFC_ALLDAYEVENT_FLAG 0xff
992 #define CALFC_END 0xffff
994 FieldLink
<Calendar
> CalendarFieldLinks
[] = {
995 { CALFC_SUBJECT
, "Subject", 0, 0, &Calendar::Subject
, 0, 0 },
996 { CALFC_NOTES
, "Notes", 0, 0, &Calendar::Notes
, 0, 0 },
997 { CALFC_LOCATION
, "Location", 0, 0, &Calendar::Location
, 0, 0 },
998 { CALFC_NOTIFICATION_TIME
,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime
},
999 { CALFC_START_TIME
, "Start Time", 0, 0, 0, 0, &Calendar::StartTime
},
1000 { CALFC_END_TIME
, "End Time", 0, 0, 0, 0, &Calendar::EndTime
},
1001 { CALFC_END
, "End of List",0, 0, 0, 0, 0 }
1004 Calendar::Calendar()
1009 Calendar::~Calendar()
1013 const unsigned char* Calendar::ParseField(const unsigned char *begin
,
1014 const unsigned char *end
)
1016 const CommonField
*field
= (const CommonField
*) begin
;
1018 // advance and check size
1019 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
1020 if( begin
> end
) // if begin==end, we are ok
1023 if( !btohs(field
->size
) ) // if field has no size, something's up
1026 // cycle through the type table
1027 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
1028 b
->type
!= CALFC_END
;
1031 if( b
->type
== field
->type
) {
1032 if( b
->strMember
) {
1033 std::string
&s
= this->*(b
->strMember
);
1034 s
.assign((const char *)field
->u
.raw
, btohs(field
->size
)-1);
1035 return begin
; // done!
1037 else if( b
->timeMember
&& btohs(field
->size
) == 4 ) {
1038 time_t &t
= this->*(b
->timeMember
);
1039 t
= min2time(field
->u
.min1900
);
1045 // handle special cases
1046 switch( field
->type
)
1048 case CALFC_APPT_TYPE_FLAG
:
1049 switch( field
->u
.raw
[0] )
1051 case 'a': // regular non-recurring appointment
1055 case '*': // recurring appointment
1060 throw Error("Calendar::ParseField: unknown appointment type");
1064 case CALFC_ALLDAYEVENT_FLAG
:
1065 AllDayEvent
= field
->u
.raw
[0] == 1;
1068 case CALFC_RECURRENCE_DATA
:
1069 if( btohs(field
->size
) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE
) {
1071 ParseRecurrenceData(&field
->u
.raw
[0]);
1075 throw Error("Calendar::ParseField: not enough data in recurrence data field");
1079 case CALFC_TIMEZONE_CODE
:
1080 if( btohs(field
->size
) == 2 ) {
1082 TimeZoneCode
= btohs(field
->u
.code
);
1085 throw Error("Calendar::ParseField: not enough data in time zone code field");
1090 // if still not handled, add to the Unknowns list
1092 uf
.type
= field
->type
;
1093 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
1094 Unknowns
.push_back(uf
);
1096 // return new pointer for next field
1100 // this function assumes the size has already been checked
1101 void Calendar::ParseRecurrenceData(const void *data
)
1103 const CalendarRecurrenceDataField
*rec
=
1104 (const CalendarRecurrenceDataField
*) data
;
1106 Interval
= btohs(rec
->interval
);
1108 Interval
= 1; // must always be >= 1
1110 if( rec
->endTime
== 0xffffffff ) {
1114 RecurringEndTime
= min2time(rec
->endTime
);
1121 RecurringType
= Day
;
1125 case CRDF_TYPE_MONTH_BY_DATE
:
1126 RecurringType
= MonthByDate
;
1127 DayOfMonth
= rec
->u
.month_by_date
.monthDay
;
1130 case CRDF_TYPE_MONTH_BY_DAY
:
1131 RecurringType
= MonthByDay
;
1132 DayOfWeek
= rec
->u
.month_by_day
.weekDay
;
1133 WeekOfMonth
= rec
->u
.month_by_day
.week
;
1136 case CRDF_TYPE_YEAR_BY_DATE
:
1137 RecurringType
= YearByDate
;
1138 DayOfMonth
= rec
->u
.year_by_date
.monthDay
;
1139 MonthOfYear
= rec
->u
.year_by_date
.month
;
1142 case CRDF_TYPE_YEAR_BY_DAY
:
1143 RecurringType
= YearByDay
;
1144 DayOfWeek
= rec
->u
.year_by_day
.weekDay
;
1145 WeekOfMonth
= rec
->u
.year_by_day
.week
;
1146 MonthOfYear
= rec
->u
.year_by_day
.month
;
1149 case CRDF_TYPE_WEEK
:
1150 RecurringType
= Week
;
1152 // Note: this simple copy is only possible since
1153 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
1154 // If this ever changes, this code will need to change.
1155 WeekDays
= rec
->u
.week
.days
;
1159 eout("Unknown recurrence data type: " << rec
->type
);
1160 throw Error("Unknown recurrence data type");
1164 // this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
1165 // available in data
1166 void Calendar::BuildRecurrenceData(void *data
)
1169 throw Error("Calendar::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
1171 CalendarRecurrenceDataField
*rec
= (CalendarRecurrenceDataField
*) data
;
1174 memset(data
, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
1176 rec
->interval
= htobs(Interval
);
1177 rec
->startTime
= time2min(StartTime
);
1179 rec
->endTime
= 0xffffffff;
1181 rec
->endTime
= time2min(RecurringEndTime
);
1183 switch( RecurringType
)
1186 rec
->type
= CRDF_TYPE_DAY
;
1191 rec
->type
= CRDF_TYPE_MONTH_BY_DATE
;
1192 rec
->u
.month_by_date
.monthDay
= DayOfMonth
;
1196 rec
->type
= CRDF_TYPE_MONTH_BY_DAY
;
1197 rec
->u
.month_by_day
.weekDay
= DayOfWeek
;
1198 rec
->u
.month_by_day
.week
= WeekOfMonth
;
1202 rec
->type
= CRDF_TYPE_YEAR_BY_DATE
;
1203 rec
->u
.year_by_date
.monthDay
= DayOfMonth
;
1204 rec
->u
.year_by_date
.month
= MonthOfYear
;
1208 rec
->type
= CRDF_TYPE_YEAR_BY_DAY
;
1209 rec
->u
.year_by_day
.weekDay
= DayOfWeek
;
1210 rec
->u
.year_by_day
.week
= WeekOfMonth
;
1211 rec
->u
.year_by_day
.month
= MonthOfYear
;
1215 rec
->type
= CRDF_TYPE_WEEK
;
1217 // Note: this simple copy is only possible since
1218 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
1219 // If this ever changes, this code will need to change.
1220 rec
->u
.week
.days
= WeekDays
;
1224 eout("Calendar::BuildRecurrenceData: "
1225 "Unknown recurrence data type: " << rec
->type
);
1226 throw Error("Calendar::BuildRecurrenceData: Unknown recurrence data type");
1230 void Calendar::ParseHeader(const Data
&data
, size_t &offset
)
1232 // no header in Calendar records
1235 void Calendar::ParseFields(const Data
&data
, size_t &offset
)
1237 const unsigned char *finish
= ParseCommonFields(*this,
1238 data
.GetData() + offset
, data
.GetData() + data
.GetSize());
1239 offset
+= finish
- (data
.GetData() + offset
);
1242 void Calendar::BuildHeader(Data
&data
, size_t &offset
) const
1244 // no header in Calendar records
1250 /// Build fields part of record.
1252 void Calendar::BuildFields(Data
&data
, size_t &offset
) const
1256 // output the type first
1257 BuildField(data
, offset
, CALFC_APPT_TYPE_FLAG
, Recurring
? '*' : 'a');
1259 // output all day event flag only if set
1261 BuildField(data
, offset
, CALFC_ALLDAYEVENT_FLAG
, (char)1);
1263 // cycle through the type table
1264 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
1265 b
->type
!= CALFC_END
;
1268 if( b
->strMember
) {
1269 const std::string
&s
= this->*(b
->strMember
);
1271 BuildField(data
, offset
, b
->type
, s
);
1273 else if( b
->timeMember
) {
1274 time_t t
= this->*(b
->timeMember
);
1276 BuildField1900(data
, offset
, b
->type
, t
);
1280 // and finally save unknowns
1281 UnknownsType::const_iterator
1282 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
1283 for( ; ub
!= ue
; ub
++ ) {
1284 BuildField(data
, offset
, ub
->type
, ub
->data
);
1287 data
.ReleaseBuffer(offset
);
1290 void Calendar::Clear()
1292 AllDayEvent
= false;
1296 NotificationTime
= StartTime
= EndTime
= 0;
1299 RecurringType
= Calendar::Week
;
1301 RecurringEndTime
= 0;
1303 TimeZoneCode
= GetTimeZoneCode(0, 0); // default to GMT
1304 DayOfWeek
= WeekOfMonth
= DayOfMonth
= MonthOfYear
= 0;
1310 void Calendar::Dump(std::ostream
&os
) const
1312 static const char *DayNames
[] = { "Sun", "Mon", "Tue", "Wed",
1313 "Thu", "Fri", "Sat" };
1314 static const char *MonthNames
[] = { "Jan", "Feb", "Mar", "Apr",
1315 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
1317 // FIXME - need a "check all data" function that make sure that all
1318 // recurrence data is within range. Then call that before using
1319 // the data, such as in Build and in Dump.
1321 os
<< "Calendar entry: 0x" << setbase(16) << RecordId
1322 << " (" << (unsigned int)RecType
<< ")\n";
1323 os
<< " All Day Event: " << (AllDayEvent
? "yes" : "no") << "\n";
1325 // cycle through the type table
1326 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
1327 b
->type
!= CALFC_END
;
1330 if( b
->strMember
) {
1331 const std::string
&s
= this->*(b
->strMember
);
1333 os
<< " " << b
->name
<< ": " << s
<< "\n";
1335 else if( b
->timeMember
) {
1336 time_t t
= this->*(b
->timeMember
);
1338 os
<< " " << b
->name
<< ": " << ctime(&t
);
1342 // print recurrence data if available
1343 os
<< " Recurring: " << (Recurring
? "yes" : "no") << "\n";
1345 switch( RecurringType
)
1348 os
<< " Every day.\n";
1352 os
<< " Every month on the "
1354 << (DayOfMonth
== 1 ? "st" : "")
1355 << (DayOfMonth
== 2 ? "nd" : "")
1356 << (DayOfMonth
== 3 ? "rd" : "")
1357 << (DayOfMonth
> 3 ? "th" : "")
1362 os
<< " Every month on the "
1363 << DayNames
[DayOfWeek
]
1370 os
<< " Every year on "
1371 << MonthNames
[MonthOfYear
-1]
1372 << " " << DayOfMonth
<< "\n";
1376 os
<< " Every year in " << MonthNames
[MonthOfYear
-1]
1378 << DayNames
[DayOfWeek
]
1379 << " of week " << WeekOfMonth
<< "\n";
1383 os
<< " Every week on: ";
1384 if( WeekDays
& CAL_WD_SUN
) os
<< "Sun ";
1385 if( WeekDays
& CAL_WD_MON
) os
<< "Mon ";
1386 if( WeekDays
& CAL_WD_TUE
) os
<< "Tue ";
1387 if( WeekDays
& CAL_WD_WED
) os
<< "Wed ";
1388 if( WeekDays
& CAL_WD_THU
) os
<< "Thu ";
1389 if( WeekDays
& CAL_WD_FRI
) os
<< "Fri ";
1390 if( WeekDays
& CAL_WD_SAT
) os
<< "Sat ";
1395 os
<< " Unknown recurrence type\n";
1399 os
<< " Interval: " << Interval
<< "\n";
1402 os
<< " Ends: never\n";
1405 << ctime(&RecurringEndTime
);
1408 // print any unknowns
1413 ///////////////////////////////////////////////////////////////////////////////
1414 // ServiceBookConfig class
1416 // service book packed field codes
1417 #define SBFCC_END 0xffff
1419 FieldLink
<ServiceBookConfig
> ServiceBookConfigFieldLinks
[] = {
1420 // { SBFC_DSID, "DSID", 0, 0, &ServiceBook::DSID, 0, 0 },
1421 { SBFCC_END
, "End of List",0, 0, 0, 0, 0 }
1424 ServiceBookConfig::ServiceBookConfig()
1430 ServiceBookConfig::~ServiceBookConfig()
1434 const unsigned char* ServiceBookConfig::ParseField(const unsigned char *begin
,
1435 const unsigned char *end
)
1438 uint16_t size
, type
;
1444 const PackedField_02
*field
= (const PackedField_02
*) begin
;
1448 begin
+= PACKED_FIELD_02_HEADER_SIZE
+ size
;
1454 const PackedField_10
*field
= (const PackedField_10
*) begin
;
1458 begin
+= PACKED_FIELD_10_HEADER_SIZE
+ size
;
1463 eout("Unknown packed field format" << Format
);
1469 if( begin
> end
) // if begin==end, we are ok
1472 if( !size
) // if field has no size, something's up
1475 // cycle through the type table
1476 for( FieldLink
<ServiceBookConfig
> *b
= ServiceBookConfigFieldLinks
;
1477 b
->type
!= SBFCC_END
;
1480 if( b
->type
== type
) {
1481 if( b
->strMember
) {
1482 std::string
&s
= this->*(b
->strMember
);
1483 s
.assign((const char *)raw
, size
-1);
1484 return begin
; // done!
1490 // handle special cases
1496 // if still not handled, add to the Unknowns list
1499 uf
.data
.assign((const char*)raw
, size
);
1500 Unknowns
.push_back(uf
);
1502 // return new pointer for next field
1506 void ServiceBookConfig::ParseHeader(const Data
&data
, size_t &offset
)
1508 MAKE_RECORD(const Barry::Protocol::ServiceBookConfigField
, sbc
, data
, offset
);
1509 offset
+= SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE
;
1510 if( data
.GetSize() >= offset
) { // size check!
1511 Format
= sbc
->format
;
1515 void ServiceBookConfig::ParseFields(const Data
&data
, size_t &offset
)
1517 const unsigned char *finish
= ParseCommonFields(*this,
1518 data
.GetData() + offset
, data
.GetData() + data
.GetSize());
1519 offset
+= finish
- (data
.GetData() + offset
);
1522 void ServiceBookConfig::BuildHeader(Data
&data
, size_t &offset
) const
1524 // make sure there is enough space
1525 data
.GetBuffer(offset
+ SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE
);
1527 MAKE_RECORD(Barry::Protocol::ServiceBookConfigField
, sbc
, data
, offset
);
1528 sbc
->format
= Format
;
1530 offset
+= SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE
;
1536 /// Build fields part of record
1538 void ServiceBookConfig::BuildFields(Data
&data
, size_t &offset
) const
1540 throw std::logic_error("ServiceBookConfig::Build not yet implemented");
1543 void ServiceBookConfig::Clear()
1548 void ServiceBookConfig::Dump(std::ostream
&os
) const
1550 os
<< " ServiceBookConfig Format: " << setbase(16) << (uint16_t)Format
<< "\n";
1552 // cycle through the type table
1553 for( const FieldLink
<ServiceBookConfig
> *b
= ServiceBookConfigFieldLinks
;
1554 b
->type
!= SBFCC_END
;
1557 if( b
->strMember
) {
1558 const std::string
&s
= this->*(b
->strMember
);
1560 os
<< " " << b
->name
<< ": " << s
<< "\n";
1562 else if( b
->timeMember
) {
1563 time_t t
= this->*(b
->timeMember
);
1565 os
<< " " << b
->name
<< ": " << ctime(&t
);
1569 // print any unknowns
1571 os
<< " ------------------- End of Config Field\n";
1575 ///////////////////////////////////////////////////////////////////////////////
1576 // ServiceBook class
1578 // service book field codes
1579 #define SBFC_OLD_NAME 0x01
1580 #define SBFC_HIDDEN_NAME 0x02
1581 #define SBFC_NAME 0x03
1582 #define SBFC_OLD_UNIQUE_ID 0x06
1583 #define SBFC_UNIQUE_ID 0x07
1584 #define SBFC_CONTENT_ID 0x08
1585 #define SBFC_CONFIG 0x09
1586 #define SBFC_OLD_DESC 0x32
1587 #define SBFC_DESCRIPTION 0x0f
1588 #define SBFC_DSID 0xa1
1589 #define SBFC_BES_DOMAIN 0xa2
1590 #define SBFC_USER_ID 0xa3
1591 #define SBFC_END 0xffff
1593 FieldLink
<ServiceBook
> ServiceBookFieldLinks
[] = {
1594 { SBFC_HIDDEN_NAME
, "Hidden Name",0, 0, &ServiceBook::HiddenName
, 0, 0 },
1595 { SBFC_DSID
, "DSID", 0, 0, &ServiceBook::DSID
, 0, 0 },
1596 { SBFC_END
, "End of List",0, 0, 0, 0, 0 }
1599 ServiceBook::ServiceBook()
1600 : NameType(SBFC_OLD_NAME
),
1601 DescType(SBFC_OLD_DESC
),
1602 UniqueIdType(SBFC_OLD_UNIQUE_ID
),
1608 ServiceBook::~ServiceBook()
1612 const unsigned char* ServiceBook::ParseField(const unsigned char *begin
,
1613 const unsigned char *end
)
1615 const CommonField
*field
= (const CommonField
*) begin
;
1617 // advance and check size
1618 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
1619 if( begin
> end
) // if begin==end, we are ok
1622 if( !btohs(field
->size
) ) // if field has no size, something's up
1625 // cycle through the type table
1626 for( FieldLink
<ServiceBook
> *b
= ServiceBookFieldLinks
;
1627 b
->type
!= SBFC_END
;
1630 if( b
->type
== field
->type
) {
1631 if( b
->strMember
) {
1632 std::string
&s
= this->*(b
->strMember
);
1633 s
.assign((const char *)field
->u
.raw
, btohs(field
->size
)-1);
1634 return begin
; // done!
1636 else if( b
->timeMember
&& btohs(field
->size
) == 4 ) {
1637 time_t &t
= this->*(b
->timeMember
);
1638 t
= min2time(field
->u
.min1900
);
1644 // handle special cases
1645 switch( field
->type
)
1647 case SBFC_OLD_NAME
: // strings with old/new type codes
1649 Name
.assign((const char *)field
->u
.raw
, btohs(field
->size
)-1);
1650 NameType
= field
->type
;
1654 case SBFC_DESCRIPTION
:
1655 Description
.assign((const char *)field
->u
.raw
, btohs(field
->size
)-1);
1656 DescType
= field
->type
;
1659 case SBFC_OLD_UNIQUE_ID
:
1660 case SBFC_UNIQUE_ID
:
1661 UniqueId
.assign((const char *)field
->u
.raw
, btohs(field
->size
));
1662 UniqueIdType
= field
->type
;
1665 case SBFC_CONTENT_ID
:
1666 ContentId
.assign((const char *)field
->u
.raw
, btohs(field
->size
));
1669 case SBFC_BES_DOMAIN
:
1670 BesDomain
.assign((const char *)field
->u
.raw
, btohs(field
->size
));
1675 Data
config((const void *)field
->u
.raw
, btohs(field
->size
));
1677 Config
.ParseHeader(config
, offset
);
1678 Config
.ParseFields(config
, offset
);
1680 break; // break here so raw packet is still visible in dump
1684 // if still not handled, add to the Unknowns list
1686 uf
.type
= field
->type
;
1687 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
1688 Unknowns
.push_back(uf
);
1690 // return new pointer for next field
1694 void ServiceBook::ParseHeader(const Data
&data
, size_t &offset
)
1696 // no header in this record (?)
1699 void ServiceBook::ParseFields(const Data
&data
, size_t &offset
)
1701 const unsigned char *finish
= ParseCommonFields(*this,
1702 data
.GetData() + offset
, data
.GetData() + data
.GetSize());
1703 offset
+= finish
- (data
.GetData() + offset
);
1706 void ServiceBook::BuildHeader(Data
&data
, size_t &offset
) const
1708 // no header in this record (?)
1714 /// Build fields part of record
1716 void ServiceBook::BuildFields(Data
&data
, size_t &offset
) const
1718 throw std::logic_error("ServiceBook::BuildFields not yet implemented");
1721 void ServiceBook::Clear()
1727 void ServiceBook::Dump(std::ostream
&os
) const
1729 os
<< "ServiceBook entry: 0x" << setbase(16) << RecordId
1730 << " (" << (unsigned int)RecType
<< ")\n";
1732 // cycle through the type table
1733 for( const FieldLink
<ServiceBook
> *b
= ServiceBookFieldLinks
;
1734 b
->type
!= SBFC_END
;
1737 if( b
->strMember
) {
1738 const std::string
&s
= this->*(b
->strMember
);
1740 os
<< " " << b
->name
<< ": " << s
<< "\n";
1742 else if( b
->timeMember
) {
1743 time_t t
= this->*(b
->timeMember
);
1745 os
<< " " << b
->name
<< ": " << ctime(&t
);
1750 if( UniqueId
.size() )
1751 os
<< " Unique ID: " << UniqueId
<< "\n";
1752 if( ContentId
.size() )
1753 os
<< " Content ID: " << ContentId
<< "\n";
1754 if( BesDomain
.size() )
1755 os
<< " (BES) Domain: " << BesDomain
<< "\n";
1759 // print any unknowns
1764 } // namespace Barry
1767 #ifdef __TEST_MODE__
1771 int main(int argc
, char *argv
[])
1774 cerr
<< "Usage: test <datafile>" << endl
;
1778 std::vector
<Data
> array
;
1779 if( !LoadDataArray(argv
[1], array
) ) {
1780 cerr
<< "Unable to load file: " << argv
[1] << endl
;
1784 cout
<< "Loaded " << array
.size() << " items" << endl
;
1786 for( std::vector
<Data
>::iterator b
= array
.begin(), e
= array
.end();
1790 // cout << d << endl;
1791 if( d
.GetSize() > 13 && d
.GetData()[6] == 0x4f ) {
1792 Barry::Contact contact
;
1794 contact
.ParseFields(d
, size
);
1795 cout
<< contact
<< endl
;
1796 contact
.DumpLdif(cout
, "ou=People,dc=example,dc=com");
1798 else if( d
.GetSize() > 13 && d
.GetData()[6] == 0x44 ) {
1799 Barry::Calendar cal
;
1801 cal
.ParseFields(d
, size
);
1802 cout
<< cal
<< endl
;