ldif.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       ldif.cc
00003 ///             Routines for reading and writing LDAP LDIF data.
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #include "ldif.h"
00023 #include "record.h"
00024 #include "r_contact.h"
00025 #include "base64.h"
00026 #include <stdexcept>
00027 #include <iostream>
00028 #include <iomanip>
00029 #include <string.h>
00030 #include "ios_state.h"
00031 
00032 #define __DEBUG_MODE__
00033 #include "debug.h"
00034 
00035 namespace Barry {
00036 
00037 const ContactLdif::NameToFunc ContactLdif::FieldMap[] = {
00038         { "Email", "Email address",
00039                 &ContactLdif::Email, &ContactLdif::SetEmail },
00040         { "Phone", "Phone number",
00041                 &ContactLdif::Phone, &ContactLdif::SetPhone },
00042         { "Fax", "Fax number",
00043                 &ContactLdif::Fax, &ContactLdif::SetFax },
00044         { "WorkPhone", "Work phone number",
00045                 &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone },
00046         { "HomePhone", "Home phone number",
00047                 &ContactLdif::HomePhone, &ContactLdif::SetHomePhone },
00048         { "MobilePhone", "Mobile phone number",
00049                 &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone },
00050         { "Pager", "Pager number",
00051                 &ContactLdif::Pager, &ContactLdif::SetPager },
00052         { "PIN", "PIN",
00053                 &ContactLdif::PIN, &ContactLdif::SetPIN },
00054         { "FirstName", "First name",
00055                 &ContactLdif::FirstName, &ContactLdif::SetFirstName },
00056         { "LastName", "Last name",
00057                 &ContactLdif::LastName, &ContactLdif::SetLastName },
00058         { "Company", "Company name",
00059                 &ContactLdif::Company, &ContactLdif::SetCompany },
00060         { "DefaultCommunicationsMethod", "Default communications method",
00061                 &ContactLdif::DefaultCommunicationsMethod, &ContactLdif::SetDefaultCommunicationsMethod },
00062         { "WorkAddress1", "Work Address, line 1",
00063                 &ContactLdif::WorkAddress1, &ContactLdif::SetWorkAddress1 },
00064         { "WorkAddress2", "Work Address, line 2",
00065                 &ContactLdif::WorkAddress2, &ContactLdif::SetWorkAddress2 },
00066         { "WorkAddress3", "Work Address, line 3",
00067                 &ContactLdif::WorkAddress3, &ContactLdif::SetWorkAddress3 },
00068         { "WorkCity", "WorkCity",
00069                 &ContactLdif::WorkCity, &ContactLdif::SetWorkCity },
00070         { "WorkProvince", "WorkProvince / State",
00071                 &ContactLdif::WorkProvince, &ContactLdif::SetWorkProvince },
00072         { "WorkPostalCode", "Work Postal / ZIP code",
00073                 &ContactLdif::WorkPostalCode, &ContactLdif::SetWorkPostalCode },
00074         { "WorkCountry", "WorkCountry",
00075                 &ContactLdif::WorkCountry, &ContactLdif::SetWorkCountry },
00076         { "JobTitle", "Job Title",
00077                 &ContactLdif::JobTitle, &ContactLdif::SetJobTitle },
00078         { "PublicKey", "Public key",
00079                 &ContactLdif::PublicKey, &ContactLdif::SetPublicKey },
00080         { "Notes", "Notes",
00081                 &ContactLdif::Notes, &ContactLdif::SetNotes },
00082         { "Image", "Contact photo",
00083                 &ContactLdif::Image, &ContactLdif::SetImage },
00084         { "WorkPostalAddress", "Mailing Work address (includes address lines, city, province, country, and postal code)",
00085                 &ContactLdif::WorkPostalAddress, &ContactLdif::SetWorkPostalAddress },
00086         { "HomePostalAddress", "Mailing home address (includes address lines, city, province, country, and postal code)",
00087                 &ContactLdif::HomePostalAddress, &ContactLdif::SetHomePostalAddress },
00088         { "FullName", "First + Last names",
00089                 &ContactLdif::FullName, &ContactLdif::SetFullName },
00090         { "FQDN", "Fully qualified domain name",
00091                 &ContactLdif::FQDN, &ContactLdif::SetFQDN },
00092         { 0, 0, 0 }
00093 };
00094 
00095 
00096 bool ContactLdif::LdifAttribute::operator<(const LdifAttribute &other) const
00097 {
00098         // the dn attribute always comes first in LDIF output
00099         if( name == "dn" ) {
00100                 if( other.name == "dn" )
00101                         return false;   // both dn, so equal
00102                 return true;
00103         }
00104         else if( other.name == "dn" )
00105                 return false;
00106 
00107         return (order < other.order && name != other.name) ||
00108                 (order == other.order && name < other.name);
00109 }
00110 
00111 bool ContactLdif::LdifAttribute::operator==(const LdifAttribute &other) const
00112 {
00113         return name == other.name;
00114 }
00115 
00116 
00117 ///////////////////////////////////////////////////////////////////////////////
00118 // ContactLdif class
00119 
00120 ContactLdif::ContactLdif(const std::string &baseDN)
00121         : m_baseDN(baseDN)
00122 {
00123         // setup some sane defaults
00124         Map("mail", &ContactLdif::Email, &ContactLdif::SetEmail);
00125         Map("facsimileTelephoneNumber", &ContactLdif::Fax, &ContactLdif::SetFax);
00126         Map("telephoneNumber", &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone);
00127         Map("homePhone", &ContactLdif::HomePhone, &ContactLdif::SetHomePhone);
00128         Map("mobile", &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone);
00129         Map("pager", &ContactLdif::Pager, &ContactLdif::SetPager);
00130         Map("l", &ContactLdif::WorkCity, &ContactLdif::SetWorkCity);
00131         Map("st", &ContactLdif::WorkProvince, &ContactLdif::SetWorkProvince);
00132         Map("postalCode", &ContactLdif::WorkPostalCode, &ContactLdif::SetWorkPostalCode);
00133         Map("o", &ContactLdif::Company, &ContactLdif::SetCompany);
00134         Map("c", &ContactLdif::WorkCountry, &ContactLdif::SetWorkCountry);
00135         SetObjectClass("c", "country");
00136 
00137         Map("title", &ContactLdif::JobTitle, &ContactLdif::SetJobTitle);
00138         Map("dn", &ContactLdif::FQDN, &ContactLdif::SetFQDN);
00139         Map("displayName", &ContactLdif::FullName, &ContactLdif::SetFullName);
00140         Map("cn", &ContactLdif::FullName, &ContactLdif::SetFullName);
00141         Map("sn", &ContactLdif::LastName, &ContactLdif::SetLastName);
00142         Map("givenName", &ContactLdif::FirstName, &ContactLdif::SetFirstName);
00143         Map("street", &ContactLdif::WorkAddress1, &ContactLdif::SetWorkAddress1);
00144         Map("postalAddress", &ContactLdif::WorkPostalAddress, &ContactLdif::SetWorkPostalAddress);
00145         Map("homePostalAddress", &ContactLdif::HomePostalAddress, &ContactLdif::SetHomePostalAddress);
00146         Map("note", &ContactLdif::Notes, &ContactLdif::SetNotes);
00147         // FIXME - jpegPhoto looks like the only LDIF field for photo
00148         // images... it is unknown which format will come from the
00149         // BlackBerry in the Image field, so we can't guarantee
00150         // that Image will be in JPG.  This mapping can be done manually
00151         // from the btool command line with "-m jpegPhoto,Image,Image"
00152         // Reading photos from LDIF should be fine, since the BlackBerry
00153         // seems to handle most formats.
00154 //      Map("jpegPhoto", &ContactLdif::Image, &ContactLdif::SetImage);
00155 
00156         // add heuristics hooks
00157         Hook("cn", &m_cn);
00158         Hook("displayName", &m_displayName);
00159         Hook("sn", &m_sn);
00160         Hook("givenName", &m_givenName);
00161 
00162         // set default DN attribute
00163         SetDNAttr("cn");
00164 }
00165 
00166 ContactLdif::~ContactLdif()
00167 {
00168 }
00169 
00170 void ContactLdif::DoWrite(Barry::Contact &con,
00171                           const std::string &attr,
00172                           const std::string &data)
00173 {
00174         // valid?
00175         if( attr.size() == 0 || data.size() == 0 )
00176                 return;
00177 
00178         // now have attr/data pair, check hooks:
00179         HookMapType::iterator hook = m_hookMap.find(attr);
00180         if( hook != m_hookMap.end() ) {
00181                 *(hook->second) = data;
00182         }
00183 
00184         // run according to map
00185         AccessMapType::iterator acc = m_map.find(attr);
00186         if( acc != m_map.end() ) {
00187                 (this->*(acc->second.write))(con, data);
00188         }
00189 }
00190 
00191 void ContactLdif::Hook(const std::string &ldifname, std::string *var)
00192 {
00193         m_hookMap[ldifname] = var;
00194 }
00195 
00196 const ContactLdif::NameToFunc*
00197 ContactLdif::GetField(const std::string &fieldname) const
00198 {
00199         for( const NameToFunc *n = FieldMap; n->name; n++ ) {
00200                 if( fieldname == n->name )
00201                         return n;
00202         }
00203         return 0;
00204 }
00205 
00206 std::string ContactLdif::GetFieldReadName(GetFunctionType read) const
00207 {
00208         for( const NameToFunc *n = FieldMap; n->name; n++ ) {
00209                 if( read == n->read )
00210                         return n->name;
00211         }
00212         return "<unknown>";
00213 }
00214 
00215 std::string ContactLdif::GetFieldWriteName(SetFunctionType write) const
00216 {
00217         for( const NameToFunc *n = FieldMap; n->name; n++ ) {
00218                 if( write == n->write )
00219                         return n->name;
00220         }
00221         return "<unknown>";
00222 }
00223 
00224 bool ContactLdif::Map(const LdifAttribute &ldifname,
00225                       const std::string &readField,
00226                       const std::string &writeField)
00227 {
00228         const NameToFunc *read = GetField(readField);
00229         const NameToFunc *write = GetField(writeField);
00230         if( !read || !write )
00231                 return false;
00232         Map(ldifname, read->read, write->write);
00233         return true;
00234 }
00235 
00236 void ContactLdif::Map(const LdifAttribute &ldifname,
00237                       GetFunctionType read,
00238                       SetFunctionType write)
00239 {
00240         m_map[ldifname] = AccessPair(read, write);
00241 }
00242 
00243 void ContactLdif::Unmap(const LdifAttribute &ldifname)
00244 {
00245         m_map.erase(ldifname);
00246 }
00247 
00248 //
00249 // SetDNAttr
00250 //
00251 /// Sets the LDIF attribute name to use when constructing the FQDN.
00252 /// The FQDN field will take this name, and combine it with the
00253 /// baseDN from the constructor to produce a FQDN for the record.
00254 ///
00255 bool ContactLdif::SetDNAttr(const LdifAttribute &name)
00256 {
00257         // try to find the attribute in the map
00258         AccessMapType::iterator i = m_map.find(name);
00259         if( i == m_map.end() )
00260                 return false;
00261 
00262         m_dnAttr = name;
00263         return true;
00264 }
00265 
00266 bool ContactLdif::SetObjectClass(const LdifAttribute &name,
00267                                  const std::string &objectClass)
00268 {
00269         AccessMapType::iterator i = m_map.find(name);
00270         if( i == m_map.end() )
00271                 return false;
00272 
00273         LdifAttribute key = i->first;
00274         AccessPair pair = i->second;
00275         m_map.erase(key);
00276         key.objectClass = objectClass;
00277         m_map[key] = pair;
00278         return true;
00279 }
00280 
00281 bool ContactLdif::SetObjectOrder(const LdifAttribute &name, int order)
00282 {
00283         AccessMapType::iterator i = m_map.find(name);
00284         if( i == m_map.end() )
00285                 return false;
00286 
00287         LdifAttribute key = i->first;
00288         AccessPair pair = i->second;
00289         m_map.erase(key);
00290         key.order = order;
00291         m_map[key] = pair;
00292         return true;
00293 }
00294 
00295 
00296 std::string ContactLdif::Email(const Barry::Contact &con) const
00297 {
00298         return con.GetEmail(m_emailIndex++);
00299 }
00300 
00301 std::string ContactLdif::Phone(const Barry::Contact &con) const
00302 {
00303         return con.Phone;
00304 }
00305 
00306 std::string ContactLdif::Fax(const Barry::Contact &con) const
00307 {
00308         return con.Fax;
00309 }
00310 
00311 std::string ContactLdif::WorkPhone(const Barry::Contact &con) const
00312 {
00313         return con.WorkPhone;
00314 }
00315 
00316 std::string ContactLdif::HomePhone(const Barry::Contact &con) const
00317 {
00318         return con.HomePhone;
00319 }
00320 
00321 std::string ContactLdif::MobilePhone(const Barry::Contact &con) const
00322 {
00323         return con.MobilePhone;
00324 }
00325 
00326 std::string ContactLdif::Pager(const Barry::Contact &con) const
00327 {
00328         return con.Pager;
00329 }
00330 
00331 std::string ContactLdif::PIN(const Barry::Contact &con) const
00332 {
00333         return con.PIN;
00334 }
00335 
00336 std::string ContactLdif::FirstName(const Barry::Contact &con) const
00337 {
00338         return con.FirstName;
00339 }
00340 
00341 std::string ContactLdif::LastName(const Barry::Contact &con) const
00342 {
00343         return con.LastName;
00344 }
00345 
00346 std::string ContactLdif::Company(const Barry::Contact &con) const
00347 {
00348         return con.Company;
00349 }
00350 
00351 std::string ContactLdif::DefaultCommunicationsMethod(const Barry::Contact &con) const
00352 {
00353         return con.DefaultCommunicationsMethod;
00354 }
00355 
00356 std::string ContactLdif::WorkAddress1(const Barry::Contact &con) const
00357 {
00358         return con.WorkAddress.Address1;
00359 }
00360 
00361 std::string ContactLdif::WorkAddress2(const Barry::Contact &con) const
00362 {
00363         return con.WorkAddress.Address2;
00364 }
00365 
00366 std::string ContactLdif::WorkAddress3(const Barry::Contact &con) const
00367 {
00368         return con.WorkAddress.Address3;
00369 }
00370 
00371 std::string ContactLdif::WorkCity(const Barry::Contact &con) const
00372 {
00373         return con.WorkAddress.City;
00374 }
00375 
00376 std::string ContactLdif::WorkProvince(const Barry::Contact &con) const
00377 {
00378         return con.WorkAddress.Province;
00379 }
00380 
00381 std::string ContactLdif::WorkPostalCode(const Barry::Contact &con) const
00382 {
00383         return con.WorkAddress.PostalCode;
00384 }
00385 
00386 std::string ContactLdif::WorkCountry(const Barry::Contact &con) const
00387 {
00388         return con.WorkAddress.Country;
00389 }
00390 
00391 std::string ContactLdif::JobTitle(const Barry::Contact &con) const
00392 {
00393         return con.JobTitle;
00394 }
00395 
00396 std::string ContactLdif::PublicKey(const Barry::Contact &con) const
00397 {
00398         return con.PublicKey;
00399 }
00400 
00401 std::string ContactLdif::Notes(const Barry::Contact &con) const
00402 {
00403         return con.Notes;
00404 }
00405 
00406 std::string ContactLdif::Image(const Barry::Contact &con) const
00407 {
00408         return con.Image;
00409 }
00410 
00411 std::string ContactLdif::WorkPostalAddress(const Barry::Contact &con) const
00412 {
00413         return con.WorkAddress.GetLabel();
00414 }
00415 
00416 std::string ContactLdif::HomePostalAddress(const Barry::Contact &con) const
00417 {
00418         return con.HomeAddress.GetLabel();
00419 }
00420 
00421 std::string ContactLdif::FullName(const Barry::Contact &con) const
00422 {
00423         return con.GetFullName();
00424 }
00425 
00426 std::string ContactLdif::FQDN(const Barry::Contact &con) const
00427 {
00428         std::string FQDN = m_dnAttr.name;
00429         FQDN += "=";
00430 
00431         AccessMapType::const_iterator i = m_map.find(m_dnAttr);
00432         if( i != m_map.end() ) {
00433                 FQDN += (this->*(i->second.read))(con);
00434         }
00435         else {
00436                 FQDN += "unknown";
00437         }
00438 
00439         FQDN += ",";
00440         FQDN += m_baseDN;
00441         return FQDN;
00442 }
00443 
00444 bool ContactLdif::IsArrayFunc(GetFunctionType getf) const
00445 {
00446         // Currently, only the Email getter has array data
00447         if( getf == &ContactLdif::Email )
00448                 return true;
00449         return false;
00450 }
00451 
00452 void ContactLdif::ClearArrayState() const
00453 {
00454         m_emailIndex = 0;
00455 }
00456 
00457 void ContactLdif::SetEmail(Barry::Contact &con, const std::string &val) const
00458 {
00459         con.EmailAddresses.push_back(val);
00460 }
00461 
00462 void ContactLdif::SetPhone(Barry::Contact &con, const std::string &val) const
00463 {
00464         con.Phone = val;
00465 }
00466 
00467 void ContactLdif::SetFax(Barry::Contact &con, const std::string &val) const
00468 {
00469         con.Fax = val;
00470 }
00471 
00472 void ContactLdif::SetWorkPhone(Barry::Contact &con, const std::string &val) const
00473 {
00474         con.WorkPhone = val;
00475 }
00476 
00477 void ContactLdif::SetHomePhone(Barry::Contact &con, const std::string &val) const
00478 {
00479         con.HomePhone = val;
00480 }
00481 
00482 void ContactLdif::SetMobilePhone(Barry::Contact &con, const std::string &val) const
00483 {
00484         con.MobilePhone = val;
00485 }
00486 
00487 void ContactLdif::SetPager(Barry::Contact &con, const std::string &val) const
00488 {
00489         con.Pager = val;
00490 }
00491 
00492 void ContactLdif::SetPIN(Barry::Contact &con, const std::string &val) const
00493 {
00494         con.PIN = val;
00495 }
00496 
00497 void ContactLdif::SetFirstName(Barry::Contact &con, const std::string &val) const
00498 {
00499         con.FirstName = val;
00500 }
00501 
00502 void ContactLdif::SetLastName(Barry::Contact &con, const std::string &val) const
00503 {
00504         con.LastName = val;
00505 }
00506 
00507 void ContactLdif::SetCompany(Barry::Contact &con, const std::string &val) const
00508 {
00509         con.Company = val;
00510 }
00511 
00512 void ContactLdif::SetDefaultCommunicationsMethod(Barry::Contact &con, const std::string &val) const
00513 {
00514         con.DefaultCommunicationsMethod = val;
00515 }
00516 
00517 void ContactLdif::SetWorkAddress1(Barry::Contact &con, const std::string &val) const
00518 {
00519         con.WorkAddress.Address1 = val;
00520 }
00521 
00522 void ContactLdif::SetWorkAddress2(Barry::Contact &con, const std::string &val) const
00523 {
00524         con.WorkAddress.Address2 = val;
00525 }
00526 
00527 void ContactLdif::SetWorkAddress3(Barry::Contact &con, const std::string &val) const
00528 {
00529         con.WorkAddress.Address3 = val;
00530 }
00531 
00532 void ContactLdif::SetWorkCity(Barry::Contact &con, const std::string &val) const
00533 {
00534         con.WorkAddress.City = val;
00535 }
00536 
00537 void ContactLdif::SetWorkProvince(Barry::Contact &con, const std::string &val) const
00538 {
00539         con.WorkAddress.Province = val;
00540 }
00541 
00542 void ContactLdif::SetWorkPostalCode(Barry::Contact &con, const std::string &val) const
00543 {
00544         con.WorkAddress.PostalCode = val;
00545 }
00546 
00547 void ContactLdif::SetWorkCountry(Barry::Contact &con, const std::string &val) const
00548 {
00549         con.WorkAddress.Country = val;
00550 }
00551 
00552 void ContactLdif::SetJobTitle(Barry::Contact &con, const std::string &val) const
00553 {
00554         con.JobTitle = val;
00555 }
00556 
00557 void ContactLdif::SetPublicKey(Barry::Contact &con, const std::string &val) const
00558 {
00559         con.PublicKey = val;
00560 }
00561 
00562 void ContactLdif::SetNotes(Barry::Contact &con, const std::string &val) const
00563 {
00564         con.Notes = val;
00565 }
00566 
00567 void ContactLdif::SetImage(Barry::Contact &con, const std::string &val) const
00568 {
00569         con.Image = val;
00570 }
00571 
00572 void ContactLdif::SetWorkPostalAddress(Barry::Contact &con, const std::string &val) const
00573 {
00574         // FIXME;
00575 //      throw std::runtime_error("SetWorkPostalAddress() not implemented");
00576 //      std::cout << "SetWorkPostalAddress() not implemented: " << val << std::endl;
00577 }
00578 
00579 void ContactLdif::SetHomePostalAddress(Barry::Contact &con, const std::string &val) const
00580 {
00581         // FIXME;
00582 //      throw std::runtime_error("SetHomePostalAddress() not implemented");
00583 //      std::cout << "SetHomePostalAddress() not implemented: " << val << std::endl;
00584 }
00585 
00586 void ContactLdif::SetFullName(Barry::Contact &con, const std::string &val) const
00587 {
00588         std::string first, last;
00589         Contact::SplitName(val, first, last);
00590         con.FirstName = first;
00591         con.LastName = last;
00592 }
00593 
00594 void ContactLdif::SetFQDN(Barry::Contact &con, const std::string &val) const
00595 {
00596         throw std::runtime_error("not implemented");
00597 }
00598 
00599 
00600 void ContactLdif::ClearHeuristics()
00601 {
00602         m_cn.clear();
00603         m_displayName.clear();
00604         m_sn.clear();
00605         m_givenName.clear();
00606 }
00607 
00608 bool ContactLdif::RunHeuristics(Barry::Contact &con)
00609 {
00610         // start fresh
00611         con.LastName.clear();
00612         con.FirstName.clear();
00613 
00614         // find the best match for name... prefer sn/givenName if available
00615         if( m_sn.size() ) {
00616                 con.LastName = m_sn;
00617         }
00618         if( m_givenName.size() ) {
00619                 con.FirstName = m_givenName;
00620         }
00621 
00622         if( !con.LastName.size() || !con.FirstName.size() ) {
00623                 std::string first, last;
00624 
00625                 // still don't have a complete name, check cn first
00626                 if( m_cn.size() ) {
00627                         Contact::SplitName(m_cn, first, last);
00628                         if( !con.LastName.size() && last.size() )
00629                                 con.LastName = last;
00630                         if( !con.FirstName.size() && first.size() )
00631                                 con.FirstName = first;
00632                 }
00633 
00634                 // displayName is last chance
00635                 if( m_displayName.size() ) {
00636                         Contact::SplitName(m_displayName, first, last);
00637                         if( !con.LastName.size() && last.size() )
00638                                 con.LastName = last;
00639                         if( !con.FirstName.size() && first.size() )
00640                                 con.FirstName = first;
00641                 }
00642         }
00643 
00644         return con.LastName.size() && con.FirstName.size();
00645 }
00646 
00647 
00648 //
00649 // DumpLdif
00650 //
00651 /// Output contact data to os in LDAP LDIF format.
00652 ///
00653 void ContactLdif::DumpLdif(std::ostream &os,
00654                        const Barry::Contact &con) const
00655 {
00656         ios_format_state state(os);
00657 
00658         // start fresh
00659         ClearArrayState();
00660 
00661         // setup stream state
00662         os.setf(std::ios::left);
00663         os.fill(' ');
00664 
00665         if( FirstName(con).size() == 0 && LastName(con).size() == 0 )
00666                 return;                 // nothing to do
00667 
00668         os << "# Contact 0x" << std::hex << con.GetID() << ", "
00669                 << FullName(con) << "\n";
00670 
00671         // cycle through the map
00672         for(    AccessMapType::const_iterator b = m_map.begin();
00673                 b != m_map.end();
00674                 ++b )
00675         {
00676                 // print only fields with data
00677                 std::string field;
00678 
00679                 do {
00680                         field = (this->*(b->second.read))(con);
00681                         if( field.size() ) {
00682                                 os << b->first.name << MakeLdifData(field) << "\n";
00683                                 if( b->first.objectClass.size() )
00684                                         os << "objectClass: " << b->first.objectClass << "\n";
00685                         }
00686                 } while( IsArrayFunc(b->second.read) && field.size() );
00687         }
00688 
00689         os << "objectClass: inetOrgPerson\n";
00690 
00691         // last line must be empty
00692         os << "\n";
00693 }
00694 
00695 bool ContactLdif::ReadLdif(std::istream &is, Barry::Contact &con)
00696 {
00697         std::string line;
00698 
00699         // start fresh
00700         con.Clear();
00701         ClearHeuristics();
00702 
00703         // search for beginning dn: line
00704         bool found = false;
00705         while( std::getline(is, line) ) {
00706                 if( strncmp(line.c_str(), "dn: ", 4) == 0 ) {
00707                         found = true;
00708                         break;
00709                 }
00710         }
00711         if( !found )
00712                 return false;
00713 
00714         // storage for various name styles
00715         std::string coded, decode, attr, data;
00716         bool b64field = false;
00717 
00718         // read ldif lines until empty line is found
00719         while( getline(is, line) && line.size() ) {
00720 
00721                 if( b64field ) {
00722                         // processing a base64 encoded field
00723                         if( line[0] == ' ' ) {
00724                                 coded += "\n";
00725                                 coded += line;
00726                                 continue;
00727                         }
00728                         else {
00729                                 // end of base64 block... ignore errors,
00730                                 // and attempt to save everything decodable...
00731                                 // the LDAP server sometimes returns incomplete
00732                                 // base64 encoding, but otherwise the data is fine
00733                                 base64_decode(coded, decode);
00734                                 DoWrite(con, attr, decode);
00735                                 coded.clear();
00736                                 b64field = false;
00737                         }
00738                         // fall through to process new line
00739                 }
00740 
00741 
00742                 // split into attribute / data
00743                 std::string::size_type delim = line.find(':'), dstart;
00744                 if( delim == std::string::npos )
00745                         continue;
00746 
00747                 attr.assign(line, 0, delim);
00748                 dstart = delim + 1;
00749                 while( line[dstart] == ' ' || line[dstart] == ':' )
00750                         dstart++;
00751                 data = line.substr(dstart);
00752 
00753                 // is this data base64 encoded?
00754                 if( line[delim + 1] == ':' ) {
00755                         coded = data;
00756                         b64field = true;
00757                         continue;
00758                 }
00759 
00760                 DoWrite(con, attr, data);
00761         }
00762 
00763         if( b64field ) {
00764                 // clean up base64 decoding... ignore errors, see above comment
00765                 base64_decode(coded, decode);
00766                 DoWrite(con, attr, decode);
00767                 coded.clear();
00768                 b64field = false;
00769         }
00770 
00771         return RunHeuristics(con);
00772 }
00773 
00774 void ContactLdif::DumpMap(std::ostream &os) const
00775 {
00776         ios_format_state state(os);
00777 
00778         os.setf(std::ios::left);
00779         os.fill(' ');
00780 
00781         os << "ContactLdif Mapping:\n";
00782 
00783         // cycle through the map
00784         for(    AccessMapType::const_iterator b = m_map.begin();
00785                 b != m_map.end();
00786                 ++b )
00787         {
00788                 os << "   " << std::left << std::setw(20) << b->first.name
00789                    << "->  " << GetFieldReadName(b->second.read)
00790                    << " / " << GetFieldWriteName(b->second.write) << "\n";
00791 
00792                 // find read/write names
00793 
00794                 if( b->first.objectClass.size() ) {
00795                         os << "   " << std::setw(20) << " "
00796                            << "objectClass: " << b->first.objectClass << "\n";
00797                 }
00798         }
00799 
00800         os << "   >>> DN attribute: " << m_dnAttr.name << "\n";
00801 }
00802 
00803 std::string ContactLdif::MakeLdifData(const std::string &str)
00804 {
00805         std::string data = ":";
00806 
00807         if( NeedsEncoding(str) ) {
00808                 std::string b64;
00809                 base64_encode(str, b64);
00810 
00811                 data += ": ";
00812                 data += b64;
00813         }
00814         else {
00815                 data += " ";
00816                 data += str;
00817         }
00818 
00819         return data;
00820 }
00821 
00822 //
00823 // RFC 2849
00824 //
00825 // Must not contain:
00826 //      0x00 (NUL), 0x0a (LF), 0x0d (CR), or anything greater than 0x7f
00827 //
00828 // First char must meet above criteria, plus must not be:
00829 //      0x20 (SPACE), 0x3a (colon), 0x3c ('<')
00830 //
00831 bool ContactLdif::NeedsEncoding(const std::string &str)
00832 {
00833         for( std::string::size_type i = 0; i < str.size(); i++ ) {
00834                 unsigned char c = str[i];
00835 
00836                 switch( c )
00837                 {
00838                 case 0x00:
00839                 case 0x0a:
00840                 case 0x0d:
00841                         return true;
00842 
00843                 case 0x20:
00844                 case 0x3a:
00845                 case 0x3c:
00846                         if( i == 0 )
00847                                 return true;
00848                 }
00849 
00850                 if( c > 0x7f )
00851                         return true;
00852         }
00853         return false;
00854 }
00855 
00856 } // namespace Barry
00857