3 /// Routines for reading and writing LDAP LDIF data.
7 Copyright (C) 2005-2008, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
30 #define __DEBUG_MODE__
35 const ContactLdif::NameToFunc
ContactLdif::FieldMap
[] = {
36 { "Email", "Email address",
37 &ContactLdif::Email
, &ContactLdif::SetEmail
},
38 { "Phone", "Phone number",
39 &ContactLdif::Phone
, &ContactLdif::SetPhone
},
40 { "Fax", "Fax number",
41 &ContactLdif::Fax
, &ContactLdif::SetFax
},
42 { "WorkPhone", "Work phone number",
43 &ContactLdif::WorkPhone
, &ContactLdif::SetWorkPhone
},
44 { "HomePhone", "Home phone number",
45 &ContactLdif::HomePhone
, &ContactLdif::SetHomePhone
},
46 { "MobilePhone", "Mobile phone number",
47 &ContactLdif::MobilePhone
, &ContactLdif::SetMobilePhone
},
48 { "Pager", "Pager number",
49 &ContactLdif::Pager
, &ContactLdif::SetPager
},
51 &ContactLdif::PIN
, &ContactLdif::SetPIN
},
52 { "FirstName", "First name",
53 &ContactLdif::FirstName
, &ContactLdif::SetFirstName
},
54 { "LastName", "Last name",
55 &ContactLdif::LastName
, &ContactLdif::SetLastName
},
56 { "Company", "Company name",
57 &ContactLdif::Company
, &ContactLdif::SetCompany
},
58 { "DefaultCommunicationsMethod", "Default communications method",
59 &ContactLdif::DefaultCommunicationsMethod
, &ContactLdif::SetDefaultCommunicationsMethod
},
60 { "Address1", "Address, line 1",
61 &ContactLdif::Address1
, &ContactLdif::SetAddress1
},
62 { "Address2", "Address, line 2",
63 &ContactLdif::Address2
, &ContactLdif::SetAddress2
},
64 { "Address3", "Address, line 3",
65 &ContactLdif::Address3
, &ContactLdif::SetAddress3
},
67 &ContactLdif::City
, &ContactLdif::SetCity
},
68 { "Province", "Province / State",
69 &ContactLdif::Province
, &ContactLdif::SetProvince
},
70 { "PostalCode", "Postal / ZIP code",
71 &ContactLdif::PostalCode
, &ContactLdif::SetPostalCode
},
72 { "Country", "Country",
73 &ContactLdif::Country
, &ContactLdif::SetCountry
},
74 { "JobTitle", "Job Title",
75 &ContactLdif::JobTitle
, &ContactLdif::SetJobTitle
},
76 { "PublicKey", "Public key",
77 &ContactLdif::PublicKey
, &ContactLdif::SetPublicKey
},
79 &ContactLdif::Notes
, &ContactLdif::SetNotes
},
80 { "PostalAddress", "Mailing address (includes address lines, city, province, country, and postal code)",
81 &ContactLdif::PostalAddress
, &ContactLdif::SetPostalAddress
},
82 { "FullName", "First + Last names",
83 &ContactLdif::FullName
, &ContactLdif::SetFullName
},
84 { "FQDN", "Fully qualified domain name",
85 &ContactLdif::FQDN
, &ContactLdif::SetFQDN
},
90 bool ContactLdif::LdifAttribute::operator<(const LdifAttribute
&other
) const
92 // the dn attribute always comes first in LDIF output
94 if( other
.name
== "dn" )
95 return false; // both dn, so equal
98 else if( other
.name
== "dn" )
101 return (order
< other
.order
&& name
!= other
.name
) ||
102 (order
== other
.order
&& name
< other
.name
);
105 bool ContactLdif::LdifAttribute::operator==(const LdifAttribute
&other
) const
107 return name
== other
.name
;
111 ///////////////////////////////////////////////////////////////////////////////
114 ContactLdif::ContactLdif(const std::string
&baseDN
)
117 // setup some sane defaults
118 Map("mail", &ContactLdif::Email
, &ContactLdif::SetEmail
);
119 Map("facsimileTelephoneNumber", &ContactLdif::Fax
, &ContactLdif::SetFax
);
120 Map("telephoneNumber", &ContactLdif::WorkPhone
, &ContactLdif::SetWorkPhone
);
121 Map("homePhone", &ContactLdif::HomePhone
, &ContactLdif::SetHomePhone
);
122 Map("mobile", &ContactLdif::MobilePhone
, &ContactLdif::SetMobilePhone
);
123 Map("pager", &ContactLdif::Pager
, &ContactLdif::SetPager
);
124 Map("l", &ContactLdif::City
, &ContactLdif::SetCity
);
125 Map("st", &ContactLdif::Province
, &ContactLdif::SetProvince
);
126 Map("postalCode", &ContactLdif::PostalCode
, &ContactLdif::SetPostalCode
);
127 Map("o", &ContactLdif::Company
, &ContactLdif::SetCompany
);
128 Map("c", &ContactLdif::Country
, &ContactLdif::SetCountry
);
129 SetObjectClass("c", "country");
131 Map("title", &ContactLdif::JobTitle
, &ContactLdif::SetJobTitle
);
132 Map("dn", &ContactLdif::FQDN
, &ContactLdif::SetFQDN
);
133 Map("displayName", &ContactLdif::FullName
, &ContactLdif::SetFullName
);
134 Map("cn", &ContactLdif::FullName
, &ContactLdif::SetFullName
);
135 Map("sn", &ContactLdif::LastName
, &ContactLdif::SetLastName
);
136 Map("givenName", &ContactLdif::FirstName
, &ContactLdif::SetFirstName
);
137 Map("street", &ContactLdif::Address1
, &ContactLdif::SetAddress1
);
138 Map("postalAddress", &ContactLdif::PostalAddress
, &ContactLdif::SetPostalAddress
);
139 Map("note", &ContactLdif::Notes
, &ContactLdif::SetNotes
);
141 // add heuristics hooks
143 Hook("displayName", &m_displayName
);
145 Hook("givenName", &m_givenName
);
147 // set default DN attribute
151 ContactLdif::~ContactLdif()
155 void ContactLdif::DoWrite(Barry::Contact
&con
,
156 const std::string
&attr
,
157 const std::string
&data
)
160 if( attr
.size() == 0 || data
.size() == 0 )
163 // now have attr/data pair, check hooks:
164 HookMapType::iterator hook
= m_hookMap
.find(attr
);
165 if( hook
!= m_hookMap
.end() ) {
166 *(hook
->second
) = data
;
169 // run according to map
170 AccessMapType::iterator acc
= m_map
.find(attr
);
171 if( acc
!= m_map
.end() ) {
172 (this->*(acc
->second
.write
))(con
, data
);
176 void ContactLdif::Hook(const std::string
&ldifname
, std::string
*var
)
178 m_hookMap
[ldifname
] = var
;
181 const ContactLdif::NameToFunc
*
182 ContactLdif::GetField(const std::string
&fieldname
) const
184 for( const NameToFunc
*n
= FieldMap
; n
->name
; n
++ ) {
185 if( fieldname
== n
->name
)
191 std::string
ContactLdif::GetFieldReadName(GetFunctionType read
) const
193 for( const NameToFunc
*n
= FieldMap
; n
->name
; n
++ ) {
194 if( read
== n
->read
)
200 std::string
ContactLdif::GetFieldWriteName(SetFunctionType write
) const
202 for( const NameToFunc
*n
= FieldMap
; n
->name
; n
++ ) {
203 if( write
== n
->write
)
209 bool ContactLdif::Map(const LdifAttribute
&ldifname
,
210 const std::string
&readField
,
211 const std::string
&writeField
)
213 const NameToFunc
*read
= GetField(readField
);
214 const NameToFunc
*write
= GetField(writeField
);
215 if( !read
|| !write
)
217 Map(ldifname
, read
->read
, write
->write
);
221 void ContactLdif::Map(const LdifAttribute
&ldifname
,
222 GetFunctionType read
,
223 SetFunctionType write
)
225 m_map
[ldifname
] = AccessPair(read
, write
);
228 void ContactLdif::Unmap(const LdifAttribute
&ldifname
)
230 m_map
.erase(ldifname
);
236 /// Sets the LDIF attribute name to use when constructing the FQDN.
237 /// The FQDN field will take this name, and combine it with the
238 /// baseDN from the constructor to produce a FQDN for the record.
240 bool ContactLdif::SetDNAttr(const LdifAttribute
&name
)
242 // try to find the attribute in the map
243 AccessMapType::iterator i
= m_map
.find(name
);
244 if( i
== m_map
.end() )
251 bool ContactLdif::SetObjectClass(const LdifAttribute
&name
,
252 const std::string
&objectClass
)
254 AccessMapType::iterator i
= m_map
.find(name
);
255 if( i
== m_map
.end() )
258 LdifAttribute key
= i
->first
;
259 AccessPair pair
= i
->second
;
261 key
.objectClass
= objectClass
;
266 bool ContactLdif::SetObjectOrder(const LdifAttribute
&name
, int order
)
268 AccessMapType::iterator i
= m_map
.find(name
);
269 if( i
== m_map
.end() )
272 LdifAttribute key
= i
->first
;
273 AccessPair pair
= i
->second
;
281 std::string
ContactLdif::Email(const Barry::Contact
&con
) const
286 std::string
ContactLdif::Phone(const Barry::Contact
&con
) const
291 std::string
ContactLdif::Fax(const Barry::Contact
&con
) const
296 std::string
ContactLdif::WorkPhone(const Barry::Contact
&con
) const
298 return con
.WorkPhone
;
301 std::string
ContactLdif::HomePhone(const Barry::Contact
&con
) const
303 return con
.HomePhone
;
306 std::string
ContactLdif::MobilePhone(const Barry::Contact
&con
) const
308 return con
.MobilePhone
;
311 std::string
ContactLdif::Pager(const Barry::Contact
&con
) const
316 std::string
ContactLdif::PIN(const Barry::Contact
&con
) const
321 std::string
ContactLdif::FirstName(const Barry::Contact
&con
) const
323 return con
.FirstName
;
326 std::string
ContactLdif::LastName(const Barry::Contact
&con
) const
331 std::string
ContactLdif::Company(const Barry::Contact
&con
) const
336 std::string
ContactLdif::DefaultCommunicationsMethod(const Barry::Contact
&con
) const
338 return con
.DefaultCommunicationsMethod
;
341 std::string
ContactLdif::Address1(const Barry::Contact
&con
) const
343 return con
.WorkAddress
.Address1
;
346 std::string
ContactLdif::Address2(const Barry::Contact
&con
) const
348 return con
.WorkAddress
.Address2
;
351 std::string
ContactLdif::Address3(const Barry::Contact
&con
) const
353 return con
.WorkAddress
.Address3
;
356 std::string
ContactLdif::City(const Barry::Contact
&con
) const
358 return con
.WorkAddress
.City
;
361 std::string
ContactLdif::Province(const Barry::Contact
&con
) const
363 return con
.WorkAddress
.Province
;
366 std::string
ContactLdif::PostalCode(const Barry::Contact
&con
) const
368 return con
.WorkAddress
.PostalCode
;
371 std::string
ContactLdif::Country(const Barry::Contact
&con
) const
373 return con
.WorkAddress
.Country
;
376 std::string
ContactLdif::JobTitle(const Barry::Contact
&con
) const
381 std::string
ContactLdif::PublicKey(const Barry::Contact
&con
) const
383 return con
.PublicKey
;
386 std::string
ContactLdif::Notes(const Barry::Contact
&con
) const
391 std::string
ContactLdif::PostalAddress(const Barry::Contact
&con
) const
393 return con
.WorkAddress
.GetLabel();
396 std::string
ContactLdif::FullName(const Barry::Contact
&con
) const
398 return con
.GetFullName();
401 std::string
ContactLdif::FQDN(const Barry::Contact
&con
) const
403 std::string FQDN
= m_dnAttr
.name
;
406 AccessMapType::const_iterator i
= m_map
.find(m_dnAttr
);
407 if( i
!= m_map
.end() ) {
408 FQDN
+= (this->*(i
->second
.read
))(con
);
419 void ContactLdif::SetEmail(Barry::Contact
&con
, const std::string
&val
) const
424 void ContactLdif::SetPhone(Barry::Contact
&con
, const std::string
&val
) const
429 void ContactLdif::SetFax(Barry::Contact
&con
, const std::string
&val
) const
434 void ContactLdif::SetWorkPhone(Barry::Contact
&con
, const std::string
&val
) const
439 void ContactLdif::SetHomePhone(Barry::Contact
&con
, const std::string
&val
) const
444 void ContactLdif::SetMobilePhone(Barry::Contact
&con
, const std::string
&val
) const
446 con
.MobilePhone
= val
;
449 void ContactLdif::SetPager(Barry::Contact
&con
, const std::string
&val
) const
454 void ContactLdif::SetPIN(Barry::Contact
&con
, const std::string
&val
) const
459 void ContactLdif::SetFirstName(Barry::Contact
&con
, const std::string
&val
) const
464 void ContactLdif::SetLastName(Barry::Contact
&con
, const std::string
&val
) const
469 void ContactLdif::SetCompany(Barry::Contact
&con
, const std::string
&val
) const
474 void ContactLdif::SetDefaultCommunicationsMethod(Barry::Contact
&con
, const std::string
&val
) const
476 con
.DefaultCommunicationsMethod
= val
;
479 void ContactLdif::SetAddress1(Barry::Contact
&con
, const std::string
&val
) const
481 con
.WorkAddress
.Address1
= val
;
484 void ContactLdif::SetAddress2(Barry::Contact
&con
, const std::string
&val
) const
486 con
.WorkAddress
.Address2
= val
;
489 void ContactLdif::SetAddress3(Barry::Contact
&con
, const std::string
&val
) const
491 con
.WorkAddress
.Address3
= val
;
494 void ContactLdif::SetCity(Barry::Contact
&con
, const std::string
&val
) const
496 con
.WorkAddress
.City
= val
;
499 void ContactLdif::SetProvince(Barry::Contact
&con
, const std::string
&val
) const
501 con
.WorkAddress
.Province
= val
;
504 void ContactLdif::SetPostalCode(Barry::Contact
&con
, const std::string
&val
) const
506 con
.WorkAddress
.PostalCode
= val
;
509 void ContactLdif::SetCountry(Barry::Contact
&con
, const std::string
&val
) const
511 con
.WorkAddress
.Country
= val
;
514 void ContactLdif::SetJobTitle(Barry::Contact
&con
, const std::string
&val
) const
519 void ContactLdif::SetPublicKey(Barry::Contact
&con
, const std::string
&val
) const
524 void ContactLdif::SetNotes(Barry::Contact
&con
, const std::string
&val
) const
529 void ContactLdif::SetPostalAddress(Barry::Contact
&con
, const std::string
&val
) const
532 // throw std::runtime_error("SetPostalAddress() not implemented");
533 // std::cout << "SetPostalAddress() not implemented: " << val << std::endl;
536 void ContactLdif::SetFullName(Barry::Contact
&con
, const std::string
&val
) const
538 std::string first
, last
;
539 Contact::SplitName(val
, first
, last
);
540 con
.FirstName
= first
;
544 void ContactLdif::SetFQDN(Barry::Contact
&con
, const std::string
&val
) const
546 throw std::runtime_error("not implemented");
550 void ContactLdif::ClearHeuristics()
553 m_displayName
.clear();
558 bool ContactLdif::RunHeuristics(Barry::Contact
&con
)
561 con
.LastName
.clear();
562 con
.FirstName
.clear();
564 // find the best match for name... prefer sn/givenName if available
568 if( m_givenName
.size() ) {
569 con
.FirstName
= m_givenName
;
572 if( !con
.LastName
.size() || !con
.FirstName
.size() ) {
573 std::string first
, last
;
575 // still don't have a complete name, check cn first
577 Contact::SplitName(m_cn
, first
, last
);
578 if( !con
.LastName
.size() && last
.size() )
580 if( !con
.FirstName
.size() && first
.size() )
581 con
.FirstName
= first
;
584 // displayName is last chance
585 if( m_displayName
.size() ) {
586 Contact::SplitName(m_displayName
, first
, last
);
587 if( !con
.LastName
.size() && last
.size() )
589 if( !con
.FirstName
.size() && first
.size() )
590 con
.FirstName
= first
;
594 return con
.LastName
.size() && con
.FirstName
.size();
601 /// Output contact data to os in LDAP LDIF format.
603 void ContactLdif::DumpLdif(std::ostream
&os
,
604 const Barry::Contact
&con
) const
606 std::ios::fmtflags oldflags
= os
.setf(std::ios::left
);
607 char fill
= os
.fill(' ');
609 if( FirstName(con
).size() == 0 && LastName(con
).size() == 0 )
610 return; // nothing to do
612 os
<< "# Contact 0x" << std::hex
<< con
.GetID() << ", "
613 << FullName(con
) << "\n";
615 // cycle through the map
616 for( AccessMapType::const_iterator b
= m_map
.begin();
620 // print only fields with data
621 const std::string field
= (this->*(b
->second
.read
))(con
);
623 os
<< b
->first
.name
<< MakeLdifData(field
) << "\n";
624 if( b
->first
.objectClass
.size() )
625 os
<< "objectClass: " << b
->first
.objectClass
<< "\n";
629 os
<< "objectClass: inetOrgPerson\n";
631 // last line must be empty
634 // cleanup the stream
639 bool ContactLdif::ReadLdif(std::istream
&is
, Barry::Contact
&con
)
647 // search for beginning dn: line
649 while( std::getline(is
, line
) ) {
650 if( strncmp(line
.c_str(), "dn: ", 4) == 0 ) {
658 // storage for various name styles
659 std::string coded
, decode
, attr
, data
;
660 bool b64field
= false;
662 // read ldif lines until empty line is found
663 while( getline(is
, line
) && line
.size() ) {
666 // processing a base64 encoded field
667 if( line
[0] == ' ' ) {
673 // end of base64 block... ignore errors,
674 // and attempt to save everything decodable...
675 // the LDAP server sometimes returns incomplete
676 // base64 encoding, but otherwise the data is fine
677 base64_decode(coded
, decode
);
678 DoWrite(con
, attr
, decode
);
682 // fall through to process new line
686 // split into attribute / data
687 std::string::size_type delim
= line
.find(':'), dstart
;
688 if( delim
== std::string::npos
)
691 attr
.assign(line
, 0, delim
);
693 while( line
[dstart
] == ' ' || line
[dstart
] == ':' )
695 data
= line
.substr(dstart
);
697 // is this data base64 encoded?
698 if( line
[delim
+ 1] == ':' ) {
704 DoWrite(con
, attr
, data
);
708 // clean up base64 decoding... ignore errors, see above comment
709 base64_decode(coded
, decode
);
710 DoWrite(con
, attr
, decode
);
715 return RunHeuristics(con
);
718 void ContactLdif::DumpMap(std::ostream
&os
) const
720 std::ios::fmtflags oldflags
= os
.setf(std::ios::left
);
721 char fill
= os
.fill(' ');
723 os
<< "ContactLdif Mapping:\n";
725 // cycle through the map
726 for( AccessMapType::const_iterator b
= m_map
.begin();
730 os
<< " " << std::left
<< std::setw(20) << b
->first
.name
731 << "-> " << GetFieldReadName(b
->second
.read
)
732 << " / " << GetFieldWriteName(b
->second
.write
) << "\n";
734 // find read/write names
736 if( b
->first
.objectClass
.size() ) {
737 os
<< " " << std::setw(20) << " "
738 << "objectClass: " << b
->first
.objectClass
<< "\n";
742 os
<< " >>> DN attribute: " << m_dnAttr
.name
<< "\n";
744 // cleanup the stream
749 std::string
ContactLdif::MakeLdifData(const std::string
&str
)
751 std::string data
= ":";
753 if( NeedsEncoding(str
) ) {
755 base64_encode(str
, b64
);
772 // 0x00 (NUL), 0x0a (LF), 0x0d (CR), or anything greater than 0x7f
774 // First char must meet above criteria, plus must not be:
775 // 0x20 (SPACE), 0x3a (colon), 0x3c ('<')
777 bool ContactLdif::NeedsEncoding(const std::string
&str
)
779 for( std::string::size_type i
= 0; i
< str
.size(); i
++ ) {
780 unsigned char c
= str
[i
];