void VObject::toNativeEncoding() { bool is_30 = !wcscmp(getVersion(), TEXT("3.0")); // line break is encoded with either one or two // characters on different platforms const int linebreaklen = wcslen(SYNC4J_LINEBREAK); for (int index = propertiesCount() - 1; index >= 0; index--) { VProperty *vprop = getProperty(index); WCHAR *foreign = vprop->getValue(); // the native encoding is always shorter than the foreign one WCHAR *native = new WCHAR[wcslen(foreign) + 1]; if (vprop->equalsEncoding(TEXT("QUOTED-PRINTABLE"))) { int in = 0, out = 0; WCHAR curr; // this is a very crude quoted-printable decoder, // based on Wikipedia's explanation of quoted-printable while ((curr = foreign[in]) != 0) { in++; if (curr == '=') { WCHAR values[2]; values[0] = foreign[in]; in++; if (!values[0]) { // incomplete?! break; } values[1] = foreign[in]; in++; if (values[0] == '\r' && values[1] == '\n') { // soft line break, ignore it } else { native[out] = (hex2int(values[0]) << 4) | hex2int(values[1]); out++; // replace \r\n with \n? if ( linebreaklen == 1 && out >= 2 && native[out - 2] == '\r' && native[out - 1] == '\n' ) { native[out - 2] = SYNC4J_LINEBREAK[0]; out--; } // the conversion to wchar on Windows is // probably missing here } } else { native[out] = curr; out++; } } native[out] = 0; out++; } else { wcscpy(native, foreign); } // decode escaped characters after backslash: // \n is line break only in 3.0 WCHAR curr; int in = 0, out = 0; while ((curr = native[in]) != 0) { in++; switch (curr) { case '\\': curr = native[in]; in++; switch (curr) { case 'n': if (is_30) { // replace with line break wcsncpy(native + out, SYNC4J_LINEBREAK, linebreaklen); out += linebreaklen; } else { // normal escaped character native[out] = curr; out++; } break; case 0: // unexpected end of string break; default: // just copy next character native[out] = curr; out++; break; } break; case ';': // field separator - must replace with something special // so that we can encode it again in fromNativeEncoding() native[out] = SEMICOLON_REPLACEMENT; out++; break; default: native[out] = curr; out++; } } native[out] = 0; out++; // charset handling: // - doesn't exist at the moment, vCards have to be in ASCII or UTF-8 // - an explicit CHARSET parameter is removed because its parameter // value might differ between 2.1 and 3.0 (quotation marks allowed in // 3.0 but not 2.1) and thus would require extra code to convert it; // when charsets really get supported this needs to be addressed WCHAR *charset = vprop->getParameterValue(TEXT("CHARSET")); if (charset) { // proper decoding of the value and the property value text // would go here, for the time being we just remove the // value if (_wcsicmp(charset, TEXT("UTF-8")) && _wcsicmp(charset, TEXT("\"UTF-8\""))) { LOG.error("ignoring unsupported charset"); } vprop->removeParameter(TEXT("CHARSET")); } vprop->setValue(native); delete [] native; } }
void VObject::fromNativeEncoding() { bool is_30 = !wcscmp(getVersion(), TEXT("3.0")); for (int index = propertiesCount() - 1; index >= 0; index--) { VProperty *vprop = getProperty(index); if (vprop->equalsEncoding(TEXT("QUOTED-PRINTABLE"))) { // remove this, we cannot recreate it vprop->removeParameter(TEXT("ENCODING")); } WCHAR *native = vprop->getValue(); // in the worst case every comma/linebreak is replaced with // two characters and each \n with =0D=0A WCHAR *foreign = new WCHAR[6 * wcslen(native) + 1]; WCHAR curr; int in = 0, out = 0; // line break is encoded with either one or two // characters on different platforms const int linebreaklen = wcslen(SYNC4J_LINEBREAK); // use backslash for special characters, // if necessary do quoted-printable encoding bool doquoted = !is_30 && wcsstr(native, SYNC4J_LINEBREAK) != NULL; while ((curr = native[in]) != 0) { in++; switch (curr) { case ',': if (!is_30) { // normal character foreign[out] = curr; out++; break; } // no break! case ';': case '\\': foreign[out] = '\\'; out++; foreign[out] = curr; out++; break; case SEMICOLON_REPLACEMENT: foreign[out] = ';'; out++; break; default: if (doquoted && (curr == '=' || (unsigned char)curr >= 128)) { // escape = and non-ASCII characters swprintf(foreign + out, 4, TEXT("=%02X"), (unsigned int)(unsigned char)curr); out += 3; } else if (!wcsncmp(native + in - 1, SYNC4J_LINEBREAK, linebreaklen)) { // line break if (is_30) { foreign[out] = '\\'; out++; foreign[out] = 'n'; out++; } else { wcscpy(foreign + out, TEXT("=0D=0A")); out += 6; } in += linebreaklen - 1; } else { foreign[out] = curr; out++; } break; } } foreign[out] = 0; vprop->setValue(foreign); delete [] foreign; if (doquoted) { // we have used quoted-printable encoding vprop->addParameter(TEXT("ENCODING"), TEXT("QUOTED-PRINTABLE")); } } }
// // Parse a vCard string and fills the propertyMap. // int WinContact::parse(const wstring dataString) { int businessTel = 0; int homeTel = 0; int imAddress = 0; WCHAR* name = NULL; WCHAR* element = NULL; // // Parse the vCard and fill the VObject. // ------------------------------------- // VObject* vo = VConverter::parse(dataString.c_str()); if (!vo) { //sprintf(lastErrorMsg, ERR_ITEM_VOBJ_PARSE); setError(1, ERR_ITEM_VOBJ_PARSE); LOG.error("%s", getLastErrorMsg()); return 1; } // Check if VObject type and version are the correct ones. if (!checkVCardTypeAndVersion(vo)) { if (vo) delete vo; return 1; } // // Conversion: vObject -> WinContact. // ---------------------------------- // Note: only properties found are added to the propertyMap, so that the // map will contain only properties effectively received. // propertyMap.clear(); for(int i=0; i < vo->propertiesCount(); i++) { VProperty* vp = vo->getProperty(i); name = vp->getName(); element = vp->getValue(0); // the first value of the property. if(!wcscmp(name, L"N")) { if(element = vp->getPropComponent(1)) setProperty(L"LastName", element); if(element = vp->getPropComponent(2)) setProperty(L"FirstName", element); if(element = vp->getPropComponent(3)) setProperty(L"MiddleName", element); if(element = vp->getPropComponent(4)) setProperty(L"Title", element); if(element = vp->getPropComponent(5)) setProperty(L"Suffix", element); } else if(!wcscmp(name, L"BDAY")) { setProperty(L"Birthday", element); } else if(!wcscmp(name, L"NOTE")) { setProperty(L"Body", element); } else if(!wcscmp(name, L"CLASS")) { WCHAR tmp[10]; if( !wcscmp(element, TEXT("PRIVATE" )) || !wcscmp(element, TEXT("CONFIDENTIAL")) ) { wsprintf(tmp, TEXT("%i"), winPrivate); // Private = 2 } else { wsprintf(tmp, TEXT("%i"), winNormal); // Normal = 0 } setProperty(L"Sensitivity", tmp); } else if(!wcscmp(name, L"CATEGORIES")) { setProperty(L"Categories", element); } else if(!wcscmp(name, L"EMAIL")) { // Mapping is: // Email1Address <-> EMAIL;INTERNET: // Email2Address <-> EMAIL;INTERNET;HOME: // Email3Address <-> EMAIL;INTERNET;WORK: if (vp->containsParameter(L"HOME")) { if (vp->containsParameter(L"X-FUNAMBOL-INSTANTMESSENGER")) { if (imAddress == 0) { setProperty(L"IMAddress", element); imAddress ++; } else if (imAddress == 1) { setProperty(L"IM2Address", element); imAddress ++; } else { setProperty(L"IM3Address", element); } } else { setProperty(L"Email2Address", element); // TODO: check the parameter value (not used now, always SMTP) if (wcslen(element) > 0) { setProperty(L"Email2AddressType", L"SMTP"); } else { setProperty(L"Email2AddressType", L""); } } } else if (vp->containsParameter(L"WORK")) { setProperty(L"Email3Address", element); // TODO: check the parameter value (not used now, always SMTP) if (wcslen(element) > 0) { setProperty(L"Email3AddressType", L"SMTP"); } else { setProperty(L"Email3AddressType", L""); } } else { setProperty(L"Email1Address", element); // TODO: check the parameter value (not used now, always SMTP) if (wcslen(element) > 0) { setProperty(L"Email1AddressType", L"SMTP"); } else { setProperty(L"Email1AddressType", L""); } } } else if(!wcscmp(name, L"FN")) { setProperty(L"FileAs", element); } else if(!wcscmp(name, L"NICKNAME")) { setProperty(L"NickName", element); } else if(!wcscmp(name, L"PRIORITY")) { setProperty(L"Importance", element); } else if(!wcscmp(name, L"ORG")) { if(element = vp->getPropComponent(1)) setProperty(L"CompanyName", element); if(element = vp->getPropComponent(2)) setProperty(L"Department", element); if(element = vp->getPropComponent(3)) setProperty(L"OfficeLocation", element); } else if(!wcscmp(name, L"ROLE")) { setProperty(L"Profession", element); } // // ---- Telephone fields ---- // else if(!wcscmp(name, L"TEL")) { if(vp->containsParameter(L"WORK")) { if (vp->containsParameter(L"FAX")) { setProperty(L"BusinessFaxNumber", element); } else if (vp->containsParameter(L"PREF")) { setProperty(L"CompanyMainTelephoneNumber", element); } else { // default, would be "else if (vp->containsParameter(L"VOICE"))" if(businessTel == 0) { setProperty(L"BusinessTelephoneNumber", element); businessTel++; } else{ setProperty(L"Business2TelephoneNumber", element); } } } else if (vp->containsParameter(L"CAR")) { setProperty(L"CarTelephoneNumber", element); } else if(vp->containsParameter(L"HOME")) { if(vp->containsParameter(L"FAX")) { setProperty(L"HomeFaxNumber", element); } else { // default, would be "else if (vp->containsParameter(L"VOICE"))" if(homeTel == 0) { setProperty(L"HomeTelephoneNumber", element); homeTel++; } else { setProperty(L"Home2TelephoneNumber", element); } } } else if (vp->containsParameter(L"CELL")) { setProperty(L"MobileTelephoneNumber", element); } else if (vp->containsParameter(L"FAX")) { setProperty(L"OtherFaxNumber", element); } else if(vp->containsParameter(L"PAGER")) { setProperty(L"PagerNumber", element); } else if (vp->containsParameter(L"PREF")) { setProperty(L"PrimaryTelephoneNumber", element); } else if(vp->containsParameter(L"X-FUNAMBOL-CALLBACK")) { setProperty(L"CallbackTelephoneNumber", element); } else if(vp->containsParameter(L"X-FUNAMBOL-RADIO")) { setProperty(L"RadioTelephoneNumber", element); } else if(vp->containsParameter(L"X-FUNAMBOL-TELEX")) { setProperty(L"TelexNumber", element); } else { // default, would be "else if (vp->containsParameter(L"VOICE"))" setProperty(L"OtherTelephoneNumber", element); } } else if(!wcscmp(name, L"TITLE")) { setProperty(L"JobTitle", element); } else if(!wcscmp(name, L"PHOTO")) { setProperty(L"Photo", element); // Photo type is ignored } // // ---- URL fields ---- // else if(!wcscmp(name, L"URL")) { if (vp->containsParameter(L"WORK")) { setProperty(L"BusinessWebPage", element); } else if(vp->containsParameter(L"HOME")) { setProperty(L"HomeWebPage", element); } else { setProperty(L"WebPage", element); } } // // ---- ADR fields ---- // else if(!wcscmp(name, L"ADR")) { if(vp->containsParameter(L"HOME")) { if(element = vp->getPropComponent(1)) setProperty(L"HomeAddressPostOfficeBox", element); if(element = vp->getPropComponent(2)) setProperty(L"HomeAddressExtended", element); if(element = vp->getPropComponent(3)) setProperty(L"HomeAddressStreet", element); if(element = vp->getPropComponent(4)) setProperty(L"HomeAddressCity", element); if(element = vp->getPropComponent(5)) setProperty(L"HomeAddressState", element); if(element = vp->getPropComponent(6)) setProperty(L"HomeAddressPostalCode", element); if(element = vp->getPropComponent(7)) setProperty(L"HomeAddressCountry", element); } else if(vp->containsParameter(L"WORK")) { if(element = vp->getPropComponent(1)) setProperty(L"BusinessAddressPostOfficeBox", element); if(element = vp->getPropComponent(2)) setProperty(L"BusinessAddressExtended", element); if(element = vp->getPropComponent(3)) setProperty(L"BusinessAddressStreet", element); if(element = vp->getPropComponent(4)) setProperty(L"BusinessAddressCity", element); if(element = vp->getPropComponent(5)) setProperty(L"BusinessAddressState", element); if(element = vp->getPropComponent(6)) setProperty(L"BusinessAddressPostalCode", element); if(element = vp->getPropComponent(7)) setProperty(L"BusinessAddressCountry", element); } else { if(element = vp->getPropComponent(1)) setProperty(L"OtherAddressPostOfficeBox", element); if(element = vp->getPropComponent(2)) setProperty(L"OtherAddressExtended", element); if(element = vp->getPropComponent(3)) setProperty(L"OtherAddressStreet", element); if(element = vp->getPropComponent(4)) setProperty(L"OtherAddressCity", element); if(element = vp->getPropComponent(5)) setProperty(L"OtherAddressState", element); if(element = vp->getPropComponent(6)) setProperty(L"OtherAddressPostalCode", element); if(element = vp->getPropComponent(7)) setProperty(L"OtherAddressCountry", element); } } // // ---- Funambol defined properties ---- // else if(!wcscmp(name, L"X-ANNIVERSARY")) { setProperty(L"Anniversary", element); } else if(!wcscmp(name, L"X-FUNAMBOL-BILLINGINFO")) { setProperty(L"BillingInformation", element); } else if(!wcscmp(name, L"X-FUNAMBOL-CHILDREN")) { setProperty(L"Children", element); } else if(!wcscmp(name, L"X-FUNAMBOL-COMPANIES")) { setProperty(L"Companies", element); } else if(!wcscmp(name, L"X-FUNAMBOL-CUSTOMERID")) { setProperty(L"CustomerID", element); } else if(!wcscmp(name, L"X-FUNAMBOL-FOLDER")) { setProperty(L"Folder", element); } else if(!wcscmp(name, L"X-FUNAMBOL-GENDER")) { setProperty(L"Gender", element); } else if(!wcscmp(name, L"X-FUNAMBOL-GOVERNMENTID")) { setProperty(L"GovernmentIDNumber", element); } else if(!wcscmp(name, L"X-FUNAMBOL-HOBBIES")) { setProperty(L"Hobby", element); } else if(!wcscmp(name, L"X-FUNAMBOL-INITIALS")) { setProperty(L"Initials", element); } else if(!wcscmp(name, L"X-FUNAMBOL-LANGUAGES")) { setProperty(L"Language", element); } else if(!wcscmp(name, L"X-MANAGER")) { setProperty(L"ManagerName", element); } else if(!wcscmp(name, L"X-FUNAMBOL-MILEAGE")) { setProperty(L"Mileage", element); } else if(!wcscmp(name, L"X-FUNAMBOL-ORGANIZATIONALID")) { setProperty(L"OrganizationalIDNumber", element); } else if(!wcscmp(name, L"X-SPOUSE")) { setProperty(L"Spouse", element); } else if(!wcscmp(name, L"X-FUNAMBOL-SUBJECT")) { setProperty(L"Subject", element); } else if(!wcscmp(name, L"X-FUNAMBOL-TELEX")) { setProperty(L"TelexNumber", element); } else if(!wcscmp(name, L"X-FUNAMBOL-YOMICOMPANYNAME")) { setProperty(L"YomiCompanyName", element); } else if(!wcscmp(name, L"X-FUNAMBOL-YOMIFIRSTNAME")) { setProperty(L"YomiFirstName", element); } else if(!wcscmp(name, L"X-FUNAMBOL-YOMILASTNAME")) { setProperty(L"YomiLastName", element); } else { // Property not supported: log a warning? } } if (vo) { delete vo; vo = NULL; } return 0; }