- opensync module work:
[barry.git] / src / record.cc
blobbce7dfdfaf227edf4cdf55dfac77633bfadd10eb
1 ///
2 /// \file record.cc
3 /// Blackberry database record classes. Help translate data
4 /// from data packets to useful structurs, and back.
5 ///
7 /*
8 Copyright (C) 2005-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.
23 #include "record.h"
24 #include "protocol.h"
25 #include "protostructs.h"
26 #include "data.h"
27 #include "time.h"
28 #include "error.h"
29 #include "endian.h"
30 #include <ostream>
31 #include <iomanip>
32 #include <time.h>
33 #include <stdexcept>
35 #define __DEBUG_MODE__
36 #include "debug.h"
38 using namespace std;
39 using namespace Barry::Protocol;
41 namespace Barry {
43 std::ostream& operator<<(std::ostream &os, const Message::Address &msga) {
44 os << msga.Name.c_str() << " <" << msga.Email.c_str() << ">";
45 return os;
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);
58 return b;
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);
69 field->type = type;
70 field->u.min1900 = time2min(t);
72 size += fieldsize;
75 void BuildField(Data &data, size_t &size, uint8_t type, char c)
77 size_t strsize = 1;
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);
83 field->type = type;
84 memcpy(field->u.raw, &c, strsize);
86 size += fieldsize;
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);
98 field->type = type;
99 memcpy(field->u.raw, str.c_str(), strsize);
101 size += fieldsize;
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);
112 field->type = type;
113 field->u.link = link;
115 size += fieldsize;
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);
136 if( headend > end )
137 return headend;
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
144 return begin;
146 if( !field->size ) // if field has no size, something's up
147 return begin;
149 Command command;
150 command.Code = field->code;
151 command.Name.assign((const char *)field->name, field->size);
152 Commands.push_back(command);
153 return begin;
156 void CommandTable::Parse(const Data &data, size_t offset)
158 if( offset >= data.GetSize() )
159 return;
161 const unsigned char *begin = data.GetData() + offset;
162 const unsigned char *end = data.GetData() + data.GetSize();
164 while( begin < end )
165 begin = ParseField(begin, end);
168 void CommandTable::Clear()
170 Commands.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 )
178 return b->Code;
179 return 0;
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
215 return begin;
217 State state;
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;
225 return begin;
228 void RecordStateTable::Parse(const Data &data)
230 size_t offset = 12; // skipping the unknown 2 bytes at start
232 if( offset >= data.GetSize() )
233 return;
235 const unsigned char *begin = data.GetData() + offset;
236 const unsigned char *end = data.GetData() + data.GetSize();
238 while( begin < end )
239 begin = ParseField(begin, end);
242 void RecordStateTable::Clear()
244 StateMap.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 ) {
256 if( pFoundIndex )
257 *pFoundIndex = i->first;
258 return true;
261 return false;
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
269 m_LastNewRecordId++;
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
278 else {
279 ++i; // next State
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;
299 os.fill(' ');
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
308 os.flags(oldflags);
309 os.fill(fill);
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);
349 if( headend > end )
350 return headend;
352 // get our header
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
358 return begin;
360 if( !ConvertHtoB(field->nameSize) ) // if field has no size, something's up
361 return begin;
363 Database db;
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);
368 return begin;
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) )
375 return;
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
390 // for header size
391 while( begin < end )
392 begin = ParseField<DBDBField>(begin, end);
394 else
395 dout("DatabaseDatabase: not enough data for parsing");
396 break;
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
405 // for header size
406 while( begin < end )
407 begin = ParseField<OldDBDBField>(begin, end);
409 else
410 dout("DatabaseDatabase: not enough data for parsing");
411 break;
413 default:
414 // unknown protocol
415 dout("Unknown protocol");
416 break;
422 void DatabaseDatabase::Clear()
424 Databases.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 ) {
433 number = b->Number;
434 return true;
436 return false;
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 ) {
445 name = b->Name;
446 return true;
448 return false;
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();
467 if( ub != ue )
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());
474 return os;
479 ///////////////////////////////////////////////////////////////////////////////
480 // Contact class
482 // Contact field codes
483 #define CFC_EMAIL 1
484 #define CFC_PHONE 2
485 #define CFC_FAX 3
486 #define CFC_WORK_PHONE 6
487 #define CFC_HOME_PHONE 7
488 #define CFC_MOBILE_PHONE 8
489 #define CFC_PAGER 9
490 #define CFC_PIN 10
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
497 #define CFC_CITY 38
498 #define CFC_PROVINCE 39
499 #define CFC_POSTAL_CODE 40
500 #define CFC_COUNTRY 41
501 #define CFC_TITLE 42
502 #define CFC_PUBLIC_KEY 43
503 #define CFC_GROUP_FLAG 44
504 #define CFC_GROUP_LINK 52
505 #define CFC_NOTES 64
506 #define CFC_INVALID_FIELD 255
508 // Contact code to field table
509 template <class Record>
510 struct FieldLink
512 int type;
513 char *name;
514 char *ldif;
515 char *objectClass;
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 }
545 Contact::Contact()
546 : RecType(0),
547 RecordId(0),
548 m_FirstNameSeen(false)
552 Contact::~Contact()
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
564 return begin;
566 if( !btohs(field->size) ) // if field has no size, something's up
567 return begin;
569 // cycle through the type table
570 for( FieldLink<Contact> *b = ContactFieldLinks;
571 b->type != CFC_INVALID_FIELD;
572 b++ )
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 )
584 case CFC_NAME: {
585 // can be used multiple times, for first/last names
586 std::string *name;
587 if( FirstName.size() || m_FirstNameSeen ) {
588 // first name already filled, use last name
589 name = &LastName;
590 m_FirstNameSeen = false;
592 else {
593 name = &FirstName;
594 m_FirstNameSeen = true;
597 name->assign((const char*)field->u.raw, btohs(field->size)-1);
599 return begin;
601 case CFC_GROUP_LINK:
602 // just add the unique ID to the list
603 GroupLinks.push_back(
604 GroupLink(field->u.link.uniqueId,
605 field->u.link.unknown));
606 return begin;
608 case CFC_GROUP_FLAG:
609 // ignore the group flag... the presense of group link items
610 // behaves as the flag in this class
611 return begin;
614 // if still not handled, add to the Unknowns list
615 UnknownField uf;
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
621 return begin;
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
643 // BuildFields
645 /// Build fields part of record
647 void Contact::BuildFields(Data &data, size_t &offset) const
649 data.Zap();
651 // check if this is a group link record, and if so, output
652 // the group flag
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;
665 b++ )
667 // print only fields with data
668 const std::string &field = this->*(b->strMember);
669 if( field.size() ) {
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()
696 Email.clear();
697 Phone.clear();
698 Fax.clear();
699 WorkPhone.clear();
700 HomePhone.clear();
701 MobilePhone.clear();
702 Pager.clear();
703 PIN.clear();
704 FirstName.clear();
705 LastName.clear();
706 Company.clear();
707 DefaultCommunicationsMethod.clear();
708 Address1.clear();
709 Address2.clear();
710 Address3.clear();
711 City.clear();
712 Province.clear();
713 PostalCode.clear();
714 Country.clear();
715 Title.clear();
716 PublicKey.clear();
717 Notes.clear();
719 GroupLinks.clear();
720 Unknowns.clear();
722 m_FirstNameSeen = false;
726 // GetPostalAddress
728 /// Format a mailing address, handling missing fields.
730 std::string Contact::GetPostalAddress() const
732 std::string address = Address1;
733 if( Address2.size() ) {
734 if( address.size() )
735 address += "\n";
736 address += Address2;
738 if( Address3.size() ) {
739 if( address.size() )
740 address += "\n";
741 address += Address3;
743 if( address.size() )
744 address += "\n";
745 if( City.size() )
746 address += City + " ";
747 if( Province.size() )
748 address += Province + " ";
749 if( Country.size() )
750 address += Country;
751 if( address.size() )
752 address += "\n";
753 if( PostalCode.size() )
754 address += PostalCode;
756 return address;
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;
776 b++ )
778 // print only fields with data
779 const std::string &field = this->*(b->strMember);
780 if( field.size() ) {
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();
789 if( gb != ge )
790 os << " GroupLinks:\n";
791 for( ; gb != ge; gb++ ) {
792 os << " ID: 0x" << setbase(16) << gb->Link << "\n";
795 // and finally print unknowns
796 os << Unknowns;
798 // cleanup the stream
799 os.flags(oldflags);
800 os.fill(fill);
803 void Contact::SplitName(const std::string &full, std::string &first, std::string &last)
805 first.clear();
806 last.clear();
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);
814 else {
815 // no space, assume only first name
816 first = full.substr(0);
822 ///////////////////////////////////////////////////////////////////////////////
823 // Message class
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 }
841 Message::Message()
845 Message::~Message()
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
857 return begin;
859 if( !btohs(field->size) ) // if field has no size, something's up
860 return begin;
862 // cycle through the type table
863 for( FieldLink<Message> *b = MessageFieldLinks;
864 b->type != MFC_END;
865 b++ )
867 if( b->type == field->type ) {
868 if( b->strMember ) {
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;
890 return begin;
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()
935 From.Name.clear();
936 From.Email.clear();
937 To.Name.clear();
938 To.Email.clear();
939 Cc.Name.clear();
940 Cc.Email.clear();
941 Subject.clear();
942 Body.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";
959 if( Subject.size() )
960 os << "Subject: " << Subject << "\n";
961 os << "\n";
962 for( std::string::const_iterator i = Body.begin();
963 i != Body.end() && *i;
964 i++)
966 if( *i == '\r' )
967 os << '\n';
968 else
969 os << *i;
971 os << "\n\n";
976 ///////////////////////////////////////////////////////////////////////////////
977 // Calendar class
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()
1006 Clear();
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
1021 return begin;
1023 if( !btohs(field->size) ) // if field has no size, something's up
1024 return begin;
1026 // cycle through the type table
1027 for( FieldLink<Calendar> *b = CalendarFieldLinks;
1028 b->type != CALFC_END;
1029 b++ )
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);
1040 return begin;
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
1052 Recurring = false;
1053 return begin;
1055 case '*': // recurring appointment
1056 Recurring = true;
1057 return begin;
1059 default:
1060 throw Error("Calendar::ParseField: unknown appointment type");
1062 break;
1064 case CALFC_ALLDAYEVENT_FLAG:
1065 AllDayEvent = field->u.raw[0] == 1;
1066 return begin;
1068 case CALFC_RECURRENCE_DATA:
1069 if( btohs(field->size) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE ) {
1070 // good data
1071 ParseRecurrenceData(&field->u.raw[0]);
1073 else {
1074 // not enough data!
1075 throw Error("Calendar::ParseField: not enough data in recurrence data field");
1077 return begin;
1079 case CALFC_TIMEZONE_CODE:
1080 if( btohs(field->size) == 2 ) {
1081 // good data
1082 TimeZoneCode = btohs(field->u.code);
1084 else {
1085 throw Error("Calendar::ParseField: not enough data in time zone code field");
1087 return begin;
1090 // if still not handled, add to the Unknowns list
1091 UnknownField uf;
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
1097 return begin;
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);
1107 if( Interval < 1 )
1108 Interval = 1; // must always be >= 1
1110 if( rec->endTime == 0xffffffff ) {
1111 Perpetual = true;
1113 else {
1114 RecurringEndTime = min2time(rec->endTime);
1115 Perpetual = false;
1118 switch( rec->type )
1120 case CRDF_TYPE_DAY:
1121 RecurringType = Day;
1122 // no extra data
1123 break;
1125 case CRDF_TYPE_MONTH_BY_DATE:
1126 RecurringType = MonthByDate;
1127 DayOfMonth = rec->u.month_by_date.monthDay;
1128 break;
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;
1134 break;
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;
1140 break;
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;
1147 break;
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;
1156 break;
1158 default:
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)
1168 if( !Recurring )
1169 throw Error("Calendar::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
1171 CalendarRecurrenceDataField *rec = (CalendarRecurrenceDataField*) data;
1173 // set all to zero
1174 memset(data, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
1176 rec->interval = htobs(Interval);
1177 rec->startTime = time2min(StartTime);
1178 if( Perpetual )
1179 rec->endTime = 0xffffffff;
1180 else
1181 rec->endTime = time2min(RecurringEndTime);
1183 switch( RecurringType )
1185 case Day:
1186 rec->type = CRDF_TYPE_DAY;
1187 // no extra data
1188 break;
1190 case MonthByDate:
1191 rec->type = CRDF_TYPE_MONTH_BY_DATE;
1192 rec->u.month_by_date.monthDay = DayOfMonth;
1193 break;
1195 case MonthByDay:
1196 rec->type = CRDF_TYPE_MONTH_BY_DAY;
1197 rec->u.month_by_day.weekDay = DayOfWeek;
1198 rec->u.month_by_day.week = WeekOfMonth;
1199 break;
1201 case YearByDate:
1202 rec->type = CRDF_TYPE_YEAR_BY_DATE;
1203 rec->u.year_by_date.monthDay = DayOfMonth;
1204 rec->u.year_by_date.month = MonthOfYear;
1205 break;
1207 case YearByDay:
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;
1212 break;
1214 case Week:
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;
1221 break;
1223 default:
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
1248 // Build
1250 /// Build fields part of record.
1252 void Calendar::BuildFields(Data &data, size_t &offset) const
1254 data.Zap();
1256 // output the type first
1257 BuildField(data, offset, CALFC_APPT_TYPE_FLAG, Recurring ? '*' : 'a');
1259 // output all day event flag only if set
1260 if( AllDayEvent )
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;
1266 b++ )
1268 if( b->strMember ) {
1269 const std::string &s = this->*(b->strMember);
1270 if( s.size() )
1271 BuildField(data, offset, b->type, s);
1273 else if( b->timeMember ) {
1274 time_t t = this->*(b->timeMember);
1275 if( t > 0 )
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;
1293 Subject.clear();
1294 Notes.clear();
1295 Location.clear();
1296 NotificationTime = StartTime = EndTime = 0;
1298 Recurring = false;
1299 RecurringType = Calendar::Week;
1300 Interval = 1;
1301 RecurringEndTime = 0;
1302 Perpetual = false;
1303 TimeZoneCode = GetTimeZoneCode(0, 0); // default to GMT
1304 DayOfWeek = WeekOfMonth = DayOfMonth = MonthOfYear = 0;
1305 WeekDays = 0;
1307 Unknowns.clear();
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;
1328 b++ )
1330 if( b->strMember ) {
1331 const std::string &s = this->*(b->strMember);
1332 if( s.size() )
1333 os << " " << b->name << ": " << s << "\n";
1335 else if( b->timeMember ) {
1336 time_t t = this->*(b->timeMember);
1337 if( t > 0 )
1338 os << " " << b->name << ": " << ctime(&t);
1342 // print recurrence data if available
1343 os << " Recurring: " << (Recurring ? "yes" : "no") << "\n";
1344 if( Recurring ) {
1345 switch( RecurringType )
1347 case Day:
1348 os << " Every day.\n";
1349 break;
1351 case MonthByDate:
1352 os << " Every month on the "
1353 << DayOfMonth
1354 << (DayOfMonth == 1 ? "st" : "")
1355 << (DayOfMonth == 2 ? "nd" : "")
1356 << (DayOfMonth == 3 ? "rd" : "")
1357 << (DayOfMonth > 3 ? "th" : "")
1358 << "\n";
1359 break;
1361 case MonthByDay:
1362 os << " Every month on the "
1363 << DayNames[DayOfWeek]
1364 << " of week "
1365 << WeekOfMonth
1366 << "\n";
1367 break;
1369 case YearByDate:
1370 os << " Every year on "
1371 << MonthNames[MonthOfYear-1]
1372 << " " << DayOfMonth << "\n";
1373 break;
1375 case YearByDay:
1376 os << " Every year in " << MonthNames[MonthOfYear-1]
1377 << " on "
1378 << DayNames[DayOfWeek]
1379 << " of week " << WeekOfMonth << "\n";
1380 break;
1382 case Week:
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 ";
1391 os << "\n";
1392 break;
1394 default:
1395 os << " Unknown recurrence type\n";
1396 break;
1399 os << " Interval: " << Interval << "\n";
1401 if( Perpetual )
1402 os << " Ends: never\n";
1403 else
1404 os << " Ends: "
1405 << ctime(&RecurringEndTime);
1408 // print any unknowns
1409 os << 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()
1425 : Format(0)
1427 Clear();
1430 ServiceBookConfig::~ServiceBookConfig()
1434 const unsigned char* ServiceBookConfig::ParseField(const unsigned char *begin,
1435 const unsigned char *end)
1437 const void *raw;
1438 uint16_t size, type;
1440 switch( Format )
1442 case 0x02:
1444 const PackedField_02 *field = (const PackedField_02 *) begin;
1445 raw = field->raw;
1446 size = field->size;
1447 type = field->type;
1448 begin += PACKED_FIELD_02_HEADER_SIZE + size;
1450 break;
1452 case 0x10:
1454 const PackedField_10 *field = (const PackedField_10 *) begin;
1455 raw = field->raw;
1456 size = field->size;
1457 type = field->type;
1458 begin += PACKED_FIELD_10_HEADER_SIZE + size;
1460 break;
1462 default:
1463 eout("Unknown packed field format" << Format);
1464 return begin + 1;
1468 // check size
1469 if( begin > end ) // if begin==end, we are ok
1470 return begin;
1472 if( !size ) // if field has no size, something's up
1473 return begin;
1475 // cycle through the type table
1476 for( FieldLink<ServiceBookConfig> *b = ServiceBookConfigFieldLinks;
1477 b->type != SBFCC_END;
1478 b++ )
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
1491 switch( type )
1496 // if still not handled, add to the Unknowns list
1497 UnknownField uf;
1498 uf.type = type;
1499 uf.data.assign((const char*)raw, size);
1500 Unknowns.push_back(uf);
1502 // return new pointer for next field
1503 return begin;
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;
1534 // BuildFields
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()
1545 Unknowns.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;
1555 b++ )
1557 if( b->strMember ) {
1558 const std::string &s = this->*(b->strMember);
1559 if( s.size() )
1560 os << " " << b->name << ": " << s << "\n";
1562 else if( b->timeMember ) {
1563 time_t t = this->*(b->timeMember);
1564 if( t > 0 )
1565 os << " " << b->name << ": " << ctime(&t);
1569 // print any unknowns
1570 os << 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),
1603 RecordId(0)
1605 Clear();
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
1620 return begin;
1622 if( !btohs(field->size) ) // if field has no size, something's up
1623 return begin;
1625 // cycle through the type table
1626 for( FieldLink<ServiceBook> *b = ServiceBookFieldLinks;
1627 b->type != SBFC_END;
1628 b++ )
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);
1639 return begin;
1644 // handle special cases
1645 switch( field->type )
1647 case SBFC_OLD_NAME: // strings with old/new type codes
1648 case SBFC_NAME:
1649 Name.assign((const char *)field->u.raw, btohs(field->size)-1);
1650 NameType = field->type;
1651 return begin;
1653 case SBFC_OLD_DESC:
1654 case SBFC_DESCRIPTION:
1655 Description.assign((const char *)field->u.raw, btohs(field->size)-1);
1656 DescType = field->type;
1657 return begin;
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;
1663 return begin;
1665 case SBFC_CONTENT_ID:
1666 ContentId.assign((const char *)field->u.raw, btohs(field->size));
1667 return begin;
1669 case SBFC_BES_DOMAIN:
1670 BesDomain.assign((const char *)field->u.raw, btohs(field->size));
1671 return begin;
1673 case SBFC_CONFIG:
1675 Data config((const void *)field->u.raw, btohs(field->size));
1676 size_t offset = 0;
1677 Config.ParseHeader(config, offset);
1678 Config.ParseFields(config, offset);
1680 break; // break here so raw packet is still visible in dump
1681 // return begin;
1684 // if still not handled, add to the Unknowns list
1685 UnknownField uf;
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
1691 return begin;
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 (?)
1712 // BuildFields
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()
1723 Unknowns.clear();
1724 Config.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;
1735 b++ )
1737 if( b->strMember ) {
1738 const std::string &s = this->*(b->strMember);
1739 if( s.size() )
1740 os << " " << b->name << ": " << s << "\n";
1742 else if( b->timeMember ) {
1743 time_t t = this->*(b->timeMember);
1744 if( t > 0 )
1745 os << " " << b->name << ": " << ctime(&t);
1749 // special cases
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";
1757 os << Config;
1759 // print any unknowns
1760 os << Unknowns;
1764 } // namespace Barry
1767 #ifdef __TEST_MODE__
1769 #include <iostream>
1771 int main(int argc, char *argv[])
1773 if( argc < 2 ) {
1774 cerr << "Usage: test <datafile>" << endl;
1775 return 1;
1778 std::vector<Data> array;
1779 if( !LoadDataArray(argv[1], array) ) {
1780 cerr << "Unable to load file: " << argv[1] << endl;
1781 return 1;
1784 cout << "Loaded " << array.size() << " items" << endl;
1786 for( std::vector<Data>::iterator b = array.begin(), e = array.end();
1787 b != e; b++ )
1789 Data &d = *b;
1790 // cout << d << endl;
1791 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
1792 Barry::Contact contact;
1793 size_t size = 13;
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;
1800 size_t size = 13;
1801 cal.ParseFields(d, size);
1802 cout << cal << endl;
1807 #endif