menu: added new Keywords tag to .desktop files
[barry.git] / src / ldif.cc
bloba42cea3c75c232b348cf5e99e1558934619d3d19
1 ///
2 /// \file ldif.cc
3 /// Routines for reading and writing LDAP LDIF data.
4 ///
6 /*
7 Copyright (C) 2005-2013, 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.
22 #include "i18n.h"
23 #include "ldif.h"
24 #include "record.h"
25 #include "r_contact.h"
26 #include "base64.h"
27 #include <stdexcept>
28 #include <iostream>
29 #include <iomanip>
30 #include <string.h>
31 #include "ios_state.h"
33 #define __DEBUG_MODE__
34 #include "debug.h"
36 namespace Barry {
38 const ContactLdif::NameToFunc ContactLdif::FieldMap[] = {
39 { "Email", N_("Email address"),
40 &ContactLdif::Email, &ContactLdif::SetEmail },
41 { "Phone", N_("Phone number"),
42 &ContactLdif::Phone, &ContactLdif::SetPhone },
43 { "Fax", N_("Fax number"),
44 &ContactLdif::Fax, &ContactLdif::SetFax },
45 { "WorkPhone", N_("Work phone number"),
46 &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone },
47 { "HomePhone", N_("Home phone number"),
48 &ContactLdif::HomePhone, &ContactLdif::SetHomePhone },
49 { "MobilePhone", N_("Mobile phone number"),
50 &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone },
51 { "Pager", N_("Pager number"),
52 &ContactLdif::Pager, &ContactLdif::SetPager },
53 { "PIN", N_("PIN"),
54 &ContactLdif::PIN, &ContactLdif::SetPIN },
55 { "FirstName", N_("First name"),
56 &ContactLdif::FirstName, &ContactLdif::SetFirstName },
57 { "LastName", N_("Last name"),
58 &ContactLdif::LastName, &ContactLdif::SetLastName },
59 { "Company", N_("Company name"),
60 &ContactLdif::Company, &ContactLdif::SetCompany },
61 { "DefaultCommunicationsMethod", N_("Default communications method"),
62 &ContactLdif::DefaultCommunicationsMethod, &ContactLdif::SetDefaultCommunicationsMethod },
63 { "WorkAddress1", N_("Work Address, line 1"),
64 &ContactLdif::WorkAddress1, &ContactLdif::SetWorkAddress1 },
65 { "WorkAddress2", N_("Work Address, line 2"),
66 &ContactLdif::WorkAddress2, &ContactLdif::SetWorkAddress2 },
67 { "WorkAddress3", N_("Work Address, line 3"),
68 &ContactLdif::WorkAddress3, &ContactLdif::SetWorkAddress3 },
69 { "WorkCity", N_("WorkCity"),
70 &ContactLdif::WorkCity, &ContactLdif::SetWorkCity },
71 { "WorkProvince", N_("WorkProvince / State"),
72 &ContactLdif::WorkProvince, &ContactLdif::SetWorkProvince },
73 { "WorkPostalCode", N_("Work Postal / ZIP code"),
74 &ContactLdif::WorkPostalCode, &ContactLdif::SetWorkPostalCode },
75 { "WorkCountry", N_("WorkCountry"),
76 &ContactLdif::WorkCountry, &ContactLdif::SetWorkCountry },
77 { "JobTitle", N_("Job Title"),
78 &ContactLdif::JobTitle, &ContactLdif::SetJobTitle },
79 { "PublicKey", N_("Public key"),
80 &ContactLdif::PublicKey, &ContactLdif::SetPublicKey },
81 { "Notes", N_("Notes"),
82 &ContactLdif::Notes, &ContactLdif::SetNotes },
83 { "Image", N_("Contact photo"),
84 &ContactLdif::Image, &ContactLdif::SetImage },
85 { "WorkPostalAddress", N_("Mailing Work address (includes address lines, city, province, country, and postal code)"),
86 &ContactLdif::WorkPostalAddress, &ContactLdif::SetWorkPostalAddress },
87 { "HomePostalAddress", N_("Mailing home address (includes address lines, city, province, country, and postal code)"),
88 &ContactLdif::HomePostalAddress, &ContactLdif::SetHomePostalAddress },
89 { "FullName", N_("First + Last names"),
90 &ContactLdif::FullName, &ContactLdif::SetFullName },
91 { "FQDN", N_("Fully qualified domain name"),
92 &ContactLdif::FQDN, &ContactLdif::SetFQDN },
93 { 0, 0, 0 }
97 bool ContactLdif::LdifAttribute::operator<(const LdifAttribute &other) const
99 // the dn attribute always comes first in LDIF output
100 if( name == "dn" ) {
101 if( other.name == "dn" )
102 return false; // both dn, so equal
103 return true;
105 else if( other.name == "dn" )
106 return false;
108 return (order < other.order && name != other.name) ||
109 (order == other.order && name < other.name);
112 bool ContactLdif::LdifAttribute::operator==(const LdifAttribute &other) const
114 return name == other.name;
118 ///////////////////////////////////////////////////////////////////////////////
119 // ContactLdif class
121 ContactLdif::ContactLdif(const std::string &baseDN)
122 : m_baseDN(baseDN)
124 // setup some sane defaults
125 Map("mail", &ContactLdif::Email, &ContactLdif::SetEmail);
126 Map("facsimileTelephoneNumber", &ContactLdif::Fax, &ContactLdif::SetFax);
127 Map("telephoneNumber", &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone);
128 Map("homePhone", &ContactLdif::HomePhone, &ContactLdif::SetHomePhone);
129 Map("mobile", &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone);
130 Map("pager", &ContactLdif::Pager, &ContactLdif::SetPager);
131 Map("l", &ContactLdif::WorkCity, &ContactLdif::SetWorkCity);
132 Map("st", &ContactLdif::WorkProvince, &ContactLdif::SetWorkProvince);
133 Map("postalCode", &ContactLdif::WorkPostalCode, &ContactLdif::SetWorkPostalCode);
134 Map("o", &ContactLdif::Company, &ContactLdif::SetCompany);
135 Map("c", &ContactLdif::WorkCountry, &ContactLdif::SetWorkCountry);
136 SetObjectClass("c", "country");
138 Map("title", &ContactLdif::JobTitle, &ContactLdif::SetJobTitle);
139 Map("dn", &ContactLdif::FQDN, &ContactLdif::SetFQDN);
140 Map("displayName", &ContactLdif::FullName, &ContactLdif::SetFullName);
141 Map("cn", &ContactLdif::FullName, &ContactLdif::SetFullName);
142 Map("sn", &ContactLdif::LastName, &ContactLdif::SetLastName);
143 Map("givenName", &ContactLdif::FirstName, &ContactLdif::SetFirstName);
144 Map("street", &ContactLdif::WorkAddress1, &ContactLdif::SetWorkAddress1);
145 Map("postalAddress", &ContactLdif::WorkPostalAddress, &ContactLdif::SetWorkPostalAddress);
146 Map("homePostalAddress", &ContactLdif::HomePostalAddress, &ContactLdif::SetHomePostalAddress);
147 Map("note", &ContactLdif::Notes, &ContactLdif::SetNotes);
148 // FIXME - jpegPhoto looks like the only LDIF field for photo
149 // images... it is unknown which format will come from the
150 // BlackBerry in the Image field, so we can't guarantee
151 // that Image will be in JPG. This mapping can be done manually
152 // from the btool command line with "-m jpegPhoto,Image,Image"
153 // Reading photos from LDIF should be fine, since the BlackBerry
154 // seems to handle most formats.
155 // Map("jpegPhoto", &ContactLdif::Image, &ContactLdif::SetImage);
157 // add heuristics hooks
158 Hook("cn", &m_cn);
159 Hook("displayName", &m_displayName);
160 Hook("sn", &m_sn);
161 Hook("givenName", &m_givenName);
163 // set default DN attribute
164 SetDNAttr("cn");
167 ContactLdif::~ContactLdif()
171 void ContactLdif::DoWrite(Barry::Contact &con,
172 const std::string &attr,
173 const std::string &data)
175 // valid?
176 if( attr.size() == 0 || data.size() == 0 )
177 return;
179 // now have attr/data pair, check hooks:
180 HookMapType::iterator hook = m_hookMap.find(attr);
181 if( hook != m_hookMap.end() ) {
182 *(hook->second) = data;
185 // run according to map
186 AccessMapType::iterator acc = m_map.find(attr);
187 if( acc != m_map.end() ) {
188 (this->*(acc->second.write))(con, data);
192 void ContactLdif::Hook(const std::string &ldifname, std::string *var)
194 m_hookMap[ldifname] = var;
197 const ContactLdif::NameToFunc* ContactLdif::GetFieldNames() const
199 return FieldMap;
202 const ContactLdif::NameToFunc*
203 ContactLdif::GetField(const std::string &fieldname) const
205 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
206 if( fieldname == n->name )
207 return n;
209 return 0;
212 std::string ContactLdif::GetFieldReadName(GetFunctionType read) const
214 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
215 if( read == n->read )
216 return n->name;
218 return _("<unknown>");
221 std::string ContactLdif::GetFieldWriteName(SetFunctionType write) const
223 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
224 if( write == n->write )
225 return n->name;
227 return _("<unknown>");
230 bool ContactLdif::Map(const LdifAttribute &ldifname,
231 const std::string &readField,
232 const std::string &writeField)
234 const NameToFunc *read = GetField(readField);
235 const NameToFunc *write = GetField(writeField);
236 if( !read || !write )
237 return false;
238 Map(ldifname, read->read, write->write);
239 return true;
242 void ContactLdif::Map(const LdifAttribute &ldifname,
243 GetFunctionType read,
244 SetFunctionType write)
246 m_map[ldifname] = AccessPair(read, write);
249 void ContactLdif::Unmap(const LdifAttribute &ldifname)
251 m_map.erase(ldifname);
255 // SetDNAttr
257 /// Sets the LDIF attribute name to use when constructing the FQDN.
258 /// The FQDN field will take this name, and combine it with the
259 /// baseDN from the constructor to produce a FQDN for the record.
261 bool ContactLdif::SetDNAttr(const LdifAttribute &name)
263 // try to find the attribute in the map
264 AccessMapType::iterator i = m_map.find(name);
265 if( i == m_map.end() )
266 return false;
268 m_dnAttr = name;
269 return true;
272 bool ContactLdif::SetObjectClass(const LdifAttribute &name,
273 const std::string &objectClass)
275 AccessMapType::iterator i = m_map.find(name);
276 if( i == m_map.end() )
277 return false;
279 LdifAttribute key = i->first;
280 AccessPair pair = i->second;
281 m_map.erase(key);
282 key.objectClass = objectClass;
283 m_map[key] = pair;
284 return true;
287 bool ContactLdif::SetObjectOrder(const LdifAttribute &name, int order)
289 AccessMapType::iterator i = m_map.find(name);
290 if( i == m_map.end() )
291 return false;
293 LdifAttribute key = i->first;
294 AccessPair pair = i->second;
295 m_map.erase(key);
296 key.order = order;
297 m_map[key] = pair;
298 return true;
302 std::string ContactLdif::Email(const Barry::Contact &con) const
304 return con.GetEmail(m_emailIndex++);
307 std::string ContactLdif::Phone(const Barry::Contact &con) const
309 return con.Phone;
312 std::string ContactLdif::Fax(const Barry::Contact &con) const
314 return con.Fax;
317 std::string ContactLdif::WorkPhone(const Barry::Contact &con) const
319 return con.WorkPhone;
322 std::string ContactLdif::HomePhone(const Barry::Contact &con) const
324 return con.HomePhone;
327 std::string ContactLdif::MobilePhone(const Barry::Contact &con) const
329 return con.MobilePhone;
332 std::string ContactLdif::Pager(const Barry::Contact &con) const
334 return con.Pager;
337 std::string ContactLdif::PIN(const Barry::Contact &con) const
339 return con.PIN;
342 std::string ContactLdif::FirstName(const Barry::Contact &con) const
344 return con.FirstName;
347 std::string ContactLdif::LastName(const Barry::Contact &con) const
349 return con.LastName;
352 std::string ContactLdif::Company(const Barry::Contact &con) const
354 return con.Company;
357 std::string ContactLdif::DefaultCommunicationsMethod(const Barry::Contact &con) const
359 return con.DefaultCommunicationsMethod;
362 std::string ContactLdif::WorkAddress1(const Barry::Contact &con) const
364 return con.WorkAddress.Address1;
367 std::string ContactLdif::WorkAddress2(const Barry::Contact &con) const
369 return con.WorkAddress.Address2;
372 std::string ContactLdif::WorkAddress3(const Barry::Contact &con) const
374 return con.WorkAddress.Address3;
377 std::string ContactLdif::WorkCity(const Barry::Contact &con) const
379 return con.WorkAddress.City;
382 std::string ContactLdif::WorkProvince(const Barry::Contact &con) const
384 return con.WorkAddress.Province;
387 std::string ContactLdif::WorkPostalCode(const Barry::Contact &con) const
389 return con.WorkAddress.PostalCode;
392 std::string ContactLdif::WorkCountry(const Barry::Contact &con) const
394 return con.WorkAddress.Country;
397 std::string ContactLdif::JobTitle(const Barry::Contact &con) const
399 return con.JobTitle;
402 std::string ContactLdif::PublicKey(const Barry::Contact &con) const
404 return con.PublicKey;
407 std::string ContactLdif::Notes(const Barry::Contact &con) const
409 return con.Notes;
412 std::string ContactLdif::Image(const Barry::Contact &con) const
414 return con.Image;
417 std::string ContactLdif::WorkPostalAddress(const Barry::Contact &con) const
419 return con.WorkAddress.GetLabel();
422 std::string ContactLdif::HomePostalAddress(const Barry::Contact &con) const
424 return con.HomeAddress.GetLabel();
427 std::string ContactLdif::FullName(const Barry::Contact &con) const
429 return con.GetFullName();
432 std::string ContactLdif::FQDN(const Barry::Contact &con) const
434 std::string FQDN = m_dnAttr.name;
435 FQDN += "=";
437 AccessMapType::const_iterator i = m_map.find(m_dnAttr);
438 if( i != m_map.end() ) {
439 FQDN += (this->*(i->second.read))(con);
441 else {
442 FQDN += _("unknown");
445 FQDN += ",";
446 FQDN += m_baseDN;
447 return FQDN;
450 bool ContactLdif::IsArrayFunc(GetFunctionType getf) const
452 // Currently, only the Email getter has array data
453 if( getf == &ContactLdif::Email )
454 return true;
455 return false;
458 void ContactLdif::ClearArrayState() const
460 m_emailIndex = 0;
463 void ContactLdif::SetEmail(Barry::Contact &con, const std::string &val) const
465 con.EmailAddresses.push_back(val);
468 void ContactLdif::SetPhone(Barry::Contact &con, const std::string &val) const
470 con.Phone = val;
473 void ContactLdif::SetFax(Barry::Contact &con, const std::string &val) const
475 con.Fax = val;
478 void ContactLdif::SetWorkPhone(Barry::Contact &con, const std::string &val) const
480 con.WorkPhone = val;
483 void ContactLdif::SetHomePhone(Barry::Contact &con, const std::string &val) const
485 con.HomePhone = val;
488 void ContactLdif::SetMobilePhone(Barry::Contact &con, const std::string &val) const
490 con.MobilePhone = val;
493 void ContactLdif::SetPager(Barry::Contact &con, const std::string &val) const
495 con.Pager = val;
498 void ContactLdif::SetPIN(Barry::Contact &con, const std::string &val) const
500 con.PIN = val;
503 void ContactLdif::SetFirstName(Barry::Contact &con, const std::string &val) const
505 con.FirstName = val;
508 void ContactLdif::SetLastName(Barry::Contact &con, const std::string &val) const
510 con.LastName = val;
513 void ContactLdif::SetCompany(Barry::Contact &con, const std::string &val) const
515 con.Company = val;
518 void ContactLdif::SetDefaultCommunicationsMethod(Barry::Contact &con, const std::string &val) const
520 con.DefaultCommunicationsMethod = val;
523 void ContactLdif::SetWorkAddress1(Barry::Contact &con, const std::string &val) const
525 con.WorkAddress.Address1 = val;
528 void ContactLdif::SetWorkAddress2(Barry::Contact &con, const std::string &val) const
530 con.WorkAddress.Address2 = val;
533 void ContactLdif::SetWorkAddress3(Barry::Contact &con, const std::string &val) const
535 con.WorkAddress.Address3 = val;
538 void ContactLdif::SetWorkCity(Barry::Contact &con, const std::string &val) const
540 con.WorkAddress.City = val;
543 void ContactLdif::SetWorkProvince(Barry::Contact &con, const std::string &val) const
545 con.WorkAddress.Province = val;
548 void ContactLdif::SetWorkPostalCode(Barry::Contact &con, const std::string &val) const
550 con.WorkAddress.PostalCode = val;
553 void ContactLdif::SetWorkCountry(Barry::Contact &con, const std::string &val) const
555 con.WorkAddress.Country = val;
558 void ContactLdif::SetJobTitle(Barry::Contact &con, const std::string &val) const
560 con.JobTitle = val;
563 void ContactLdif::SetPublicKey(Barry::Contact &con, const std::string &val) const
565 con.PublicKey = val;
568 void ContactLdif::SetNotes(Barry::Contact &con, const std::string &val) const
570 con.Notes = val;
573 void ContactLdif::SetImage(Barry::Contact &con, const std::string &val) const
575 con.Image = val;
578 void ContactLdif::SetWorkPostalAddress(Barry::Contact &con, const std::string &val) const
580 // FIXME;
581 // throw std::runtime_error("SetWorkPostalAddress() not implemented");
582 // std::cout << "SetWorkPostalAddress() not implemented: " << val << std::endl;
585 void ContactLdif::SetHomePostalAddress(Barry::Contact &con, const std::string &val) const
587 // FIXME;
588 // throw std::runtime_error("SetHomePostalAddress() not implemented");
589 // std::cout << "SetHomePostalAddress() not implemented: " << val << std::endl;
592 void ContactLdif::SetFullName(Barry::Contact &con, const std::string &val) const
594 std::string first, last;
595 Contact::SplitName(val, first, last);
596 con.FirstName = first;
597 con.LastName = last;
600 void ContactLdif::SetFQDN(Barry::Contact &con, const std::string &val) const
602 throw std::runtime_error("not implemented");
606 void ContactLdif::ClearHeuristics()
608 m_cn.clear();
609 m_displayName.clear();
610 m_sn.clear();
611 m_givenName.clear();
614 bool ContactLdif::RunHeuristics(Barry::Contact &con)
616 // start fresh
617 con.LastName.clear();
618 con.FirstName.clear();
620 // find the best match for name... prefer sn/givenName if available
621 if( m_sn.size() ) {
622 con.LastName = m_sn;
624 if( m_givenName.size() ) {
625 con.FirstName = m_givenName;
628 if( !con.LastName.size() || !con.FirstName.size() ) {
629 std::string first, last;
631 // still don't have a complete name, check cn first
632 if( m_cn.size() ) {
633 Contact::SplitName(m_cn, first, last);
634 if( !con.LastName.size() && last.size() )
635 con.LastName = last;
636 if( !con.FirstName.size() && first.size() )
637 con.FirstName = first;
640 // displayName is last chance
641 if( m_displayName.size() ) {
642 Contact::SplitName(m_displayName, first, last);
643 if( !con.LastName.size() && last.size() )
644 con.LastName = last;
645 if( !con.FirstName.size() && first.size() )
646 con.FirstName = first;
650 return con.LastName.size() && con.FirstName.size();
655 // DumpLdif
657 /// Output contact data to os in LDAP LDIF format.
659 void ContactLdif::DumpLdif(std::ostream &os,
660 const Barry::Contact &con) const
662 ios_format_state state(os);
664 // start fresh
665 ClearArrayState();
667 // setup stream state
668 os.setf(std::ios::left);
669 os.fill(' ');
671 if( FirstName(con).size() == 0 && LastName(con).size() == 0 )
672 return; // nothing to do
674 os << "# Contact 0x" << std::hex << con.GetID() << ", "
675 << FullName(con) << "\n";
677 // cycle through the map
678 for( AccessMapType::const_iterator b = m_map.begin();
679 b != m_map.end();
680 ++b )
682 // print only fields with data
683 std::string field;
685 do {
686 field = (this->*(b->second.read))(con);
687 if( field.size() ) {
688 os << b->first.name << MakeLdifData(field) << "\n";
689 if( b->first.objectClass.size() )
690 os << "objectClass: " << b->first.objectClass << "\n";
692 } while( IsArrayFunc(b->second.read) && field.size() );
695 os << "objectClass: inetOrgPerson\n";
697 // last line must be empty
698 os << "\n";
701 bool ContactLdif::ReadLdif(std::istream &is, Barry::Contact &con)
703 std::string line;
705 // start fresh
706 con.Clear();
707 ClearHeuristics();
709 // search for beginning dn: line
710 bool found = false;
711 while( std::getline(is, line) ) {
712 if( strncmp(line.c_str(), "dn: ", 4) == 0 ) {
713 found = true;
714 break;
717 if( !found )
718 return false;
720 // storage for various name styles
721 std::string coded, decode, attr, data;
722 bool b64field = false;
724 // read ldif lines until empty line is found
725 while( getline(is, line) && line.size() ) {
727 if( b64field ) {
728 // processing a base64 encoded field
729 if( line[0] == ' ' ) {
730 coded += "\n";
731 coded += line;
732 continue;
734 else {
735 // end of base64 block... ignore errors,
736 // and attempt to save everything decodable...
737 // the LDAP server sometimes returns incomplete
738 // base64 encoding, but otherwise the data is fine
739 base64_decode(coded, decode);
740 DoWrite(con, attr, decode);
741 coded.clear();
742 b64field = false;
744 // fall through to process new line
748 // split into attribute / data
749 std::string::size_type delim = line.find(':'), dstart;
750 if( delim == std::string::npos )
751 continue;
753 attr.assign(line, 0, delim);
754 dstart = delim + 1;
755 while( line[dstart] == ' ' || line[dstart] == ':' )
756 dstart++;
757 data = line.substr(dstart);
759 // is this data base64 encoded?
760 if( line[delim + 1] == ':' ) {
761 coded = data;
762 b64field = true;
763 continue;
766 DoWrite(con, attr, data);
769 if( b64field ) {
770 // clean up base64 decoding... ignore errors, see above comment
771 base64_decode(coded, decode);
772 DoWrite(con, attr, decode);
773 coded.clear();
774 b64field = false;
777 return RunHeuristics(con);
780 void ContactLdif::DumpMap(std::ostream &os) const
782 ios_format_state state(os);
784 os.setf(std::ios::left);
785 os.fill(' ');
787 os << _("ContactLdif Mapping:\n");
789 // cycle through the map
790 for( AccessMapType::const_iterator b = m_map.begin();
791 b != m_map.end();
792 ++b )
794 os << " " << std::left << std::setw(20) << b->first.name
795 << "-> " << GetFieldReadName(b->second.read)
796 << " / " << GetFieldWriteName(b->second.write) << "\n";
798 // find read/write names
800 if( b->first.objectClass.size() ) {
801 os << " " << std::setw(20) << " "
802 << "objectClass: " << b->first.objectClass << "\n";
806 os << _(" >>> DN attribute: ") << m_dnAttr.name << "\n";
809 std::string ContactLdif::MakeLdifData(const std::string &str)
811 std::string data = ":";
813 if( NeedsEncoding(str) ) {
814 std::string b64;
815 base64_encode(str, b64);
817 data += ": ";
818 data += b64;
820 else {
821 data += " ";
822 data += str;
825 return data;
829 // RFC 2849
831 // Must not contain:
832 // 0x00 (NUL), 0x0a (LF), 0x0d (CR), or anything greater than 0x7f
834 // First char must meet above criteria, plus must not be:
835 // 0x20 (SPACE), 0x3a (colon), 0x3c ('<')
837 bool ContactLdif::NeedsEncoding(const std::string &str)
839 for( std::string::size_type i = 0; i < str.size(); i++ ) {
840 unsigned char c = str[i];
842 switch( c )
844 case 0x00:
845 case 0x0a:
846 case 0x0d:
847 return true;
849 case 0x20:
850 case 0x3a:
851 case 0x3c:
852 if( i == 0 )
853 return true;
856 if( c > 0x7f )
857 return true;
859 return false;
862 } // namespace Barry