00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00031 #define __DEBUG_MODE__
00032 #include "debug.h"
00033
00034 namespace Barry {
00035
00036 const ContactLdif::NameToFunc ContactLdif::FieldMap[] = {
00037 { "Email", "Email address",
00038 &ContactLdif::Email, &ContactLdif::SetEmail },
00039 { "Phone", "Phone number",
00040 &ContactLdif::Phone, &ContactLdif::SetPhone },
00041 { "Fax", "Fax number",
00042 &ContactLdif::Fax, &ContactLdif::SetFax },
00043 { "WorkPhone", "Work phone number",
00044 &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone },
00045 { "HomePhone", "Home phone number",
00046 &ContactLdif::HomePhone, &ContactLdif::SetHomePhone },
00047 { "MobilePhone", "Mobile phone number",
00048 &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone },
00049 { "Pager", "Pager number",
00050 &ContactLdif::Pager, &ContactLdif::SetPager },
00051 { "PIN", "PIN",
00052 &ContactLdif::PIN, &ContactLdif::SetPIN },
00053 { "FirstName", "First name",
00054 &ContactLdif::FirstName, &ContactLdif::SetFirstName },
00055 { "LastName", "Last name",
00056 &ContactLdif::LastName, &ContactLdif::SetLastName },
00057 { "Company", "Company name",
00058 &ContactLdif::Company, &ContactLdif::SetCompany },
00059 { "DefaultCommunicationsMethod", "Default communications method",
00060 &ContactLdif::DefaultCommunicationsMethod, &ContactLdif::SetDefaultCommunicationsMethod },
00061 { "Address1", "Address, line 1",
00062 &ContactLdif::Address1, &ContactLdif::SetAddress1 },
00063 { "Address2", "Address, line 2",
00064 &ContactLdif::Address2, &ContactLdif::SetAddress2 },
00065 { "Address3", "Address, line 3",
00066 &ContactLdif::Address3, &ContactLdif::SetAddress3 },
00067 { "City", "City",
00068 &ContactLdif::City, &ContactLdif::SetCity },
00069 { "Province", "Province / State",
00070 &ContactLdif::Province, &ContactLdif::SetProvince },
00071 { "PostalCode", "Postal / ZIP code",
00072 &ContactLdif::PostalCode, &ContactLdif::SetPostalCode },
00073 { "Country", "Country",
00074 &ContactLdif::Country, &ContactLdif::SetCountry },
00075 { "JobTitle", "Job Title",
00076 &ContactLdif::JobTitle, &ContactLdif::SetJobTitle },
00077 { "PublicKey", "Public key",
00078 &ContactLdif::PublicKey, &ContactLdif::SetPublicKey },
00079 { "Notes", "Notes",
00080 &ContactLdif::Notes, &ContactLdif::SetNotes },
00081 { "PostalAddress", "Mailing address (includes address lines, city, province, country, and postal code)",
00082 &ContactLdif::PostalAddress, &ContactLdif::SetPostalAddress },
00083 { "FullName", "First + Last names",
00084 &ContactLdif::FullName, &ContactLdif::SetFullName },
00085 { "FQDN", "Fully qualified domain name",
00086 &ContactLdif::FQDN, &ContactLdif::SetFQDN },
00087 { 0, 0, 0 }
00088 };
00089
00090
00091 bool ContactLdif::LdifAttribute::operator<(const LdifAttribute &other) const
00092 {
00093
00094 if( name == "dn" ) {
00095 if( other.name == "dn" )
00096 return false;
00097 return true;
00098 }
00099 else if( other.name == "dn" )
00100 return false;
00101
00102 return (order < other.order && name != other.name) ||
00103 (order == other.order && name < other.name);
00104 }
00105
00106 bool ContactLdif::LdifAttribute::operator==(const LdifAttribute &other) const
00107 {
00108 return name == other.name;
00109 }
00110
00111
00112
00113
00114
00115 ContactLdif::ContactLdif(const std::string &baseDN)
00116 : m_baseDN(baseDN)
00117 {
00118
00119 Map("mail", &ContactLdif::Email, &ContactLdif::SetEmail);
00120 Map("facsimileTelephoneNumber", &ContactLdif::Fax, &ContactLdif::SetFax);
00121 Map("telephoneNumber", &ContactLdif::WorkPhone, &ContactLdif::SetWorkPhone);
00122 Map("homePhone", &ContactLdif::HomePhone, &ContactLdif::SetHomePhone);
00123 Map("mobile", &ContactLdif::MobilePhone, &ContactLdif::SetMobilePhone);
00124 Map("pager", &ContactLdif::Pager, &ContactLdif::SetPager);
00125 Map("l", &ContactLdif::City, &ContactLdif::SetCity);
00126 Map("st", &ContactLdif::Province, &ContactLdif::SetProvince);
00127 Map("postalCode", &ContactLdif::PostalCode, &ContactLdif::SetPostalCode);
00128 Map("o", &ContactLdif::Company, &ContactLdif::SetCompany);
00129 Map("c", &ContactLdif::Country, &ContactLdif::SetCountry);
00130 SetObjectClass("c", "country");
00131
00132 Map("title", &ContactLdif::JobTitle, &ContactLdif::SetJobTitle);
00133 Map("dn", &ContactLdif::FQDN, &ContactLdif::SetFQDN);
00134 Map("displayName", &ContactLdif::FullName, &ContactLdif::SetFullName);
00135 Map("cn", &ContactLdif::FullName, &ContactLdif::SetFullName);
00136 Map("sn", &ContactLdif::LastName, &ContactLdif::SetLastName);
00137 Map("givenName", &ContactLdif::FirstName, &ContactLdif::SetFirstName);
00138 Map("street", &ContactLdif::Address1, &ContactLdif::SetAddress1);
00139 Map("postalAddress", &ContactLdif::PostalAddress, &ContactLdif::SetPostalAddress);
00140 Map("note", &ContactLdif::Notes, &ContactLdif::SetNotes);
00141
00142
00143 Hook("cn", &m_cn);
00144 Hook("displayName", &m_displayName);
00145 Hook("sn", &m_sn);
00146 Hook("givenName", &m_givenName);
00147
00148
00149 SetDNAttr("cn");
00150 }
00151
00152 ContactLdif::~ContactLdif()
00153 {
00154 }
00155
00156 void ContactLdif::DoWrite(Barry::Contact &con,
00157 const std::string &attr,
00158 const std::string &data)
00159 {
00160
00161 if( attr.size() == 0 || data.size() == 0 )
00162 return;
00163
00164
00165 HookMapType::iterator hook = m_hookMap.find(attr);
00166 if( hook != m_hookMap.end() ) {
00167 *(hook->second) = data;
00168 }
00169
00170
00171 AccessMapType::iterator acc = m_map.find(attr);
00172 if( acc != m_map.end() ) {
00173 (this->*(acc->second.write))(con, data);
00174 }
00175 }
00176
00177 void ContactLdif::Hook(const std::string &ldifname, std::string *var)
00178 {
00179 m_hookMap[ldifname] = var;
00180 }
00181
00182 const ContactLdif::NameToFunc*
00183 ContactLdif::GetField(const std::string &fieldname) const
00184 {
00185 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
00186 if( fieldname == n->name )
00187 return n;
00188 }
00189 return 0;
00190 }
00191
00192 std::string ContactLdif::GetFieldReadName(GetFunctionType read) const
00193 {
00194 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
00195 if( read == n->read )
00196 return n->name;
00197 }
00198 return "<unknown>";
00199 }
00200
00201 std::string ContactLdif::GetFieldWriteName(SetFunctionType write) const
00202 {
00203 for( const NameToFunc *n = FieldMap; n->name; n++ ) {
00204 if( write == n->write )
00205 return n->name;
00206 }
00207 return "<unknown>";
00208 }
00209
00210 bool ContactLdif::Map(const LdifAttribute &ldifname,
00211 const std::string &readField,
00212 const std::string &writeField)
00213 {
00214 const NameToFunc *read = GetField(readField);
00215 const NameToFunc *write = GetField(writeField);
00216 if( !read || !write )
00217 return false;
00218 Map(ldifname, read->read, write->write);
00219 return true;
00220 }
00221
00222 void ContactLdif::Map(const LdifAttribute &ldifname,
00223 GetFunctionType read,
00224 SetFunctionType write)
00225 {
00226 m_map[ldifname] = AccessPair(read, write);
00227 }
00228
00229 void ContactLdif::Unmap(const LdifAttribute &ldifname)
00230 {
00231 m_map.erase(ldifname);
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241 bool ContactLdif::SetDNAttr(const LdifAttribute &name)
00242 {
00243
00244 AccessMapType::iterator i = m_map.find(name);
00245 if( i == m_map.end() )
00246 return false;
00247
00248 m_dnAttr = name;
00249 return true;
00250 }
00251
00252 bool ContactLdif::SetObjectClass(const LdifAttribute &name,
00253 const std::string &objectClass)
00254 {
00255 AccessMapType::iterator i = m_map.find(name);
00256 if( i == m_map.end() )
00257 return false;
00258
00259 LdifAttribute key = i->first;
00260 AccessPair pair = i->second;
00261 m_map.erase(key);
00262 key.objectClass = objectClass;
00263 m_map[key] = pair;
00264 return true;
00265 }
00266
00267 bool ContactLdif::SetObjectOrder(const LdifAttribute &name, int order)
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.order = order;
00277 m_map[key] = pair;
00278 return true;
00279 }
00280
00281
00282 std::string ContactLdif::Email(const Barry::Contact &con) const
00283 {
00284 return con.GetEmail(m_emailIndex++);
00285 }
00286
00287 std::string ContactLdif::Phone(const Barry::Contact &con) const
00288 {
00289 return con.Phone;
00290 }
00291
00292 std::string ContactLdif::Fax(const Barry::Contact &con) const
00293 {
00294 return con.Fax;
00295 }
00296
00297 std::string ContactLdif::WorkPhone(const Barry::Contact &con) const
00298 {
00299 return con.WorkPhone;
00300 }
00301
00302 std::string ContactLdif::HomePhone(const Barry::Contact &con) const
00303 {
00304 return con.HomePhone;
00305 }
00306
00307 std::string ContactLdif::MobilePhone(const Barry::Contact &con) const
00308 {
00309 return con.MobilePhone;
00310 }
00311
00312 std::string ContactLdif::Pager(const Barry::Contact &con) const
00313 {
00314 return con.Pager;
00315 }
00316
00317 std::string ContactLdif::PIN(const Barry::Contact &con) const
00318 {
00319 return con.PIN;
00320 }
00321
00322 std::string ContactLdif::FirstName(const Barry::Contact &con) const
00323 {
00324 return con.FirstName;
00325 }
00326
00327 std::string ContactLdif::LastName(const Barry::Contact &con) const
00328 {
00329 return con.LastName;
00330 }
00331
00332 std::string ContactLdif::Company(const Barry::Contact &con) const
00333 {
00334 return con.Company;
00335 }
00336
00337 std::string ContactLdif::DefaultCommunicationsMethod(const Barry::Contact &con) const
00338 {
00339 return con.DefaultCommunicationsMethod;
00340 }
00341
00342 std::string ContactLdif::Address1(const Barry::Contact &con) const
00343 {
00344 return con.WorkAddress.Address1;
00345 }
00346
00347 std::string ContactLdif::Address2(const Barry::Contact &con) const
00348 {
00349 return con.WorkAddress.Address2;
00350 }
00351
00352 std::string ContactLdif::Address3(const Barry::Contact &con) const
00353 {
00354 return con.WorkAddress.Address3;
00355 }
00356
00357 std::string ContactLdif::City(const Barry::Contact &con) const
00358 {
00359 return con.WorkAddress.City;
00360 }
00361
00362 std::string ContactLdif::Province(const Barry::Contact &con) const
00363 {
00364 return con.WorkAddress.Province;
00365 }
00366
00367 std::string ContactLdif::PostalCode(const Barry::Contact &con) const
00368 {
00369 return con.WorkAddress.PostalCode;
00370 }
00371
00372 std::string ContactLdif::Country(const Barry::Contact &con) const
00373 {
00374 return con.WorkAddress.Country;
00375 }
00376
00377 std::string ContactLdif::JobTitle(const Barry::Contact &con) const
00378 {
00379 return con.JobTitle;
00380 }
00381
00382 std::string ContactLdif::PublicKey(const Barry::Contact &con) const
00383 {
00384 return con.PublicKey;
00385 }
00386
00387 std::string ContactLdif::Notes(const Barry::Contact &con) const
00388 {
00389 return con.Notes;
00390 }
00391
00392 std::string ContactLdif::PostalAddress(const Barry::Contact &con) const
00393 {
00394 return con.WorkAddress.GetLabel();
00395 }
00396
00397 std::string ContactLdif::FullName(const Barry::Contact &con) const
00398 {
00399 return con.GetFullName();
00400 }
00401
00402 std::string ContactLdif::FQDN(const Barry::Contact &con) const
00403 {
00404 std::string FQDN = m_dnAttr.name;
00405 FQDN += "=";
00406
00407 AccessMapType::const_iterator i = m_map.find(m_dnAttr);
00408 if( i != m_map.end() ) {
00409 FQDN += (this->*(i->second.read))(con);
00410 }
00411 else {
00412 FQDN += "unknown";
00413 }
00414
00415 FQDN += ",";
00416 FQDN += m_baseDN;
00417 return FQDN;
00418 }
00419
00420 bool ContactLdif::IsArrayFunc(GetFunctionType getf) const
00421 {
00422
00423 if( getf == &ContactLdif::Email )
00424 return true;
00425 return false;
00426 }
00427
00428 void ContactLdif::ClearArrayState() const
00429 {
00430 m_emailIndex = 0;
00431 }
00432
00433 void ContactLdif::SetEmail(Barry::Contact &con, const std::string &val) const
00434 {
00435 con.EmailAddresses.push_back(val);
00436 }
00437
00438 void ContactLdif::SetPhone(Barry::Contact &con, const std::string &val) const
00439 {
00440 con.Phone = val;
00441 }
00442
00443 void ContactLdif::SetFax(Barry::Contact &con, const std::string &val) const
00444 {
00445 con.Fax = val;
00446 }
00447
00448 void ContactLdif::SetWorkPhone(Barry::Contact &con, const std::string &val) const
00449 {
00450 con.WorkPhone = val;
00451 }
00452
00453 void ContactLdif::SetHomePhone(Barry::Contact &con, const std::string &val) const
00454 {
00455 con.HomePhone = val;
00456 }
00457
00458 void ContactLdif::SetMobilePhone(Barry::Contact &con, const std::string &val) const
00459 {
00460 con.MobilePhone = val;
00461 }
00462
00463 void ContactLdif::SetPager(Barry::Contact &con, const std::string &val) const
00464 {
00465 con.Pager = val;
00466 }
00467
00468 void ContactLdif::SetPIN(Barry::Contact &con, const std::string &val) const
00469 {
00470 con.PIN = val;
00471 }
00472
00473 void ContactLdif::SetFirstName(Barry::Contact &con, const std::string &val) const
00474 {
00475 con.FirstName = val;
00476 }
00477
00478 void ContactLdif::SetLastName(Barry::Contact &con, const std::string &val) const
00479 {
00480 con.LastName = val;
00481 }
00482
00483 void ContactLdif::SetCompany(Barry::Contact &con, const std::string &val) const
00484 {
00485 con.Company = val;
00486 }
00487
00488 void ContactLdif::SetDefaultCommunicationsMethod(Barry::Contact &con, const std::string &val) const
00489 {
00490 con.DefaultCommunicationsMethod = val;
00491 }
00492
00493 void ContactLdif::SetAddress1(Barry::Contact &con, const std::string &val) const
00494 {
00495 con.WorkAddress.Address1 = val;
00496 }
00497
00498 void ContactLdif::SetAddress2(Barry::Contact &con, const std::string &val) const
00499 {
00500 con.WorkAddress.Address2 = val;
00501 }
00502
00503 void ContactLdif::SetAddress3(Barry::Contact &con, const std::string &val) const
00504 {
00505 con.WorkAddress.Address3 = val;
00506 }
00507
00508 void ContactLdif::SetCity(Barry::Contact &con, const std::string &val) const
00509 {
00510 con.WorkAddress.City = val;
00511 }
00512
00513 void ContactLdif::SetProvince(Barry::Contact &con, const std::string &val) const
00514 {
00515 con.WorkAddress.Province = val;
00516 }
00517
00518 void ContactLdif::SetPostalCode(Barry::Contact &con, const std::string &val) const
00519 {
00520 con.WorkAddress.PostalCode = val;
00521 }
00522
00523 void ContactLdif::SetCountry(Barry::Contact &con, const std::string &val) const
00524 {
00525 con.WorkAddress.Country = val;
00526 }
00527
00528 void ContactLdif::SetJobTitle(Barry::Contact &con, const std::string &val) const
00529 {
00530 con.JobTitle = val;
00531 }
00532
00533 void ContactLdif::SetPublicKey(Barry::Contact &con, const std::string &val) const
00534 {
00535 con.PublicKey = val;
00536 }
00537
00538 void ContactLdif::SetNotes(Barry::Contact &con, const std::string &val) const
00539 {
00540 con.Notes = val;
00541 }
00542
00543 void ContactLdif::SetPostalAddress(Barry::Contact &con, const std::string &val) const
00544 {
00545
00546
00547
00548 }
00549
00550 void ContactLdif::SetFullName(Barry::Contact &con, const std::string &val) const
00551 {
00552 std::string first, last;
00553 Contact::SplitName(val, first, last);
00554 con.FirstName = first;
00555 con.LastName = last;
00556 }
00557
00558 void ContactLdif::SetFQDN(Barry::Contact &con, const std::string &val) const
00559 {
00560 throw std::runtime_error("not implemented");
00561 }
00562
00563
00564 void ContactLdif::ClearHeuristics()
00565 {
00566 m_cn.clear();
00567 m_displayName.clear();
00568 m_sn.clear();
00569 m_givenName.clear();
00570 }
00571
00572 bool ContactLdif::RunHeuristics(Barry::Contact &con)
00573 {
00574
00575 con.LastName.clear();
00576 con.FirstName.clear();
00577
00578
00579 if( m_sn.size() ) {
00580 con.LastName = m_sn;
00581 }
00582 if( m_givenName.size() ) {
00583 con.FirstName = m_givenName;
00584 }
00585
00586 if( !con.LastName.size() || !con.FirstName.size() ) {
00587 std::string first, last;
00588
00589
00590 if( m_cn.size() ) {
00591 Contact::SplitName(m_cn, first, last);
00592 if( !con.LastName.size() && last.size() )
00593 con.LastName = last;
00594 if( !con.FirstName.size() && first.size() )
00595 con.FirstName = first;
00596 }
00597
00598
00599 if( m_displayName.size() ) {
00600 Contact::SplitName(m_displayName, first, last);
00601 if( !con.LastName.size() && last.size() )
00602 con.LastName = last;
00603 if( !con.FirstName.size() && first.size() )
00604 con.FirstName = first;
00605 }
00606 }
00607
00608 return con.LastName.size() && con.FirstName.size();
00609 }
00610
00611
00612
00613
00614
00615
00616
00617 void ContactLdif::DumpLdif(std::ostream &os,
00618 const Barry::Contact &con) const
00619 {
00620
00621 ClearArrayState();
00622
00623
00624 std::ios::fmtflags oldflags = os.setf(std::ios::left);
00625 char fill = os.fill(' ');
00626
00627 if( FirstName(con).size() == 0 && LastName(con).size() == 0 )
00628 return;
00629
00630 os << "# Contact 0x" << std::hex << con.GetID() << ", "
00631 << FullName(con) << "\n";
00632
00633
00634 for( AccessMapType::const_iterator b = m_map.begin();
00635 b != m_map.end();
00636 ++b )
00637 {
00638
00639 std::string field;
00640
00641 do {
00642 field = (this->*(b->second.read))(con);
00643 if( field.size() ) {
00644 os << b->first.name << MakeLdifData(field) << "\n";
00645 if( b->first.objectClass.size() )
00646 os << "objectClass: " << b->first.objectClass << "\n";
00647 }
00648 } while( IsArrayFunc(b->second.read) && field.size() );
00649 }
00650
00651 os << "objectClass: inetOrgPerson\n";
00652
00653
00654 os << "\n";
00655
00656
00657 os.flags(oldflags);
00658 os.fill(fill);
00659 }
00660
00661 bool ContactLdif::ReadLdif(std::istream &is, Barry::Contact &con)
00662 {
00663 std::string line;
00664
00665
00666 con.Clear();
00667 ClearHeuristics();
00668
00669
00670 bool found = false;
00671 while( std::getline(is, line) ) {
00672 if( strncmp(line.c_str(), "dn: ", 4) == 0 ) {
00673 found = true;
00674 break;
00675 }
00676 }
00677 if( !found )
00678 return false;
00679
00680
00681 std::string coded, decode, attr, data;
00682 bool b64field = false;
00683
00684
00685 while( getline(is, line) && line.size() ) {
00686
00687 if( b64field ) {
00688
00689 if( line[0] == ' ' ) {
00690 coded += "\n";
00691 coded += line;
00692 continue;
00693 }
00694 else {
00695
00696
00697
00698
00699 base64_decode(coded, decode);
00700 DoWrite(con, attr, decode);
00701 coded.clear();
00702 b64field = false;
00703 }
00704
00705 }
00706
00707
00708
00709 std::string::size_type delim = line.find(':'), dstart;
00710 if( delim == std::string::npos )
00711 continue;
00712
00713 attr.assign(line, 0, delim);
00714 dstart = delim + 1;
00715 while( line[dstart] == ' ' || line[dstart] == ':' )
00716 dstart++;
00717 data = line.substr(dstart);
00718
00719
00720 if( line[delim + 1] == ':' ) {
00721 coded = data;
00722 b64field = true;
00723 continue;
00724 }
00725
00726 DoWrite(con, attr, data);
00727 }
00728
00729 if( b64field ) {
00730
00731 base64_decode(coded, decode);
00732 DoWrite(con, attr, decode);
00733 coded.clear();
00734 b64field = false;
00735 }
00736
00737 return RunHeuristics(con);
00738 }
00739
00740 void ContactLdif::DumpMap(std::ostream &os) const
00741 {
00742 std::ios::fmtflags oldflags = os.setf(std::ios::left);
00743 char fill = os.fill(' ');
00744
00745 os << "ContactLdif Mapping:\n";
00746
00747
00748 for( AccessMapType::const_iterator b = m_map.begin();
00749 b != m_map.end();
00750 ++b )
00751 {
00752 os << " " << std::left << std::setw(20) << b->first.name
00753 << "-> " << GetFieldReadName(b->second.read)
00754 << " / " << GetFieldWriteName(b->second.write) << "\n";
00755
00756
00757
00758 if( b->first.objectClass.size() ) {
00759 os << " " << std::setw(20) << " "
00760 << "objectClass: " << b->first.objectClass << "\n";
00761 }
00762 }
00763
00764 os << " >>> DN attribute: " << m_dnAttr.name << "\n";
00765
00766
00767 os.flags(oldflags);
00768 os.fill(fill);
00769 }
00770
00771 std::string ContactLdif::MakeLdifData(const std::string &str)
00772 {
00773 std::string data = ":";
00774
00775 if( NeedsEncoding(str) ) {
00776 std::string b64;
00777 base64_encode(str, b64);
00778
00779 data += ": ";
00780 data += b64;
00781 }
00782 else {
00783 data += " ";
00784 data += str;
00785 }
00786
00787 return data;
00788 }
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799 bool ContactLdif::NeedsEncoding(const std::string &str)
00800 {
00801 for( std::string::size_type i = 0; i < str.size(); i++ ) {
00802 unsigned char c = str[i];
00803
00804 switch( c )
00805 {
00806 case 0x00:
00807 case 0x0a:
00808 case 0x0d:
00809 return true;
00810
00811 case 0x20:
00812 case 0x3a:
00813 case 0x3c:
00814 if( i == 0 )
00815 return true;
00816 }
00817
00818 if( c > 0x7f )
00819 return true;
00820 }
00821 return false;
00822 }
00823
00824 }
00825