Esempio n. 1
0
void tst_QVersitWriter::testByteArrayOutput()
{
    const QByteArray vCard30(
        "BEGIN:VCARD\r\n"
        "VERSION:3.0\r\n"
        "FN:John\r\n"
        "END:VCARD\r\n");

    delete mWriter; // we don't want the init()ed writer.

    QByteArray output;
    mWriter = new QVersitWriter(&output);

    QVERIFY(mWriter->device() == 0);

    QVersitDocument document(QVersitDocument::VCard30Type);
    document.setComponentType(QStringLiteral("VCARD"));
    QVersitProperty property;
    property.setName(QString(QStringLiteral("FN")));
    property.setValue(QStringLiteral("John"));
    document.addProperty(property);
    QVERIFY2(mWriter->startWriting(document), QString::number(mWriter->error()).toLatin1().data());
    QVERIFY2(mWriter->waitForFinished(), QString::number(mWriter->error()).toLatin1().data());
    QCOMPARE(output, vCard30);
}
Esempio n. 2
0
void tst_QVersitDocument::testEquality()
{
    QVersitDocument document1;
    QVersitDocument document2;
    QVERIFY(document1.isEmpty());
    QVERIFY(document1 == document2);
    QVERIFY(!(document1 != document2));
    QVersitProperty property;
    property.setName(QLatin1String("FN"));
    property.setValue(QLatin1String("John Citizen"));
    document2.addProperty(property);
    QVERIFY(!(document1 == document2));
    QVERIFY(document1 != document2);
    QVERIFY(!document2.isEmpty());

    document1.addProperty(property);
    QVERIFY(document1 == document2);
    QVERIFY(!(document1 != document2));

    document2.clear();
    QVERIFY(document2.isEmpty());

    document1.clear();
    QVERIFY(document1 == document2);
    QVERIFY(!(document1 != document2));

    document2.setType(QVersitDocument::VCard21Type);
    QVERIFY(!(document1 == document2));
    QVERIFY(document1 != document2);
    QVERIFY(!document2.isEmpty());

    document2 = document1;
    QVERIFY(document1 == document2);
}
Esempio n. 3
0
void CntVersitPrefPlugin::propertyProcessed(
    const QVersitDocument& document,
    const QVersitProperty& property,
    const QContact& contact,
    bool* alreadyProcessed,
    QList<QContactDetail>* updatedDetails)
{
    Q_UNUSED(document);
    Q_UNUSED(contact);
    
    if (*alreadyProcessed && !updatedDetails->isEmpty())
    {
        QStringList typeParameters = property.parameters().values(QLatin1String("TYPE"));
        if (typeParameters.contains(QLatin1String("PREF"), Qt::CaseInsensitive))
        {
            if ((mDetailMappings.value(property.name()) == QContactPhoneNumber::DefinitionName) ||
                (mDetailMappings.value(property.name()) == QContactEmailAddress::DefinitionName) ||
                (mDetailMappings.value(property.name()) == QContactOnlineAccount::DefinitionName) ||
                (mDetailMappings.value(property.name()) == QContactUrl::DefinitionName))
            {
                // This method is called before the corresponding detail gets imported to QContact.
                // setPreferredDetail() cannot be called here -> detail is stored and will be set
                // preferred after whole versit document is processed
                mPrefDetailList.append(updatedDetails->last()); 
            }
        }
    }
}
Esempio n. 4
0
void MT_CntVersitMyCardPlugin::exportOwnContact()
{
    //create contact
    QContact phonecontact;
    QContactName contactName;
    contactName.setFirstName("Jo");
    contactName.setLastName("Black");
    phonecontact.saveDetail(&contactName);
    QContactPhoneNumber number;
    number.setContexts("Home");
    number.setSubTypes("Mobile");
    number.setNumber("+02644424429");
    phonecontact.saveDetail(&number);
    
    //set MyCard detail
    QContactDetail myCard(MYCARD_DEFINTION_NAME);
    phonecontact.saveDetail(&myCard);
    
    //export
    QList<QContact> list;
    list.append(phonecontact);
    QVersitContactExporter exporter;
    QVERIFY(exporter.exportContacts(list, QVersitDocument::VCard21Type));
    QList<QVersitDocument> documents = exporter.documents();
    
    //X-SELF property is exported if MyCard
    QVersitDocument document  = documents.first();
    QVersitProperty property;
    property.setName(QLatin1String("X-SELF"));
    property.setValue("0");
    bool propertyFound = document.properties().contains(property);
    QVERIFY(propertyFound);
}
Esempio n. 5
0
void tst_QVersitDocument::testRemoveProperty()
{
    // Remove an empty property.
    QCOMPARE(mVersitDocument->properties().count(), 0);
    QVersitProperty property;
    mVersitDocument->addProperty(property);
    mVersitDocument->removeProperty(property);
    QCOMPARE(mVersitDocument->properties().count(), 0);

    // A full property.
    property.setName(QLatin1String("TEL"));
    property.setGroups(QStringList(QLatin1String("HOME")));
    QMultiHash<QString, QString> params;
    params.insert(QLatin1String("TYPE"), QLatin1String("HOME"));
    property.setParameters(params);
    property.setValue(QLatin1String("123"));
    mVersitDocument->addProperty(property);
    QCOMPARE(mVersitDocument->properties().count(), 1);
    QVersitProperty property2;
    property2.setName(QLatin1String("TEL"));
    // Remove with a partial property fails.
    mVersitDocument->removeProperty(property2);
    QCOMPARE(mVersitDocument->properties().count(), 1);
    property2.setGroups(QStringList(QLatin1String("HOME")));
    property2.setParameters(params);
    property2.setValue(QLatin1String("123"));
    // Remove with a fully specified property succeeds.
    mVersitDocument->removeProperty(property2);
    QCOMPARE(mVersitDocument->properties().count(), 0);
}
void SeasidePropertyHandler::propertyProcessed(const QVersitDocument &, const QVersitProperty &property, const QContact &, bool *alreadyProcessed, QList<QContactDetail> * updatedDetails)
{
    if (property.name().toLower() == QLatin1String("photo")) {
        processPhoto(property, alreadyProcessed, updatedDetails);
    } else if (property.name().toLower() == QLatin1String("x-nemomobile-onlineaccount-demo")) {
        processOnlineAccount(property, alreadyProcessed, updatedDetails);
    }
}
Esempio n. 7
0
/*!
 * Encodes the groups and name in the \a property and writes it to the device
 */
void QVersitDocumentWriter::encodeGroupsAndName(const QVersitProperty& property)
{
    QStringList groups = property.groups();
    if (!groups.isEmpty()) {
        writeString(groups.join(QStringLiteral(".")));
        writeString(QStringLiteral("."));
    }
    writeString(property.name());
}
Esempio n. 8
0
 void contactProcessed(const QContact& contact,
                       QVersitDocument* document)
 {
     Q_UNUSED(contact)
     QVersitProperty property;
     property.setName("TEST-PROPERTY");
     property.setValue("3");
     document->addProperty(property);
 }
void tst_QVersitProperty::testEmbeddedDocument()
{
    QVersitDocument document;
    QVersitProperty property;
    property.setName(QLatin1String("X-tension"));
    document.addProperty(property);
    mVersitProperty->setValue(QVariant::fromValue(document));
    QList<QVersitProperty> embeddedDocumentProperties =
        mVersitProperty->value<QVersitDocument>().properties();
    QCOMPARE(embeddedDocumentProperties.count(),1);
    QCOMPARE(embeddedDocumentProperties[0].name(),QLatin1String("X-TENSION"));
}
Esempio n. 10
0
/*! Returns the hash value for \a key. */
uint qHash(const QVersitProperty &key)
{
    uint hash = QT_PREPEND_NAMESPACE(qHash)(key.name()) + QT_PREPEND_NAMESPACE(qHash)(key.value());
    foreach (const QString& group, key.groups()) {
        hash += QT_PREPEND_NAMESPACE(qHash)(group);
    }
    QHash<QString,QString>::const_iterator it = key.parameters().constBegin();
    QHash<QString,QString>::const_iterator end = key.parameters().constEnd();
    while (it != end) {
        hash += QT_PREPEND_NAMESPACE(qHash)(it.key()) + QT_PREPEND_NAMESPACE(qHash)(it.value());
        ++it;
    }
    return hash;
}
Esempio n. 11
0
 foreach(const QVersitProperty& expectedProperty, expectedProperties) {
     QList<QVersitProperty> actualProperties =
         findPropertiesByName(subDocuments.first(), expectedProperty.name());
     if (!actualProperties.contains(expectedProperty)) {
         qDebug() << "Actual:" << actualProperties;
         qDebug() << "Expected to find:" << expectedProperty;
         QVERIFY(false);
     }
 }
Esempio n. 12
0
/*!
 * Encodes the \a property and writes it to the device.
 */
void QVCard30Writer::encodeVersitProperty(const QVersitProperty& property)
{
    QVersitProperty modifiedProperty(property);
    QString name = mPropertyNameMappings.value(property.name(),property.name());
    modifiedProperty.setName(name);
    encodeGroupsAndName(modifiedProperty);

    QVariant variant(modifiedProperty.variantValue());
    if (variant.type() == QVariant::ByteArray) {
        modifiedProperty.insertParameter(QLatin1String("ENCODING"), QLatin1String("b"));
    }
    encodeParameters(modifiedProperty.parameters());
    writeString(QLatin1String(":"));

    QString renderedValue;
    QByteArray renderedBytes;
    if (variant.canConvert<QVersitDocument>()) {
        QVersitDocument embeddedDocument = variant.value<QVersitDocument>();
        QByteArray data;
        QBuffer buffer(&data);
        buffer.open(QIODevice::WriteOnly);
        QVCard30Writer subWriter(mType);
        subWriter.setCodec(mCodec);
        subWriter.setDevice(&buffer);
        subWriter.encodeVersitDocument(embeddedDocument);
        QString documentString(mCodec->toUnicode(data));
        backSlashEscape(&documentString);
        renderedValue = documentString;
    } else if (variant.type() == QVariant::String) {
        renderedValue = variant.toString();
        if (property.valueType() != QVersitProperty::PreformattedType) {
            backSlashEscape(&renderedValue);
        }
    } else if (variant.type() == QVariant::StringList) {
        // We need to backslash escape and concatenate the values in the list
        QStringList values = property.variantValue().toStringList();
        QString separator;
        if (property.valueType() == QVersitProperty::CompoundType) {
            separator = QLatin1String(";");
        } else {
            if (property.valueType() != QVersitProperty::ListType) {
                qWarning("Variant value is a QStringList but the property's value type is neither "
                         "CompoundType or ListType");
            }
            // Assume it's a ListType
            separator = QLatin1String(",");
        }
        bool first = true;
        foreach (QString value, values) {
            if (!(value.isEmpty() && property.valueType() == QVersitProperty::ListType)) {
                if (!first) {
                    renderedValue += separator;
                }
                backSlashEscape(&value);
                renderedValue += value;
                first = false;
            }
        }
    } else if (variant.type() == QVariant::ByteArray) {
Esempio n. 13
0
bool QVCardRestoreHandler::propertyProcessed(
        const QVersitProperty& property,
        QList<QContactDetail>* updatedDetails)
{
    bool success = false;
    QString group;
    if (!property.groups().isEmpty())
        group = property.groups().first();
    if (property.name() == PropertyName) {
        if (property.groups().size() != 1)
            return false;
        QMultiHash<QString, QString> parameters = property.parameters();
        QContactDetail::DetailType detailType = QContactDetail::DetailType(parameters.value(DetailTypeParameter).toUInt());
        QString fieldName = parameters.value(FieldParameter);
        // Find a detail previously seen with the same definitionName, which was generated from
        // a property from the same group
        QContactDetail detail(detailType);
        foreach (const QContactDetail& previousDetail, mDetailGroupMap.detailsInGroup(group)) {
            if (previousDetail.type() == detailType) {
                detail = previousDetail;
            }
        }
        // If not found, it's a new empty detail with the definitionName set.

        detail.setValue(fieldName.toInt(), deserializeValue(property));

        // Replace the equivalent detail in updatedDetails with the new one
        QMutableListIterator<QContactDetail> it(*updatedDetails);
        while (it.hasNext()) {
            if (it.next().key() == detail.key()) {
                it.remove();
                break;
            }
        }
        updatedDetails->append(detail);
        success = true;
    }
    if (!group.isEmpty()) {
        // Keep track of which details were generated from which Versit groups
        foreach (const QContactDetail& detail, *updatedDetails) {
            mDetailGroupMap.insert(group, detail);
        }
    }
Esempio n. 14
0
void tst_QVersitWriter::testWritingVersions()
{
    mWriter->setDevice(mOutputDevice);
    mOutputDevice->open(QBuffer::ReadWrite);

    QVersitDocument document;
    QVersitProperty property;
    property.setName(QString(QString::fromLatin1("FN")));
    property.setValue(QString::fromLatin1("John"));
    document.addProperty(property);

    QByteArray vCard30(
        "BEGIN:VCARD\r\n"
        "VERSION:3.0\r\n"
        "FN:John\r\n"
        "END:VCARD\r\n");
    QByteArray vCard21(
        "BEGIN:VCARD\r\n"
        "VERSION:2.1\r\n"
        "FN:John\r\n"
        "END:VCARD\r\n");

    // Given no type or componentType, it should be vCard 3.0
    QVERIFY(mWriter->startWriting(document));
    mWriter->waitForFinished();
    QCOMPARE(mOutputDevice->buffer(), vCard30);

    // document type should override the guess
    document.setType(QVersitDocument::VCard21Type);
    mOutputDevice->buffer().clear();
    mOutputDevice->seek(0);
    QVERIFY(mWriter->startWriting(document));
    mWriter->waitForFinished();
    QCOMPARE(mOutputDevice->buffer(), vCard21);

    // param to startWriting should override document type
    mOutputDevice->buffer().clear();
    mOutputDevice->seek(0);
    QVERIFY(mWriter->startWriting(document, QVersitDocument::VCard30Type));
    mWriter->waitForFinished();
    QCOMPARE(mOutputDevice->buffer(), vCard30);
}
Esempio n. 15
0
void tst_QVersitContactPlugins::testImporterPlugins() {
    QVersitContactImporter importer("Test");
    QVersitDocument document;
    document.setComponentType("VCARD");
    QVersitProperty property;
    property.setName("FN");
    property.setValue("Bob");
    document.addProperty(property);
    QVERIFY(importer.importDocuments(QList<QVersitDocument>() << document));
    QCOMPARE(importer.contacts().size(), 1);
    QList<QContactDetail> details(importer.contacts().first().details("TestDetail"));
    QCOMPARE(details.size(), 5);
    // The plugins have had their index set such that they should be executed in reverse order
    // Check that they are all loaded, and run in the correct order
    QCOMPARE(details.at(0).value<int>("Plugin"), 5);
    QCOMPARE(details.at(1).value<int>("Plugin"), 4);
    QCOMPARE(details.at(2).value<int>("Plugin"), 3);
    QCOMPARE(details.at(3).value<int>("Plugin"), 2);
    QCOMPARE(details.at(4).value<int>("Plugin"), 1);
}
Esempio n. 16
0
QDebug operator<<(QDebug dbg, const QVersitProperty& property)
{
    QStringList groups = property.groups();
    QString name = property.name();
    QMultiHash<QString,QString> parameters = property.parameters();
    dbg.nospace() << "QVersitProperty(";
    foreach (const QString& group, groups) {
        dbg.nospace() << group << '.';
    }
    dbg.nospace() << name;
    QHash<QString,QString>::const_iterator it;
    for (it = parameters.constBegin(); it != parameters.constEnd(); ++it) {
        dbg.nospace() << ';' << it.key() << '=' << it.value();
    }
    if (property.valueType() == QVersitProperty::VersitDocumentType)
        dbg.nospace() << ':' << property.value<QVersitDocument>();
    else
        dbg.nospace() << ':' << property.variantValue();
    dbg.nospace() << ')';
    return dbg.maybeSpace();
}
void CntVersitFavoritePlugin::propertyProcessed(
    const QVersitDocument& document,
    const QVersitProperty& property,
    const QContact& contact,
    bool* alreadyProcessed,
    QList<QContactDetail>* updatedDetails)
{
    Q_UNUSED(document);
    Q_UNUSED(contact);
    Q_UNUSED(alreadyProcessed);
    Q_UNUSED(updatedDetails);
    
    if (property.name().contains(QLatin1String("X-FAVORITE"), Qt::CaseInsensitive)) {
        // This method is called before the corresponding detail gets imported to QContact.
        // Detail is saved after whole versit document is processed.
        QContactFavorite favorite;
        favorite.setFavorite(true);
        favorite.setIndex(property.value().toInt());
        mFavoriteDetailList.append(favorite);
    }
}
Esempio n. 18
0
/*!
  Called for each processed detail from QVersitContactExporter during 
  contact export. 
 */
void CntVersitMyCardPlugin::detailProcessed(
    const QContact& contact,
    const QContactDetail& detail,
    const QVersitDocument& document,
    QSet<QString>* processedFields,
    QList<QVersitProperty>* toBeRemoved,
    QList<QVersitProperty>* toBeAdded)
{
    Q_UNUSED(contact);
    Q_UNUSED(processedFields);
    Q_UNUSED(document);
    Q_UNUSED(toBeRemoved);

    if (detail.definitionName() == MYCARD_DEFINITION_NAME) {
        QVersitProperty property;
        property.setName(QLatin1String("X-SELF"));
        property.setValue("0");
        toBeAdded->append(property);
    }
    // QVersitContactExporter takes care of adding the toBeAdded properties
    // to the versit document.
}
Esempio n. 19
0
void tst_QVCard21Writer::testEncodeGroupsAndName()
{
    QVersitProperty property;
    QByteArray result;
    QBuffer buffer(&result);
    mWriter->setDevice(&buffer);
    buffer.open(QIODevice::WriteOnly);

    // No groups

    property.setName(QString::fromLatin1("name"));
    QByteArray expected("NAME");
    mWriter->encodeGroupsAndName(property);
    QCOMPARE(result, expected);

    // One group
    mWriter->writeCrlf(); // so it doesn't start folding
    buffer.close();
    result.clear();
    buffer.open(QIODevice::WriteOnly);
    property.setGroups(QStringList(QString::fromLatin1("group")));
    expected = "group.NAME";
    mWriter->encodeGroupsAndName(property);
    QCOMPARE(result, expected);

    // Two groups
    mWriter->writeCrlf(); // so it doesn't start folding
    buffer.close();
    result.clear();
    buffer.open(QIODevice::WriteOnly);
    QStringList groups(QString::fromLatin1("group1"));
    groups.append(QString::fromLatin1("group2"));
    property.setGroups(groups);
    expected = "group1.group2.NAME";
    mWriter->encodeGroupsAndName(property);
    QCOMPARE(result, expected);
}
Esempio n. 20
0
void tst_QVersitDocument::testRemoveAllProperties()
{
    QString name(QLatin1String("FN"));

    // Try to remove from an empty document
    QCOMPARE(0, mVersitDocument->properties().count());
    mVersitDocument->removeProperties(name);
    QCOMPARE(0, mVersitDocument->properties().count());

    // Try to remove from a non-empty document, name does not match
    QVersitProperty property;
    property.setName(QLatin1String("TEL"));
    mVersitDocument->addProperty(property);
    QCOMPARE(1, mVersitDocument->properties().count());
    mVersitDocument->removeProperties(name);
    QCOMPARE(1, mVersitDocument->properties().count());

    // Remove from a non-empty document, name matches
    mVersitDocument->removeProperties(QLatin1String("TEL"));
    QCOMPARE(0, mVersitDocument->properties().count());

    // Remove from a document with two properties, first matches
    property.setName(name);
    mVersitDocument->addProperty(property);
    property.setName(QLatin1String("TEL"));
    mVersitDocument->addProperty(property);
    QCOMPARE(2, mVersitDocument->properties().count());
    mVersitDocument->removeProperties(name);
    QCOMPARE(1, mVersitDocument->properties().count());

    // Remove from a document with two properties, second matches
    property.setName(name);
    mVersitDocument->addProperty(property);
    QCOMPARE(2, mVersitDocument->properties().count());
    mVersitDocument->removeProperties(name);
    QCOMPARE(1, mVersitDocument->properties().count());
}
void importExample()
{
    //! [Import example]
    QVersitContactImporter importer;

    QVersitDocument document;

    QVersitProperty property;
    property.setName(QString::fromAscii("N"));
    property.setValue("Citizen;John;Q;;");
    document.addProperty(property);

    property.setName(QString::fromAscii("X-UNKNOWN-PROPERTY"));
    property.setValue("some value");
    document.addProperty(property);

    if (importer.importDocuments(QList<QVersitDocument>() << document)) {
        QList<QContact> contactList = importer.contacts();
        // contactList.first() now contains the "N" property as a QContactName
        // propertyHandler.mUnknownProperties contains the list of unknown properties
    }

    //! [Import example]
}
Esempio n. 22
0
/*!
   Called for each processed versit property from QVersitContactImporter
   during contact import. 
 */
void CntVersitMyCardPlugin::propertyProcessed(
    const QVersitDocument& document,
    const QVersitProperty& property,
    const QContact& contact,
    bool* alreadyProcessed,
    QList<QContactDetail>* updatedDetails)
{
    Q_UNUSED(document);
    Q_UNUSED(contact);
    Q_UNUSED(updatedDetails);
    Q_UNUSED(contact);
    Q_UNUSED(alreadyProcessed);    
    if (property.name() == QLatin1String("X-SELF")) {
        mIsMyCard = true;
    }
}
Esempio n. 23
0
/*!
 * Encodes the \a property and writes it to the device.
 */
void QVCard21Writer::encodeVersitProperty(const QVersitProperty& property)
{
    encodeGroupsAndName(property);
    QMultiHash<QString,QString> parameters = property.parameters();
    QVariant variant(property.variantValue());

    QString renderedValue;
    QByteArray renderedBytes;

    /* Structured values need to have their components backslash-escaped (in vCard 2.1, semicolons
       must be escaped for compound values and commas must be escaped for list values). */
    if (variant.type() == QVariant::StringList) {
        QStringList values = property.variantValue().toStringList();
        QString separator;
        if (property.valueType() == QVersitProperty::CompoundType) {
            separator = QLatin1String(";");
        } else {
            if (property.valueType() != QVersitProperty::ListType) {
                qWarning("Variant value is a QStringList but the property's value type is neither "
                         "CompoundType or ListType");
            }
            // Assume it's a ListType
            separator = QLatin1String(",");
        }
        QString replacement = QLatin1Char('\\') + separator;
        QRegExp separatorRegex = QRegExp(separator);

        // Check first if any of the values need to be UTF-8 encoded (if so, all of them must be
        // UTF-8 encoded)
        bool forceUtf8 = requiresUtf8(values);

        bool first = true;
        foreach (QString value, values) {
            if (!(value.isEmpty() && property.valueType() == QVersitProperty::ListType)) {
                encodeVersitValue(parameters, value, forceUtf8);
                if (!first) {
                    renderedValue += separator;
                }
                renderedValue += value.replace(separatorRegex, replacement);
                first = false;
            }
        }
    } else if (variant.type() == QVariant::String) {
Esempio n. 24
0
void tst_QVCard30Writer::testEncodeVersitProperty_data()
{
    QTest::addColumn<QVersitProperty>("property");
    QTest::addColumn<QByteArray>("expectedResult");

    QVersitProperty property;
    QByteArray expectedResult;

    // No parameters
    expectedResult = "FN:John Citizen\r\n";
    property.setName(QString::fromAscii("FN"));
    property.setValue(QString::fromAscii("John Citizen"));
    QTest::newRow("No parameters") << property << expectedResult;

    // With parameter(s)
    expectedResult = "TEL;TYPE=HOME:123\r\n";
    property.setName(QString::fromAscii("TEL"));
    property.setValue(QString::fromAscii("123"));
    property.insertParameter(QString::fromAscii("TYPE"),QString::fromAscii("HOME"));
    QTest::newRow("With parameters, plain value") << property << expectedResult;

    // normal FN property is backslash escaped
    property.clear();
    property.setName(QLatin1String("FN"));
    property.setValue(QLatin1String(";,:\\"));
    // semicolons, commas and backslashes are escaped (not colons, as per RFC2426)
    expectedResult = "FN:\\;\\,:\\\\\r\n";
    QTest::newRow("FN property") << property << expectedResult;

    // Structured N
    property.setName(QLatin1String("N"));
    property.setValue(QStringList()
                      << QLatin1String("La;st")    // needs to be backslash escaped
                      << QLatin1String("Fi,rst")
                      << QLatin1String("Mi:ddle")
                      << QLatin1String("Pr\\efix") // needs to be QP encoded
                      << QLatin1String("Suffix"));
    property.setValueType(QVersitProperty::CompoundType);
    expectedResult = "N:La\\;st;Fi\\,rst;Mi:ddle;Pr\\\\efix;Suffix\r\n";
    QTest::newRow("N property") << property << expectedResult;

    // Structured CATEGORIES
    property.setName(QLatin1String("CATEGORIES"));
    property.setValue(QStringList()
                      << QLatin1String("re;d")
                      << QLatin1String("gr,een")
                      << QLatin1String("bl:ue")
                      << QLatin1String("ye\\llow"));
    property.setValueType(QVersitProperty::ListType);
    expectedResult = "CATEGORIES:re\\;d,gr\\,een,bl:ue,ye\\\\llow\r\n";
    QTest::newRow("CATEGORIES property") << property << expectedResult;

    // Convert X-NICKNAME to NICKNAME
    expectedResult = "NICKNAME:Jack\r\n";
    property.setParameters(QMultiHash<QString,QString>());
    property.setName(QString::fromAscii("X-NICKNAME"));
    property.setValue(QString::fromAscii("Jack"));
    QTest::newRow("NICKNAME property") << property << expectedResult;

    // Convert X-IMPP to IMPP;
    expectedResult = "IMPP:msn:msn-address\r\n";
    property.setParameters(QMultiHash<QString,QString>());
    property.setName(QString::fromAscii("X-IMPP"));
    property.setValue(QString::fromAscii("msn:msn-address"));
    QTest::newRow("IMPP property") << property << expectedResult;

    // AGENT property
    expectedResult = "AGENT:BEGIN:VCARD\\nVERSION:3.0\\nFN:Secret Agent\\nEND:VCARD\\n\r\n";
    property.setName(QString::fromAscii("AGENT"));
    property.setValue(QString());
    QVersitDocument document(QVersitDocument::VCard30Type);
    document.setComponentType(QLatin1String("VCARD"));
    QVersitProperty embeddedProperty;
    embeddedProperty.setName(QString(QString::fromAscii("FN")));
    embeddedProperty.setValue(QString::fromAscii("Secret Agent"));
    document.addProperty(embeddedProperty);
    property.setValue(QVariant::fromValue(document));
    QTest::newRow("AGENT property") << property << expectedResult;

    // Value is base64 encoded.
    QByteArray value("value");
    expectedResult = "Springfield.HOUSE.PHOTO;ENCODING=b:" + value.toBase64() + "\r\n";
    QStringList groups(QString::fromAscii("Springfield"));
    groups.append(QString::fromAscii("HOUSE"));
    property.setGroups(groups);
    property.setParameters(QMultiHash<QString,QString>());
    property.setName(QString::fromAscii("PHOTO"));
    property.setValue(value);
    QTest::newRow("base64 encoded") << property << expectedResult;

    // Characters other than ASCII:
    expectedResult = "ORG:" + KATAKANA_NOKIA.toUtf8() + "\r\n";
    property = QVersitProperty();
    property.setName(QLatin1String("ORG"));
    property.setValue(KATAKANA_NOKIA);
    QTest::newRow("non-ASCII") << property << expectedResult;

    // No CHARSET and QUOTED-PRINTABLE parameters
    expectedResult = "EMAIL:john@" + KATAKANA_NOKIA.toUtf8() + ".com\r\n";
    property = QVersitProperty();
    property.setName(QLatin1String("EMAIL"));
    property.setValue(QString::fromAscii("john@%1.com").arg(KATAKANA_NOKIA));
    QTest::newRow("special chars") << property << expectedResult;
}
Esempio n. 25
0
void tst_QVersitWriter::testWritingDocument_data()
{
    QTest::addColumn<QVersitDocument>("document");
    QTest::addColumn<QByteArray>("expected");

    QVersitDocument document(QVersitDocument::VCard21Type);
    document.setComponentType(QStringLiteral("VCARD"));
    QVersitProperty property;
    property.setName(QStringLiteral("FN"));
    property.setValue(QStringLiteral("Bob"));
    document.addProperty(property);
    QTest::newRow("basic vCard 2.1") << document << QByteArray(
            "BEGIN:VCARD\r\n"
            "VERSION:2.1\r\n"
            "FN:Bob\r\n"
            "END:VCARD\r\n"
            );

    document.setComponentType(QStringLiteral("VCARD"));
    document.setType(QVersitDocument::VCard30Type);
    QTest::newRow("basic vCard 3.0") << document << QByteArray(
            "BEGIN:VCARD\r\n"
            "VERSION:3.0\r\n"
            "FN:Bob\r\n"
            "END:VCARD\r\n"
            );

    document.setComponentType(QStringLiteral("VCARD"));
    document.setType(QVersitDocument::VCard40Type);
    QTest::newRow("basic vCard 4.0") << document << QByteArray(
            "BEGIN:VCARD\r\n"
            "VERSION:4.0\r\n"
            "FN:Bob\r\n"
            "END:VCARD\r\n"
            );
    
    {
        QVersitDocument document(QVersitDocument::ICalendar20Type);
        document.setComponentType(QStringLiteral("VCALENDAR"));
        QVersitDocument subdocument(QVersitDocument::ICalendar20Type);
        subdocument.setComponentType(QStringLiteral("VEVENT"));
        property.setValueType(QVersitProperty::PreformattedType);
        property.setName(QStringLiteral("RRULE"));
        property.setValue(QStringLiteral("FREQ=MONTHLY;BYMONTHDAY=1,3"));
        subdocument.addProperty(property);
        document.addSubDocument(subdocument);
        QTest::newRow("basic iCalendar 2.0") << document << QByteArray(
                "BEGIN:VCALENDAR\r\n"
                "VERSION:2.0\r\n"
                "BEGIN:VEVENT\r\n"
                "RRULE:FREQ=MONTHLY;BYMONTHDAY=1,3\r\n"
                "END:VEVENT\r\n"
                "END:VCALENDAR\r\n");
    }

    {
        QVersitDocument document(QVersitDocument::ICalendar20Type);
        document.setComponentType(QStringLiteral("VCALENDAR"));
        QVersitProperty property;
        property.setName(QStringLiteral("PRODID"));
        property.setValue(QStringLiteral("-//hacksw/handcal//NONSGML v1.0//EN"));
        document.addProperty(property);
        QVersitDocument nested(QVersitDocument::ICalendar20Type);
        nested.setComponentType(QStringLiteral("VEVENT"));
        property.setName(QStringLiteral("DTSTART"));
        property.setValue(QStringLiteral("19970714T170000Z"));
        nested.addProperty(property);
        property.setName(QStringLiteral("DTEND"));
        property.setValue(QStringLiteral("19970715T035959Z"));
        nested.addProperty(property);
        property.setName(QStringLiteral("SUMMARY"));
        property.setValue(QStringLiteral("Bastille Day Party"));
        nested.addProperty(property);
        document.addSubDocument(nested);
        QTest::newRow("iCalendar 2.0 from spec") << document << QByteArray(
                        "BEGIN:VCALENDAR\r\n"
                        "VERSION:2.0\r\n"
                        "PRODID:-//hacksw/handcal//NONSGML v1.0//EN\r\n"
                        "BEGIN:VEVENT\r\n"
                        "DTSTART:19970714T170000Z\r\n"
                        "DTEND:19970715T035959Z\r\n"
                        "SUMMARY:Bastille Day Party\r\n"
                        "END:VEVENT\r\n"
                        "END:VCALENDAR\r\n");
    }

    {
        QString longString(QLatin1String(
            "4567890123456789012345678901234567890123456789012345678901234567890123456"
            "234567890123456789012345678901234567890123456789012345678901234567890123456"
            "234567890123456789012"));
        QVersitDocument document(QVersitDocument::VCard21Type);
        document.setComponentType(QStringLiteral("VCARD"));
        QVersitProperty property;
        property.setName(QStringLiteral("FN"));
        property.setValue(longString);
        document.addProperty(property);
        QByteArray expected21(
                "BEGIN:VCARD\r\n"
                "VERSION:2.1\r\n"
                "FN:4567890123456789012345678901234567890123456789012345678901234567890123456\r\n"
                " 234567890123456789012345678901234567890123456789012345678901234567890123456\r\n"
                " 234567890123456789012\r\n"
                "END:VCARD\r\n");
        QTest::newRow("folding 2.1") << document << expected21;

        document.setType(QVersitDocument::VCard30Type);
        QByteArray expected30(
                "BEGIN:VCARD\r\n"
                "VERSION:3.0\r\n"
                "FN:4567890123456789012345678901234567890123456789012345678901234567890123456\r\n"
                " 234567890123456789012345678901234567890123456789012345678901234567890123456\r\n"
                " 234567890123456789012\r\n"
                "END:VCARD\r\n");
        QTest::newRow("folding 3.0") << document << expected30;

        document.setType(QVersitDocument::VCard21Type);
        property.setValue(longString.toLatin1());
        property.setValueType(QVersitProperty::BinaryType);
        document.removeProperties("FN");
        document.addProperty(property);
        QByteArray expected21b(
                "BEGIN:VCARD\r\n"
                "VERSION:2.1\r\n"
                "FN;ENCODING=BASE64:\r\n"
                " NDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODk\r\n"
                " wMTIzNDU2Nzg5MDEyMzQ1NjIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MD\r\n"
                " EyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1NjIzNDU2Nzg5MDEyMzQ1Njc4OTAxM\r\n"
                " g==\r\n"
                "\r\n"
                "END:VCARD\r\n");
        QTest::newRow("folding 2.1") << document << expected21b;

        document.setType(QVersitDocument::VCard30Type);
        QByteArray expected30b(
                "BEGIN:VCARD\r\n"
                "VERSION:3.0\r\n"
                "FN;ENCODING=b:NDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OT\r\n"
                " AxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1NjIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwM\r\n"
                " TIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1NjIzNDU2Nzg5MDEy\r\n"
                " MzQ1Njc4OTAxMg==\r\n"
                "END:VCARD\r\n");
        QTest::newRow("folding 3.0") << document << expected30b;
    }
}
Esempio n. 26
0
void tst_QVCard21Writer::testEncodeVersitProperty_data()
{
    QTest::addColumn<QVersitProperty>("property");
    QTest::addColumn<QByteArray>("expectedResult");
    QTest::addColumn<QByteArray>("codec");

    QVersitProperty property;
    QByteArray expectedResult;
    QByteArray codec("ISO-8859_1");

    // normal case
    property.setName(QString::fromLatin1("FN"));
    property.setValue(QString::fromLatin1("John Citizen"));
    property.setValueType(QVersitProperty::PlainType);
    expectedResult = "FN:John Citizen\r\n";
    QTest::newRow("No parameters") << property << expectedResult << codec;

    // Structured N - escaping should happen for semicolons, not for commas
    property.setName(QStringLiteral("N"));
    property.setValue(QStringList()
                      << QStringLiteral("La;st")    // needs to be backslash escaped
                      << QStringLiteral("Fi,rst")
                      << QStringLiteral("Mi:ddle")
                      << QStringLiteral("Pr\\efix") // needs to be QP encoded
                      << QStringLiteral("Suffix"));
    property.setValueType(QVersitProperty::CompoundType);
    expectedResult = "N;ENCODING=QUOTED-PRINTABLE:La\\;st;Fi,rst;Mi:ddle;Pr=5Cefix;Suffix\r\n";
    QTest::newRow("N property") << property << expectedResult << codec;

    // Structured N - there was a bug where if two fields had to be escaped,
    // two ENCODING parameters were added
    property.setName(QStringLiteral("N"));
    property.setValue(QStringList()
                      << QStringLiteral("La\\st")
                      << QStringLiteral("Fi\\rst")
                      << QString()
                      << QString()
                      << QString());
    property.setValueType(QVersitProperty::CompoundType);
    expectedResult = "N;ENCODING=QUOTED-PRINTABLE:La=5Cst;Fi=5Crst;;;\r\n";
    QTest::newRow("N property, double-encoded") << property << expectedResult << codec;

    // Structured N - one field needs to be encoded in UTF-8 while the other doesn't
    // correct behaviour is to encode the whole thing in UTF-8
    property.setName(QStringLiteral("N"));
    property.setValue(QStringList()
                      << QString::fromUtf8("\xE2\x82\xAC") // euro sign
                      << QString::fromLatin1("\xA3") // pound sign (upper Latin-1)
                      << QString()
                      << QString()
                      << QString());
    property.setValueType(QVersitProperty::CompoundType);
    expectedResult = "N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E2=82=AC;=C2=A3;;;\r\n";
    QTest::newRow("N property, double-encoded") << property << expectedResult << codec;

    // Structured CATEGORIES - escaping should happen for commas, not semicolons
    property.setName(QStringLiteral("CATEGORIES"));
    property.setValue(QStringList()
                      << QStringLiteral("re;d")
                      << QStringLiteral("gr,een")
                      << QStringLiteral("bl:ue"));
    property.setValueType(QVersitProperty::ListType);
    expectedResult = "CATEGORIES:re;d,gr\\,een,bl:ue\r\n";
    QTest::newRow("CATEGORIES property") << property << expectedResult << codec;

    // With parameter(s). No special characters in the value.
    // -> No need to Quoted-Printable encode the value.
    expectedResult = "TEL;HOME:123\r\n";
    property.setName(QString::fromLatin1("TEL"));
    property.setValue(QString::fromLatin1("123"));
    property.insertParameter(QString::fromLatin1("TYPE"),QString::fromLatin1("HOME"));
    QTest::newRow("With parameters, plain value") << property << expectedResult << codec;

    expectedResult = "EMAIL;HOME;ENCODING=QUOTED-PRINTABLE:john.citizen=40example.com\r\n";
    property.setName(QString::fromLatin1("EMAIL"));
    property.setValue(QString::fromLatin1("*****@*****.**"));
    QTest::newRow("With parameters, special value") << property << expectedResult << codec;

    // AGENT property with parameter
    expectedResult =
"AGENT;X-PARAMETER=VALUE:\r\n\
BEGIN:VCARD\r\n\
VERSION:2.1\r\n\
FN:Secret Agent\r\n\
END:VCARD\r\n\
\r\n";
    property.setParameters(QMultiHash<QString,QString>());
    property.setName(QString::fromLatin1("AGENT"));
    property.setValue(QString());
    property.insertParameter(QString::fromLatin1("X-PARAMETER"),QString::fromLatin1("VALUE"));
    QVersitDocument document(QVersitDocument::VCard21Type);
    document.setComponentType(QStringLiteral("VCARD"));
    QVersitProperty embeddedProperty;
    embeddedProperty.setName(QString(QString::fromLatin1("FN")));
    embeddedProperty.setValue(QString::fromLatin1("Secret Agent"));
    document.addProperty(embeddedProperty);
    property.setValue(QVariant::fromValue(document));
    QTest::newRow("AGENT property") << property << expectedResult << codec;

    // Value is base64 encoded.
    // Check that the extra folding and the line break are added
    QByteArray value("value");
    expectedResult = "Springfield.HOUSE.PHOTO;ENCODING=BASE64:\r\n " + value.toBase64() + "\r\n\r\n";
    QStringList groups(QString::fromLatin1("Springfield"));
    groups.append(QString::fromLatin1("HOUSE"));
    property.setGroups(groups);
    property.setParameters(QMultiHash<QString,QString>());
    property.setName(QString::fromLatin1("PHOTO"));
    property.setValue(value);
    QTest::newRow("base64 encoded") << property << expectedResult << codec;

    // Characters other than ASCII:
    // Note: KATAKANA_NOKIA is defined as: QString::fromUtf8("\xe3\x83\x8e\xe3\x82\xad\xe3\x82\xa2")
    // The expected behaviour is to convert to UTF8, then encode with quoted-printable.
    // Because the result overflows one line, it should be split onto two lines using a
    // quoted-printable soft line break (EQUALS-CR-LF).  (Note: Versit soft line breaks
    // (CR-LF-SPACE) are not supported by the native Symbian vCard importers).
    expectedResult = "ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E3=83=8E=E3=82=AD=E3=82=A2=E3=\r\n"
                     "=83=8E=E3=82=AD=E3=82=A2\r\n";
    property = QVersitProperty();
    property.setName(QStringLiteral("ORG"));
    property.setValue(KATAKANA_NOKIA + KATAKANA_NOKIA);
    QTest::newRow("non-ASCII 1") << property << expectedResult << codec;

    expectedResult = "ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:a=E3=83=8E=E3=82=AD=E3=82=A2=E3=\r\n"
                     "=83=8E=E3=82=AD=E3=82=A2\r\n";
    property = QVersitProperty();
    property.setName(QStringLiteral("ORG"));
    property.setValue("a" + KATAKANA_NOKIA + KATAKANA_NOKIA);
    QTest::newRow("non-ASCII 2") << property << expectedResult << codec;

    expectedResult = "ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:aa=E3=83=8E=E3=82=AD=E3=82=A2=\r\n"
                     "=E3=83=8E=E3=82=AD=E3=82=A2\r\n";
    property = QVersitProperty();
    property.setName(QStringLiteral("ORG"));
    property.setValue("aa" + KATAKANA_NOKIA + KATAKANA_NOKIA);
    QTest::newRow("non-ASCII 3") << property << expectedResult << codec;

    // In Shift-JIS codec.
    QTextCodec* jisCodec = QTextCodec::codecForName("Shift-JIS");
    expectedResult = jisCodec->fromUnicode(
            QStringLiteral("ORG:") + KATAKANA_NOKIA + QStringLiteral("\r\n"));
    property = QVersitProperty();
    property.setName(QStringLiteral("ORG"));
    property.setValue(KATAKANA_NOKIA);
    QTest::newRow("JIS codec") << property << expectedResult << QByteArray("Shift-JIS");
}
Esempio n. 27
0
void tst_QVersitOrganizerExporter::testExport_data()
{
    QTest::addColumn<QList<QOrganizerItem> >("items");
    QTest::addColumn<QVersitDocument>("expectedDocument");

    {
        QVersitDocument document(QVersitDocument::ICalendar20Type);
        document.setComponentType(QLatin1String("VCALENDAR"));
        QVersitDocument nested(QVersitDocument::ICalendar20Type);
        nested.setComponentType(QLatin1String("VEVENT"));
        QVersitProperty property;
        property.setName(QLatin1String("SUMMARY"));
        property.setValue(QLatin1String("Bastille Day Party"));
        nested.addProperty(property);
        property.setName(QLatin1String("DTSTART"));
        property.setValue(QLatin1String("19970714T170000Z"));
        nested.addProperty(property);
        property.setName(QLatin1String("DTEND"));
        property.setValue(QLatin1String("19970715T035959Z"));
        nested.addProperty(property);
        document.addSubDocument(nested);

        nested.clear();
        nested.setType(QVersitDocument::ICalendar20Type);
        nested.setComponentType(QLatin1String("VTODO"));
        property.setName(QLatin1String("SUMMARY"));
        property.setValue(QLatin1String("Take out the garbage"));
        nested.addProperty(property);
        property.setName(QLatin1String("DTSTART"));
        property.setValue(QLatin1String("20100609T080000"));
        nested.addProperty(property);
        property.setName(QLatin1String("DUE"));
        property.setValue(QLatin1String("20100610T080000"));
        nested.addProperty(property);
        document.addSubDocument(nested);

        nested.clear();
        nested.setType(QVersitDocument::ICalendar20Type);
        nested.setComponentType(QLatin1String("VJOURNAL"));
        property.setName(QLatin1String("SUMMARY"));
        property.setValue(QLatin1String("Trip to Thailand"));
        nested.addProperty(property);
        property.setName(QLatin1String("DTSTART"));
        property.setValue(QLatin1String("20100615T112300"));
        nested.addProperty(property);
        document.addSubDocument(nested);

        QOrganizerEvent event;
        event.setDisplayLabel(QLatin1String("Bastille Day Party"));
        event.setStartDateTime(QDateTime(QDate(1997, 7, 14), QTime(17, 0, 0), Qt::UTC));
        event.setEndDateTime(QDateTime(QDate(1997, 7, 15), QTime(3, 59, 59), Qt::UTC));

        QOrganizerTodo todo;
        todo.setDisplayLabel(QLatin1String("Take out the garbage"));
        todo.setStartDateTime(QDateTime(QDate(2010, 6, 9), QTime(8, 0, 0)));
        todo.setDueDateTime(QDateTime(QDate(2010, 6, 10), QTime(8, 0, 0)));

        QOrganizerJournal journal;
        journal.setDisplayLabel(QLatin1String("Trip to Thailand"));
        journal.setDateTime(QDateTime(QDate(2010, 6, 15), QTime(11, 23, 0)));

        QList<QOrganizerItem> items;
        items << static_cast<QOrganizerItem>(event);
        items << static_cast<QOrganizerItem>(todo);
        items << static_cast<QOrganizerItem>(journal);

        QTest::newRow("sample event, todo and journal") << items << document;
    }

    {
        QVersitDocument document(QVersitDocument::ICalendar20Type);
        document.setComponentType(QLatin1String("VCALENDAR"));
        QVersitDocument nested(QVersitDocument::ICalendar20Type);
        nested.setComponentType(QLatin1String("VEVENT"));
        QVersitProperty property;
        property.setName(QLatin1String("UID"));
        property.setValue(QLatin1String("1234"));
        nested.addProperty(property);
        property.setName(QLatin1String("RECURRENCE-ID"));
        property.setValue(QLatin1String("20100608"));
        nested.addProperty(property);
        document.addSubDocument(nested);

        // An event occurrence with OriginalDate and Guid set
        QOrganizerEventOccurrence event;
        event.setGuid(QLatin1String("1234"));
        event.setOriginalDate(QDate(2010, 6, 8));

        QList<QOrganizerItem> items;
        items << static_cast<QOrganizerItem>(event);

        QTest::newRow("event occurrence exception") << items << document;
    }
}
 // ... then, do the rest of the properties.
 foreach (const QVersitProperty& property, properties) {
     QStringList typeParameters = property.parameters().values(QStringLiteral("TYPE"));
     if (!typeParameters.contains(QStringLiteral("PREF"), Qt::CaseInsensitive))
         importProperty(document, property, contactIndex, contact);
 }
Esempio n. 29
0
void SeasidePhotoHandler::propertyProcessed(const QVersitDocument &, const QVersitProperty &property, const QContact &, bool *alreadyProcessed, QList<QContactDetail> * updatedDetails)
{
    // if the property is a PHOTO property, store the data to disk
    // and then create an avatar detail which points to it.
    if (property.name().toLower() != QLatin1String("photo"))
        return;

#ifndef QT_VERSION_5
    // The Qt4 / QtMobility version has QContactThumbnail support.
    // We need to remove any such thumbnail detail from the output,
    // as some backends (such as qtcontacts-sqlite) do not support
    // that detail type.
    for (int i = 0; i < updatedDetails->size(); ++i) {
        if (updatedDetails->at(i).definitionName() == QContactThumbnail::DefinitionName) {
            updatedDetails->removeAt(i);
            --i;
        }
    }
#endif

    // The data might be either a URL, a file path, or encoded image data
    // It's hard to tell what the content is, because versit removes the encoding
    // information in the process of decoding the data...

    // Try to interpret the data as a URL
    QString path(property.variantValue().toString());
    QUrl url(path);
    if (url.isValid()) {
        // Treat remote URL as a true URL, and reference it in the avatar
        if (!url.scheme().isEmpty() && !url.isLocalFile()) {
            QContactAvatar newAvatar;
            newAvatar.setImageUrl(url);
            updatedDetails->append(newAvatar);

            // we have successfully processed this PHOTO property.
            *alreadyProcessed = true;
            return;
        }
    }

    if (!url.isValid()) {
        // See if we can resolve the data as a local file path
        url = QUrl::fromLocalFile(path);
    }

    QByteArray photoData;

    if (url.isValid()) {
        // Try to read the data from the referenced file
        const QString filePath(url.path());
        if (QFile::exists(filePath)) {
            QFile file(filePath);
            if (!file.open(QIODevice::ReadOnly)) {
                qWarning() << "Unable to process photo data as file:" << path;
                return;
            } else {
                photoData = file.readAll();
            }
        }
    }

    if (photoData.isEmpty()) {
        // Try to interpret the encoded property data as the image
        photoData = property.variantValue().toByteArray();
        if (photoData.isEmpty()) {
            qWarning() << "Failed to extract avatar data from vCard PHOTO property";
            return;
        }
    }

    QImage img;
    bool loaded = img.loadFromData(photoData);
    if (!loaded) {
        qWarning() << "Failed to load avatar image from vCard PHOTO data";
        return;
    }

    // We will save the avatar image to disk in the system's data location
    // Since we're importing user data, it should not require privileged access
    const QString subdirectory(QString::fromLatin1(".local/share/system/Contacts/avatars"));
    const QString photoDirPath(QDir::home().filePath(subdirectory));

    // create the photo file dir if it doesn't exist.
    QDir photoDir;
    if (!photoDir.mkpath(photoDirPath)) {
        qWarning() << "Failed to create avatar image directory when loading avatar image from vCard PHOTO data";
        return;
    }

    // construct the filename of the new avatar image.
    QString photoFilePath = QString::fromLatin1(QCryptographicHash::hash(photoData, QCryptographicHash::Md5).toHex());
    photoFilePath = photoDirPath + QDir::separator() + photoFilePath + QString::fromLatin1(".jpg");

    // save the file to disk
    bool saved = img.save(photoFilePath);
    if (!saved) {
        qWarning() << "Failed to save avatar image from vCard PHOTO data to" << photoFilePath;
        return;
    }

    qWarning() << "Successfully saved avatar image from vCard PHOTO data to" << photoFilePath;

    // save the avatar detail - TODO: mark the avatar as "owned by the contact" (remove on delete)
    QContactAvatar newAvatar;
    newAvatar.setImageUrl(QUrl::fromLocalFile(photoFilePath));
    updatedDetails->append(newAvatar);

    // we have successfully processed this PHOTO property.
    *alreadyProcessed = true;
}