debian: fixed broken debian/*.install target files
[barry/progweb.git] / src / ldif.cc
blob82ee6c9610a61aba494c20ebd536a7c2727a3fe4
1 ///
2 /// \file ldif.cc
3 /// Routines for reading and writing LDAP LDIF data.
4 ///
6 /*
7 Copyright (C) 2005-2011, 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 "ldif.h"
23 #include "record.h"
24 #include "r_contact.h"
25 #include "base64.h"
26 #include <stdexcept>
27 #include <iostream>
28 #include <iomanip>
29 #include <string.h>
30 #include "ios_state.h"
32 #define __DEBUG_MODE__
33 #include "debug.h"
35 namespace Barry {
37 const ContactLdif::NameToFunc ContactLdif::FieldMap[] = {
38 { "Email", "Email address",
39 &ContactLdif::Email, &ContactLdif::SetEmail },
40 { "Phone", "Phone number",
41 &ContactLdif::Phone, &ContactLdif::SetPhone },
42 { "Fax", "Fax number",
43 &ContactLdif::Fax, &ContactLdif::SetFax },
44 { "WorkPhone", "Work phone number",
45 &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone },
46 { "HomePhone", "Home phone number",
47 &ContactLdif::HomePhone, &ContactLdif::SetHomePhone },
48 { "MobilePhone", "Mobile phone number",
49 &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone },
50 { "Pager", "Pager number",
51 &ContactLdif::Pager, &ContactLdif::SetPager },
52 { "PIN", "PIN",
53 &ContactLdif::PIN, &ContactLdif::SetPIN },
54 { "FirstName", "First name",
55 &ContactLdif::FirstName, &ContactLdif::SetFirstName },
56 { "LastName", "Last name",
57 &ContactLdif::LastName, &ContactLdif::SetLastName },
58 { "Company", "Company name",
59 &ContactLdif::Company, &ContactLdif::SetCompany },
60 { "DefaultCommunicationsMethod", "Default communications method",
61 &ContactLdif::DefaultCommunicationsMethod, &ContactLdif::SetDefaultCommunicationsMethod },
62 { "WorkAddress1", "Work Address, line 1",
63 &ContactLdif::WorkAddress1, &ContactLdif::SetWorkAddress1 },
64 { "WorkAddress2", "Work Address, line 2",
65 &ContactLdif::WorkAddress2, &ContactLdif::SetWorkAddress2 },
66 { "WorkAddress3", "Work Address, line 3",
67 &ContactLdif::WorkAddress3, &ContactLdif::SetWorkAddress3 },
68 { "WorkCity", "WorkCity",
69 &ContactLdif::WorkCity, &ContactLdif::SetWorkCity },
70 { "WorkProvince", "WorkProvince / State",
71 &ContactLdif::WorkProvince, &ContactLdif::SetWorkProvince },
72 { "WorkPostalCode", "Work Postal / ZIP code",
73 &ContactLdif::WorkPostalCode, &ContactLdif::SetWorkPostalCode },
74 { "WorkCountry", "WorkCountry",
75 &ContactLdif::WorkCountry, &ContactLdif::SetWorkCountry },
76 { "JobTitle", "Job Title",
77 &ContactLdif::JobTitle, &ContactLdif::SetJobTitle },
78 { "PublicKey", "Public key",
79 &ContactLdif::PublicKey, &ContactLdif::SetPublicKey },
80 { "Notes", "Notes",
81 &ContactLdif::Notes, &ContactLdif::SetNotes },
82 { "Image", "Contact photo",
83 &ContactLdif::Image, &ContactLdif::SetImage },
84 { "WorkPostalAddress", "Mailing Work address (includes address lines, city, province, country, and postal code)",
85 &ContactLdif::WorkPostalAddress, &ContactLdif::SetWorkPostalAddress },
86 { "HomePostalAddress", "Mailing home address (includes address lines, city, province, country, and postal code)",
87 &ContactLdif::HomePostalAddress, &ContactLdif::SetHomePostalAddress },
88 { "FullName", "First + Last names",
89 &ContactLdif::FullName, &ContactLdif::SetFullName },
90 { "FQDN", "Fully qualified domain name",
91 &ContactLdif::FQDN, &ContactLdif::SetFQDN },
92 { 0, 0, 0 }
96 bool ContactLdif::LdifAttribute::operator<(const LdifAttribute &other) const
98 // the dn attribute always comes first in LDIF output
99 if( name == "dn" ) {
100 if( other.name == "dn" )
101 return false; // both dn, so equal
102 return true;
104 else if( other.name == "dn" )
105 return false;
107 return (order < other.order && name != other.name) ||
108 (order == other.order && name < other.name);
111 bool ContactLdif::LdifAttribute::operator==(const LdifAttribute &other) const
113 return name == other.name;
117 ///////////////////////////////////////////////////////////////////////////////
118 // ContactLdif class
120 ContactLdif::ContactLdif(const std::string &baseDN)
121 : m_baseDN(baseDN)
123 // setup some sane defaults
124 Map("mail", &ContactLdif::Email, &ContactLdif::SetEmail);
125 Map("facsimileTelephoneNumber", &ContactLdif::Fax, &ContactLdif::SetFax);
126 Map("telephoneNumber", &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone);
127 Map("homePhone", &ContactLdif::HomePhone, &ContactLdif::SetHomePhone);
128 Map("mobile", &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone);
129 Map("pager", &ContactLdif::Pager, &ContactLdif::SetPager);
130 Map("l", &ContactLdif::WorkCity, &ContactLdif::SetWorkCity);
131 Map("st", &ContactLdif::WorkProvince, &ContactLdif::SetWorkProvince);
132 Map("postalCode", &ContactLdif::WorkPostalCode, &ContactLdif::SetWorkPostalCode);
133 Map("o", &ContactLdif::Company, &ContactLdif::SetCompany);
134 Map("c", &ContactLdif::WorkCountry, &ContactLdif::SetWorkCountry);
135 SetObjectClass("c", "country");
137 Map("title", &ContactLdif::JobTitle, &ContactLdif::SetJobTitle);
138 Map("dn", &ContactLdif::FQDN, &ContactLdif::SetFQDN);
139 Map("displayName", &ContactLdif::FullName, &ContactLdif::SetFullName);
140 Map("cn", &ContactLdif::FullName, &ContactLdif::SetFullName);
141 Map("sn", &ContactLdif::LastName, &ContactLdif::SetLastName);
142 Map("givenName", &ContactLdif::FirstName, &ContactLdif::SetFirstName);
143 Map("street", &ContactLdif::WorkAddress1, &ContactLdif::SetWorkAddress1);
144 Map("postalAddress", &ContactLdif::WorkPostalAddress, &ContactLdif::SetWorkPostalAddress);
145 Map("homePostalAddress", &ContactLdif::HomePostalAddress, &ContactLdif::SetHomePostalAddress);
146 Map("note", &ContactLdif::Notes, &ContactLdif::SetNotes);
147 // FIXME - jpegPhoto looks like the only LDIF field for photo
148 // images... it is unknown which format will come from the
149 // BlackBerry in the Image field, so we can't guarantee
150 // that Image will be in JPG. This mapping can be done manually
151 // from the btool command line with "-m jpegPhoto,Image,Image"
152 // Reading photos from LDIF should be fine, since the BlackBerry
153 // seems to handle most formats.
154 // Map("jpegPhoto", &ContactLdif::Image, &ContactLdif::SetImage);
156 // add heuristics hooks
157 Hook("cn", &m_cn);
158 Hook("displayName", &m_displayName);
159 Hook("sn", &m_sn);
160 Hook("givenName", &m_givenName);
162 // set default DN attribute
163 SetDNAttr("cn");
166 ContactLdif::~ContactLdif()
170 void ContactLdif::DoWrite(Barry::Contact &con,
171 const std::string &attr,
172 const std::string &data)
174 // valid?
175 if( attr.size() == 0 || data.size() == 0 )
176 return;
178 // now have attr/data pair, check hooks:
179 HookMapType::iterator hook = m_hookMap.find(attr);
180 if( hook != m_hookMap.end() ) {
181 *(hook->second) = data;
184 // run according to map
185 AccessMapType::iterator acc = m_map.find(attr);
186 if( acc != m_map.end() ) {
187 (this->*(acc->second.write))(con, data);
191 void ContactLdif::Hook(const std::string &ldifname, std::string *var)
193 m_hookMap[ldifname] = var;
196 const ContactLdif::NameToFunc*
197 ContactLdif::GetField(const std::string &fieldname) const
199 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
200 if( fieldname == n->name )
201 return n;
203 return 0;
206 std::string ContactLdif::GetFieldReadName(GetFunctionType read) const
208 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
209 if( read == n->read )
210 return n->name;
212 return "<unknown>";
215 std::string ContactLdif::GetFieldWriteName(SetFunctionType write) const
217 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
218 if( write == n->write )
219 return n->name;
221 return "<unknown>";
224 bool ContactLdif::Map(const LdifAttribute &ldifname,
225 const std::string &readField,
226 const std::string &writeField)
228 const NameToFunc *read = GetField(readField);
229 const NameToFunc *write = GetField(writeField);
230 if( !read || !write )
231 return false;
232 Map(ldifname, read->read, write->write);
233 return true;
236 void ContactLdif::Map(const LdifAttribute &ldifname,
237 GetFunctionType read,
238 SetFunctionType write)
240 m_map[ldifname] = AccessPair(read, write);
243 void ContactLdif::Unmap(const LdifAttribute &ldifname)
245 m_map.erase(ldifname);
249 // SetDNAttr
251 /// Sets the LDIF attribute name to use when constructing the FQDN.
252 /// The FQDN field will take this name, and combine it with the
253 /// baseDN from the constructor to produce a FQDN for the record.
255 bool ContactLdif::SetDNAttr(const LdifAttribute &name)
257 // try to find the attribute in the map
258 AccessMapType::iterator i = m_map.find(name);
259 if( i == m_map.end() )
260 return false;
262 m_dnAttr = name;
263 return true;
266 bool ContactLdif::SetObjectClass(const LdifAttribute &name,
267 const std::string &objectClass)
269 AccessMapType::iterator i = m_map.find(name);
270 if( i == m_map.end() )
271 return false;
273 LdifAttribute key = i->first;
274 AccessPair pair = i->second;
275 m_map.erase(key);
276 key.objectClass = objectClass;
277 m_map[key] = pair;
278 return true;
281 bool ContactLdif::SetObjectOrder(const LdifAttribute &name, int order)
283 AccessMapType::iterator i = m_map.find(name);
284 if( i == m_map.end() )
285 return false;
287 LdifAttribute key = i->first;
288 AccessPair pair = i->second;
289 m_map.erase(key);
290 key.order = order;
291 m_map[key] = pair;
292 return true;
296 std::string ContactLdif::Email(const Barry::Contact &con) const
298 return con.GetEmail(m_emailIndex++);
301 std::string ContactLdif::Phone(const Barry::Contact &con) const
303 return con.Phone;
306 std::string ContactLdif::Fax(const Barry::Contact &con) const
308 return con.Fax;
311 std::string ContactLdif::WorkPhone(const Barry::Contact &con) const
313 return con.WorkPhone;
316 std::string ContactLdif::HomePhone(const Barry::Contact &con) const
318 return con.HomePhone;
321 std::string ContactLdif::MobilePhone(const Barry::Contact &con) const
323 return con.MobilePhone;
326 std::string ContactLdif::Pager(const Barry::Contact &con) const
328 return con.Pager;
331 std::string ContactLdif::PIN(const Barry::Contact &con) const
333 return con.PIN;
336 std::string ContactLdif::FirstName(const Barry::Contact &con) const
338 return con.FirstName;
341 std::string ContactLdif::LastName(const Barry::Contact &con) const
343 return con.LastName;
346 std::string ContactLdif::Company(const Barry::Contact &con) const
348 return con.Company;
351 std::string ContactLdif::DefaultCommunicationsMethod(const Barry::Contact &con) const
353 return con.DefaultCommunicationsMethod;
356 std::string ContactLdif::WorkAddress1(const Barry::Contact &con) const
358 return con.WorkAddress.Address1;
361 std::string ContactLdif::WorkAddress2(const Barry::Contact &con) const
363 return con.WorkAddress.Address2;
366 std::string ContactLdif::WorkAddress3(const Barry::Contact &con) const
368 return con.WorkAddress.Address3;
371 std::string ContactLdif::WorkCity(const Barry::Contact &con) const
373 return con.WorkAddress.City;
376 std::string ContactLdif::WorkProvince(const Barry::Contact &con) const
378 return con.WorkAddress.Province;
381 std::string ContactLdif::WorkPostalCode(const Barry::Contact &con) const
383 return con.WorkAddress.PostalCode;
386 std::string ContactLdif::WorkCountry(const Barry::Contact &con) const
388 return con.WorkAddress.Country;
391 std::string ContactLdif::JobTitle(const Barry::Contact &con) const
393 return con.JobTitle;
396 std::string ContactLdif::PublicKey(const Barry::Contact &con) const
398 return con.PublicKey;
401 std::string ContactLdif::Notes(const Barry::Contact &con) const
403 return con.Notes;
406 std::string ContactLdif::Image(const Barry::Contact &con) const
408 return con.Image;
411 std::string ContactLdif::WorkPostalAddress(const Barry::Contact &con) const
413 return con.WorkAddress.GetLabel();
416 std::string ContactLdif::HomePostalAddress(const Barry::Contact &con) const
418 return con.HomeAddress.GetLabel();
421 std::string ContactLdif::FullName(const Barry::Contact &con) const
423 return con.GetFullName();
426 std::string ContactLdif::FQDN(const Barry::Contact &con) const
428 std::string FQDN = m_dnAttr.name;
429 FQDN += "=";
431 AccessMapType::const_iterator i = m_map.find(m_dnAttr);
432 if( i != m_map.end() ) {
433 FQDN += (this->*(i->second.read))(con);
435 else {
436 FQDN += "unknown";
439 FQDN += ",";
440 FQDN += m_baseDN;
441 return FQDN;
444 bool ContactLdif::IsArrayFunc(GetFunctionType getf) const
446 // Currently, only the Email getter has array data
447 if( getf == &ContactLdif::Email )
448 return true;
449 return false;
452 void ContactLdif::ClearArrayState() const
454 m_emailIndex = 0;
457 void ContactLdif::SetEmail(Barry::Contact &con, const std::string &val) const
459 con.EmailAddresses.push_back(val);
462 void ContactLdif::SetPhone(Barry::Contact &con, const std::string &val) const
464 con.Phone = val;
467 void ContactLdif::SetFax(Barry::Contact &con, const std::string &val) const
469 con.Fax = val;
472 void ContactLdif::SetWorkPhone(Barry::Contact &con, const std::string &val) const
474 con.WorkPhone = val;
477 void ContactLdif::SetHomePhone(Barry::Contact &con, const std::string &val) const
479 con.HomePhone = val;
482 void ContactLdif::SetMobilePhone(Barry::Contact &con, const std::string &val) const
484 con.MobilePhone = val;
487 void ContactLdif::SetPager(Barry::Contact &con, const std::string &val) const
489 con.Pager = val;
492 void ContactLdif::SetPIN(Barry::Contact &con, const std::string &val) const
494 con.PIN = val;
497 void ContactLdif::SetFirstName(Barry::Contact &con, const std::string &val) const
499 con.FirstName = val;
502 void ContactLdif::SetLastName(Barry::Contact &con, const std::string &val) const
504 con.LastName = val;
507 void ContactLdif::SetCompany(Barry::Contact &con, const std::string &val) const
509 con.Company = val;
512 void ContactLdif::SetDefaultCommunicationsMethod(Barry::Contact &con, const std::string &val) const
514 con.DefaultCommunicationsMethod = val;
517 void ContactLdif::SetWorkAddress1(Barry::Contact &con, const std::string &val) const
519 con.WorkAddress.Address1 = val;
522 void ContactLdif::SetWorkAddress2(Barry::Contact &con, const std::string &val) const
524 con.WorkAddress.Address2 = val;
527 void ContactLdif::SetWorkAddress3(Barry::Contact &con, const std::string &val) const
529 con.WorkAddress.Address3 = val;
532 void ContactLdif::SetWorkCity(Barry::Contact &con, const std::string &val) const
534 con.WorkAddress.City = val;
537 void ContactLdif::SetWorkProvince(Barry::Contact &con, const std::string &val) const
539 con.WorkAddress.Province = val;
542 void ContactLdif::SetWorkPostalCode(Barry::Contact &con, const std::string &val) const
544 con.WorkAddress.PostalCode = val;
547 void ContactLdif::SetWorkCountry(Barry::Contact &con, const std::string &val) const
549 con.WorkAddress.Country = val;
552 void ContactLdif::SetJobTitle(Barry::Contact &con, const std::string &val) const
554 con.JobTitle = val;
557 void ContactLdif::SetPublicKey(Barry::Contact &con, const std::string &val) const
559 con.PublicKey = val;
562 void ContactLdif::SetNotes(Barry::Contact &con, const std::string &val) const
564 con.Notes = val;
567 void ContactLdif::SetImage(Barry::Contact &con, const std::string &val) const
569 con.Image = val;
572 void ContactLdif::SetWorkPostalAddress(Barry::Contact &con, const std::string &val) const
574 // FIXME;
575 // throw std::runtime_error("SetWorkPostalAddress() not implemented");
576 // std::cout << "SetWorkPostalAddress() not implemented: " << val << std::endl;
579 void ContactLdif::SetHomePostalAddress(Barry::Contact &con, const std::string &val) const
581 // FIXME;
582 // throw std::runtime_error("SetHomePostalAddress() not implemented");
583 // std::cout << "SetHomePostalAddress() not implemented: " << val << std::endl;
586 void ContactLdif::SetFullName(Barry::Contact &con, const std::string &val) const
588 std::string first, last;
589 Contact::SplitName(val, first, last);
590 con.FirstName = first;
591 con.LastName = last;
594 void ContactLdif::SetFQDN(Barry::Contact &con, const std::string &val) const
596 throw std::runtime_error("not implemented");
600 void ContactLdif::ClearHeuristics()
602 m_cn.clear();
603 m_displayName.clear();
604 m_sn.clear();
605 m_givenName.clear();
608 bool ContactLdif::RunHeuristics(Barry::Contact &con)
610 // start fresh
611 con.LastName.clear();
612 con.FirstName.clear();
614 // find the best match for name... prefer sn/givenName if available
615 if( m_sn.size() ) {
616 con.LastName = m_sn;
618 if( m_givenName.size() ) {
619 con.FirstName = m_givenName;
622 if( !con.LastName.size() || !con.FirstName.size() ) {
623 std::string first, last;
625 // still don't have a complete name, check cn first
626 if( m_cn.size() ) {
627 Contact::SplitName(m_cn, first, last);
628 if( !con.LastName.size() && last.size() )
629 con.LastName = last;
630 if( !con.FirstName.size() && first.size() )
631 con.FirstName = first;
634 // displayName is last chance
635 if( m_displayName.size() ) {
636 Contact::SplitName(m_displayName, first, last);
637 if( !con.LastName.size() && last.size() )
638 con.LastName = last;
639 if( !con.FirstName.size() && first.size() )
640 con.FirstName = first;
644 return con.LastName.size() && con.FirstName.size();
649 // DumpLdif
651 /// Output contact data to os in LDAP LDIF format.
653 void ContactLdif::DumpLdif(std::ostream &os,
654 const Barry::Contact &con) const
656 ios_format_state state(os);
658 // start fresh
659 ClearArrayState();
661 // setup stream state
662 os.setf(std::ios::left);
663 os.fill(' ');
665 if( FirstName(con).size() == 0 && LastName(con).size() == 0 )
666 return; // nothing to do
668 os << "# Contact 0x" << std::hex << con.GetID() << ", "
669 << FullName(con) << "\n";
671 // cycle through the map
672 for( AccessMapType::const_iterator b = m_map.begin();
673 b != m_map.end();
674 ++b )
676 // print only fields with data
677 std::string field;
679 do {
680 field = (this->*(b->second.read))(con);
681 if( field.size() ) {
682 os << b->first.name << MakeLdifData(field) << "\n";
683 if( b->first.objectClass.size() )
684 os << "objectClass: " << b->first.objectClass << "\n";
686 } while( IsArrayFunc(b->second.read) && field.size() );
689 os << "objectClass: inetOrgPerson\n";
691 // last line must be empty
692 os << "\n";
695 bool ContactLdif::ReadLdif(std::istream &is, Barry::Contact &con)
697 std::string line;
699 // start fresh
700 con.Clear();
701 ClearHeuristics();
703 // search for beginning dn: line
704 bool found = false;
705 while( std::getline(is, line) ) {
706 if( strncmp(line.c_str(), "dn: ", 4) == 0 ) {
707 found = true;
708 break;
711 if( !found )
712 return false;
714 // storage for various name styles
715 std::string coded, decode, attr, data;
716 bool b64field = false;
718 // read ldif lines until empty line is found
719 while( getline(is, line) && line.size() ) {
721 if( b64field ) {
722 // processing a base64 encoded field
723 if( line[0] == ' ' ) {
724 coded += "\n";
725 coded += line;
726 continue;
728 else {
729 // end of base64 block... ignore errors,
730 // and attempt to save everything decodable...
731 // the LDAP server sometimes returns incomplete
732 // base64 encoding, but otherwise the data is fine
733 base64_decode(coded, decode);
734 DoWrite(con, attr, decode);
735 coded.clear();
736 b64field = false;
738 // fall through to process new line
742 // split into attribute / data
743 std::string::size_type delim = line.find(':'), dstart;
744 if( delim == std::string::npos )
745 continue;
747 attr.assign(line, 0, delim);
748 dstart = delim + 1;
749 while( line[dstart] == ' ' || line[dstart] == ':' )
750 dstart++;
751 data = line.substr(dstart);
753 // is this data base64 encoded?
754 if( line[delim + 1] == ':' ) {
755 coded = data;
756 b64field = true;
757 continue;
760 DoWrite(con, attr, data);
763 if( b64field ) {
764 // clean up base64 decoding... ignore errors, see above comment
765 base64_decode(coded, decode);
766 DoWrite(con, attr, decode);
767 coded.clear();
768 b64field = false;
771 return RunHeuristics(con);
774 void ContactLdif::DumpMap(std::ostream &os) const
776 ios_format_state state(os);
778 os.setf(std::ios::left);
779 os.fill(' ');
781 os << "ContactLdif Mapping:\n";
783 // cycle through the map
784 for( AccessMapType::const_iterator b = m_map.begin();
785 b != m_map.end();
786 ++b )
788 os << " " << std::left << std::setw(20) << b->first.name
789 << "-> " << GetFieldReadName(b->second.read)
790 << " / " << GetFieldWriteName(b->second.write) << "\n";
792 // find read/write names
794 if( b->first.objectClass.size() ) {
795 os << " " << std::setw(20) << " "
796 << "objectClass: " << b->first.objectClass << "\n";
800 os << " >>> DN attribute: " << m_dnAttr.name << "\n";
803 std::string ContactLdif::MakeLdifData(const std::string &str)
805 std::string data = ":";
807 if( NeedsEncoding(str) ) {
808 std::string b64;
809 base64_encode(str, b64);
811 data += ": ";
812 data += b64;
814 else {
815 data += " ";
816 data += str;
819 return data;
823 // RFC 2849
825 // Must not contain:
826 // 0x00 (NUL), 0x0a (LF), 0x0d (CR), or anything greater than 0x7f
828 // First char must meet above criteria, plus must not be:
829 // 0x20 (SPACE), 0x3a (colon), 0x3c ('<')
831 bool ContactLdif::NeedsEncoding(const std::string &str)
833 for( std::string::size_type i = 0; i < str.size(); i++ ) {
834 unsigned char c = str[i];
836 switch( c )
838 case 0x00:
839 case 0x0a:
840 case 0x0d:
841 return true;
843 case 0x20:
844 case 0x3a:
845 case 0x3c:
846 if( i == 0 )
847 return true;
850 if( c > 0x7f )
851 return true;
853 return false;
856 } // namespace Barry