void ContactList::compareWith(ContactList &pairList) { for (int i=0; i<pairList.count(); i++) pairList[i].pairState = ContactItem::PairNotFound; for (int i=0; i<count(); i++) { ContactItem& item = (*this)[i]; item.pairState = ContactItem::PairNotFound; item.pairItem = 0; // At first, search complete matching for(int j=0; j<pairList.count(); j++) { ContactItem& candidate = pairList[j]; if (item.identicalTo(candidate)) { item.pairState = ContactItem::PairIdentical; item.pairItem = &candidate; item.pairIndex = j; candidate.pairItem = &item; candidate.pairState = ContactItem::PairIdentical; candidate.pairIndex = i; break; } } // If no identical records, search similar if (item.pairState==ContactItem::PairNotFound) for (int j=1; j<=MAX_COMPARE_PRIORITY_LEVEL; j++) { bool pairFound = false; for(int k=0; k<pairList.count(); k++) { ContactItem& candidate = pairList[k]; if (item.similarTo(candidate, j)) { pairFound = true; item.pairState = ContactItem::PairSimilar; item.pairItem = &candidate; item.pairIndex = k; candidate.pairItem = &item; candidate.pairState = ContactItem::PairSimilar; candidate.pairIndex = i; break; } } if (pairFound) break; } } }
bool UDXFile::exportRecords(const QString &url, ContactList &list) { QDomDocument::clear(); QDomElement root = createElement("DataExchangeInfo"); appendChild(root); // Original format also was UDX? bool wasUDX = false; if (!list.isEmpty()) if (list[0].originalFormat=="UDX") wasUDX = true; // XML header QDomNode node = createProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\""); insertBefore(node, firstChild()); // UDX header QDomElement recInfo = addElement(root, "RecordInfo"); addElement(recInfo, "VendorInfo", "VendorUDX"); addElement(recInfo, "DeviceInfo", "DeviceUDX"); if (wasUDX) addElement(recInfo, "UdxVersion", list[0].version); else addElement(recInfo, "UdxVersion", "1.0"); addElement(recInfo, "UserAgent", "AgentUDX"); addElement(recInfo, "UserInfo", "UserUDX"); addElement(recInfo, "Encoding", "UTF-8"); addElement(recInfo, "FileSize", " "); // strongly 10 spaces! (~~) addElement(recInfo, "Date", QDate::currentDate().toString("dd.MM.yyyy")); addElement(recInfo, "Language","CHS"); // wtf, but this code was in real russian-language udx QDomElement vcfInfo = addElement(recInfo, "RecordOfvCard"); if (wasUDX) addElement(vcfInfo, "vCardVersion", list[0].subVersion); else addElement(vcfInfo, "vCardVersion", "2.1"); addElement(vcfInfo, "vCardRecord", QString::number(list.count())); addElement(vcfInfo, "vCardLength", " "); // strongly 10 spaces! (~~) addElement(recInfo, "RecordOfvCalendar"); addElement(recInfo, "RecordOfSMS"); addElement(recInfo, "RecordOfMMS"); addElement(recInfo, "RecordOfEmail"); // Parent tag for all records QDomElement vCard = addElement(root, "vCard"); // Add missing sequences to records int maxSeq = 0; if (wasUDX) { // wasUDX - simply add missing foreach (const ContactItem& item, list) if (item.id.toInt()>maxSeq) maxSeq = item.id.toInt(); QSet<int> seqs; for (int i=0; i<list.count(); i++) { ContactItem& item = list[i]; int currentID = item.id.toInt(); if (currentID<1) // simply missing item.id = QString::number(++maxSeq); if (seqs.contains(currentID)) { // duplicate? _errors << QObject::tr("Warning: contact %1, duplicate id %2 changed to %3") .arg(item.visibleName).arg(currentID).arg(++maxSeq); item.id = QString::number(maxSeq); } seqs.insert(item.id.toInt()); } } else { // if original wasn't UDX, completely renumerate all for (int i=0; i<list.count(); i++)
bool UDXFile::importRecords(const QString &url, ContactList &list, bool append) { if (!openFile(url, QIODevice::ReadOnly)) return false; // Read XML QString err_msg; int err_line, err_col; if (!setContent(&file, &err_msg, &err_line, &err_col)) { _errors << QObject::tr("Can't read content from file %1\n%2\nline %3, col %4\n") .arg(url).arg(err_msg).arg(err_line).arg(err_col); closeFile(); return false; } closeFile(); // Root element QDomElement root = documentElement(); if (root.nodeName()!="DataExchangeInfo") { _errors << QObject::tr("Root node is not 'DataExchangeInfo' at file\n%1").arg(url); return false; } // Codepage, version, expected record count QDomElement recInfo = root.firstChildElement("RecordInfo"); if (recInfo.isNull()) { _errors << QObject::tr("Can't find 'RecordInfo' tag at file\n%1").arg(url); return false; } QString charSet = recInfo.firstChildElement("Encoding").text(); if (charSet.isEmpty()) { _errors << QObject::tr("Warning: codepage not found, trying use UTF-8..."); charSet = "UTF-8"; } QString udxVer = recInfo.firstChildElement("UdxVersion").text(); if (udxVer.isEmpty()) { _errors << QObject::tr("Warning: udx version not found, treat as 1.0..."); udxVer = "1.0"; } QDomElement vcfInfo = recInfo.firstChildElement("RecordOfvCard"); QString vcVer = vcfInfo.firstChildElement("vCardVersion").text(); int expCount = vcfInfo.firstChildElement("vCardRecord").text().toInt(); // vCard set QDomElement vCard = root.firstChildElement("vCard"); if (vCard.isNull()) { _errors << QObject::tr("Can't find 'vCard' records at file\n%1").arg(url); return false; } // QTextCodec* codec = QTextCodec::codecForName(charSet.toLocal8Bit()); TODO not works on windows ContactItem item; if (!append) list.clear(); QDomElement vCardInfo = vCard.firstChildElement("vCardInfo"); while (!vCardInfo.isNull()) { item.clear(); item.originalFormat = "UDX"; item.version = udxVer; item.subVersion = vcVer; item.id = vCardInfo.firstChildElement("Sequence").text(); QDomElement fields = vCardInfo.firstChildElement("vCardField"); if (fields.isNull()) _errors << QObject::tr("Can't find 'vCardField' at sequence %1").arg(item.id); QDomElement field = fields.firstChildElement(); while (!field.isNull()) { QString fldName = field.nodeName().toUpper(); QString fldValue = field.text(); // codec->toUnicode(field.text().toLocal8Bit()); TODO not works on windows if (fldName=="N") { fldValue.replace("\\;", " "); // In ALL known me udx files part before first ; was EMPTY fldValue.remove(";"); item.names = fldValue.split(" "); // If empty parts not in-middle, remove it item.dropFinalEmptyNames(); } else if (fldName.startsWith("TEL")) { Phone phone; phone.number = fldValue; if (fldName=="TEL") phone.tTypes << "CELL"; else if (fldName=="TELHOME") phone.tTypes << "HOME"; else if (fldName=="TELWORK") phone.tTypes << "WORK"; else if (fldName=="TELFAX") phone.tTypes << "FAX"; else _errors << QObject::tr("Unknown phone type: %1 (%2)").arg(phone.number).arg(item.names[0]); item.phones.push_back(phone); } else if (fldName=="ORGNAME") item.organization = fldValue; else if (fldName=="BDAY") item.birthday.value = QDateTime::fromString(fldValue, "yyyyMMdd"); // TODO Maybe, use DateItem::fromString else if (fldName=="EMAIL") { Email email; email.address = fldValue; email.emTypes << "pref"; item.emails.push_back(email); } else _errors << QObject::tr("Unknown 'vCardfield' type: %1").arg(fldName); field = field.nextSiblingElement(); } item.calculateFields(); list.push_back(item); vCardInfo = vCardInfo.nextSiblingElement(); } if (list.count()!=expCount) _errors << QObject::tr("%1 records read, %2 expected").arg(list.count()).arg(expCount); // Unknown tags statistics int totalUnknownTags = 0; foreach (const ContactItem& _item, list) totalUnknownTags += _item.unknownTags.count(); if (totalUnknownTags) _errors << QObject::tr("%1 unknown tags found").arg(totalUnknownTags); // Ready return (!list.isEmpty()); }
// Main program loop int Convertor::start() { out << tr("Contact convertor by Mikhail Y. Zvyozdochkin\n"); // Parse command line if (arguments().count()<2) { printUsage(); return 1; } QString inPath, outPath, outFormat; bool forceOverwrite = false; bool forceSingleFile = false; bool forceDirectory = false; for (int i=1; i<arguments().count(); i++) { if (arguments()[i]=="-i") { i++; if (i==arguments().count()) { out << tr("linvert error: -i option present, but file path is missing\n"); printUsage(); return 2; } inPath = arguments()[i]; continue; } else if (arguments()[i]=="-o") { i++; if (i==arguments().count()) { out << tr("linvert error: -o option present, but file path is missing\n"); printUsage(); return 3; } outPath = arguments()[i]; continue; } else if (arguments()[i]=="-f") { i++; if (i==arguments().count()) { out << tr("linvert error: -f option present, but format name is missing\n"); printUsage(); return 4; } outFormat = arguments()[i]; if (outFormat!="vcf21" && outFormat!="vcf30" && outFormat!="vcfauto" && outFormat!="udx" && outFormat!="copy") { out << tr("Unknown output format: %1\n").arg(outFormat); printUsage(); return 5; } continue; } else if (arguments()[i]=="-w") forceOverwrite = true; else if (arguments()[i]=="-s") forceSingleFile = true; else if (arguments()[i]=="-d") forceDirectory = true; else { out << tr("Unknown option: %1\n").arg(arguments()[i]); printUsage(); return 6; } } // Check input data completion if (inPath.isEmpty()) { out << tr("Input path is missing\n"); printUsage(); return 7; } if (outPath.isEmpty()) { out << tr("Output path is missing\n"); printUsage(); return 8; } if (outFormat.isEmpty()) { out << tr("Output format name is missing\n"); printUsage(); return 9; } if (forceSingleFile && forceDirectory) { out << tr("Options -s and -d are not compatible\n"); printUsage(); return 10; } if (forceDirectory && !outFormat.contains("vcf") && outFormat!="copy") { out << tr("-d option applicable only for vCard format"); return 11; } // Check if output file exists QFile of(outPath); if (of.exists() && !forceOverwrite && !QFileInfo(outPath).isDir()) { out << tr("Output file already exists, use -w if necessary\n"); printUsage(); return 12; } // Define, create file or directory at output // (default: as input) FormatType ift = QFileInfo(inPath).isDir() ? ftDirectory : ftFile; FormatType oft = ift; if (forceSingleFile) oft = ftFile; else if (forceDirectory) oft = ftDirectory; // Read IFormat* iFormat = 0; FormatFactory factory; if (ift==ftFile) iFormat = factory.createObject(inPath); else iFormat = new VCFDirectory(); if (!iFormat) { out << factory.error << "\n"; return 13; } ContactList items; bool res = iFormat->importRecords(inPath, items, false); logFormat(iFormat); delete iFormat; if (!res) return 14; out << tr("%1 records read\n").arg(items.count()); //Define output format gd.preferredVCFVersion = GlobalConfig::VCF21; IFormat* oFormat = 0; if (oft==ftDirectory) oFormat = new VCFDirectory(); else if (outFormat.contains("vcf")) { if (outFormat=="vcf30") gd.preferredVCFVersion = GlobalConfig::VCF30; gd.useOriginalFileVersion = (outFormat=="vcfauto"); oFormat = new VCFFile(); } else if (outFormat.contains("udx")) oFormat = new UDXFile(); else { // copy input format if (VCFFile::detect(inPath)) { gd.useOriginalFileVersion = true; oFormat = new VCFFile(); } else if (UDXFile::detect(inPath)) oFormat = new UDXFile(); // else if (MPBFile::detect(inPath)) { TODO m.b. implement? else if (inPath.contains(".mpb", Qt::CaseInsensitive)) { // temp hack out << "MPB format is read-only and incompatible with 'copy' option. Use VCF or UDX for output file\n"; return 15; } else { out << "Can't autodetect input format\n"; return 16; } } // Write res = oFormat->exportRecords(outPath, items); logFormat(oFormat); delete oFormat; out << "Output file successfully written\n"; return res ? 0 : 17; }