lib: added Backup parser and Restore builder classes
[barry.git] / src / ldif.cc
blob3dc43cf9f2ba478b6f8c6b6dc057010ed7e7984b
1 ///
2 /// \file ldif.cc
3 /// Routines for reading and writing LDAP LDIF data.
4 ///
6 /*
7 Copyright (C) 2005-2010, 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>
31 #define __DEBUG_MODE__
32 #include "debug.h"
34 namespace Barry {
36 const ContactLdif::NameToFunc ContactLdif::FieldMap[] = {
37 { "Email", "Email address",
38 &ContactLdif::Email, &ContactLdif::SetEmail },
39 { "Phone", "Phone number",
40 &ContactLdif::Phone, &ContactLdif::SetPhone },
41 { "Fax", "Fax number",
42 &ContactLdif::Fax, &ContactLdif::SetFax },
43 { "WorkPhone", "Work phone number",
44 &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone },
45 { "HomePhone", "Home phone number",
46 &ContactLdif::HomePhone, &ContactLdif::SetHomePhone },
47 { "MobilePhone", "Mobile phone number",
48 &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone },
49 { "Pager", "Pager number",
50 &ContactLdif::Pager, &ContactLdif::SetPager },
51 { "PIN", "PIN",
52 &ContactLdif::PIN, &ContactLdif::SetPIN },
53 { "FirstName", "First name",
54 &ContactLdif::FirstName, &ContactLdif::SetFirstName },
55 { "LastName", "Last name",
56 &ContactLdif::LastName, &ContactLdif::SetLastName },
57 { "Company", "Company name",
58 &ContactLdif::Company, &ContactLdif::SetCompany },
59 { "DefaultCommunicationsMethod", "Default communications method",
60 &ContactLdif::DefaultCommunicationsMethod, &ContactLdif::SetDefaultCommunicationsMethod },
61 { "WorkAddress1", "Work Address, line 1",
62 &ContactLdif::WorkAddress1, &ContactLdif::SetWorkAddress1 },
63 { "WorkAddress2", "Work Address, line 2",
64 &ContactLdif::WorkAddress2, &ContactLdif::SetWorkAddress2 },
65 { "WorkAddress3", "Work Address, line 3",
66 &ContactLdif::WorkAddress3, &ContactLdif::SetWorkAddress3 },
67 { "WorkCity", "WorkCity",
68 &ContactLdif::WorkCity, &ContactLdif::SetWorkCity },
69 { "WorkProvince", "WorkProvince / State",
70 &ContactLdif::WorkProvince, &ContactLdif::SetWorkProvince },
71 { "WorkPostalCode", "Work Postal / ZIP code",
72 &ContactLdif::WorkPostalCode, &ContactLdif::SetWorkPostalCode },
73 { "WorkCountry", "WorkCountry",
74 &ContactLdif::WorkCountry, &ContactLdif::SetWorkCountry },
75 { "JobTitle", "Job Title",
76 &ContactLdif::JobTitle, &ContactLdif::SetJobTitle },
77 { "PublicKey", "Public key",
78 &ContactLdif::PublicKey, &ContactLdif::SetPublicKey },
79 { "Notes", "Notes",
80 &ContactLdif::Notes, &ContactLdif::SetNotes },
81 { "Image", "Contact photo",
82 &ContactLdif::Image, &ContactLdif::SetImage },
83 { "WorkPostalAddress", "Mailing Work address (includes address lines, city, province, country, and postal code)",
84 &ContactLdif::WorkPostalAddress, &ContactLdif::SetWorkPostalAddress },
85 { "HomePostalAddress", "Mailing home address (includes address lines, city, province, country, and postal code)",
86 &ContactLdif::HomePostalAddress, &ContactLdif::SetHomePostalAddress },
87 { "FullName", "First + Last names",
88 &ContactLdif::FullName, &ContactLdif::SetFullName },
89 { "FQDN", "Fully qualified domain name",
90 &ContactLdif::FQDN, &ContactLdif::SetFQDN },
91 { 0, 0, 0 }
95 bool ContactLdif::LdifAttribute::operator<(const LdifAttribute &other) const
97 // the dn attribute always comes first in LDIF output
98 if( name == "dn" ) {
99 if( other.name == "dn" )
100 return false; // both dn, so equal
101 return true;
103 else if( other.name == "dn" )
104 return false;
106 return (order < other.order && name != other.name) ||
107 (order == other.order && name < other.name);
110 bool ContactLdif::LdifAttribute::operator==(const LdifAttribute &other) const
112 return name == other.name;
116 ///////////////////////////////////////////////////////////////////////////////
117 // ContactLdif class
119 ContactLdif::ContactLdif(const std::string &baseDN)
120 : m_baseDN(baseDN)
122 // setup some sane defaults
123 Map("mail", &ContactLdif::Email, &ContactLdif::SetEmail);
124 Map("facsimileTelephoneNumber", &ContactLdif::Fax, &ContactLdif::SetFax);
125 Map("telephoneNumber", &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone);
126 Map("homePhone", &ContactLdif::HomePhone, &ContactLdif::SetHomePhone);
127 Map("mobile", &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone);
128 Map("pager", &ContactLdif::Pager, &ContactLdif::SetPager);
129 Map("l", &ContactLdif::WorkCity, &ContactLdif::SetWorkCity);
130 Map("st", &ContactLdif::WorkProvince, &ContactLdif::SetWorkProvince);
131 Map("postalCode", &ContactLdif::WorkPostalCode, &ContactLdif::SetWorkPostalCode);
132 Map("o", &ContactLdif::Company, &ContactLdif::SetCompany);
133 Map("c", &ContactLdif::WorkCountry, &ContactLdif::SetWorkCountry);
134 SetObjectClass("c", "country");
136 Map("title", &ContactLdif::JobTitle, &ContactLdif::SetJobTitle);
137 Map("dn", &ContactLdif::FQDN, &ContactLdif::SetFQDN);
138 Map("displayName", &ContactLdif::FullName, &ContactLdif::SetFullName);
139 Map("cn", &ContactLdif::FullName, &ContactLdif::SetFullName);
140 Map("sn", &ContactLdif::LastName, &ContactLdif::SetLastName);
141 Map("givenName", &ContactLdif::FirstName, &ContactLdif::SetFirstName);
142 Map("street", &ContactLdif::WorkAddress1, &ContactLdif::SetWorkAddress1);
143 Map("postalAddress", &ContactLdif::WorkPostalAddress, &ContactLdif::SetWorkPostalAddress);
144 Map("homePostalAddress", &ContactLdif::HomePostalAddress, &ContactLdif::SetHomePostalAddress);
145 Map("note", &ContactLdif::Notes, &ContactLdif::SetNotes);
146 // FIXME - jpegPhoto looks like the only LDIF field for photo
147 // images... it is unknown which format will come from the
148 // BlackBerry in the Image field, so we can't guarantee
149 // that Image will be in JPG. This mapping can be done manually
150 // from the btool command line with "-m jpegPhoto,Image,Image"
151 // Reading photos from LDIF should be fine, since the BlackBerry
152 // seems to handle most formats.
153 // Map("jpegPhoto", &ContactLdif::Image, &ContactLdif::SetImage);
155 // add heuristics hooks
156 Hook("cn", &m_cn);
157 Hook("displayName", &m_displayName);
158 Hook("sn", &m_sn);
159 Hook("givenName", &m_givenName);
161 // set default DN attribute
162 SetDNAttr("cn");
165 ContactLdif::~ContactLdif()
169 void ContactLdif::DoWrite(Barry::Contact &con,
170 const std::string &attr,
171 const std::string &data)
173 // valid?
174 if( attr.size() == 0 || data.size() == 0 )
175 return;
177 // now have attr/data pair, check hooks:
178 HookMapType::iterator hook = m_hookMap.find(attr);
179 if( hook != m_hookMap.end() ) {
180 *(hook->second) = data;
183 // run according to map
184 AccessMapType::iterator acc = m_map.find(attr);
185 if( acc != m_map.end() ) {
186 (this->*(acc->second.write))(con, data);
190 void ContactLdif::Hook(const std::string &ldifname, std::string *var)
192 m_hookMap[ldifname] = var;
195 const ContactLdif::NameToFunc*
196 ContactLdif::GetField(const std::string &fieldname) const
198 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
199 if( fieldname == n->name )
200 return n;
202 return 0;
205 std::string ContactLdif::GetFieldReadName(GetFunctionType read) const
207 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
208 if( read == n->read )
209 return n->name;
211 return "<unknown>";
214 std::string ContactLdif::GetFieldWriteName(SetFunctionType write) const
216 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
217 if( write == n->write )
218 return n->name;
220 return "<unknown>";
223 bool ContactLdif::Map(const LdifAttribute &ldifname,
224 const std::string &readField,
225 const std::string &writeField)
227 const NameToFunc *read = GetField(readField);
228 const NameToFunc *write = GetField(writeField);
229 if( !read || !write )
230 return false;
231 Map(ldifname, read->read, write->write);
232 return true;
235 void ContactLdif::Map(const LdifAttribute &ldifname,
236 GetFunctionType read,
237 SetFunctionType write)
239 m_map[ldifname] = AccessPair(read, write);
242 void ContactLdif::Unmap(const LdifAttribute &ldifname)
244 m_map.erase(ldifname);
248 // SetDNAttr
250 /// Sets the LDIF attribute name to use when constructing the FQDN.
251 /// The FQDN field will take this name, and combine it with the
252 /// baseDN from the constructor to produce a FQDN for the record.
254 bool ContactLdif::SetDNAttr(const LdifAttribute &name)
256 // try to find the attribute in the map
257 AccessMapType::iterator i = m_map.find(name);
258 if( i == m_map.end() )
259 return false;
261 m_dnAttr = name;
262 return true;
265 bool ContactLdif::SetObjectClass(const LdifAttribute &name,
266 const std::string &objectClass)
268 AccessMapType::iterator i = m_map.find(name);
269 if( i == m_map.end() )
270 return false;
272 LdifAttribute key = i->first;
273 AccessPair pair = i->second;
274 m_map.erase(key);
275 key.objectClass = objectClass;
276 m_map[key] = pair;
277 return true;
280 bool ContactLdif::SetObjectOrder(const LdifAttribute &name, int order)
282 AccessMapType::iterator i = m_map.find(name);
283 if( i == m_map.end() )
284 return false;
286 LdifAttribute key = i->first;
287 AccessPair pair = i->second;
288 m_map.erase(key);
289 key.order = order;
290 m_map[key] = pair;
291 return true;
295 std::string ContactLdif::Email(const Barry::Contact &con) const
297 return con.GetEmail(m_emailIndex++);
300 std::string ContactLdif::Phone(const Barry::Contact &con) const
302 return con.Phone;
305 std::string ContactLdif::Fax(const Barry::Contact &con) const
307 return con.Fax;
310 std::string ContactLdif::WorkPhone(const Barry::Contact &con) const
312 return con.WorkPhone;
315 std::string ContactLdif::HomePhone(const Barry::Contact &con) const
317 return con.HomePhone;
320 std::string ContactLdif::MobilePhone(const Barry::Contact &con) const
322 return con.MobilePhone;
325 std::string ContactLdif::Pager(const Barry::Contact &con) const
327 return con.Pager;
330 std::string ContactLdif::PIN(const Barry::Contact &con) const
332 return con.PIN;
335 std::string ContactLdif::FirstName(const Barry::Contact &con) const
337 return con.FirstName;
340 std::string ContactLdif::LastName(const Barry::Contact &con) const
342 return con.LastName;
345 std::string ContactLdif::Company(const Barry::Contact &con) const
347 return con.Company;
350 std::string ContactLdif::DefaultCommunicationsMethod(const Barry::Contact &con) const
352 return con.DefaultCommunicationsMethod;
355 std::string ContactLdif::WorkAddress1(const Barry::Contact &con) const
357 return con.WorkAddress.Address1;
360 std::string ContactLdif::WorkAddress2(const Barry::Contact &con) const
362 return con.WorkAddress.Address2;
365 std::string ContactLdif::WorkAddress3(const Barry::Contact &con) const
367 return con.WorkAddress.Address3;
370 std::string ContactLdif::WorkCity(const Barry::Contact &con) const
372 return con.WorkAddress.City;
375 std::string ContactLdif::WorkProvince(const Barry::Contact &con) const
377 return con.WorkAddress.Province;
380 std::string ContactLdif::WorkPostalCode(const Barry::Contact &con) const
382 return con.WorkAddress.PostalCode;
385 std::string ContactLdif::WorkCountry(const Barry::Contact &con) const
387 return con.WorkAddress.Country;
390 std::string ContactLdif::JobTitle(const Barry::Contact &con) const
392 return con.JobTitle;
395 std::string ContactLdif::PublicKey(const Barry::Contact &con) const
397 return con.PublicKey;
400 std::string ContactLdif::Notes(const Barry::Contact &con) const
402 return con.Notes;
405 std::string ContactLdif::Image(const Barry::Contact &con) const
407 return con.Image;
410 std::string ContactLdif::WorkPostalAddress(const Barry::Contact &con) const
412 return con.WorkAddress.GetLabel();
415 std::string ContactLdif::HomePostalAddress(const Barry::Contact &con) const
417 return con.HomeAddress.GetLabel();
420 std::string ContactLdif::FullName(const Barry::Contact &con) const
422 return con.GetFullName();
425 std::string ContactLdif::FQDN(const Barry::Contact &con) const
427 std::string FQDN = m_dnAttr.name;
428 FQDN += "=";
430 AccessMapType::const_iterator i = m_map.find(m_dnAttr);
431 if( i != m_map.end() ) {
432 FQDN += (this->*(i->second.read))(con);
434 else {
435 FQDN += "unknown";
438 FQDN += ",";
439 FQDN += m_baseDN;
440 return FQDN;
443 bool ContactLdif::IsArrayFunc(GetFunctionType getf) const
445 // Currently, only the Email getter has array data
446 if( getf == &ContactLdif::Email )
447 return true;
448 return false;
451 void ContactLdif::ClearArrayState() const
453 m_emailIndex = 0;
456 void ContactLdif::SetEmail(Barry::Contact &con, const std::string &val) const
458 con.EmailAddresses.push_back(val);
461 void ContactLdif::SetPhone(Barry::Contact &con, const std::string &val) const
463 con.Phone = val;
466 void ContactLdif::SetFax(Barry::Contact &con, const std::string &val) const
468 con.Fax = val;
471 void ContactLdif::SetWorkPhone(Barry::Contact &con, const std::string &val) const
473 con.WorkPhone = val;
476 void ContactLdif::SetHomePhone(Barry::Contact &con, const std::string &val) const
478 con.HomePhone = val;
481 void ContactLdif::SetMobilePhone(Barry::Contact &con, const std::string &val) const
483 con.MobilePhone = val;
486 void ContactLdif::SetPager(Barry::Contact &con, const std::string &val) const
488 con.Pager = val;
491 void ContactLdif::SetPIN(Barry::Contact &con, const std::string &val) const
493 con.PIN = val;
496 void ContactLdif::SetFirstName(Barry::Contact &con, const std::string &val) const
498 con.FirstName = val;
501 void ContactLdif::SetLastName(Barry::Contact &con, const std::string &val) const
503 con.LastName = val;
506 void ContactLdif::SetCompany(Barry::Contact &con, const std::string &val) const
508 con.Company = val;
511 void ContactLdif::SetDefaultCommunicationsMethod(Barry::Contact &con, const std::string &val) const
513 con.DefaultCommunicationsMethod = val;
516 void ContactLdif::SetWorkAddress1(Barry::Contact &con, const std::string &val) const
518 con.WorkAddress.Address1 = val;
521 void ContactLdif::SetWorkAddress2(Barry::Contact &con, const std::string &val) const
523 con.WorkAddress.Address2 = val;
526 void ContactLdif::SetWorkAddress3(Barry::Contact &con, const std::string &val) const
528 con.WorkAddress.Address3 = val;
531 void ContactLdif::SetWorkCity(Barry::Contact &con, const std::string &val) const
533 con.WorkAddress.City = val;
536 void ContactLdif::SetWorkProvince(Barry::Contact &con, const std::string &val) const
538 con.WorkAddress.Province = val;
541 void ContactLdif::SetWorkPostalCode(Barry::Contact &con, const std::string &val) const
543 con.WorkAddress.PostalCode = val;
546 void ContactLdif::SetWorkCountry(Barry::Contact &con, const std::string &val) const
548 con.WorkAddress.Country = val;
551 void ContactLdif::SetJobTitle(Barry::Contact &con, const std::string &val) const
553 con.JobTitle = val;
556 void ContactLdif::SetPublicKey(Barry::Contact &con, const std::string &val) const
558 con.PublicKey = val;
561 void ContactLdif::SetNotes(Barry::Contact &con, const std::string &val) const
563 con.Notes = val;
566 void ContactLdif::SetImage(Barry::Contact &con, const std::string &val) const
568 con.Image = val;
571 void ContactLdif::SetWorkPostalAddress(Barry::Contact &con, const std::string &val) const
573 // FIXME;
574 // throw std::runtime_error("SetWorkPostalAddress() not implemented");
575 // std::cout << "SetWorkPostalAddress() not implemented: " << val << std::endl;
578 void ContactLdif::SetHomePostalAddress(Barry::Contact &con, const std::string &val) const
580 // FIXME;
581 // throw std::runtime_error("SetHomePostalAddress() not implemented");
582 // std::cout << "SetHomePostalAddress() not implemented: " << val << std::endl;
585 void ContactLdif::SetFullName(Barry::Contact &con, const std::string &val) const
587 std::string first, last;
588 Contact::SplitName(val, first, last);
589 con.FirstName = first;
590 con.LastName = last;
593 void ContactLdif::SetFQDN(Barry::Contact &con, const std::string &val) const
595 throw std::runtime_error("not implemented");
599 void ContactLdif::ClearHeuristics()
601 m_cn.clear();
602 m_displayName.clear();
603 m_sn.clear();
604 m_givenName.clear();
607 bool ContactLdif::RunHeuristics(Barry::Contact &con)
609 // start fresh
610 con.LastName.clear();
611 con.FirstName.clear();
613 // find the best match for name... prefer sn/givenName if available
614 if( m_sn.size() ) {
615 con.LastName = m_sn;
617 if( m_givenName.size() ) {
618 con.FirstName = m_givenName;
621 if( !con.LastName.size() || !con.FirstName.size() ) {
622 std::string first, last;
624 // still don't have a complete name, check cn first
625 if( m_cn.size() ) {
626 Contact::SplitName(m_cn, first, last);
627 if( !con.LastName.size() && last.size() )
628 con.LastName = last;
629 if( !con.FirstName.size() && first.size() )
630 con.FirstName = first;
633 // displayName is last chance
634 if( m_displayName.size() ) {
635 Contact::SplitName(m_displayName, first, last);
636 if( !con.LastName.size() && last.size() )
637 con.LastName = last;
638 if( !con.FirstName.size() && first.size() )
639 con.FirstName = first;
643 return con.LastName.size() && con.FirstName.size();
648 // DumpLdif
650 /// Output contact data to os in LDAP LDIF format.
652 void ContactLdif::DumpLdif(std::ostream &os,
653 const Barry::Contact &con) const
655 // start fresh
656 ClearArrayState();
658 // setup stream state
659 std::ios::fmtflags oldflags = os.setf(std::ios::left);
660 char fill = os.fill(' ');
662 if( FirstName(con).size() == 0 && LastName(con).size() == 0 )
663 return; // nothing to do
665 os << "# Contact 0x" << std::hex << con.GetID() << ", "
666 << FullName(con) << "\n";
668 // cycle through the map
669 for( AccessMapType::const_iterator b = m_map.begin();
670 b != m_map.end();
671 ++b )
673 // print only fields with data
674 std::string field;
676 do {
677 field = (this->*(b->second.read))(con);
678 if( field.size() ) {
679 os << b->first.name << MakeLdifData(field) << "\n";
680 if( b->first.objectClass.size() )
681 os << "objectClass: " << b->first.objectClass << "\n";
683 } while( IsArrayFunc(b->second.read) && field.size() );
686 os << "objectClass: inetOrgPerson\n";
688 // last line must be empty
689 os << "\n";
691 // cleanup the stream
692 os.flags(oldflags);
693 os.fill(fill);
696 bool ContactLdif::ReadLdif(std::istream &is, Barry::Contact &con)
698 std::string line;
700 // start fresh
701 con.Clear();
702 ClearHeuristics();
704 // search for beginning dn: line
705 bool found = false;
706 while( std::getline(is, line) ) {
707 if( strncmp(line.c_str(), "dn: ", 4) == 0 ) {
708 found = true;
709 break;
712 if( !found )
713 return false;
715 // storage for various name styles
716 std::string coded, decode, attr, data;
717 bool b64field = false;
719 // read ldif lines until empty line is found
720 while( getline(is, line) && line.size() ) {
722 if( b64field ) {
723 // processing a base64 encoded field
724 if( line[0] == ' ' ) {
725 coded += "\n";
726 coded += line;
727 continue;
729 else {
730 // end of base64 block... ignore errors,
731 // and attempt to save everything decodable...
732 // the LDAP server sometimes returns incomplete
733 // base64 encoding, but otherwise the data is fine
734 base64_decode(coded, decode);
735 DoWrite(con, attr, decode);
736 coded.clear();
737 b64field = false;
739 // fall through to process new line
743 // split into attribute / data
744 std::string::size_type delim = line.find(':'), dstart;
745 if( delim == std::string::npos )
746 continue;
748 attr.assign(line, 0, delim);
749 dstart = delim + 1;
750 while( line[dstart] == ' ' || line[dstart] == ':' )
751 dstart++;
752 data = line.substr(dstart);
754 // is this data base64 encoded?
755 if( line[delim + 1] == ':' ) {
756 coded = data;
757 b64field = true;
758 continue;
761 DoWrite(con, attr, data);
764 if( b64field ) {
765 // clean up base64 decoding... ignore errors, see above comment
766 base64_decode(coded, decode);
767 DoWrite(con, attr, decode);
768 coded.clear();
769 b64field = false;
772 return RunHeuristics(con);
775 void ContactLdif::DumpMap(std::ostream &os) const
777 std::ios::fmtflags oldflags = os.setf(std::ios::left);
778 char fill = os.fill(' ');
780 os << "ContactLdif Mapping:\n";
782 // cycle through the map
783 for( AccessMapType::const_iterator b = m_map.begin();
784 b != m_map.end();
785 ++b )
787 os << " " << std::left << std::setw(20) << b->first.name
788 << "-> " << GetFieldReadName(b->second.read)
789 << " / " << GetFieldWriteName(b->second.write) << "\n";
791 // find read/write names
793 if( b->first.objectClass.size() ) {
794 os << " " << std::setw(20) << " "
795 << "objectClass: " << b->first.objectClass << "\n";
799 os << " >>> DN attribute: " << m_dnAttr.name << "\n";
801 // cleanup the stream
802 os.flags(oldflags);
803 os.fill(fill);
806 std::string ContactLdif::MakeLdifData(const std::string &str)
808 std::string data = ":";
810 if( NeedsEncoding(str) ) {
811 std::string b64;
812 base64_encode(str, b64);
814 data += ": ";
815 data += b64;
817 else {
818 data += " ";
819 data += str;
822 return data;
826 // RFC 2849
828 // Must not contain:
829 // 0x00 (NUL), 0x0a (LF), 0x0d (CR), or anything greater than 0x7f
831 // First char must meet above criteria, plus must not be:
832 // 0x20 (SPACE), 0x3a (colon), 0x3c ('<')
834 bool ContactLdif::NeedsEncoding(const std::string &str)
836 for( std::string::size_type i = 0; i < str.size(); i++ ) {
837 unsigned char c = str[i];
839 switch( c )
841 case 0x00:
842 case 0x0a:
843 case 0x0d:
844 return true;
846 case 0x20:
847 case 0x3a:
848 case 0x3c:
849 if( i == 0 )
850 return true;
853 if( c > 0x7f )
854 return true;
856 return false;
859 } // namespace Barry