Hello community,

here is the log from the commit of package libkgapi for openSUSE:Factory 
checked in at 2012-12-28 22:44:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libkgapi (Old)
 and      /work/SRC/openSUSE:Factory/.libkgapi.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libkgapi", Maintainer is ""

Changes:
--------
--- /work/SRC/openSUSE:Factory/libkgapi/libkgapi.changes        2012-10-24 
07:13:03.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.libkgapi.new/libkgapi.changes   2012-12-28 
22:44:09.000000000 +0100
@@ -1,0 +2,9 @@
+Wed Dec 19 15:17:01 UTC 2012 - [email protected]
+
+- Update to 0.4.4
+  * fixed kde#309314 - Contacts with XML-unsafe characters not stored
+  * fixed kde#309318 - Support custom IM protocols
+  * fixed kde#310177 - Support birthday dates without year specified
+  * fixed kde#310851 - Handle Microsoft TZIDs
+
+-------------------------------------------------------------------

Old:
----
  libkgapi-0.4.3.tar.bz2

New:
----
  libkgapi-0.4.4.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libkgapi.spec ++++++
--- /var/tmp/diff_new_pack.LsuNnI/_old  2012-12-28 22:44:10.000000000 +0100
+++ /var/tmp/diff_new_pack.LsuNnI/_new  2012-12-28 22:44:10.000000000 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           libkgapi
-Version:        0.4.3
+Version:        0.4.4
 Release:        0
 Summary:        Extension for accessing your Google data
 License:        GPL-2.0+

++++++ libkgapi-0.4.3.tar.bz2 -> libkgapi-0.4.4.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkgapi-0.4.3/CMakeLists.txt 
new/libkgapi-0.4.4/CMakeLists.txt
--- old/libkgapi-0.4.3/CMakeLists.txt   2012-10-21 18:11:23.000000000 +0200
+++ new/libkgapi-0.4.4/CMakeLists.txt   2012-12-16 19:23:52.000000000 +0100
@@ -1,8 +1,8 @@
 project(libkgapi)
 set(KGAPI_VERSION_MAJOR 0)
 set(KGAPI_VERSION_MINOR 4)
-set(KGAPI_VERSION_RELEASE 3)
-set(KGAPI_VERSION 0.4.3)
+set(KGAPI_VERSION_RELEASE 4)
+set(KGAPI_VERSION 0.4.4)
 
 cmake_minimum_required(VERSION 2.8)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkgapi-0.4.3/libkgapi/objects/contact.cpp 
new/libkgapi-0.4.4/libkgapi/objects/contact.cpp
--- old/libkgapi-0.4.3/libkgapi/objects/contact.cpp     2012-10-21 
18:11:23.000000000 +0200
+++ new/libkgapi-0.4.4/libkgapi/objects/contact.cpp     2012-12-16 
19:23:52.000000000 +0100
@@ -258,10 +258,10 @@
     case AIM:
         return "AIM";
     default:
-        return "OTHER";
+        return "Other";
     }
 
-    return "OTHER";
+    return "Other";
 }
 
 QString Contact::IMSchemeToProtocolName(const QString& scheme)
@@ -274,11 +274,21 @@
     QString proto;
     if (protocolName.toUpper() == "XMPP") {
         proto = "JABBER";
-    } else {
+    } else if ((protocolName.toUpper() == QLatin1String("ICQ")) ||
+               (protocolName.toUpper() == QLatin1String("GOOGLE_TALK")) ||
+               (protocolName.toUpper() == QLatin1String("QQ")) ||
+               (protocolName.toUpper() == QLatin1String("SKYPE")) ||
+               (protocolName.toUpper() == QLatin1String("YAHOO")) ||
+               (protocolName.toUpper() == QLatin1String("MSN")) ||
+               (protocolName.toUpper() == QLatin1String("AIM")))
+    {
         proto = protocolName.toUpper();
+        return SCHEME_URL + proto;
     }
 
-    return SCHEME_URL + proto;
+    /* If the protocolName is not officially supported by Google, then instead
+     * of full scheme, Google expects just a name of the protocol. */
+    return protocolName;
 }
 
 Contact::IMProtocol Contact::IMSchemeToProtocol(const QString& scheme)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkgapi-0.4.3/libkgapi/services/calendar.cpp 
new/libkgapi-0.4.4/libkgapi/services/calendar.cpp
--- old/libkgapi-0.4.3/libkgapi/services/calendar.cpp   2012-10-21 
18:11:23.000000000 +0200
+++ new/libkgapi-0.4.4/libkgapi/services/calendar.cpp   2012-12-16 
19:23:52.000000000 +0100
@@ -63,6 +63,22 @@
     static KGAPI::Object* JSONToEvent(const QVariantMap& event);
     static QVariantMap eventToJSON(KGAPI::Object *event);
     static QList<KGAPI::Object*> parseEventJSONFeed(const QVariantList& feed);
+
+    /**
+     * Checks whether TZID is in Olson format and converts it to it if 
neccessary
+     *
+     * This is mainly to handle crazy Microsoft TZIDs like
+     * "(GMT) Greenwich Mean Time/Dublin/Edinburgh/London", because Google only
+     * accepts TZIDs in Olson format ("Europe/London").
+     *
+     * It first tries to match the given \p tzid to all TZIDs in 
KTimeZones::zones().
+     * If it fails, it parses the \p event, looking for X-MICROSOFT-CDO-TZID
+     * property and than matches it to Olson-formatted TZID using a table.
+     *
+     * When the method fails to process the TZID, it returns the original \p 
tzid
+     * in hope, that Google will cope with it.
+     */
+    static QString checkAndConverCDOTZID(const QString &tzid, const 
Objects::Event *event);
 };
 
 }
@@ -627,7 +643,7 @@
         tzStart = KTimeZone::utc().name();
     }
     if (!tzStart.isEmpty()) {
-        start["timeZone"] = tzStart;
+        start["timeZone"] = checkAndConverCDOTZID(tzStart, object);
     }
     data["start"] = start;
 
@@ -646,7 +662,7 @@
         tzEnd = KTimeZone::utc().name();
     }
     if (!tzEnd.isEmpty()) {
-        end["timeZone"] = tzEnd;
+        end["timeZone"] = checkAndConverCDOTZID(tzEnd, object);
     }
     data["end"] = end;
 
@@ -794,3 +810,176 @@
 
     return list;
 }
+
+
+static QMap<int, QString> initMSCDOTZIDTable()
+{
+    QMap<int, QString> map;
+
+    /* Based on "Time Zone to CdoTimeZoneId Map"
+     * http://msdn.microsoft.com/en-us/library/aa563018%28loband%29.aspx
+     *
+     * The mapping is not exact, since the CdoTimeZoneId usually refers to a
+     * region of multiple countries, so I always picked one of the countries
+     * in the specified region and used it's TZID.
+     */
+    map.insert(0,  QLatin1String("UTC"));
+    map.insert(1,  QLatin1String("Europe/London"));                       /* 
GMT Greenwich Mean Time; Dublin, Edinburgh, London */
+    /* Seriously? *sigh* Let's handle these two in checkAndConvertCDOTZID() */
+    //map.insertMulti(2, QLatin1String("Europe/Lisbon"));         /* GMT 
Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London */
+    //map.insertMulti(2, QLatin1String("Europe/Sarajevo"));       /* GMT+01:00 
Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb */
+    map.insert(3,  QLatin1String("Europe/Paris"));              /* GMT+01:00 
Paris, Madrid, Brussels, Copenhagen */
+    map.insert(4,  QLatin1String("Europe/Berlin"));             /* GMT+01:00 
Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna */
+    map.insert(5,  QLatin1String("Europe/Bucharest"));          /* GMT+02:00 
Bucharest */
+    map.insert(6,  QLatin1String("Europe/Prague"));             /* GMT+01:00 
Prague, Central Europe */
+    map.insert(7,  QLatin1String("Europe/Athens"));             /* GMT+02:00 
Athens, Istanbul, Minsk */
+    map.insert(8,  QLatin1String("America/Brazil"));            /* GMT-03:00 
Brasilia */
+    map.insert(9,  QLatin1String("America/Halifax"));           /* GMT-04:00 
Atlantic time (Canada) */
+    map.insert(10, QLatin1String("America/New_York"));          /* GMT-05:00 
Eastern Time (US & Canada) */
+    map.insert(11, QLatin1String("America/Chicago"));           /* GMT-06:00 
Central Time (US & Canada) */
+    map.insert(12, QLatin1String("America/Denver"));            /* GMT-07:00 
Mountain Time (US & Canada) */
+    map.insert(13, QLatin1String("America/Los_Angeles"));       /* GMT-08:00 
Pacific Time (US & Canada); Tijuana */
+    map.insert(14, QLatin1String("America/Anchorage"));         /* GMT-09:00 
Alaska */
+    map.insert(15, QLatin1String("Pacific/Honolulu"));          /* GMT-10:00 
Hawaii */
+    map.insert(16, QLatin1String("Pacific/Apia"));              /* GMT-11:00 
Midway Island, Samoa */
+    map.insert(17, QLatin1String("Pacific/Auckland"));          /* GMT+12:00 
Auckland, Wellington */
+    map.insert(18, QLatin1String("Australia/Brisbane"));        /* GMT+10:00 
Brisbane, East Australia */
+    map.insert(19, QLatin1String("Australia/Adelaide"));        /* GMT+09:30 
Adelaide, Central Australia */
+    map.insert(20, QLatin1String("Asia/Tokyo"));                /* GMT+09:00 
Osaka, Sapporo, Tokyo */
+    map.insert(21, QLatin1String("Asia/Singapore"));            /* GMT+08:00 
Kuala Lumpur, Singapore */
+    map.insert(22, QLatin1String("Asia/Bangkok"));              /* GMT+07:00 
Bangkok, Hanoi, Jakarta */
+    map.insert(23, QLatin1String("Asia/Calcutta"));             /* GMT+05:30 
Kolkata, Chennai, Mumbai, New Delhi, India Standard Time */
+    map.insert(24, QLatin1String("Asia/Dubai"));                /* GMT+04:00 
Abu Dhabi, Muscat */
+    map.insert(25, QLatin1String("Asia/Tehran"));               /* GMT+03:30 
Tehran */
+    map.insert(26, QLatin1String("Asia/Baghdad"));              /* GMT+03:00 
Baghdad */
+    map.insert(27, QLatin1String("Asia/Jerusalem"));            /* GMT+02:00 
Israel, Jerusalem Standard Time */
+    map.insert(28, QLatin1String("America/St_Johns"));          /* GMT-03:30 
Newfoundland */
+    map.insert(29, QLatin1String("Atlantic/Portugal"));         /* GMT-01:00 
Azores */
+    map.insert(30, QLatin1String("America/Noronha"));           /* GMT-02:00 
Mid-Atlantic */
+    map.insert(31, QLatin1String("Africa/Monrovia"));           /* GMT 
Casablanca, Monrovia */
+    map.insert(32, QLatin1String("America/Argentina/Buenos_Aires")); /* 
GMT-03:00 Buenos Aires, Georgetown */
+    map.insert(33, QLatin1String("America/La_Paz"));            /* GMT-04:00 
Caracas, La Paz */
+    map.insert(34, QLatin1String("America/New_York"));          /* GMT-05:00 
Indiana (East) */
+    map.insert(35, QLatin1String("America/Bogota"));            /* GMT-05:00 
Bogota, Lima, Quito */
+    map.insert(36, QLatin1String("America/Winnipeg"));          /* GMT-06:00 
Saskatchewan */
+    map.insert(37, QLatin1String("America/Mexico_City"));       /* GMT-06:00 
Mexico City, Tegucigalpa */
+    map.insert(38, QLatin1String("America/Phoenix"));           /* GMT-07:00 
Arizona */
+    map.insert(39, QLatin1String("Pacific/Kwajalein"));         /* GMT-12:00 
Eniwetok, Kwajalein, Dateline Time */
+    map.insert(40, QLatin1String("Pacific/Fiji"));              /* GMT+12:00 
Fušál, Kamchatka, Mashall Is. */
+    map.insert(41, QLatin1String("Pacific/Noumea"));            /* GMT+11:00 
Magadan, Solomon Is., New Caledonia */
+    map.insert(42, QLatin1String("Australia/Hobart"));          /* GMT+10:00 
Hobart, Tasmania */
+    map.insert(43, QLatin1String("Pacific/Guam"));              /* GMT+10:00 
Guam, Port Moresby */
+    map.insert(44, QLatin1String("Australia/Darwin"));          /* GMT+09:30 
Darwin */
+    map.insert(45, QLatin1String("Asia/Shanghai"));             /* GMT+08:00 
Beijing, Chongqing, Hong Kong SAR, Urumqi */
+    map.insert(46, QLatin1String("Asia/Omsk"));                 /* GMT+06:00 
Almaty, Novosibirsk, North Central Asia */
+    map.insert(47, QLatin1String("Asia/Karachi"));              /* GMT+05:00 
Islamabad, Karachi, Tashkent */
+    map.insert(48, QLatin1String("Asia/Kabul"));                /* GMT+04:30 
Kabul */
+    map.insert(49, QLatin1String("Africa/Cairo"));              /* GMT+02:00 
Cairo */
+    map.insert(50, QLatin1String("Africa/Harare"));             /* GMT+02:00 
Harare, Pretoria */
+    map.insert(51, QLatin1String("Europe/Moscow"));             /* GMT+03:00 
Moscow, St. Petersburg, Volgograd */
+    map.insert(53, QLatin1String("Atlantic/Cape_Verde"));       /* GMT-01:00 
Cape Verde Is. */
+    map.insert(54, QLatin1String("Asia/Tbilisi"));              /* GMT+04:00 
Baku, Tbilisi, Yerevan */
+    map.insert(55, QLatin1String("America/Tegucigalpa"));       /* GMT-06:00 
Central America */
+    map.insert(56, QLatin1String("Africa/Nairobi"));            /* GMT+03:00 
East Africa, Nairobi */
+    map.insert(58, QLatin1String("Asia/Yekaterinburg"));        /* GMT+05:00 
Ekaterinburg */
+    map.insert(59, QLatin1String("Europe/Helsinki"));           /* GMT+02:00 
Helsinki, Riga, Tallinn */
+    map.insert(60, QLatin1String("America/Greenland"));         /* GMT-03:00 
Greenland */
+    map.insert(61, QLatin1String("Asia/Rangoon"));              /* GMT+06:30 
Yangon (Rangoon) */
+    map.insert(62, QLatin1String("Asia/Katmandu"));             /* GMT+05:45 
Kathmandu, Nepal */
+    map.insert(63, QLatin1String("Asia/Irkutsk"));              /* GMT+08:00 
Irkutsk, Ulaan Bataar */
+    map.insert(64, QLatin1String("Asia/Krasnoyarsk"));          /* GMT+07:00 
Krasnoyarsk */
+    map.insert(65, QLatin1String("America/Santiago"));          /* GMT-04:00 
Santiago */
+    map.insert(66, QLatin1String("Asia/Colombo"));              /* GMT+06:00 
Sri Jayawardenepura, Sri Lanka */
+    map.insert(67, QLatin1String("Pacific/Tongatapu"));         /* GMT+13:00 
Nuku'alofa, Tonga */
+    map.insert(68, QLatin1String("Asia/Vladivostok"));          /* GMT+10:00 
Vladivostok */
+    map.insert(69, QLatin1String("Africa/Bangui"));             /* GMT+01:00 
West Central Africa */
+    map.insert(70, QLatin1String("Asia/Yakutsk"));              /* GMT+09:00 
Yakutsk */
+    map.insert(71, QLatin1String("Asia/Dhaka"));                /* GMT+06:00 
Astana, Dhaka */
+    map.insert(72, QLatin1String("Asia/Seoul"));                /* GMT+09:00 
Seoul, Korea Standard time */
+    map.insert(73, QLatin1String("Australia/Perth"));           /* GMT+08:00 
Perth, Western Australia */
+    map.insert(74, QLatin1String("Asia/Kuwait"));               /* GMT+03:00 
Arab, Kuwait, Riyadh */
+    map.insert(75, QLatin1String("Asia/Taipei"));               /* GMT+08:00 
Taipei */
+    map.insert(76, QLatin1String("Australia/Sydney"));          /* GMT+10:00 
Canberra, Melbourne, Sydney */
+
+    return map;
+}
+
+static const QMap<int, QString> MSCDOTZIDTable = initMSCDOTZIDTable();
+
+QString Services::CalendarPrivate::checkAndConverCDOTZID(const QString& tzid, 
const Objects::Event* event)
+{
+    /* Try to match the @tzid to any valid timezone we know. */
+    KTimeZones timeZones;
+    const KTimeZones::ZoneMap zones = timeZones.zones();
+    KTimeZones::ZoneMap::const_iterator iter;
+    for (iter = zones.constBegin(); iter != zones.constEnd(); iter++) {
+        if (iter.key() == tzid) {
+            /* Yay, @tzid is a valid TZID in Olson format */
+            return tzid;
+        }
+    }
+
+    /* Damn, no match. Parse the iCal and try to find X-MICROSOFT-CDO-TZID
+     * property that we can match against the MSCDOTZIDTable */
+    ICalFormat format;
+    /* Use a copy of @event, otherwise it would be deleted when ptr is 
destroyed */
+    Incidence::Ptr ptr(new Event(*dynamic_cast<const Event*>(event)));
+    const QString vcard = format.toICalString(ptr);
+    const QStringList properties = vcard.split(QLatin1Char('\n'));
+    int CDOId = -1;
+    Q_FOREACH(const QString &property, properties) {
+        if (property.startsWith(QLatin1String("X-MICROSOFT-CDO-TZID"))) {
+            QStringList parsed = property.split(QLatin1Char(':'));
+            if (parsed.length() != 2) {
+                /* Fail */
+                return tzid;
+            }
+
+            CDOId = parsed.at(1).toInt();
+            break;
+        }
+    }
+
+    /* Wheeee, we have X-MICROSOFT-CDO-TZID, try to map it to Olson format */
+    if (CDOId > -1) {
+
+        /* *sigh* Some expert in MS assigned the same ID to two two different 
timezones... */
+        if (CDOId == 2) {
+
+            /* GMT Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London */
+            if (tzid.contains(QLatin1String("Dublin")) ||
+                tzid.contains(QLatin1String("Edinburgh")) ||
+                tzid.contains(QLatin1String("Lisbon")) ||
+                tzid.contains(QLatin1String("London")))
+            {
+                return QLatin1String("Europe/London");
+            }
+
+            /* GMT+01:00 Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb */
+            else if (tzid.contains(QLatin1String("Sarajevo")) ||
+                     tzid.contains(QLatin1String("Skopje")) ||
+                     tzid.contains(QLatin1String("Sofija")) ||
+                     tzid.contains(QLatin1String("Vilnius")) ||
+                     tzid.contains(QLatin1String("Warsaw")) ||
+                     tzid.contains(QLatin1String("Zagreb")))
+            {
+                return QLatin1String("Europe/Sarajevo");
+            }
+
+            /* This should never ever happen. I don't know what to do if it 
happens */
+            else {
+                return tzid;
+            }
+        }
+
+        if (MSCDOTZIDTable.contains(CDOId)) {
+            return MSCDOTZIDTable.value(CDOId);
+        }
+    }
+
+    /* Fail. The event does not have a valid timezone, does not contain
+     * X-MICROSOFT-CDO-TZID or we failed to map the CDO-TZID to a real 
timezone.
+     * We are screwed. Just return the original TZID and hope Google will 
accept it
+     * (though we know they won't) */
+    return tzid;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libkgapi-0.4.3/libkgapi/services/contacts.cpp 
new/libkgapi-0.4.4/libkgapi/services/contacts.cpp
--- old/libkgapi-0.4.3/libkgapi/services/contacts.cpp   2012-10-21 
18:11:23.000000000 +0200
+++ new/libkgapi-0.4.4/libkgapi/services/contacts.cpp   2012-12-16 
19:23:52.000000000 +0100
@@ -22,6 +22,9 @@
 
 #include <kurl.h>
 
+/* Qt::escape() */
+#include <QTextDocument>
+
 #include <qjson/parser.h>
 #include <qjson/serializer.h>
 
@@ -527,8 +530,16 @@
 
     /* Birthday */
     QVariantMap bDay = data["gContact$birthday"].toMap();
-    if (!bDay.isEmpty())
-        object->setBirthday(QDateTime::fromString(bDay["when"].toString(), 
"yyyy-MM-dd"));
+    if (!bDay.isEmpty()) {
+       QString birthday = bDay["when"].toString();
+       /* Birthdays in format "--MM-DD" are valid and mean that no year has
+         * been specified. Since KABC does not support birthdays without year,
+         * we simulate that by specifying a fake year - 1900 */
+       if (birthday.startsWith("--")) {
+           birthday = "1900" + birthday.mid(1);
+       }
+        object->setBirthday(QDateTime::fromString(birthday, "yyyy-MM-dd"));
+    }
 
     /* User-defined fields */
     QVariantList userDefined = data["gContact$userDefinedField"].toList();
@@ -562,34 +573,34 @@
     /* Name */
     output.append("<gd:name>");
     if (!contact->givenName().isEmpty())
-        
output.append("<gd:givenName>").append(contact->givenName().toUtf8()).append("</gd:givenName>");
+        
output.append("<gd:givenName>").append(Qt::escape(contact->givenName()).toUtf8()).append("</gd:givenName>");
     if (!contact->familyName().isEmpty())
-        
output.append("<gd:familyName>").append(contact->familyName().toUtf8()).append("</gd:familyName>");
+        
output.append("<gd:familyName>").append(Qt::escape(contact->familyName()).toUtf8()).append("</gd:familyName>");
     if (!contact->assembledName().isEmpty())
-        
output.append("<gd:fullName>").append(contact->formattedName().toUtf8()).append("</gd:fullName>");
+        
output.append("<gd:fullName>").append(Qt::escape(contact->formattedName()).toUtf8()).append("</gd:fullName>");
     if (!contact->additionalName().isEmpty())
-        
output.append("<gd:additionalName>").append(contact->additionalName().toUtf8()).append("</gd:additionalName>");
+        
output.append("<gd:additionalName>").append(Qt::escape(contact->additionalName()).toUtf8()).append("</gd:additionalName>");
     if (!contact->prefix().isEmpty())
-        
output.append("<gd:namePrefix>").append(contact->prefix().toUtf8()).append("</gd:namePrefix>");
+        
output.append("<gd:namePrefix>").append(Qt::escape(contact->prefix()).toUtf8()).append("</gd:namePrefix>");
     if (!contact->suffix().isEmpty())
-        
output.append("<gd:nameSuffix>").append(contact->suffix().toUtf8()).append("</gd:nameSuffix>");
+        
output.append("<gd:nameSuffix>").append(Qt::escape(contact->suffix()).toUtf8()).append("</gd:nameSuffix>");
     output.append("</gd:name>");
 
     /* Notes */
     if (!contact->note().isEmpty())
-        output.append("<atom:content 
type='text'>").append(contact->note().toUtf8()).append("</atom:content>");
+        output.append("<atom:content 
type='text'>").append(Qt::escape(contact->note()).toUtf8()).append("</atom:content>");
 
     /* Organization (work) */
     QByteArray org;
     QString office = contact->office();
     if (!contact->organization().isEmpty())
-        
org.append("<gd:orgName>").append(contact->organization().toUtf8()).append("</gd:orgName>");
+        
org.append("<gd:orgName>").append(Qt::escape(contact->organization()).toUtf8()).append("</gd:orgName>");
     if (!contact->department().isEmpty())
-        
org.append("<gd:orgDepartment>").append(contact->department().toUtf8()).append("</gd:orgDepartment>");
+        
org.append("<gd:orgDepartment>").append(Qt::escape(contact->department()).toUtf8()).append("</gd:orgDepartment>");
     if (!contact->title().isEmpty())
-        
org.append("<gd:orgTitle>").append(contact->title().toUtf8()).append("</gd:orgTitle>");
+        
org.append("<gd:orgTitle>").append(Qt::escape(contact->title()).toUtf8()).append("</gd:orgTitle>");
     if (!office.isEmpty()) {
-        org.append("<gd:where>").append(office.toUtf8()).append("</gd:where>");
+        
org.append("<gd:where>").append(Qt::escape(office).toUtf8()).append("</gd:where>");
         parsedCustoms << "KADDRESSBOOK-X-Office";
     }
     if (!org.isEmpty())
@@ -597,56 +608,56 @@
 
     /* Nickname */
     if (!contact->nickName().isEmpty())
-        
output.append("<gContact:nickname>").append(contact->nickName().toUtf8()).append("</gContact:nickname>");
+        
output.append("<gContact:nickname>").append(Qt::escape(contact->nickName()).toUtf8()).append("</gContact:nickname>");
 
     /* Occupation */
     if (!contact->profession().isEmpty()) {
-        
output.append("<gContact:occupation>").append(contact->profession().toUtf8()).append("</gContact:occupation>");
+        
output.append("<gContact:occupation>").append(Qt::escape(contact->profession()).toUtf8()).append("</gContact:occupation>");
         parsedCustoms << "KADDRESSBOOK-X-Profession";
     }
 
     /* Spouse */
     QString spouse = contact->spousesName();
     if (!spouse.isEmpty()) {
-        output.append("<gContact:relation 
rel=\"spouse\">").append(spouse.toUtf8()).append("</gContact:relation>");
+        output.append("<gContact:relation 
rel=\"spouse\">").append(Qt::escape(spouse).toUtf8()).append("</gContact:relation>");
         parsedCustoms << "KADDRESSBOOK-X-SpousesName";
     }
 
     /* Manager */
     QString manager = contact->managersName();
     if (!manager.isEmpty()) {
-        output.append("<gContact:relation 
rel=\"manager\">").append(manager.toUtf8()).append("</gContact:relation>");
+        output.append("<gContact:relation 
rel=\"manager\">").append(Qt::escape(manager).toUtf8()).append("</gContact:relation>");
         parsedCustoms << "KADDRESSBOOK-X-ManagersName";
     }
 
     /* Assistant */
     QString assistant = contact->assistantsName();
     if (!assistant.isEmpty()) {
-        output.append("<gContact:relation 
rel=\"assistant\">").append(assistant.toUtf8()).append("</gContact:relation>");
+        output.append("<gContact:relation 
rel=\"assistant\">").append(Qt::escape(assistant).toUtf8()).append("</gContact:relation>");
         parsedCustoms << "KADDRESSBOOK-X-AssistantsName";
     }
 
     /* Anniversary */
     QString anniversary = contact->anniversary();
     if (!anniversary.isEmpty()) {
-        output.append("<gContact:event rel=\"anniversary\"><gd:when 
startTime=\"").append(anniversary.toUtf8()).append("\" /></gContact:event>");
+        output.append("<gContact:event rel=\"anniversary\"><gd:when 
startTime=\"").append(Qt::escape(anniversary).toUtf8()).append("\" 
/></gContact:event>");
         parsedCustoms << "KADDRESSBOOK-X-Anniversary";
     }
 
     /* Homepage */
     if (!contact->url().isEmpty())
-        output.append("<gContact:website rel=\"home-page\" 
href=\"").append(contact->url().prettyUrl().toUtf8()).append("\" />");
+        output.append("<gContact:website rel=\"home-page\" 
href=\"").append(Qt::escape(contact->url().prettyUrl()).toUtf8()).append("\" 
/>");
 
     /* Blog */
     QString blog = contact->blogFeed();
     if (!blog.isEmpty()) {
-        output.append("<gContact:website rel=\"blog\" 
href=\"").append(blog.toUtf8()).append("\" />");
+        output.append("<gContact:website rel=\"blog\" 
href=\"").append(Qt::escape(blog).toUtf8()).append("\" />");
         parsedCustoms << "KADDRESSBOOK-BlogFeed";
     }
 
     /* Emails */
     Q_FOREACH(const QString &email, contact->emails()) {
-        output.append("<gd:email rel='http://schemas.google.com/g/2005#home' 
address='").append(email.toUtf8()).append("' />");
+        output.append("<gd:email rel='http://schemas.google.com/g/2005#home' 
address='").append(Qt::escape(email).toUtf8()).append("' />");
     }
 
     /* IMs */
@@ -660,6 +671,17 @@
             bool primary = (contact->custom("KADDRESSBOOK", "X-IMAddress") == 
value);
             output.append(im_str.arg(value, 
Objects::Contact::IMProtocolNameToScheme(proto), (primary ? "true" : 
"false")).toUtf8());
             parsedCustoms << key;
+        /* X-messaging is probably a new key (?) used by KAddressbook when 
importing
+         * contacts from vCard. */
+        } else if (im.startsWith(QLatin1String("X-messaging"))) {
+            const QString key = im.left(im.indexOf(QLatin1Char(':')));
+            const QString value = im.mid(im.indexOf(QLatin1Char(':')) + 1);
+            QString proto = key.mid(12); /* strlen("X-messaging/") */
+            if (proto.endsWith(QLatin1String("-All"))) {
+                proto.chop(4);
+            }
+            output.append(im_str.arg(value, proto, 
QLatin1String("false")).toUtf8());
+            parsedCustoms << key;
         }
     }
     parsedCustoms << "KADDRESSBOOK-X-IMAddress";
@@ -677,24 +699,33 @@
         .append("'>");
 
         if (!address.locality().isEmpty())
-            
output.append("<gd:city>").append(address.locality().toUtf8()).append("</gd:city>");
+            
output.append("<gd:city>").append(Qt::escape(address.locality()).toUtf8()).append("</gd:city>");
         if (!address.street().isEmpty())
-            
output.append("<gd:street>").append(address.street().toUtf8()).append("</gd:street>");
+            
output.append("<gd:street>").append(Qt::escape(address.street()).toUtf8()).append("</gd:street>");
         if (!address.region().isEmpty())
-            
output.append("<gd:region>").append(address.region().toUtf8()).append("</gd:region>");
+            
output.append("<gd:region>").append(Qt::escape(address.region()).toUtf8()).append("</gd:region>");
         if (!address.postalCode().isEmpty())
-            
output.append("<gd:postcode>").append(address.postalCode().toUtf8()).append("</gd:postcode>");
+            
output.append("<gd:postcode>").append(Qt::escape(address.postalCode()).toUtf8()).append("</gd:postcode>");
         if (!address.country().isEmpty())
-            
output.append("<gd:country>").append(address.country().toUtf8()).append("</gd:country>");
+            
output.append("<gd:country>").append(Qt::escape(address.country()).toUtf8()).append("</gd:country>");
         if (!address.formattedAddress().isEmpty())
-            
output.append("<gd:formattedAddress>").append(address.formattedAddress().toUtf8()).append("</gd:formattedAddress>");
+            
output.append("<gd:formattedAddress>").append(Qt::escape(address.formattedAddress()).toUtf8()).append("</gd:formattedAddress>");
         output.append("</gd:structuredPostalAddress>");
     }
 
     /* Birthday */
     QDate birthday = contact->birthday().date();
     if (birthday.isValid()) {
-        QString birthdayStr = birthday.toString("yyyy-MM-dd");
+       QString birthdayStr;
+       /* We use year 1900 as a fake year for birthdays without a year 
specified.
+        * Here we assume that nobody actually has a contact born in 1900 and so
+        * we replace 1900 by "-", so that we get "--MM-dd" date, which is a 
valid
+        * birthday date according to RFC6350 */
+       if (birthday.year() == 1900) {
+           birthdayStr = birthday.toString("--MM-dd");
+       } else {
+           birthdayStr = birthday.toString("yyyy-MM-dd");
+       }
         output.append("<gContact:birthday 
when='").append(birthdayStr.toUtf8()).append("'/>");
     }
 
@@ -715,7 +746,7 @@
         QString key = customStr.left(customStr.indexOf(':'));
         if (!parsedCustoms.contains(key)) {
             QString value = customStr.mid(customStr.indexOf(':') + 1);
-            output.append(defined_str.arg(key, value).toUtf8());
+            output.append(defined_str.arg(Qt::escape(key), 
Qt::escape(value)).toUtf8());
         }
     }
 
@@ -730,8 +761,8 @@
 
     output.append("<atom:category 
scheme=\"http://schemas.google.com/g/2005#kind\"; "
                   "term=\"http://schemas.google.com/g/2005#group\"/>");
-    output.append("<atom:title 
type=\"text\">").append(group->title().toUtf8()).append("</atom:title>");
-    output.append("<atom:content 
type=\"text\">").append(group->content().toUtf8()).append("</atom:content>");
+    output.append("<atom:title 
type=\"text\">").append(Qt::escape(group->title()).toUtf8()).append("</atom:title>");
+    output.append("<atom:content 
type=\"text\">").append(Qt::escape(group->content()).toUtf8()).append("</atom:content>");
 
     return output;
 }
@@ -979,7 +1010,14 @@
 
         /* Birthday */
         if (e.tagName() == "gContact:birthday") {
-            contact->setBirthday(QDateTime::fromString(e.attribute("when"), 
"yyyy-MM-dd"));
+           QString birthday = e.attribute("when");
+           /* Birthdays in format "--MM-DD" are valid and mean that no year has
+            * been specified. Since KABC does not support birthdays without 
year,
+            * we simulate that by specifying a fake year - 1900 */
+           if (birthday.startsWith("--")) {
+               birthday = "1900" + birthday.mid(1);
+           }
+            contact->setBirthday(QDateTime::fromString(birthday, 
"yyyy-MM-dd"));
             continue;
         }
 

-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to