record.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       record.cc
00003 ///             Blackberry database record classes.  Help translate data
00004 ///             from data packets to useful structurs, and back.
00005 ///             This header provides the common types and classes
00006 ///             used by the general record parser classes in the
00007 ///             r_*.h files.  Only application-safe API stuff goes in
00008 ///             here.  Internal library types go in record-internal.h
00009 ///
00010 
00011 /*
00012     Copyright (C) 2005-2009, Net Direct Inc. (http://www.netdirect.ca/)
00013 
00014     This program is free software; you can redistribute it and/or modify
00015     it under the terms of the GNU General Public License as published by
00016     the Free Software Foundation; either version 2 of the License, or
00017     (at your option) any later version.
00018 
00019     This program is distributed in the hope that it will be useful,
00020     but WITHOUT ANY WARRANTY; without even the implied warranty of
00021     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 
00023     See the GNU General Public License in the COPYING file at the
00024     root directory of this project for more details.
00025 */
00026 
00027 #include "record.h"
00028 #include "record-internal.h"
00029 #include "protocol.h"
00030 #include "protostructs.h"
00031 #include "data.h"
00032 #include "time.h"
00033 #include "error.h"
00034 #include "endian.h"
00035 #include <sstream>
00036 #include <iomanip>
00037 #include <time.h>
00038 #include <string.h>
00039 #include <stdio.h>                      // for sscanf()
00040 #include <stdexcept>
00041 
00042 #define __DEBUG_MODE__
00043 #include "debug.h"
00044 
00045 using namespace std;
00046 using namespace Barry::Protocol;
00047 
00048 namespace Barry {
00049 
00050 //////////////////////////////////////////////////////////////////////////////
00051 // Field builder helper functions
00052 
00053 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
00054 {
00055         size_t timesize = COMMON_FIELD_MIN1900_SIZE;
00056         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
00057         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00058         CommonField *field = (CommonField *) pd;
00059 
00060         field->size = htobs(timesize);
00061         field->type = type;
00062         field->u.min1900 = time2min(t);
00063 
00064         size += fieldsize;
00065 }
00066 
00067 void BuildField(Data &data, size_t &size, uint8_t type, char c)
00068 {
00069         BuildField(data, size, type, (uint8_t)c);
00070 }
00071 
00072 void BuildField(Data &data, size_t &size, uint8_t type, uint8_t c)
00073 {
00074         size_t strsize = 1;
00075         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
00076         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00077         CommonField *field = (CommonField *) pd;
00078 
00079         field->size = htobs(strsize);
00080         field->type = type;
00081         memcpy(field->u.raw, &c, strsize);
00082 
00083         size += fieldsize;
00084 }
00085 
00086 void BuildField(Data &data, size_t &size, uint8_t type, uint16_t value)
00087 {
00088         size_t strsize = 2;
00089         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
00090         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00091         CommonField *field = (CommonField *) pd;
00092 
00093         field->size = htobs(strsize);
00094         field->type = type;
00095 
00096         uint16_t store = htobs(value);
00097         memcpy(field->u.raw, &store, strsize);
00098 
00099         size += fieldsize;
00100 }
00101 
00102 void BuildField(Data &data, size_t &size, uint8_t type, uint32_t value)
00103 {
00104         size_t strsize = 4;
00105         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
00106         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00107         CommonField *field = (CommonField *) pd;
00108 
00109         field->size = htobl(strsize);
00110         field->type = type;
00111 
00112         uint32_t store = htobs(value);
00113         memcpy(field->u.raw, &store, strsize);
00114 
00115         size += fieldsize;
00116 }
00117 
00118 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
00119 {
00120         // include null terminator
00121         BuildField(data, size, type, str.c_str(), str.size() + 1);
00122 }
00123 
00124 void BuildField(Data &data, size_t &size, uint8_t type,
00125                 const void *buf, size_t bufsize)
00126 {
00127         // include null terminator
00128         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + bufsize;
00129         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00130         CommonField *field = (CommonField *) pd;
00131 
00132         field->size = htobs(bufsize);
00133         field->type = type;
00134         memcpy(field->u.raw, buf, bufsize);
00135 
00136         size += fieldsize;
00137 }
00138 
00139 void BuildField(Data &data, size_t &size, const Barry::UnknownField &field)
00140 {
00141         BuildField(data, size, field.type,
00142                 field.data.raw_data.data(), field.data.raw_data.size());
00143 }
00144 
00145 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
00146 {
00147         size_t linksize = sizeof(Barry::Protocol::GroupLink);
00148         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
00149         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00150         CommonField *field = (CommonField *) pd;
00151 
00152         field->size = htobs(linksize);
00153         field->type = type;
00154         field->u.link = link;
00155 
00156         size += fieldsize;
00157 }
00158 
00159 std::string ParseFieldString(const Barry::Protocol::CommonField *field)
00160 {
00161         // make no assumptions here, and pass the full size in as
00162         // the maxlen, even though 99% of the time, it will be a null...
00163         // this function can be used by non-null terminated strings as well
00164         return ParseFieldString(field->u.raw, btohs(field->size));
00165 }
00166 
00167 std::string ParseFieldString(const void *data, uint16_t maxlen)
00168 {
00169         const char *str = (const char *)data;
00170 
00171         // find last non-null character, since some fields
00172         // can have multiple null terminators
00173         while( maxlen && str[maxlen-1] == 0 )
00174                 maxlen--;
00175 
00176         return std::string(str, maxlen);
00177 }
00178 
00179 
00180 ///////////////////////////////////////////////////////////////////////////////
00181 // CommandTable class
00182 
00183 CommandTable::CommandTable()
00184 {
00185 }
00186 
00187 CommandTable::~CommandTable()
00188 {
00189 }
00190 
00191 const unsigned char* CommandTable::ParseField(const unsigned char *begin,
00192                                               const unsigned char *end)
00193 {
00194         // check if there is enough data for a header
00195         const unsigned char *headend = begin + sizeof(CommandTableField);
00196         if( headend > end )
00197                 return headend;
00198 
00199         const CommandTableField *field = (const CommandTableField *) begin;
00200 
00201         // advance and check size
00202         begin += COMMAND_FIELD_HEADER_SIZE + field->size;       // size is byte
00203         if( begin > end )               // if begin==end, we are ok
00204                 return begin;
00205 
00206         if( !field->size )              // if field has no size, something's up
00207                 return begin;
00208 
00209         Command command;
00210         command.Code = field->code;
00211         command.Name.assign((const char *)field->name, field->size);
00212         Commands.push_back(command);
00213         return begin;
00214 }
00215 
00216 void CommandTable::Parse(const Data &data, size_t offset)
00217 {
00218         if( offset >= data.GetSize() )
00219                 return;
00220 
00221         const unsigned char *begin = data.GetData() + offset;
00222         const unsigned char *end = data.GetData() + data.GetSize();
00223 
00224         while( begin < end )
00225                 begin = ParseField(begin, end);
00226 }
00227 
00228 void CommandTable::Clear()
00229 {
00230         Commands.clear();
00231 }
00232 
00233 unsigned int CommandTable::GetCommand(const std::string &name) const
00234 {
00235         CommandArrayType::const_iterator b = Commands.begin();
00236         for( ; b != Commands.end(); b++ )
00237                 if( b->Name == name )
00238                         return b->Code;
00239         return 0;
00240 }
00241 
00242 void CommandTable::Dump(std::ostream &os) const
00243 {
00244         CommandArrayType::const_iterator b = Commands.begin();
00245         os << "Command table:\n";
00246         for( ; b != Commands.end(); b++ ) {
00247                 os << "    Command: 0x" << setbase(16) << b->Code
00248                    << " '" << b->Name << "'\n";
00249         }
00250 }
00251 
00252 
00253 
00254 ///////////////////////////////////////////////////////////////////////////////
00255 // RecordStateTable class
00256 
00257 RecordStateTable::RecordStateTable()
00258         : m_LastNewRecordId(1)
00259 {
00260 }
00261 
00262 RecordStateTable::~RecordStateTable()
00263 {
00264 }
00265 
00266 const unsigned char* RecordStateTable::ParseField(const unsigned char *begin,
00267                                                   const unsigned char *end)
00268 {
00269         const RecordStateTableField *field = (const RecordStateTableField *) begin;
00270 
00271         // advance and check size
00272         begin += sizeof(RecordStateTableField);
00273         if( begin > end )               // if begin==end, we are ok
00274                 return begin;
00275 
00276         State state;
00277         state.Index = btohs(field->index);
00278         state.RecordId = btohl(field->uniqueId);
00279         state.Dirty = (field->flags & BARRY_RSTF_DIRTY) != 0;
00280         state.RecType = field->rectype;
00281         state.Unknown2.assign((const char*)field->unknown2, sizeof(field->unknown2));
00282         StateMap[state.Index] = state;
00283 
00284         return begin;
00285 }
00286 
00287 void RecordStateTable::Parse(const Data &data)
00288 {
00289         size_t offset = 12;     // skipping the unknown 2 bytes at start
00290 
00291         if( offset >= data.GetSize() )
00292                 return;
00293 
00294         const unsigned char *begin = data.GetData() + offset;
00295         const unsigned char *end = data.GetData() + data.GetSize();
00296 
00297         while( begin < end )
00298                 begin = ParseField(begin, end);
00299 }
00300 
00301 void RecordStateTable::Clear()
00302 {
00303         StateMap.clear();
00304         m_LastNewRecordId = 1;
00305 }
00306 
00307 // Searches the StateMap table for RecordId, and returns the "index"
00308 // in the map if found.  Returns true if found, false if not.
00309 // pFoundIndex can be null if only the existence of the index is desired
00310 bool RecordStateTable::GetIndex(uint32_t RecordId, IndexType *pFoundIndex) const
00311 {
00312         StateMapType::const_iterator i = StateMap.begin();
00313         for( ; i != StateMap.end(); ++i ) {
00314                 if( i->second.RecordId == RecordId ) {
00315                         if( pFoundIndex )
00316                                 *pFoundIndex = i->first;
00317                         return true;
00318                 }
00319         }
00320         return false;
00321 }
00322 
00323 // Generate a new RecordId that is not in the state table.
00324 // Starts at 1 and keeps incrementing until a free one is found.
00325 uint32_t RecordStateTable::MakeNewRecordId() const
00326 {
00327         // start with next Id
00328         m_LastNewRecordId++;
00329 
00330         // make sure it doesn't already exist
00331         StateMapType::const_iterator i = StateMap.begin();
00332         while( i != StateMap.end() ) {
00333                 if( m_LastNewRecordId == i->second.RecordId ) {
00334                         m_LastNewRecordId++;            // try again
00335                         i = StateMap.begin();           // start over
00336                 }
00337                 else {
00338                         ++i;                            // next State
00339                 }
00340         }
00341         return m_LastNewRecordId;
00342 }
00343 
00344 void RecordStateTable::Dump(std::ostream &os) const
00345 {
00346         ios::fmtflags oldflags = os.setf(ios::right);
00347         char fill = os.fill(' ');
00348         bool bPrintAscii = Data::PrintAscii();
00349         Data::PrintAscii(false);
00350 
00351         os << "  Index  RecordId    Dirty  RecType" << endl;
00352         os << "-------  ----------  -----  -------" << endl;
00353 
00354         StateMapType::const_iterator b, e = StateMap.end();
00355         for( b = StateMap.begin(); b != e ; ++b ) {
00356                 const State &state = b->second;
00357 
00358                 os.fill(' ');
00359                 os << setbase(10) << setw(7) << state.Index;
00360                 os << "  0x" << setbase(16) << setfill('0') << setw(8) << state.RecordId;
00361                 os << "  " << setfill(' ') << setw(5) << (state.Dirty ? "yes" : "no");
00362                 os << "     0x" << setbase(16) << setfill('0') << setw(2) << state.RecType;
00363                 os << "   " << Data(state.Unknown2.data(), state.Unknown2.size());
00364         }
00365 
00366         // cleanup the stream
00367         os.flags(oldflags);
00368         os.fill(fill);
00369         Data::PrintAscii(bPrintAscii);
00370 }
00371 
00372 
00373 
00374 ///////////////////////////////////////////////////////////////////////////////
00375 // DatabaseDatabase class
00376 
00377 DatabaseDatabase::DatabaseDatabase()
00378 {
00379 }
00380 
00381 DatabaseDatabase::~DatabaseDatabase()
00382 {
00383 }
00384 
00385 template <class RecordType, class FieldType>
00386 void DatabaseDatabase::ParseRec(const RecordType &rec, const unsigned char *end)
00387 {
00388 }
00389 
00390 template <class FieldType>
00391 const unsigned char* DatabaseDatabase::ParseField(const unsigned char *begin,
00392                                                   const unsigned char *end)
00393 {
00394         // check if there is enough data for a header
00395         const unsigned char *headend = begin + sizeof(FieldType);
00396         if( headend > end )
00397                 return headend;
00398 
00399         // get our header
00400         const FieldType *field = (const FieldType *) begin;
00401 
00402         // advance and check size
00403         begin += sizeof(FieldType) - sizeof(field->name) + ConvertHtoB(field->nameSize);
00404         if( begin > end )               // if begin==end, we are ok
00405                 return begin;
00406 
00407         if( !ConvertHtoB(field->nameSize) ) // if field has no size, something's up
00408                 return begin;
00409 
00410         Database db;
00411         db.Number = ConvertHtoB(field->dbNumber);
00412         db.RecordCount = ConvertHtoB(field->dbRecordCount);
00413         db.Name.assign((const char *)field->name, ConvertHtoB(field->nameSize) - 1);
00414         Databases.push_back(db);
00415         return begin;
00416 }
00417 
00418 void DatabaseDatabase::Parse(const Data &data)
00419 {
00420         // check size to make sure we have up to the DBAccess operation byte
00421         if( data.GetSize() < (SB_PACKET_DBACCESS_HEADER_SIZE + 1) )
00422                 return;
00423 
00424         MAKE_PACKET(pack, data);
00425         const unsigned char *begin = 0;
00426         const unsigned char *end = data.GetData() + data.GetSize();
00427 
00428         switch( pack->u.db.u.response.operation )
00429         {
00430         case SB_DBOP_GET_DBDB:
00431                 // using the new protocol
00432                 if( data.GetSize() > SB_PACKET_DBDB_HEADER_SIZE ) {
00433                         begin = (const unsigned char *)
00434                                 &pack->u.db.u.response.u.dbdb.field[0];
00435 
00436                         // this while check is ok, since ParseField checks
00437                         // for header size
00438                         while( begin < end )
00439                                 begin = ParseField<DBDBField>(begin, end);
00440                 }
00441                 else
00442                         dout("DatabaseDatabase: not enough data for parsing");
00443                 break;
00444 
00445         case SB_DBOP_OLD_GET_DBDB:
00446                 // using the old protocol
00447                 if( data.GetSize() > SB_PACKET_OLD_DBDB_HEADER_SIZE ) {
00448                         begin = (const unsigned char *)
00449                                 &pack->u.db.u.response.u.old_dbdb.field[0];
00450 
00451                         // this while check is ok, since ParseField checks
00452                         // for header size
00453                         while( begin < end )
00454                                 begin = ParseField<OldDBDBField>(begin, end);
00455                 }
00456                 else
00457                         dout("DatabaseDatabase: not enough data for parsing");
00458                 break;
00459 
00460         default:
00461                 // unknown protocol
00462                 dout("Unknown protocol");
00463                 break;
00464         }
00465 
00466 
00467 }
00468 
00469 void DatabaseDatabase::Clear()
00470 {
00471         Databases.clear();
00472 }
00473 
00474 bool DatabaseDatabase::GetDBNumber(const std::string &name,
00475                                    unsigned int &number) const
00476 {
00477         DatabaseArrayType::const_iterator b = Databases.begin();
00478         for( ; b != Databases.end(); b++ )
00479                 if( b->Name == name ) {
00480                         number = b->Number;
00481                         return true;
00482                 }
00483         return false;
00484 }
00485 
00486 bool DatabaseDatabase::GetDBName(unsigned int number,
00487                                  std::string &name) const
00488 {
00489         DatabaseArrayType::const_iterator b = Databases.begin();
00490         for( ; b != Databases.end(); b++ )
00491                 if( b->Number == number ) {
00492                         name = b->Name;
00493                         return true;
00494                 }
00495         return false;
00496 }
00497 
00498 void DatabaseDatabase::Dump(std::ostream &os) const
00499 {
00500         DatabaseArrayType::const_iterator b = Databases.begin();
00501         os << "Database database:\n";
00502         for( ; b != Databases.end(); b++ ) {
00503                 os << "    Database: 0x" << setbase(16) << b->Number
00504                    << " '" << b->Name << "' (records: "
00505                    << setbase(10) << b->RecordCount << ")\n";
00506         }
00507 }
00508 
00509 
00510 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
00511 {
00512         std::vector<UnknownField>::const_iterator
00513                 ub = unknowns.begin(), ue = unknowns.end();
00514         if( ub != ue )
00515                 os << "    Unknowns:\n";
00516         for( ; ub != ue; ub++ ) {
00517                 os << "        Type: 0x" << setbase(16)
00518                    << (unsigned int) ub->type
00519                    << " Data:\n" << Data(ub->data.data(), ub->data.size());
00520         }
00521         return os;
00522 }
00523 
00524 
00525 
00526 ///////////////////////////////////////////////////////////////////////////////
00527 // EmailAddress class
00528 
00529 std::ostream& operator<<(std::ostream &os, const EmailAddress &msga) {
00530         os << msga.Name << " <" << msga.Email << ">";
00531         return os;
00532 }
00533 
00534 std::ostream& operator<<(std::ostream &os, const EmailAddressList &elist) {
00535         for( EmailAddressList::const_iterator i = elist.begin(); i != elist.end(); ++i ) {
00536                 if( i != elist.begin() )
00537                         os << ", ";
00538                 os << *i;
00539         }
00540         return os;
00541 }
00542 
00543 
00544 ///////////////////////////////////////////////////////////////////////////////
00545 // PostalAddress class
00546 
00547 //
00548 // GetLabel
00549 //
00550 /// Format a mailing address into a single string, handling missing fields.
00551 ///
00552 std::string PostalAddress::GetLabel() const
00553 {
00554         std::string address = Address1;
00555         if( Address2.size() ) {
00556                 if( address.size() )
00557                         address += "\n";
00558                 address += Address2;
00559         }
00560         if( Address3.size() ) {
00561                 if( address.size() )
00562                         address += "\n";
00563                 address += Address3;
00564         }
00565         if( address.size() )
00566                 address += "\n";
00567         if( City.size() )
00568                 address += City + " ";
00569         if( Province.size() )
00570                 address += Province + " ";
00571         if( Country.size() )
00572                 address += Country;
00573         if( address.size() )
00574                 address += "\n";
00575         if( PostalCode.size() )
00576                 address += PostalCode;
00577 
00578         return address;
00579 }
00580 
00581 void PostalAddress::Clear()
00582 {
00583         Address1.clear();
00584         Address2.clear();
00585         Address3.clear();
00586         City.clear();
00587         Province.clear();
00588         PostalCode.clear();
00589         Country.clear();
00590 }
00591 
00592 std::ostream& operator<<(std::ostream &os, const PostalAddress &post) {
00593         os << post.GetLabel();
00594         return os;
00595 }
00596 
00597 
00598 
00599 ///////////////////////////////////////////////////////////////////////////////
00600 // Date class
00601 
00602 Date::Date(const struct tm *timep)
00603 {
00604         FromTm(timep);
00605 }
00606 
00607 void Date::Clear()
00608 {
00609         Month = Day = Year = 0;
00610 }
00611 
00612 void Date::ToTm(struct tm *timep) const
00613 {
00614         memset(timep, 0, sizeof(tm));
00615         timep->tm_year = Year - 1900;
00616         timep->tm_mon = Month;
00617         timep->tm_mday = Day;
00618 }
00619 
00620 std::string Date::ToYYYYMMDD() const
00621 {
00622         std::ostringstream oss;
00623         // setfill and setw not sticky.
00624         oss     << setw(4) << setfill('0') << Year
00625                 << setw(2) << setfill('0') << Month + 1
00626                 << setw(2) << setfill('0') << Day;
00627         return oss.str();
00628 }
00629 
00630 //
00631 // ToBBString
00632 //
00633 /// The Blackberry stores Birthday and Anniversary date fields
00634 /// with the format: DD/MM/YYYY
00635 ///
00636 std::string Date::ToBBString() const
00637 {
00638         std::ostringstream oss;
00639         // setw() ain't 'sticky'!
00640         oss     << setw(2) << setfill('0') << Day << '/'
00641                 << setw(2) << setfill('0') << Month + 1 << '/'
00642                 << setw(2) << setfill('0') << Year;
00643         return oss.str();
00644 }
00645 
00646 bool Date::FromTm(const struct tm *timep)
00647 {
00648         Year = timep->tm_year + 1900;
00649         Month = timep->tm_mon;
00650         Day = timep->tm_mday;
00651         return true;
00652 }
00653 
00654 bool Date::FromBBString(const std::string &str)
00655 {
00656         int m, d, y;
00657         if( 3 == sscanf(str.c_str(), "%d/%d/%d", &d, &m, &y) ) {
00658                 Year = y;
00659                 Month = m - 1;
00660                 Day = d;
00661                 return true;
00662         }
00663         return false;
00664 }
00665 
00666 bool Date::FromYYYYMMDD(const std::string &str)
00667 {
00668         int m, d, y;
00669         if( 3 == sscanf(str.c_str(), "%4d%2d%2d", &y, &m, &d) ) {
00670                 Year = y;
00671                 Month = m - 1;
00672                 Day = d;
00673                 return true;
00674         }
00675         return false;
00676 }
00677 
00678 std::ostream& operator<<(std::ostream &os, const Date &date)
00679 {
00680         os      << setw(4) << date.Year << '/'
00681                 << setw(2) << date.Month << '/'
00682                 << setw(2) << date.Day;
00683         return os;
00684 }
00685 
00686 
00687 ///////////////////////////////////////////////////////////////////////////////
00688 // CategoryList class
00689 
00690 /// Parses the given comma delimited category string into
00691 /// this CategoryList object, appending each token to the vector.
00692 /// Will clear vector beforehand.
00693 void CategoryList::CategoryStr2List(const std::string &str)
00694 {
00695         // start fresh
00696         clear();
00697 
00698         if( !str.size() )
00699                 return;
00700 
00701         // parse the comma-delimited string to a list, stripping away
00702         // any white space around each category name
00703         string::size_type start = 0, end = 0, delim = str.find(',', start);
00704         while( start != string::npos ) {
00705                 if( delim == string::npos )
00706                         end = str.size() - 1;
00707                 else
00708                         end = delim - 1;
00709 
00710                 // strip surrounding whitespace
00711                 while( str[start] == ' ' )
00712                         start++;
00713                 while( end && str[end] == ' ' )
00714                         end--;
00715 
00716                 if( start <= end ) {
00717                         string token = str.substr(start, end-start+1);
00718                         push_back(token);
00719                 }
00720 
00721                 // next
00722                 start = delim;
00723                 if( start != string::npos )
00724                         start++;
00725                 delim = str.find(',', start);
00726         }
00727 }
00728 
00729 /// Turns the current vectory into a comma delimited category
00730 /// string suitable for use in Calendar, Task, and Memo protocol values.
00731 void CategoryList::CategoryList2Str(std::string &str) const
00732 {
00733         str.clear();
00734 
00735         Barry::CategoryList::const_iterator i = begin();
00736         for( ; i != end(); ++i ) {
00737                 if( str.size() )
00738                         str += ", ";
00739                 str += *i;
00740         }
00741 }
00742 
00743 
00744 } // namespace Barry
00745 
00746 
00747 #ifdef __TEST_MODE__
00748 
00749 #include <iostream>
00750 
00751 int main(int argc, char *argv[])
00752 {
00753         if( argc < 2 ) {
00754                 cerr << "Usage: test <datafile>" << endl;
00755                 return 1;
00756         }
00757 
00758         std::vector<Data> array;
00759         if( !LoadDataArray(argv[1], array) ) {
00760                 cerr << "Unable to load file: " << argv[1] << endl;
00761                 return 1;
00762         }
00763 
00764         cout << "Loaded " << array.size() << " items" << endl;
00765 
00766         for( std::vector<Data>::iterator b = array.begin(), e = array.end();
00767                 b != e; b++ )
00768         {
00769                 Data &d = *b;
00770 //              cout << d << endl;
00771                 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
00772                         Barry::Contact contact;
00773                         size_t size = 13;
00774                         contact.ParseFields(d, size);
00775                         cout << contact << endl;
00776                         contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
00777                 }
00778                 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
00779                         Barry::Calendar cal;
00780                         size_t size = 13;
00781                         cal.ParseFields(d, size);
00782                         cout << cal << endl;
00783                 }
00784         }
00785 }
00786 
00787 #endif
00788 

Generated on Tue Jun 30 16:08:14 2009 for Barry by  doxygen 1.5.8